Files
chaussette.sale/tools/divise-texture-par-3/source/main.d
Guillaume Piolat 2cf35c8d6b Un nouvel outil qui aide a réduire du pixel art magnifié 3x3 en pixel art qui est vraiment du pixel art.
L'avantage est de pouvoir le modifier dans un logiciel de pixel art, sauver un peu de mémoire et d'espace.
2025-03-23 11:37:02 +01:00

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;
}