269 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			D
		
	
	
	
	
	
			
		
		
	
	
			269 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			D
		
	
	
	
	
	
| module main;
 | |
| 
 | |
| import std.stdio;
 | |
| import std.json;
 | |
| import std.file;
 | |
| import std.conv;
 | |
| import std.string;
 | |
| import gamut;
 | |
| import expression;
 | |
| 
 | |
| void usage()
 | |
| {
 | |
|     stderr.writeln();
 | |
|     stderr.writeln("Decoupe-Voiture");
 | |
|     stderr.writeln("l'indispensable outil de decoupe de voiture\n");
 | |
|     stderr.writeln("usage: decoupe-voiture input.png [-p <pattern>] output.png\n");
 | |
|     stderr.writeln();
 | |
|     stderr.writeln("Arguments:");
 | |
|     stderr.writeln("    -p            Pattern to execute, default = classic_complete_48x48");
 | |
|     stderr.writeln("                  See patterns/ for availability.");
 | |
|     stderr.writeln("    --help        Shows this help.");
 | |
|     stderr.writeln();
 | |
| }
 | |
| 
 | |
| 
 | |
| int main(string[] args)
 | |
| {
 | |
|     try
 | |
|     {
 | |
|         string input = null;
 | |
|         string output = null;
 | |
|         string pattern = "classic_complete_48x48";
 | |
|         bool showHelp = false;
 | |
| 
 | |
|         for(int i = 1; i < args.length; ++i)
 | |
|         {
 | |
|             string arg = args[i];
 | |
|             if (arg == "-p" || arg == "--pattern")
 | |
|             {
 | |
|                 ++i;
 | |
|                 pattern = args[i];
 | |
|             }
 | |
|             else 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, pattern);
 | |
|     }
 | |
|     catch(Exception e)
 | |
|     {
 | |
|         writefln("error: %s", e.message);
 | |
|         usage();
 | |
|         return 1;
 | |
|     }
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| struct Point
 | |
| {
 | |
|     int x, y;
 | |
| }
 | |
| 
 | |
| struct Size
 | |
| {
 | |
|     int w, h;
 | |
| }
 | |
| 
 | |
| struct Rect
 | |
| {
 | |
|     Point pos;
 | |
|     Size size;
 | |
| }
 | |
| 
 | |
| void processFile(string inputPath, string outputPath, string pattern)
 | |
| {
 | |
|     alias jsonTrue = JSONType.true_;
 | |
|     alias jsonFalse = JSONType.false_;
 | |
| 
 | |
|     // try to open pattern file
 | |
| 
 | |
|     string patternPath = "patterns/" ~ pattern ~ ".json";
 | |
| 
 | |
|     JSONValue patternFile = parseJSON(cast(string)(std.file.read(patternPath)));
 | |
| 
 | |
|     Image input;
 | |
|     input.loadFromFile(inputPath);
 | |
|     if (!input.isValid)
 | |
|         throw new Exception("cant load " ~ inputPath);
 | |
| 
 | |
|     input.convertTo(PixelType.rgba8);
 | |
|     
 | |
|     Size defaultSize = parseSize(patternFile, "default-size", Size(16, 16));
 | |
| 
 | |
|     Size inputSize = parseSize(patternFile, "input-size", defaultSize);
 | |
|     Size outputSize = parseSize(patternFile, "output-size", defaultSize);
 | |
| 
 | |
|     if (inputSize.w != input.width || inputSize.h != input.height)
 | |
|         throw new Exception("input size mismatch");
 | |
| 
 | |
|     Image output;
 | |
|     output.create(outputSize.w, outputSize.h, PixelType.rgba8);
 | |
| 
 | |
| 
 | |
|     void copyRect(Rect src, Rect dst)
 | |
|     {
 | |
|         rectInsideImage(input, src);
 | |
|         rectInsideImage(output, dst);
 | |
| 
 | |
|         int w = src.size.w;
 | |
|         int h = src.size.h;
 | |
| 
 | |
|         for (int y = 0; y < h; ++y)
 | |
|         {
 | |
|             ubyte[] srcScan = cast(ubyte[])  input.scanline(src.pos.y + y);
 | |
|             ubyte[] dstScan = cast(ubyte[]) output.scanline(dst.pos.y + y);
 | |
|             int bytes = w * 4;
 | |
|             int ii    = 4 * src.pos.x;
 | |
|             int oo    = 4 * dst.pos.x;
 | |
|             dstScan[oo .. oo+bytes] = srcScan[ii .. ii+bytes];
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     foreach(JSONValue tr; patternFile["copyRects"].array)
 | |
|     {
 | |
|         Rect from = parseRect(tr, "from", defaultSize);
 | |
|         Rect to   = parseRect(tr,   "to", defaultSize);
 | |
| 
 | |
|         // If from rectangle is smaller than to rectangle,
 | |
|         // center the dest rectangle inside the given destination
 | |
| 
 | |
|         if (from.size.w < to.size.w)
 | |
|         {
 | |
|             int marginX = (to.size.w - from.size.w) / 2;
 | |
|             to.pos.x += marginX;
 | |
|             to.size.w = from.size.w;
 | |
|         }
 | |
|         if (from.size.h < to.size.h)
 | |
|         {
 | |
|             int marginY = (to.size.h - from.size.h) / 2;
 | |
|             to.pos.y += marginY;
 | |
|             to.size.h = from.size.h;
 | |
|         }
 | |
| 
 | |
|         if (from.size != to.size)
 | |
|             throw new Exception(format("rectangle size mismatch for key: %s", tr));
 | |
| 
 | |
|         //writefln("DEBUG: copy %s to %s", from, to);
 | |
| 
 | |
|         copyRect(from, to);
 | |
|     }
 | |
|     writeln("All rectangles copied");
 | |
| 
 | |
|     int encodeFlags = 0;
 | |
|     bool r = output.saveToFile(outputPath, encodeFlags);
 | |
|     if (!r)
 | |
|     {
 | |
|         throw new Exception("Couldn't save file " ~ outputPath);
 | |
|     }
 | |
| }
 | |
| 
 | |
| void rectInsideImage(ref Image i, Rect r)
 | |
| {
 | |
|     if (r.pos.x < 0)
 | |
|         throw new Exception("Rectangle x is < 0");
 | |
|     if (r.pos.y < 0)
 | |
|         throw new Exception("Rectangle y is < 0");
 | |
|     if (r.size.w < 0)
 | |
|         throw new Exception("Rectangle width is < 0");
 | |
|     if (r.size.h < 0)
 | |
|         throw new Exception("Rectangle height is < 0");
 | |
|     if (r.pos.x + r.size.w > i.width)
 | |
|         throw new Exception("Rectangle exceeds width of image");
 | |
|     if (r.pos.y + r.size.h > i.height)
 | |
|         throw new Exception("Rectangle exceeds height of image");
 | |
| }
 | |
| 
 | |
| Point parsePoint(JSONValue parent, string keyName, Size defaultSize)
 | |
| {
 | |
|     string r = parent[keyName].str;
 | |
|     return parsePoint(r, defaultSize);
 | |
| }
 | |
| 
 | |
| Point parsePoint(string r, Size defaultSize)
 | |
| {
 | |
|     r = strip(r);
 | |
|     int cPos = cast(int) r.indexOf(",");
 | |
|     if (cPos == -1)
 | |
|         throw new Exception("Point should follow this in format: 4,5");
 | |
|     int x = parseIntegerExpression(r[0..cPos], defaultSize);
 | |
|     int y = parseIntegerExpression(r[cPos+1..$], defaultSize);
 | |
|     return Point(x, y);
 | |
| }
 | |
| 
 | |
| Size parseSize(JSONValue parent, string keyName, Size defaultSize)
 | |
| {
 | |
|     return parseSize(parent[keyName].str, defaultSize);
 | |
| }
 | |
| 
 | |
| Size parseSize(string r, Size defaultSize)
 | |
| {    
 | |
|     r = strip(r);
 | |
|     int xPos = cast(int) r.indexOf("x");
 | |
|     if (xPos == -1)
 | |
|         throw new Exception("Size should follow this in format: 48x48");
 | |
|     int w = parseIntegerExpression(r[0..xPos], defaultSize);
 | |
|     int h = parseIntegerExpression(r[xPos+1..$], defaultSize);
 | |
|     return Size(w, h);
 | |
| }
 | |
| 
 | |
| Rect parseRect(JSONValue parent, 
 | |
|                string keyName,
 | |
|                Size defaultSize)
 | |
| {
 | |
|     string r = parent[keyName].str;
 | |
|     return parseRect(r, defaultSize);
 | |
| }
 | |
| 
 | |
| Rect parseRect(string r, Size defaultSize)
 | |
| {
 | |
|     r = strip(r);
 | |
|     int mPos = cast(int) r.indexOf("--");
 | |
| 
 | |
|     Size size;
 | |
|     Point pos;
 | |
| 
 | |
|     if (mPos == -1)
 | |
|     {
 | |
|         pos = parsePoint(r, defaultSize);
 | |
|         size = defaultSize;        
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         // U+2014 is 3 bytes long
 | |
|         pos = parsePoint(r[0..mPos], defaultSize);
 | |
|         size = parseSize(r[mPos+2..$], defaultSize);
 | |
|     }
 | |
|     
 | |
|     return Rect(pos, size);
 | |
| }
 | |
| 
 | |
| int parseIntegerExpression(string source, 
 | |
|                            Size defaultSize)
 | |
| {
 | |
|     source = strip(source);
 | |
|     auto e = compileExpression!int(source);
 | |
|     //e["TX"] = defaultSize.w;
 | |
|     //e["TY"] = defaultSize.h;
 | |
|     return e();
 | |
| } | 
