1 module main; 2 3 import std.stdio; 4 import std.file; 5 import std.conv; 6 import std.path; 7 import std.string; 8 import std.algorithm; 9 import gamut; 10 import core.stdc.stdlib: free; 11 12 void usage() 13 { 14 writeln(); 15 writeln("Usage: qoix\n"); 16 writeln(" This just run the test suite."); 17 writeln; 18 } 19 20 int main(string[] args) 21 { 22 // Encore all image in test suite in QOI 23 auto files = filter!`endsWith(a.name,".png")`(dirEntries("test-images",SpanMode.depth)); 24 25 double mean_encode_mpps = 0; 26 double mean_decode_mpps = 0; 27 double mean_bpp = 0; 28 29 long timeStart = getTickUs(); 30 31 int N = 0; 32 foreach(f; files) 33 { 34 writeln(); 35 36 ubyte[] originalImage = cast(ubyte[]) std.file.read(f); 37 38 double original_size_kb = originalImage.length / 1024.0; 39 writefln("*** image of size %.1f kb: %s", original_size_kb, f); 40 41 Image image; 42 image.loadFromMemory(originalImage); 43 if (image.errored) 44 throw new Exception(to!string(image.errorMessage)); 45 46 int width = image.width; 47 int height = image.height; 48 49 if (image.errored) 50 throw new Exception(to!string(image.errorMessage)); 51 52 ubyte[] qoix_encoded; 53 double qoix_encode_ms = measure( { qoix_encoded = image.saveToMemory(ImageFormat.QOIX); } ); 54 55 if (qoix_encoded is null) 56 throw new Exception("encoding failed"); 57 58 scope(exit) free(qoix_encoded.ptr); 59 double qoix_size_kb = qoix_encoded.length / 1024.0; 60 double qoix_decode_ms = measure( { image.loadFromMemory(qoix_encoded); } ); 61 double qoix_encode_mpps = (width * height * 1.0e-6) / (qoix_encode_ms * 0.001); 62 double qoix_decode_mpps = (width * height * 1.0e-6) / (qoix_decode_ms * 0.001); 63 double bit_per_pixel = (qoix_encoded.length * 8.0) / (width * height); 64 65 mean_encode_mpps += qoix_encode_mpps; 66 mean_decode_mpps += qoix_decode_mpps; 67 mean_bpp += bit_per_pixel; 68 double size_vs_original = qoix_size_kb / original_size_kb; 69 70 writefln(" decode decode mpps encode mpps bit-per-pixel size reduction"); 71 writefln(" %8.2f ms %8.2f %8.2f %8.5f %9.1f kb %9.4f", qoix_decode_ms, qoix_decode_mpps, qoix_encode_mpps, bit_per_pixel, qoix_size_kb, size_vs_original); 72 N += 1; 73 74 // Check encoding is properly done. 75 { 76 Image image2; 77 image2.loadFromMemory(qoix_encoded); 78 assert(!image2.errored); 79 image2.convertTo8Bit(); 80 string path = "output/" ~ baseName(f) ~ ".png"; 81 image2.saveToFile(path, ImageFormat.PNG); 82 } 83 } 84 long timeTotal = getTickUs() - timeStart; 85 mean_encode_mpps /= N; 86 mean_decode_mpps /= N; 87 mean_bpp /= N; 88 writefln("\nTOTAL decode mpps encode mpps bit-per-pixel"); 89 writefln(" %8.2f %8.2f %8.5f", mean_decode_mpps, mean_encode_mpps, mean_bpp); 90 91 92 double totalSecs = timeTotal / 1000000.0; 93 writefln("\nTOTAL time = %s secs\n", totalSecs); 94 95 return 0; 96 } 97 98 long getTickUs() nothrow @nogc 99 { 100 import core.time; 101 return convClockFreq(MonoTime.currTime.ticks, MonoTime.ticksPerSecond, 1_000_000); 102 } 103 104 105 106 double measure(void delegate() nothrow @nogc dg) nothrow @nogc 107 { 108 long A = getTickUs(); 109 dg(); 110 long B = getTickUs(); 111 return cast(double)( (B - A) / 1000.0 ); 112 }