Refactor /img to be more modular

This commit is contained in:
UnknownShadow200 2017-02-16 15:14:03 +11:00
parent e3500790f1
commit bddec7cca6
6 changed files with 260 additions and 275 deletions

View File

@ -41,44 +41,36 @@ namespace MCGalaxy.Commands.Building {
public override void Use(Player p, string message) { public override void Use(Player p, string message) {
if (!Directory.Exists("extra/images/")) if (!Directory.Exists("extra/images/"))
Directory.CreateDirectory("extra/images/"); Directory.CreateDirectory("extra/images/");
bool layer = false;
byte popType = 1;
string bitmapLoc = null;
if (message == "") { Help(p); return; } if (message == "") { Help(p); return; }
string[] parts = message.SplitSpaces(3);
if (message.IndexOf(' ') != -1) {
string[] args = message.Split(' ');
for (int i = 0; i < args.Length; i++)
{
if (args[i] == "layer" || args[i] == "l") layer = true;
else if (args[i] == "1" || args[i] == "2color") popType = 1;
else if (args[i] == "2" || args[i] == "1color") popType = 2;
else if (args[i] == "3" || args[i] == "2gray") popType = 3;
else if (args[i] == "4" || args[i] == "1gray") popType = 4;
else if (args[i] == "5" || args[i] == "bw") popType = 5;
else if (args[i] == "6" || args[i] == "gray") popType = 6;
}
message = args[args.Length - 1];
}
if (message.IndexOf('/') == -1 && message.IndexOf('.') != -1) {
if (!DownloadWebFile("http://www.imgur.com/" + message, p)) return;
bitmapLoc = "tempImage_" + p.name;
} else if (message.IndexOf('.') != -1) {
if (!DownloadWebFile(message, p)) return;
bitmapLoc = "tempImage_" + p.name;
} else {
bitmapLoc = message;
}
if (!File.Exists("extra/images/" + bitmapLoc + ".bmp")) {
Player.Message(p, "The URL entered was invalid!"); return;
}
DrawArgs dArgs = default(DrawArgs); DrawArgs dArgs = default(DrawArgs);
dArgs.layer = layer; dArgs.palette = ImagePalette.Palettes[0];
dArgs.name = bitmapLoc; dArgs.dualLayered = true;
dArgs.popType = popType;
if (parts.Length == 3) {
string mode = parts[2];
if (mode.CaselessEq("horizontal")) dArgs.layer = true;
if (mode.CaselessEq("vertical")) dArgs.dualLayered = false;
}
if (parts.Length >= 2) {
dArgs.palette = ImagePalette.Find(parts[1]);
if (dArgs.palette == null) {
Player.Message(p, "Palette {0} not found.", parts[1]); return;
}
}
if (message.IndexOf('.') != -1) {
if (!DownloadWebFile(message, p)) return;
dArgs.name = "tempImage_" + p.name;
} else {
dArgs.name = message;
}
if (!File.Exists("extra/images/" + dArgs.name + ".bmp")) {
Player.Message(p, "The URL entered was invalid!"); return;
}
Player.Message(p, "Place two blocks to determine direction."); Player.Message(p, "Place two blocks to determine direction.");
p.MakeSelection(2, dArgs, DoImage); p.MakeSelection(2, dArgs, DoImage);
} }
@ -123,30 +115,20 @@ namespace MCGalaxy.Commands.Building {
} }
op.SetLevel(p.level); op.SetLevel(p.level);
op.Player = p; op.Player = p; op.Source = bmp;
op.Source = bmp; op.LayerMode = dArgs.layer; op.DualLayer = dArgs.dualLayered;
op.Layer = dArgs.layer; op.Palette = dArgs.palette; op.Filename = dArgs.name;
op.Mode = dArgs.popType;
op.Filename = dArgs.name;
if (op.Layer) {
if (op.Mode == 1) op.Mode = 2;
if (op.Mode == 3) op.Mode = 4;
}
DrawOpPerformer.Do(op, null, p, m, false); DrawOpPerformer.Do(op, null, p, m, false);
} }
public override void Help(Player p) { public override void Help(Player p) {
Player.Message(p, "/imageprint <switch> <localfile> - Print local file in extra/images/ folder. Must be type .bmp, type filename without extension."); Player.Message(p, "%T/imageprint [file] [palette] <mode> %H- Prints image in extra/images/ folder. (File must have .bmp extension)");
Player.Message(p, "/imageprint <switch> <imgurfile.extension> - Print IMGUR stored file. Example: /i piCCm.gif will print www.imgur.com/piCCm.gif. Case-sensitive"); Player.Message(p, "%T/imageprint [url] [palette] <mode> %H- Downloads then prints the given image. (transparency supported)");
Player.Message(p, "/imageprint <switch> <webfile> - Print web file in format domain.com/folder/image.jpg. Does not need http:// or www."); Player.Message(p, "%HPalettes: &f{0}", ImagePalette.Palettes.Join(pal => pal.Name));
Player.Message(p, "Available switches: (&f1%S) 2-Layer Color image, (&f2%S) 1-Layer Color Image, " + Player.Message(p, "%HModes: &fVertical, Vertical2Layer, Horizontal");
"(&f3%S) 2-Layer Grayscale, (&f4%S) 1-Layer Grayscale, (%f5%S) Black and White, (&f6%S) Mathematical Grayscale");
Player.Message(p, "Local filetypes: .bmp. Remote Filetypes: .gif .png .jpg .bmp. PNG and GIF may use transparency");
Player.Message(p, "Use switch (&flayer%S) or (&fl%S) to print horizontally.");
} }
struct DrawArgs { public bool layer; public byte popType; public string name; } struct DrawArgs { public bool layer, dualLayered; public ImagePalette palette; public string name; }
} }
} }

