The OpenD Programming Language

1 /**
2 BMP support.
3 
4 Copyright: Copyright Guillaume Piolat 2022
5 License:   $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
6 */
7 module gamut.plugins.bmp;
8 
9 nothrow @nogc @safe:
10 
11 import core.stdc.stdlib: malloc, free, realloc;
12 import gamut.types;
13 import gamut.io;
14 import gamut.plugin;
15 import gamut.image;
16 import gamut.internals.errors;
17 import gamut.internals.types;
18 
19 
20 version(decodeBMP) import gamut.codecs.stbdec;
21 else version(encodeBMP) import gamut.codecs.bmpenc;
22 
23 ImageFormatPlugin makeBMPPlugin()
24 {
25     ImageFormatPlugin p;
26     p.format = "BMP";
27     p.extensionList = "bmp";
28     p.mimeTypes = "image/bmp";
29     version(decodeBMP)
30         p.loadProc = &loadBMP;
31     else
32         p.loadProc = null;
33     version(encodeBMP)
34         p.saveProc = &saveBMP;
35     else
36         p.saveProc = null;
37     p.detectProc = &detectBMP;
38     return p;
39 }
40 
41 // FUTURE: Note: detection API should report I/O errors other than yes/no for the test, 
42 // since stream might be fatally errored.
43 // Returning a ternary would be extra-nice.
44 
45 bool detectBMP(IOStream *io, IOHandle handle) @trusted
46 {
47     // save I/O cursor
48     c_long offset = io.tell(handle);
49     if (offset == -1) // IO error
50         return false;
51 
52     uint ds;
53     bool err;    
54     ubyte b = io.read_ubyte(handle, &err); if (err) return false; // IO error
55     if (b != 'B') goto no_match;
56 
57     b = io.read_ubyte(handle, &err); if (err) return false; // IO error
58     if (b != 'M') goto no_match;
59     
60     if (!io.skipBytes(handle, 12))
61         return false; // IO error
62 
63     ds = io.read_uint_LE(handle, &err); if (err) return false; // IO error
64 
65     if (ds == 12 || ds == 40 || ds == 52 || ds == 56 || ds == 108 || ds == 124)
66         goto match;
67     else
68         goto no_match;
69 
70 match:
71     // restore I/O cursor
72     if (!io.seekAbsolute(handle, offset))
73         return false; // IO error
74     return true;
75 
76 no_match:
77     // restore I/O cursor
78     if (!io.seekAbsolute(handle, offset))
79         return false; // IO error
80 
81     return false;
82 }
83 
84 version(encodeBMP)
85 bool saveBMP(ref const(Image) image, IOStream *io, IOHandle handle, int page, int flags, void *data) @trusted
86 {
87     if (page != 0)
88         return false;    
89 
90     int components;
91 
92     // For now, can save RGB and RGBA 8-bit images.
93     switch (image._type)
94     {
95         case PixelType.rgb8:
96             components = 3; break;
97         case PixelType.rgba8:
98             components = 4; 
99             break;
100         default:
101             return false;
102     }
103 
104     int width = image._width;
105     int height = image._height;
106     int pitch = image._pitch;
107     if (width < 1 || height < 1 || width > 32767 || height > 32767)
108         return false; // Can't be saved as BMP
109 
110     bool success = write_bmp(image, io, handle, width, height, components);
111 
112     return success;
113 }