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 }