The OpenD Programming Language

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 ];