import std.file, std.math, std.path, std.stdio, std.zlib; const ubyte[] header = [ 137, 80, 78, 71, 13, 10, 26, 10 ]; const uint WIDTH = 100, HEIGHT = 100; align(1) struct IHDR { uint width = WIDTH; uint height = HEIGHT; ubyte bitDepth = 8; ubyte colourType = 2; ubyte compressMethod, filterMethod, interlaceMethod; version (LittleEndian) { IHDR bigEndian() { IHDR result = this; result.width = .bigEndian(width); result.height = .bigEndian(height); return result; } } else { ref IHDR bigEndian() { return this; } } } align(1) struct RGB { ubyte red, green, blue; } void main(string[] a) { ubyte filterType; string outputFile = "makepng.png"; if (a.length > 1 && a[1].length == 1 && a[1][0] >= '0' && a[1][0] <= '4') { filterType = cast(ubyte) (a[1][0] - '0'); a = a[1..$]; } if (a.length > 1) { outputFile = defaultExtension(a[1], "png"); } void[] pngData = header.dup; pngData ~= makeChunk("IHDR", IHDR.init.bigEndian); RGB[HEIGHT][WIDTH] imageData; foreach (y, ref scanline; imageData) { foreach (x, ref pixel; scanline) { if ((x-50) * (x-50) + (y-50) * (y-50) < 2000) { pixel.red = 255; pixel.green = cast(ubyte) (2 * x); pixel.blue = cast(ubyte) (255 * y / 100); } } } ubyte[] imageData2; if (filterType == 0) { foreach (y, ref scanLine; imageData) { imageData2 ~= 0; imageData2 ~= cast(ubyte[]) scanLine; } } else { Predictor predictor = predictors[filterType]; ubyte[] prevLine, thisLine; prevLine.length = 3 * (WIDTH + 1); thisLine.length = 3 * (WIDTH + 1); foreach (y, ref scanLine; imageData) { thisLine[3..$] = cast(ubyte[]) scanLine; imageData2 ~= filterType; for (uint x = 0; x < 3 * WIDTH; x++) { imageData2 ~= cast(ubyte) (thisLine[x + 3] - predictor(thisLine[x], prevLine[x+3], prevLine[x])); } ubyte[] tempLine = prevLine; prevLine = thisLine; thisLine = tempLine; } } assert (imageData2.length == (3 * WIDTH + 1) * HEIGHT); pngData ~= makeChunkV("IDAT", compress(imageData2)); writeln(pngData.length); pngData ~= makeChunkV("IEND", null); std.file.write(outputFile, pngData); } version (LittleEndian) { uint bigEndian(uint value) { return (value << 24) | ((value & 0x0000FF00) << 8) | ((value & 0x00FF0000) >> 8) | (value >> 24); } } else { uint bigEndian(uint value) { return value; } } void[] bigEndianBytes(uint value) { uint[] be = [bigEndian(value)]; return be; } void[] makeChunkV(in string type, in void[] data) in { assert(type.length == 4); } body { void[] typeAndData = type ~ data; uint crc = crc32(0, typeAndData); return bigEndianBytes(data.length) ~ typeAndData ~ bigEndianBytes(crc); } void[] makeChunk(T)(in string type, in T data) { static assert (!is(T : void[])); return makeChunkV(type, cast(void[]) (&data)[0..1]); } // FILTER PREDICTOR FUNCTIONS alias ubyte function(ubyte, ubyte, ubyte) Predictor; Predictor[5] predictors = [ &noFilter, &subFilter, &upFilter, &averageFilter, &paethFilter ]; ubyte noFilter(ubyte left, ubyte up, ubyte leftUp) { return 0; } ubyte subFilter(ubyte left, ubyte up, ubyte leftUp) { return left; } ubyte upFilter(ubyte left, ubyte up, ubyte leftUp) { return up; } ubyte averageFilter(ubyte left, ubyte up, ubyte leftUp) { return (left + up) >> 1; } ubyte paethFilter(ubyte left, ubyte up, ubyte leftUp) { int p = left + up - leftUp, pa = abs(p - left), pb = abs(p - up), pc = abs(p - leftUp); if (pa <= pb && pa <= pc) return left; if (pb <= pc) return up; return leftUp; }