View File

@ -20,68 +20,70 @@ using System.Drawing;
namespace MCGalaxy.Drawing { namespace MCGalaxy.Drawing {
public interface IPalette { public interface IPaletteMatcher {
/// <summary> Sets the blocks available for this palette to pick from. </summary> /// <summary> Sets the palette of blocks used to match colours from. </summary>
void SetAvailableBlocks(PaletteEntry[] blocks); void SetPalette(ImagePalette palette);
/// <summary> Returns the best matching block for the given color, /// <summary> Returns the best matching block for the given color,
/// based on this palette's colourspace. </summary> /// based on this palette's colourspace. </summary>
byte BestMatch(byte R, byte G, byte B, out int position); byte BestMatch(byte R, byte G, byte B);
/// <summary> Returns the best matching block for the given color,
/// based on this palette's colourspace. </summary>
byte BestMatch(byte R, byte G, byte B, out bool backLayer);
} }
public sealed class GrayscalePalette : IPalette { public sealed class RgbPaletteMatcher : IPaletteMatcher {
public void SetAvailableBlocks(PaletteEntry[] blocks) { } ImagePalette palette;
public void SetPalette(ImagePalette palette) {
this.palette = palette;
}
public byte BestMatch(byte R, byte G, byte B, out int position) { public byte BestMatch(byte R, byte G, byte B) {
int brightness = (R + G + B) / 3; position = -1; int pos;
if (brightness < (256 / 4)) MinDist(R, G, B, palette.FrontLayer, out pos);
return Block.obsidian; return palette.FrontLayer[pos].Block;
else if (brightness >= (256 / 4) && brightness < (256 / 4) * 2) }
return Block.darkgrey;
else if (brightness >= (256 / 4) * 2 && brightness < (256 / 4) * 3) public byte BestMatch(byte R, byte G, byte B, out bool backLayer) {
return Block.lightgrey; int frontPos, backPos;
else int frontDist = MinDist(R, G, B, palette.FrontLayer, out frontPos);
return Block.white; int backDist = MinDist(R, G, B, palette.BackLayer, out backPos);
backLayer = backDist <= frontDist;
return backLayer ? palette.BackLayer[backPos].Block : palette.FrontLayer[frontPos].Block;
}
static int MinDist(byte R, byte G, byte B, PaletteEntry[] entries, out int pos) {
int minDist = int.MaxValue; pos = 0;
for (int i = 0; i < entries.Length; i++) {
PaletteEntry entry = entries[i];
int dist = (R - entry.R) * (R - entry.R)
+ (G - entry.G) * (G - entry.G)
+ (B - entry.B) * (B - entry.B);
if (dist < minDist) { minDist = dist; pos = i; }
}
return minDist;
} }
} }
public sealed class RgbPalette : IPalette { public sealed class LabPaletteMatcher : IPaletteMatcher {
PaletteEntry[] palette;
public void SetAvailableBlocks(PaletteEntry[] blocks) {
this.palette = blocks;
}
public byte BestMatch(byte R, byte G, byte B, out int position) {
int minimum = int.MaxValue; position = 0;
for (int i = 0; i < palette.Length; i++) {
PaletteEntry pixel = palette[i];
int dist = (R - pixel.R) * (R - pixel.R)
+ (G - pixel.G) * (G - pixel.G)
+ (B - pixel.B) * (B - pixel.B);
if (dist < minimum) {
minimum = dist; position = i;
}
}
return palette[position].Block;
}
}
public sealed class LabPalette : IPalette {
LabColor[] palette; LabColor[] palette;
public void SetAvailableBlocks(PaletteEntry[] blocks) { public void SetPalette(ImagePalette palette) {
palette = new LabColor[blocks.Length]; this.palette = new LabColor[palette.FrontLayer.Length];
for (int i = 0; i < palette.Length; i++) for (int i = 0; i < palette.FrontLayer.Length; i++)
palette[i] = RgbToLab(blocks[i]); this.palette[i] = RgbToLab(palette.FrontLayer[i]);
} }
public byte BestMatch(byte r, byte g, byte b, out int position) { public byte BestMatch(byte R, byte G, byte B) {
double minimum = int.MaxValue; position = 0; double minDist = int.MaxValue; int pos = 0;
LabColor col = RgbToLab(r, g, b); LabColor col = RgbToLab(R, G, B);
for (int i = 0; i < palette.Length; i++) { for (int i = 0; i < palette.Length; i++) {
LabColor pixel = palette[i]; LabColor pixel = palette[i];
@ -90,13 +92,17 @@ namespace MCGalaxy.Drawing {
+ (col.A - pixel.A) * (col.A - pixel.A) + (col.A - pixel.A) * (col.A - pixel.A)
+ (col.B - pixel.B) * (col.B - pixel.B); + (col.B - pixel.B) * (col.B - pixel.B);
if (dist < minimum) { if (dist < minDist) { minDist = dist; pos = i; }
minimum = dist; position = i;
} }
return palette[pos].Block;
} }
return palette[position].Block;
public byte BestMatch(byte R, byte G, byte B, out bool backLayer) {
backLayer = false;
return BestMatch(R, G, B, out backLayer);
} }
struct LabColor { struct LabColor {
public double L, A, B; public double L, A, B;
public byte Block; public byte Block;

View File

@ -1,4 +1,4 @@
/* /*
Copyright 2011 MCForge Copyright 2011 MCForge
Dual-licensed under the Educational Community License, Version 2.0 and Dual-licensed under the Educational Community License, Version 2.0 and
@ -19,23 +19,43 @@ using System.Collections.Generic;
namespace MCGalaxy.Drawing { namespace MCGalaxy.Drawing {
internal static class ImagePalette { /// <summary> Represents a mapping of block ids to RGB colours. </summary>
public sealed class ImagePalette {
internal static string[] Names = { /// <summary> The name of this palette. </summary>
null, "2-layer color", "1-layer color", "2-layer grayscale", public string Name;
"1-layer grayscale", "Black and White", "Mathematical grayscale"
}; /// <summary> Blocks in the front, used in vertical and layer mode. </summary>
public static PaletteEntry[] GetPalette(int type) { public PaletteEntry[] FrontLayer;
if (type == 1) return Color_2;
if (type == 2) return Color_1; /// <summary> Blocks in the back, used only in two-layer vertical mode. </summary>
if (type == 3) return Grayscale_2; public PaletteEntry[] BackLayer;
if (type == 4) return Grayscale_1;
if (type == 5) return BlackWhite_1; public ImagePalette(string name, PaletteEntry[] front, PaletteEntry[] back) {
Name = name; FrontLayer = front; BackLayer = back;
}
/// <summary> All supported palettes. </summary>
public static List<ImagePalette> Palettes = new List<ImagePalette>();
public static ImagePalette Find(string name) {
foreach (ImagePalette entry in Palettes) {
if (entry.Name.CaselessEq(name)) return entry;
}
return null; return null;
} }
internal static PaletteEntry[] Color_2 = { public static void Load() {
// front layer Palettes.Clear();
Palettes.Add(new ImagePalette("Color", Color_Front, Color_Back));
Palettes.Add(new ImagePalette("Grayscale", Grayscale_Front, Grayscale_Back));
Palettes.Add(new ImagePalette("BlackWhite", BlackWhite_Front, null));
Palettes.Add(new ImagePalette("SimpleGrayscale", Grayscale_Mathematical, null));
}
static PaletteEntry[] Color_Front = {
new PaletteEntry(128, 86, 57, Block.dirt), new PaletteEntry(128, 86, 57, Block.dirt),
new PaletteEntry(162, 129, 75, Block.wood), new PaletteEntry(162, 129, 75, Block.wood),
new PaletteEntry(244, 237, 174, Block.sand), new PaletteEntry(244, 237, 174, Block.sand),
@ -56,6 +76,8 @@ namespace MCGalaxy.Drawing {
new PaletteEntry(135, 145, 130, Block.lightgrey), new PaletteEntry(135, 145, 130, Block.lightgrey),
new PaletteEntry(230, 240, 225, Block.white), new PaletteEntry(230, 240, 225, Block.white),
new PaletteEntry(163, 163, 163, Block.staircasefull), new PaletteEntry(163, 163, 163, Block.staircasefull),
new PaletteEntry(0, 0, 0, Block.obsidian),
};
/*Turns out the back layer blocks are handled awfully. /*Turns out the back layer blocks are handled awfully.
new PaletteEntry(217, 131, 155, 55), new PaletteEntry(217, 131, 155, 55),
new PaletteEntry(56, 77, 24, 56), new PaletteEntry(56, 77, 24, 56),
@ -63,7 +85,7 @@ namespace MCGalaxy.Drawing {
new PaletteEntry(39, 51, 154, 58), new PaletteEntry(39, 51, 154, 58),
new PaletteEntry(39, 117, 149, 59),*/ new PaletteEntry(39, 117, 149, 59),*/
// back layer static PaletteEntry[] Color_Back = {
new PaletteEntry(57, 38, 25, Block.dirt), new PaletteEntry(57, 38, 25, Block.dirt),
new PaletteEntry(72, 57, 33, Block.wood), new PaletteEntry(72, 57, 33, Block.wood),
new PaletteEntry(109, 105, 77, Block.sand), new PaletteEntry(109, 105, 77, Block.sand),
@ -87,50 +109,28 @@ namespace MCGalaxy.Drawing {
new PaletteEntry(0, 0, 0, Block.obsidian), new PaletteEntry(0, 0, 0, Block.obsidian),
}; };
internal static PaletteEntry[] Color_1 = { static PaletteEntry[] Grayscale_Front = {
new PaletteEntry(128, 86, 57, Block.dirt), new PaletteEntry(0, 0, 0, Block.obsidian),
new PaletteEntry(162, 129, 75, Block.wood),
new PaletteEntry(244, 237, 174, Block.sand),
new PaletteEntry(93, 70, 38, Block.trunk),
new PaletteEntry(226, 31, 38, Block.red),
new PaletteEntry(223, 135, 37, Block.orange),
new PaletteEntry(230, 241, 25, Block.yellow),
new PaletteEntry(127, 234, 26, Block.lightgreen),
new PaletteEntry(25, 234, 20, Block.green),
new PaletteEntry(31, 234, 122, Block.aquagreen),
new PaletteEntry(27, 239, 225, Block.cyan),
new PaletteEntry(99, 166, 226, Block.lightblue),
new PaletteEntry(111, 124, 235, Block.blue),
new PaletteEntry(126, 34, 218, Block.purple),
new PaletteEntry(170, 71, 219, Block.lightpurple),
new PaletteEntry(227, 39, 225, Block.pink),
new PaletteEntry(234, 39, 121, Block.darkpink),
new PaletteEntry(46, 68, 47, Block.darkgrey), new PaletteEntry(46, 68, 47, Block.darkgrey),
new PaletteEntry(135, 145, 130, Block.lightgrey), new PaletteEntry(135, 145, 130, Block.lightgrey),
new PaletteEntry(230, 240, 225, Block.white), new PaletteEntry(230, 240, 225, Block.white),
new PaletteEntry(0, 0, 0, Block.obsidian),
}; };
internal static PaletteEntry[] Grayscale_2 = { static PaletteEntry[] Grayscale_Back = {
// front layer new PaletteEntry(0, 0, 0, Block.obsidian),
new PaletteEntry(46, 68, 47, Block.darkgrey),
new PaletteEntry(135, 145, 130, Block.lightgrey),
new PaletteEntry(230, 240, 225, Block.white),
// back layer
new PaletteEntry(20, 30, 21, Block.darkgrey), new PaletteEntry(20, 30, 21, Block.darkgrey),
new PaletteEntry(60, 64, 58, Block.lightgrey), new PaletteEntry(60, 64, 58, Block.lightgrey),
new PaletteEntry(102, 107, 100, Block.white), new PaletteEntry(102, 107, 100, Block.white),
new PaletteEntry(0, 0, 0, Block.obsidian),
}; };
internal static PaletteEntry[] Grayscale_1 = { static PaletteEntry[] Grayscale_Mathematical = {
new PaletteEntry(46, 68, 47, Block.darkgrey), new PaletteEntry(32, 32, 32, Block.obsidian),
new PaletteEntry(135, 145, 130, Block.lightgrey), new PaletteEntry(96, 96, 96, Block.darkgrey),
new PaletteEntry(230, 240, 225, Block.white), new PaletteEntry(160, 160, 160, Block.lightgrey),
new PaletteEntry(0, 0, 0, Block.obsidian), new PaletteEntry(224, 224, 224, Block.white),
}; };
internal static PaletteEntry[] BlackWhite_1 = { static PaletteEntry[] BlackWhite_Front = {
new PaletteEntry(255, 255, 255, Block.white), new PaletteEntry(255, 255, 255, Block.white),
new PaletteEntry(0, 0, 0, Block.obsidian), new PaletteEntry(0, 0, 0, Block.obsidian),
}; };

View File

@ -31,23 +31,20 @@ namespace MCGalaxy.Drawing.Ops {
} }
internal Draw.Bitmap Source; internal Draw.Bitmap Source;
internal int Mode, Direction; internal int Direction;
internal bool Layer; internal bool DualLayer, LayerMode;
internal string Filename; internal string Filename;
public ImagePalette Palette;
Vec3S32 dx, dy, adj; Vec3S32 dx, dy, adj;
IPalette selector; IPaletteMatcher selector;
int threshold;
public override void Perform(Vec3S32[] marks, Brush brush, Action<DrawOpBlock> output) { public override void Perform(Vec3S32[] marks, Brush brush, Action<DrawOpBlock> output) {
selector = null; selector = null;
CalcThreshold(); CalcState(Direction);
CalcDirectionVectors(Direction);
PaletteEntry[] palette = ImagePalette.GetPalette(Mode); selector = new RgbPaletteMatcher();
if (Mode == 6) selector = new GrayscalePalette(); selector.SetPalette(Palette);
else selector = new RgbPalette();
selector.SetAvailableBlocks(palette);
using (PixelGetter getter = new PixelGetter(Source)) { using (PixelGetter getter = new PixelGetter(Source)) {
getter.Init(); getter.Init();
@ -58,7 +55,7 @@ namespace MCGalaxy.Drawing.Ops {
Source = null; Source = null;
if (Filename == "tempImage_" + Player.name) if (Filename == "tempImage_" + Player.name)
File.Delete("extra/images/tempImage_" + Player.name + ".bmp"); File.Delete("extra/images/tempImage_" + Player.name + ".bmp");
Player.Message(Player, "Finished printing image using " + ImagePalette.Names[Mode]); Player.Message(Player, "Finished printing image using {0} palette.", Palette.Name);
} }
void OutputPixel(Pixel P, Action<DrawOpBlock> output) { void OutputPixel(Pixel P, Action<DrawOpBlock> output) {
@ -67,25 +64,23 @@ namespace MCGalaxy.Drawing.Ops {
ushort z = (ushort)(Origin.Z + dx.Z * P.X + dy.Z * P.Y); ushort z = (ushort)(Origin.Z + dx.Z * P.X + dy.Z * P.Y);
if (P.A < 20) { output(Place(x, y, z, Block.air, 0)); return; } if (P.A < 20) { output(Place(x, y, z, Block.air, 0)); return; }
int position; if (!DualLayer) {
byte block = selector.BestMatch(P.R, P.G, P.B, out position); byte block = selector.BestMatch(P.R, P.G, P.B);
// Back layer block output(Place(x, y, z, block, 0));
if (position <= threshold) { } else {
bool backLayer;
byte block = selector.BestMatch(P.R, P.G, P.B, out backLayer);
if (backLayer) {
x = (ushort)(x + adj.X); x = (ushort)(x + adj.X);
z = (ushort)(z + adj.Z); z = (ushort)(z + adj.Z);
} }
output(Place(x, y, z, block, 0)); output(Place(x, y, z, block, 0));
} }
void CalcThreshold() {
threshold = -1;
if (Mode == 1 || Mode == 3) {
threshold = Mode == 1 ? 20 : 3;
}
} }
void CalcDirectionVectors(int dir) { void CalcState(int dir) {
dx = default(Vec3S32); dy = default(Vec3S32); adj = default(Vec3S32); dx = default(Vec3S32); dy = default(Vec3S32); adj = default(Vec3S32);
DualLayer = DualLayer && !LayerMode && Palette.BackLayer != null;
// Calculate back layer offset // Calculate back layer offset
if (dir == 0) adj.Z = 1; if (dir == 0) adj.Z = 1;
@ -93,7 +88,7 @@ namespace MCGalaxy.Drawing.Ops {
if (dir == 2) adj.X = -1; if (dir == 2) adj.X = -1;
if (dir == 3) adj.X = 1; if (dir == 3) adj.X = 1;
if (Layer) { if (LayerMode) {
if (dir == 0) { dx.X = 1; dy.Z = -1; } if (dir == 0) { dx.X = 1; dy.Z = -1; }
if (dir == 1) { dx.X = -1; dy.Z = 1; } if (dir == 1) { dx.X = -1; dy.Z = 1; }
if (dir == 2) { dx.Z = 1; dy.X = 1; } if (dir == 2) { dx.Z = 1; dy.X = 1; }

View File

@ -445,7 +445,7 @@
<Compile Include="Drawing\Flip.cs" /> <Compile Include="Drawing\Flip.cs" />
<Compile Include="Drawing\Image\ImagePalette.cs" /> <Compile Include="Drawing\Image\ImagePalette.cs" />
<Compile Include="Drawing\Image\ImagePrintDrawOp.cs" /> <Compile Include="Drawing\Image\ImagePrintDrawOp.cs" />
<Compile Include="Drawing\Image\IPalette.cs" /> <Compile Include="Drawing\Image\IPaletteMatcher.cs" />
<Compile Include="Drawing\Image\PixelGetter.cs" /> <Compile Include="Drawing\Image\PixelGetter.cs" />
<Compile Include="Drawing\TransformFactories\RotateTransform.cs" /> <Compile Include="Drawing\TransformFactories\RotateTransform.cs" />
<Compile Include="Drawing\TransformFactories\SimpleTransforms.cs" /> <Compile Include="Drawing\TransformFactories\SimpleTransforms.cs" />

View File

@ -21,6 +21,7 @@ using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using MCGalaxy.Blocks; using MCGalaxy.Blocks;
using MCGalaxy.Commands.World; using MCGalaxy.Commands.World;
using MCGalaxy.Drawing;
using MCGalaxy.Games; using MCGalaxy.Games;
using MCGalaxy.Tasks; using MCGalaxy.Tasks;
using MCGalaxy.Util; using MCGalaxy.Util;
@ -171,6 +172,7 @@ namespace MCGalaxy {
Alias.Load(); Alias.Load();
Bots.BotsFile.Load(); Bots.BotsFile.Load();
BlockDefinition.LoadGlobal(); BlockDefinition.LoadGlobal();
ImagePalette.Load();
SrvProperties.Load("properties/server.properties"); SrvProperties.Load("properties/server.properties");
Updater.Load("properties/update.properties"); Updater.Load("properties/update.properties");