1 /** 2 General functions. 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.plugin; 8 9 import core.stdc.string; 10 import gamut.types; 11 import gamut.image; 12 import gamut.io; 13 14 import gamut.plugins.jpeg; 15 import gamut.plugins.png; 16 import gamut.plugins.qoi; 17 import gamut.plugins.qoix; 18 import gamut.plugins.dds; 19 20 nothrow @nogc @safe: 21 22 /// Function that loads a image from this format. 23 /// I/O rewinding: this function must be given an I/O cursor at the start of the the format. 24 /// It doesn't have to preserve that I/O cursor. 25 alias LoadImageProc = void function(ref Image image, IOStream *io, IOHandle handle, int page, int flags, void *data); 26 27 /// Saves an image from this format. 28 alias SaveImageProc = bool function(ref const(Image) image, IOStream *io, IOHandle handle, int page, int flags, void *data); 29 30 /// Function that detects this format. 31 /// I/O rewinding: this function must preserve the I/O cursor by contract. 32 alias DetectImageFormatProc = bool function(IOStream *io, IOHandle handle); 33 34 struct ImageFormatPlugin 35 { 36 /// Type string for the bitmap. For example, a plugin that loads BMPs returns the string "BMP". 37 const(char)* format; 38 39 /// Comma-separated list of extension. A JPEG plugin would return "jpeg,jif,jfif". 40 const(char)* extensionList; 41 42 /// MIME types, the first one being the best one. 43 const(char)* mimeTypes; 44 45 LoadImageProc loadProc = null; // null => no read supported 46 SaveImageProc saveProc = null; // null => no write supported 47 DetectImageFormatProc detectProc = null; 48 } 49 50 ImageFormat identifyImageFormatFromFilename(const(char) *filename) @trusted 51 { 52 if (filename is null) 53 return ImageFormat.unknown; 54 55 // find extension inside filename 56 size_t ilen = strlen(filename); 57 size_t pos = ilen; 58 assert(filename[pos] == 0); 59 while(pos > 0 && filename[pos] != '.') 60 pos = pos - 1; 61 if (filename[pos] == '.') 62 pos++; 63 64 int extLength = cast(int)(ilen - pos); 65 66 const(char)* fextension = filename + pos; // ex: "jpg", "png"... 67 68 for(ImageFormat fif = ImageFormat.first; fif <= ImageFormat.max; ++fif) 69 { 70 // Is fextension in the list? 71 const(char)* str = g_plugins[fif].extensionList; 72 73 while(true) 74 { 75 const(char)* end = str; 76 while (*end != ',' && *end != '\0') 77 end++; 78 size_t sublen = end - str; 79 if (sublen == 0) 80 break; 81 82 if (extLength == sublen && strncmp(fextension, str, sublen) == 0) 83 return fif; 84 85 if (*end == '\0') // last extension for this format 86 break; 87 88 str = end + 1; 89 } 90 } 91 return ImageFormat.unknown; 92 } 93 unittest 94 { 95 assert(identifyImageFormatFromFilename("mysueprduperphoto.jpg") == ImageFormat.JPEG); 96 assert(identifyImageFormatFromFilename("mysueprduperphoto.jfif") == ImageFormat.JPEG); 97 assert(identifyImageFormatFromFilename("c:\\compromising-photo.qoi") == ImageFormat.QOI); 98 assert(identifyImageFormatFromFilename("my/path/to/file.qoix") == ImageFormat.QOIX); 99 } 100 101 package: 102 103 104 105 // For now, all plugin resides in a static __gshared part of the memory. 106 static immutable __gshared ImageFormatPlugin[ImageFormat.max+1] g_plugins = 107 [ 108 makeJPEGPlugin(), 109 makePNGPlugin(), 110 makeQOIPlugin(), 111 makeQOIXPlugin(), 112 makeDDSPlugin(), 113 ];