From 2cf35c8d6bd54f34cb6914782a3fd4081cca96a3 Mon Sep 17 00:00:00 2001 From: Guillaume Piolat Date: Sun, 23 Mar 2025 11:37:02 +0100 Subject: [PATCH] =?UTF-8?q?Un=20nouvel=20outil=20qui=20aide=20a=20r=C3=A9d?= =?UTF-8?q?uire=20du=20pixel=20art=20magnifi=C3=A9=203x3=20en=20pixel=20ar?= =?UTF-8?q?t=20qui=20est=20vraiment=20du=20pixel=20art.=20L'avantage=20est?= =?UTF-8?q?=20de=20pouvoir=20le=20modifier=20dans=20un=20logiciel=20de=20p?= =?UTF-8?q?ixel=20art,=20sauver=20un=20peu=20de=20m=C3=A9moire=20et=20d'es?= =?UTF-8?q?pace.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tools/divise-texture-par-3/README.md | 6 + tools/divise-texture-par-3/dub.json | 15 ++ tools/divise-texture-par-3/source/main.d | 203 +++++++++++++++++++++++ 3 files changed, 224 insertions(+) create mode 100644 tools/divise-texture-par-3/README.md create mode 100644 tools/divise-texture-par-3/dub.json create mode 100644 tools/divise-texture-par-3/source/main.d diff --git a/tools/divise-texture-par-3/README.md b/tools/divise-texture-par-3/README.md new file mode 100644 index 0000000..9469961 --- /dev/null +++ b/tools/divise-texture-par-3/README.md @@ -0,0 +1,6 @@ +- Installe le D +- `dub` + +Ce programme sert à divier les texture par 3, et sort un masque de différence pour permettre de voir où la texture n'est pas strictement agrandie par 3, afin d'éviter les erreurs. + +Ce programme ne réduit qu'une image dont chaque petit carré 3x3 contient la même couleur, et est aligné sur une coordonnée multiple de 3. \ No newline at end of file diff --git a/tools/divise-texture-par-3/dub.json b/tools/divise-texture-par-3/dub.json new file mode 100644 index 0000000..1299df8 --- /dev/null +++ b/tools/divise-texture-par-3/dub.json @@ -0,0 +1,15 @@ +{ + "name": "divise-texture-par-3", + "description": "Remettre le pixel art en pixel!", + "license": "BSL-1.0", + + "dependencies": + { + "gamut": "~>3.0" + }, + + "subConfigurations": + { + "gamut": "boost+mit" + } +} diff --git a/tools/divise-texture-par-3/source/main.d b/tools/divise-texture-par-3/source/main.d new file mode 100644 index 0000000..1a83863 --- /dev/null +++ b/tools/divise-texture-par-3/source/main.d @@ -0,0 +1,203 @@ +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; +} \ No newline at end of file