L'avantage est de pouvoir le modifier dans un logiciel de pixel art, sauver un peu de mémoire et d'espace.
203 lines
5.0 KiB
D
203 lines
5.0 KiB
D
module main;
|
|
|
|
import std.stdio;
|
|
import std.json;
|
|
import std.file;
|
|
import std.conv;
|
|
import std.string;
|
|
import gamut;
|
|
|
|
void usage()
|
|
{
|
|
stderr.writeln();
|
|
stderr.writeln("Divise-texture-par-3");
|
|
stderr.writeln("l'indispensable outil de division par 3\n");
|
|
stderr.writeln("usage: divise-texture-par-3 input.png output.png\n");
|
|
stderr.writeln();
|
|
stderr.writeln("Arguments:");
|
|
stderr.writeln(" --help Shows this help.");
|
|
stderr.writeln("A noter qu'un fichier diff.png est cree qui montre pourquoi on peut pas réduire par 3 en rouge.");
|
|
}
|
|
|
|
|
|
int main(string[] args)
|
|
{
|
|
try
|
|
{
|
|
string input = null;
|
|
string output = null;
|
|
bool showHelp = false;
|
|
|
|
for(int i = 1; i < args.length; ++i)
|
|
{
|
|
string arg = args[i];
|
|
if (arg == "-h" || arg == "--help")
|
|
{
|
|
showHelp = true;
|
|
}
|
|
else
|
|
{
|
|
if (input)
|
|
{
|
|
if (output)
|
|
throw new Exception("Too many files provided");
|
|
else
|
|
output = arg;
|
|
}
|
|
else
|
|
input = arg;
|
|
}
|
|
}
|
|
|
|
if (showHelp || input is null || output is null)
|
|
{
|
|
usage();
|
|
return 0;
|
|
}
|
|
|
|
processFile(input, output, "diff.png");
|
|
}
|
|
catch(Exception e)
|
|
{
|
|
writefln("error: %s", e.message);
|
|
usage();
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
struct Color
|
|
{
|
|
ubyte r, g, b, a;
|
|
}
|
|
|
|
void processFile(string inputPath, string outputPath, string diffPath)
|
|
{
|
|
Image input;
|
|
input.loadFromFile(inputPath);
|
|
if (!input.isValid)
|
|
throw new Exception("cant load " ~ inputPath);
|
|
|
|
input.convertTo(PixelType.rgba8);
|
|
|
|
if ( (input.width % 3) || (input.height % 3))
|
|
throw new Exception("input is not a multiple of 3 in size");
|
|
|
|
int w = input.width;
|
|
int h = input.height;
|
|
int w3 = input.width / 3;
|
|
int h3 = input.height / 3;
|
|
|
|
// show outlier pixels
|
|
// compare each pixel with pixel at center of 3x3 patch, make it RED if not
|
|
// else keep original color toned down 0.5x
|
|
Image diff;
|
|
diff.create(w, h, PixelType.rgba8);
|
|
|
|
bool successOfDownscale = true;
|
|
|
|
for(int y = 0; y < h; ++y)
|
|
{
|
|
Color[] scanInput = cast(Color[]) input.scanline(y);
|
|
|
|
Color[] scanDiff = cast(Color[]) diff.scanline(y);
|
|
|
|
// Copy input to diff, darkened
|
|
for(int x = 0; x < w; ++x)
|
|
{
|
|
Color c = scanInput[x];
|
|
c.r /= 2;
|
|
c.g /= 2;
|
|
c.b /= 2;
|
|
scanDiff[x] = c;
|
|
}
|
|
|
|
int refY = 1 + 3 * (y / 3);
|
|
Color[] scanInputRef = cast(Color[]) input.scanline(refY);
|
|
|
|
for(int x = 0; x < w; ++x)
|
|
{
|
|
int refX = 1 + 3 * (x / 3);
|
|
Color centerPixel = scanInputRef[refX];
|
|
|
|
Color thisPixel = scanInput[x];
|
|
if (!isSameColor(thisPixel, centerPixel))
|
|
{
|
|
scanDiff[x] = Color(255, 0, 0, 255);
|
|
successOfDownscale = false;
|
|
}
|
|
else
|
|
{
|
|
scanDiff[x].g = 255; // green = OK
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!diff.saveToFile(diffPath))
|
|
{
|
|
throw new Exception("Couln't save " ~ diffPath);
|
|
}
|
|
|
|
|
|
// Output image
|
|
|
|
Image output;
|
|
output.create(w3, h3, PixelType.rgba8);
|
|
|
|
// if the diff image isn't all black, there are errors that need to be fixed
|
|
|
|
// always create the divided by 3 image
|
|
// this is just sampling in the middle
|
|
for(int y = 0; y < h3; ++y)
|
|
{
|
|
Color[] scanInput = cast(Color[]) input.scanline(y*3);
|
|
Color[] scanOutput = cast(Color[]) output.scanline(y);
|
|
for(int x = 0; x < w3; ++x)
|
|
{
|
|
// Take the first pixel of the 3x3 patch that isn't fully transparent
|
|
|
|
Color found;
|
|
for (int yy = 0; yy < 3; ++yy)
|
|
{
|
|
for (int xx = 0; xx < 3; ++xx)
|
|
{
|
|
Color c = (cast(Color[]) input.scanline(y*3+yy))[x*3+xx];
|
|
if (c.a != 0)
|
|
{
|
|
found = c;
|
|
goto okay;
|
|
}
|
|
}
|
|
}
|
|
okay:
|
|
|
|
|
|
scanOutput[x] = found;
|
|
}
|
|
}
|
|
|
|
if (!output.saveToFile(outputPath))
|
|
{
|
|
throw new Exception("Couln't save " ~ outputPath);
|
|
}
|
|
|
|
if (successOfDownscale)
|
|
{
|
|
writeln("SUCCESS");
|
|
writeln("Image reduced by 3x, do not forget to use GIMP to make it palettized again!");
|
|
}
|
|
else
|
|
{
|
|
writeln("FAILURE");
|
|
writeln("See diff.png for the problem in input image that prevents its reduction.");
|
|
writeln("Not everything is align to 3x3 pixel patchs.");
|
|
}
|
|
}
|
|
|
|
bool isSameColor(Color a, Color b)
|
|
{
|
|
if ((a.a == 0) && (b.a == 0))
|
|
return true; // do not check colors of transparent pixels
|
|
else
|
|
return a == b;
|
|
} |