The OpenD Programming Language

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 }