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