1 module sbylib.wrapper.freeimage.image;
2 
3 import derelict.freeimage.freeimage;
4 import sbylib.wrapper.freeimage.freeimage;
5 
6 public import sbylib.wrapper.freeimage.constants;
7 
8 import std.format : format;
9 
10 /** 
11   Class represented FIBITMAP.
12 */
13 class Image {
14 
15     private FIBITMAP* bitmap;
16 
17     /**
18     Constructor by image size and image type.
19 
20     Params:
21         width = image width
22         height = image height
23         type = flag used to specify the bitmap type 
24     */
25     this(int width, int height, ImageType type) {
26         this(FreeImage().allocate(width, height, type));
27     }
28 
29     /**
30     Constructor by image size and bpp.
31     
32     Params:
33         width = image width
34         height = image height
35         bpp = number to specify the bit depth of the image
36     */
37     this(int width, int height, int bpp) {
38         this(FreeImage().allocate(width, height, bpp));
39     }
40 
41     package this(FIBITMAP* bitmap)
42         in(bitmap !is null)
43     {
44         this.bitmap = bitmap;
45     }
46 
47     ~this() {
48         FreeImage().unload(this.bitmap);
49     }
50 
51     /**
52     Load image.
53     This function is same as ImageLoader.load.
54 
55     Params:
56         path = image file path
57 
58     Returns: loaded image
59 
60     See_Also: ImageLoader.load
61     */
62     static Image load(string path) {
63         import sbylib.wrapper.freeimage.imageloader : ImageLoader;
64 
65         return ImageLoader.load(path);
66     }
67 
68     /**
69     Converts a bitmap to 32 bits. A clone of the input bitmap is returned for 32-bit bitmaps.
70     For 48-bit RGB images, conversion is done by dividing each 16-bit channel by 256 and by setting the alpha channel to an opaque value (0xFF). For 64-bit RGBA images, conversion is done by dividing each 16-bit channel by 256. A NULL value is returned for other nonstandard bitmap types.
71 
72     Returns: conveted image
73     */
74     Image to32bit() {
75         return new Image(FreeImage().convertTo32Bits(bitmap));
76     }
77 
78     /**
79     Returns the width of the bitmap in pixel units.
80 
81     Returns: the width of the bitmap in pixel units
82     */
83     int width() {
84         return FreeImage().getWidth(bitmap);
85     }
86 
87     /**
88     Returns the height of the bitmap in pixel units.
89 
90     Returns: the height of the bitmap in pixel units
91     */
92     int height() {
93         return FreeImage().getHeight(bitmap);
94     }
95 
96     /**
97     Returns the size of one pixel in the bitmap in bits. For example when each pixel takes 32-bits of space in the bitmap, this function returns 32. Possible bit depths are 1, 4, 8, 16, 24, 32 for standard bitmaps and 16-, 32-, 48-, 64-, 96- and 128-bit for non standard bitmaps. 
98 
99     Returns: the bit depth of the bitmap
100     */
101     uint bpp() {
102         return FreeImage().getBPP(bitmap);
103     }
104 
105     /**
106     Returns how many channels the image has.
107     This is calculated by (bpp) / (1 channel bits).
108     1 channel bits is estimated by image type.
109 
110     Returns: the channels of the image
111     */
112     uint channels() {
113         return bpp / getTypeBits(imageType);
114     }
115 
116     /**
117     Returns the raw pointer to the image data for low level API.
118 
119     Returns: the pointer to the raw image data.
120     */
121     void* rawPointer() {
122         return FreeImage().getBits(bitmap);
123     }
124 
125     RGBQUAD* palette() {
126         return FreeImage().getPalette(bitmap);
127     }
128 
129     /**
130     Returns the raw image data as T[].
131 
132     Returns: an array pointing to the raw data.
133     */
134     T[] dataArray(T=ubyte)() {
135         return (cast(T*)rawPointer)[0..width * height * bpp / (8*T.sizeof)];
136     }
137 
138     /**
139     Returns the type of the image.
140 
141     Returns: the type of the image
142     */
143     ImageType imageType() {
144         import std.conv : to;
145         return FreeImage_GetImageType(bitmap).to!ImageType;
146     }
147 
148     /**
149     Save the image.
150     The extension is estimated by the output path.
151 
152     Params:
153         path = where the image is saved
154     */
155     void save(string path) {
156         import std.path : extension;
157 
158         switch(path.extension) {
159             case ".bmp":
160                 saveAsBmp(path);
161                 break;
162             case ".exr":
163                 saveAsExr(path);
164                 break;
165             case ".jpeg":
166             case ".jpg":
167                 saveAsJpeg(path);
168                 break;
169             case ".png":
170                 saveAsPng(path);
171                 break;
172             case ".tiff":
173                 saveAsTiff(path);
174                 break;
175             default:
176                 assert(false, "Unrecognized format '" ~ path.extension ~ "'");
177         }
178     }
179 
180     /**
181     Save the image as bmp.
182 
183     Params:
184         path = where the image is saved
185         rle = compress the bitmap using RLE when saving
186     */
187     void saveAsBmp(string path, bool rle = false) {
188         save(ImageFormat.Bmp, path, (rle ? BMP_SAVE_RLE : BMP_DEFAULT));
189     }
190 
191     /**
192     Save the image as exr.
193 
194     Params:
195         path = where the image is saved
196         saveAs32Bit = save data as float instead of as half (not recommended)
197         compress = compression method
198 
199     See_Also: ExrCompressOption
200     */
201     void saveAsExr(string path, bool saveAs32Bit = false, ExrCompressOption compress = ExrCompressOption.None) {
202         save(ImageFormat.Exr, path,
203                   (saveAs32Bit ? EXR_FLOAT : EXR_DEFAULT)
204                 | (compress));
205     }
206 
207     /**
208     Save the image as j2k.
209 
210     Params:
211         path = where the image is saved
212         rate = Save with a rate:1
213     */
214     void saveAsJ2k(string path, int rate = J2K_DEFAULT)
215     in(1 <= rate && rate <= 512)
216     {
217         save(ImageFormat.J2k, path, rate);
218     }
219 
220     /**
221     Save the image as jp2.
222 
223     Params:
224         path = where the image is saved
225         rate = Save with a rate:1
226     */
227     void saveAsJp2(string path, int rate = JP2_DEFAULT)
228     in(1 <= rate && rate <= 512)
229     {
230         save(ImageFormat.Jp2, path, rate);
231     }
232 
233     /**
234     Save the image as jpeg.
235 
236     Params:
237         path = where the image is saved
238         quality = save quality(%)
239         progressive = save as a progressive JPEG file 
240         subsampling = for SubSampling4ab, save with 4:a:b
241         optimize = On saving, compute optimal Huffman coding tables (can reduce a few percent of file size)
242         baseline = Save basic JPEG, without metadata or any markers 
243     */
244     void saveAsJpeg(string path, int quality = 75, bool progressive = false, 
245             JpegSaveSubsamplingOption subsampling = JpegSaveSubsamplingOption.Subsampling420,
246             bool optimize = false, bool baseline = false)
247     in(0 <= quality && quality <= 100)
248     {
249         save(ImageFormat.Jpeg, path, 
250                 quality
251                 | (progressive & JPEG_PROGRESSIVE)
252                 | (subsampling)
253                 | (optimize & JPEG_OPTIMIZE)
254                 | (baseline & JPEG_BASELINE));
255     }
256 
257     /**
258     Save the image as png.
259 
260     Params:
261         path = where the image is saved
262         compress = compression level
263         interlace = Save using Adam7 interlacing 
264     */
265     void saveAsPng(string path,
266             PngCompressOption compress = PngCompressOption.DefaultCompression, bool interlace = false) {
267         save(ImageFormat.Png, path,
268                   (compress)
269                 | (interlace & PNG_INTERLACED));
270     }
271 
272     /**
273     Save the image as pbm.
274 
275     Params:
276         path = where the image is saved
277         option = Saves the bitmap as a binary file or an ascii file
278     */
279     void saveAsPbm(string path, PnmSaveOption option = PnmSaveOption.Raw) {
280         save(ImageFormat.Pbm, path, option);
281     }
282 
283     /**
284     Save the image as pgm.
285 
286     Params:
287         path = where the image is saved
288         option = Saves the bitmap as a binary file or an ascii file
289     */
290     void saveAsPgm(string path, PnmSaveOption option = PnmSaveOption.Raw) {
291         save(ImageFormat.Pgm, path, option);
292     }
293 
294     /**
295     Save the image as ppm.
296 
297     Params:
298         path = where the image is saved
299         option = Saves the bitmap as a binary file or an ascii file
300     */
301     void saveAsPpm(string path, PnmSaveOption option = PnmSaveOption.Raw) {
302         save(ImageFormat.Ppm, path, option);
303     }
304 
305     /**
306     Save the image as tiff.
307 
308     Params:
309         path = where the image is saved
310         cmyk = Stores tags for separated CMYK (use | to combine with
311 TIFF compression flags)
312         compress = compression level
313     */
314     void saveAsTiff(string path, bool cmyk = false, TiffCompressOption compress = TiffCompressOption.LZW) {
315         save(ImageFormat.Tiff, path,
316                   (cmyk & TIFF_CMYK)
317                 | (compress));
318     }
319 
320     private void save(ImageFormat fmt, string path, int option = 0) {
321         import std..string : toStringz;
322 
323         const saveResult = FreeImage_Save(fmt, bitmap, path.toStringz, option);
324 
325         assert(saveResult, format!"Failed to Save '%s'"(path));
326     }
327 
328     private static int getTypeBits(ImageType type) {
329         final switch(type) {
330             case ImageType.Unknown: assert(false, format!"Invalid Enum: %s"(type));
331             case ImageType.Bitmap: return 8;
332             case ImageType.Rgb16: return 16;
333             case ImageType.Rgba16: return 16;
334             case ImageType.Rgbf: return 32;
335             case ImageType.Rgbaf: return 32;
336             case ImageType.Uint16: return 16;
337             case ImageType.Int16: return 16;
338             case ImageType.Uint32: return 32;
339             case ImageType.Int32: return 32;
340             case ImageType.Float: return 32;
341             case ImageType.Double: return 64;
342             case ImageType.Complex: return 128;
343         }
344     }
345 
346     bool flipVertical() {
347         return FreeImage_FlipVertical(bitmap) > 0;
348     }
349 
350     bool flipHorizontal() {
351         return FreeImage_FlipHorizontal(bitmap) > 0;
352     }
353 }