The OpenD Programming Language

1 /**
2 TGA support.
3 
4 Copyright: Copyright Guillaume Piolat 2023
5 License:   $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
6 */
7 module gamut.plugins.tga;
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 version(decodeTGA) import gamut.codecs.tga;
20 else version(encodeTGA) import gamut.codecs.tga;
21 
22 ImageFormatPlugin makeTGAPlugin()
23 {
24     ImageFormatPlugin p;
25     p.format = "TGA";
26     p.extensionList = "tga";
27     p.mimeTypes = "image/tga";
28     version(decodeTGA)
29         p.loadProc = &loadTGA;
30     else
31         p.loadProc = null;
32     version(encodeTGA)
33         p.saveProc = &saveTGA;
34     else
35         p.saveProc = null;
36     p.detectProc = &detectTGA;
37     return p;
38 }
39 
40 
41 version(decodeTGA)
42 void loadTGA(ref Image image, IOStream *io, IOHandle handle, int page, int flags, void *data) @trusted
43 {
44     TGADecoder decoder;
45     if (!decoder.initialize(io, handle))
46     {
47         image.error(kStrImageDecodingFailed);
48         return;
49     }
50     
51     if (!decoder.getImageInfo())
52     {
53         image.error(kStrImageDecodingFailed);
54         return;
55     }
56 
57     if (!imageIsValidSize(1, decoder._width, decoder._height))
58     {
59         image.error(kStrImageTooLarge);
60         return;
61     }
62 
63     // Allocate space for loaded image, following constraints.
64     int decodedComponents;
65     ubyte* decoded = decoder.decodeImage(&decodedComponents);
66 
67     if (decoded is null)
68     {
69         image.error(kStrImageDecodingFailed);
70         return;
71     }
72 
73     if (decodedComponents == 1)
74         image._type = PixelType.l8;
75     else if (decodedComponents == 2)
76         image._type = PixelType.la8;
77     else if (decodedComponents == 3)
78         image._type = PixelType.rgb8;
79     else if (decodedComponents == 4)
80         image._type = PixelType.rgba8;
81 
82     image._width = decoder._width;
83     image._height = decoder._height;
84     image._allocArea = decoded;
85     image._data = decoded;
86     image._pitch = decoder._width * decodedComponents;
87     image._pixelAspectRatio = GAMUT_UNKNOWN_ASPECT_RATIO;
88     image._resolutionY = GAMUT_UNKNOWN_RESOLUTION;
89     image._layoutConstraints = LAYOUT_DEFAULT;
90     image._layerCount = 1;
91     image._layerOffset = 0;
92 
93     // Convert to target type and constraints
94     image.convertTo(applyLoadFlags(image._type, flags), cast(LayoutConstraints) flags);
95 }
96 
97 bool detectTGA(IOStream *io, IOHandle handle) @trusted
98 {
99     version(decodeTGA)
100     {
101         // save I/O cursor
102         c_long offset = io.tell(handle);
103 
104         bool res = false;
105         {
106             TGADecoder decoder;
107             if (decoder.initialize(io, handle))
108             {
109                 res = decoder.getImageInfo();
110             }
111         }
112 
113         // restore I/O cursor
114         if (!io.seekAbsolute(handle, offset))
115         {
116             // TODO: that rare error should propagate somehow, 
117             // we couldn't reset the cursor hence more detection will fail.
118             return false; 
119         }
120         return res;
121     }
122     else
123     {
124         return false;
125     }
126 }
127 
128 version(encodeTGA)
129 bool saveTGA(ref const(Image) image, IOStream *io, IOHandle handle, int page, int flags, void *data) @trusted
130 {
131     if (page != 0)
132         return false;
133 
134     TGAEncoder encoder;
135 
136     bool enableRLE = true; // No real reason not to enable RLE.
137     if (!encoder.initialize(io, handle, image._type, image._width, image._height, enableRLE))
138     {
139         return false;
140     }
141 
142     // Encode scanline one by one, to allow conversion on write.
143     for (int y = image._height - 1; y >= 0; --y)
144     {
145         if (!encoder.encodeScanline(image.scanptr(y)))
146             return false;
147     }
148 
149     return true;
150 }