1 /** 2 Images suitable to be drawn on a canvas. This is an _undecoded_ image, with metadata extracted. 3 4 Copyright: Guillaume Piolat 2018. 5 License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 6 */ 7 module printed.canvas.image; 8 9 import std.exception; 10 import std.file; 11 import std.math; 12 import std.typecons; 13 import std.base64; 14 import std.array; 15 16 /// Represented an encoded image (JPEG or PNG). 17 class Image 18 { 19 /// Input is an image file. 20 this(const(char)[] relativePath) 21 { 22 ubyte[] dataFromFile = cast(ubyte[])( std.file.read(relativePath) ); 23 // has been allocated, can be assumed unique 24 this( assumeUnique(dataFromFile) ); 25 } 26 27 /// Input is PNG or JPEG memory image directly. 28 this(const(ubyte)[] data) 29 { 30 // embed for future use 31 _data = data.idup; 32 33 import gamut; 34 35 ImageFormat fmt = gamut.Image.identifyFormatFromMemory(data); 36 bool isPNG = (fmt == ImageFormat.PNG); 37 bool isJPEG = (fmt == ImageFormat.JPEG); 38 if (!isPNG && !isJPEG) 39 throw new Exception("Unidentified format"); 40 41 gamut.image.Image img; 42 img.loadFromMemory(data, LOAD_NO_PIXELS); 43 44 if (img.isError) 45 throw new Exception("Can't decode image."); 46 47 _width = img.width(); 48 _height = img.height(); 49 50 if (isPNG) _MIME = "image/png"; 51 else _MIME = "image/jpeg"; 52 53 // Use defaults if missing 54 55 float pixelAspectRatio = img.pixelAspectRatio == GAMUT_UNKNOWN_ASPECT_RATIO ? 1.0 : img.pixelAspectRatio; 56 float verticalDPI = img.dotsPerInchY() == GAMUT_UNKNOWN_RESOLUTION ? 1.0 : img.dotsPerInchY; 57 58 _pixelsPerMeterX = convertMetersToInches(verticalDPI * pixelAspectRatio); 59 _pixelsPerMeterY = convertMetersToInches(verticalDPI); 60 } 61 62 string MIME() 63 { 64 return _MIME; 65 } 66 67 string toDataURI() 68 { 69 string r = "data:"; 70 r ~= _MIME; 71 r ~= ";charset=utf-8;base64,"; 72 r ~= Base64.encode(_data); 73 return r; 74 } 75 76 /// Width in pixels. 77 int width() 78 { 79 return _width; 80 } 81 82 /// Height in pixels. 83 int height() 84 { 85 return _height; 86 } 87 88 /// Default width when printed, in mm. 89 float printWidth() 90 { 91 return 1000 * _width / pixelsPerMeterX(); 92 } 93 94 /// Default height when printed, in mm. 95 float printHeight() 96 { 97 return 1000 * _height / pixelsPerMeterY(); 98 } 99 100 float pixelsPerMeterX() 101 { 102 return _pixelsPerMeterX; 103 } 104 105 float pixelsPerMeterY() 106 { 107 return _pixelsPerMeterY; 108 } 109 110 immutable(ubyte)[] encodedData() const 111 { 112 return _data; 113 } 114 115 private: 116 117 // Number of horizontal pixels. 118 int _width = -1; 119 120 // Number of vertical pixels. 121 int _height = -1; 122 123 // DPI and aspect ratio information, critical for print 124 float _pixelsPerMeterX = float.nan; // stays NaN if not available 125 float _pixelsPerMeterY = float.nan; // stays NaN if not available 126 127 // Encoded data. 128 immutable(ubyte)[] _data; 129 130 // Parsed MIME type. 131 string _MIME; 132 }