The OpenD Programming Language

1 /**
2 TGA read/writer from stb_image.h
3 
4 Copyright: Copyright Guillaume Piolat 2023
5 License:   $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
6 */
7 /*
8 ------------------------------------------------------------------------------
9 This software is available under 2 licenses -- choose whichever you prefer.
10 ------------------------------------------------------------------------------
11 ALTERNATIVE A - MIT License
12 Copyright (c) 2017 Sean Barrett
13 Permission is hereby granted, free of charge, to any person obtaining a copy of
14 this software and associated documentation files (the "Software"), to deal in
15 the Software without restriction, including without limitation the rights to
16 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
17 of the Software, and to permit persons to whom the Software is furnished to do
18 so, subject to the following conditions:
19 The above copyright notice and this permission notice shall be included in all
20 copies or substantial portions of the Software.
21 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 SOFTWARE.
28 ------------------------------------------------------------------------------
29 ALTERNATIVE B - Public Domain (www.unlicense.org)
30 This is free and unencumbered software released into the public domain.
31 Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
32 software, either in source code form or as a compiled binary, for any purpose,
33 commercial or non-commercial, and by any means.
34 In jurisdictions that recognize copyright laws, the author or authors of this
35 software dedicate any and all copyright interest in the software to the public
36 domain. We make this dedication for the benefit of the public at large and to
37 the detriment of our heirs and successors. We intend this dedication to be an
38 overt act of relinquishment in perpetuity of all present and future rights to
39 this software under copyright law.
40 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
41 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
42 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
43 AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
44 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
45 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
46 ------------------------------------------------------------------------------
47 */
48 module gamut.codecs.tga;
49 
50 import core.stdc.stdlib: malloc, free;
51 
52 import gamut.scanline;
53 import gamut.types;
54 import gamut.io;
55 
56 
57 /// This supports l8, la8, rgb8, rgba8 as input, and can output RGB8 or RGBA8 .TGA with RLE
58 /// Reference: http://www.paulbourke.net/dataformats/tga/
59 version(encodeTGA)
60 struct TGAEncoder
61 {
62 nothrow:
63 @nogc:
64 
65     IOStream* _io;
66     void* _handle;
67     PixelType _inputType;
68     scanlineConversionFunction_t _scanConvert;
69     int _channels;
70     int _width;
71     int _height;
72     ubyte* scanSpace;
73     byte* similarMask; // 0 => different as previous pixel n-1      1 => same as previous pixel n-1
74     byte* opcode;      // RLE or Raw
75     bool _enableRLE;
76 
77     bool initialize(IOStream* io, void* handle, PixelType inputType, int width, int height, bool enableRLE)
78     {
79         _inputType = inputType;
80         _io = io;
81         _handle = handle;
82         _enableRLE = enableRLE;
83 
84         if (width > 65535) // not supported by TARGA
85             return false;
86         if (height > 65535)
87             return false;
88 
89         _width = width;
90         _height = height;
91         
92         switch(inputType) with (PixelType)
93         {
94             case unknown: assert(false);
95             case l8:
96                 _channels = 3;
97                 _scanConvert = &scanline_convert_l8_to_rgb8; 
98                 break;
99             case la8:   
100                 _channels = 4;
101                 _scanConvert = &scanline_convert_la8_to_rgba8; 
102                 break;
103             case rgb8:  
104                 _channels = 3;
105                 _scanConvert = &scanline_convert_rgb8_to_rgb8; 
106                 break;
107             case rgba8: 
108                 _channels = 4;
109                 _scanConvert = &scanline_convert_rgba8_to_rgba8; 
110                 break;
111             default:
112                 return false; // Unsupported format
113         }
114 
115         // Always puts out a RGB8 or RGBA8 TGA file.
116         // single alloc for all buffers
117         scanSpace = cast(ubyte*) malloc(width * _channels + width*2);
118         similarMask = cast(byte*)(scanSpace + (width * _channels));
119         opcode = similarMask + width;
120 
121         ubyte[18] header;
122         header[0] = 0;
123         header[1] = 0;
124         header[2] = _enableRLE ? 10 : 2;
125         header[3..12] = 0;
126 
127         header[12] = width & 0xff;
128         header[13] = (width & 0xff00) >>> 8;
129         header[14] = height & 0xff;
130         header[15] = (height & 0xff00) >>> 8;
131 
132         header[16] = cast(ubyte)(_channels * 8); // write 24 or 32
133         header[17] = 0;
134 
135         if (18 != io.write(header.ptr, 1, 18, handle))
136             return false;
137 
138         return true;
139     }
140 
141     ~this()
142     {
143         free(scanSpace);
144     }
145 
146     static struct Color
147     {
148         ubyte r, g, b, a;
149     }
150 
151     // Note: .tga scanlines are stored reversed, call this in reverse order.
152     bool encodeScanline(const(void)* scanptr)
153     {
154         if (_width == 0)
155             return true; // in case we ever want 0-width TGA
156 
157         _scanConvert(cast(const(ubyte)*)scanptr, scanSpace, _width, null);
158 
159         // swap R and B, since we need to write in BGRA order
160         if (_channels == 4)
161         {
162             for (int x = 0; x < _width; ++x)
163             {
164                 ubyte r = scanSpace[4*x];
165                 ubyte b = scanSpace[4*x+2];
166                 scanSpace[4*x+2] = r;
167                 scanSpace[4*x] = b;
168             }
169         }
170         else if (_channels == 3)
171         {
172             for (int x = 0; x < _width; ++x)
173             {
174                 ubyte r = scanSpace[3*x];
175                 ubyte b = scanSpace[3*x+2];
176                 scanSpace[3*x+2] = r;
177                 scanSpace[3*x] = b;
178             }
179         }
180         else
181             assert(false);
182 
183         if (_enableRLE)
184         {
185             // Probably need a flag to enable this.
186             bool dropColorInformationInTransparentAreas = false;
187 
188             // 1. Compute a similarity between consecutive pixels
189             {
190                 Color last;  
191                 for (int x = 0; x < _width; ++x)
192                 {
193                     // Read color
194                     Color c;
195                     c.r = scanSpace[_channels*x];
196                     c.g = scanSpace[_channels*x+1];
197                     c.b = scanSpace[_channels*x+2];
198                     c.a = (_channels == 3) ? 255 : scanSpace[x*4+3];
199                     bool similar = (c == last) || (c.a == 0 && dropColorInformationInTransparentAreas);
200                     similarMask[x] = similar ? 1 : 0;
201                     last = c;
202                 }
203                 similarMask[0] = 0;
204             }
205 
206             // 2. Compute the length of subsequent pixels that are all different from earlier ones, 
207             //    or all the same as earlier ones.
208             int numSame = 0; // the number of FOLLOWING pixel that are same as the one considered
209             int numDifferent = 0; // the number of FOLLOWING pixel that are different neighbour by neighbour
210             for (int x = _width - 1; x >= 0; --x)
211             {
212                 // Decision of opcode here. How to best encode that pixel and the following from here?
213                 float bppRaw = (1 + numDifferent * _channels) / cast(float)numDifferent;
214                 float bppRLE = (1 + _channels) / cast(float)numSame;
215 
216                 if (bppRaw <= bppRLE)
217                 {
218                     // Encode as RAW
219                     assert(numDifferent >= 0 && numDifferent < 128);
220                     opcode[x] = cast(byte)numDifferent;
221                 }
222                 else
223                 {
224                     // Encode as RLE
225                     assert(numSame >= 0 && numSame < 128);
226                     opcode[x] = cast(byte)(0x80 | numSame);
227                 }
228 
229                 // Compute chains of pixels for the n-1.
230                 if (similarMask[x])
231                 {
232                     numSame += 1;
233                     if (numSame >= 127)
234                         numSame = 127;
235                     numDifferent = 0;
236                 }
237                 else
238                 {
239                     numDifferent += 1;
240                     if (numDifferent >= 127)
241                         numDifferent = 127;
242                     numSame = 0;
243                 }
244             }
245 
246             // Rewrite similarMask to write the number of times a color is repeated.
247             //      0 means:   single color pixel, can be encoded in either mode
248             // 1..127 means:   Raw, n+1 pixel encodings follow
249             // -1..-128 means: RLE: -n colors not followed by a similar color.
250 
251             // 3. We can encode with the opcode we computed, acts like a trivial decision.
252             int x = 0;
253             for (; x < _width; )
254             {
255                 byte hint = opcode[x];
256 
257                 // The hint can be used directly as opcode.
258                 if (1 != _io.write(&hint, 1, 1, _handle))
259                     return false;
260 
261                 // Amazingly, raw and rle can be handled by same code.
262                 int num = (hint & 127)+1;
263                 size_t nbytes = (hint >= 0) ? num * _channels : _channels;
264                 size_t written = _io.write(&scanSpace[x*_channels], 1, nbytes, _handle);
265                 if (written != nbytes)
266                     return false;
267                 x += num;
268             }
269             assert(x == _width);
270         }
271         else
272         {
273             // only raw encode, just paste the scanline
274             size_t nbytes = _width * _channels;
275             size_t written = _io.write(scanSpace, 1, nbytes, _handle);
276             if (written != nbytes)
277                 return false;
278         }
279         return true;
280     }
281 }
282 
283 version(decodeTGA)
284 struct TGADecoder
285 {
286 nothrow:
287 @nogc:
288 
289     IOStream* _io;
290     void* _handle;
291 
292     ubyte _cmapType; // 0 = not indexed      1 = indexed
293     ushort _paletteStart;
294     ushort _paletteLen;
295     ubyte _imageType; 
296     ubyte _cmapSize;
297     ubyte _bpp;
298     int _inverted;
299     int _width = 0;
300     int _height = 0;
301     bool _isRLE;
302     bool _rgb16;
303     ubyte _dataOffset;
304 
305     bool initialize(IOStream* io, IOHandle handle)
306     {
307         _io = io;
308         _handle = handle;
309         return true;
310     }
311 
312     // If successful, this is a probable TGA (header isn't very decisive...) and you can read _width/_height/_bpp
313     bool getImageInfo()
314     {
315         // Taken right from stb_image.h
316 
317         bool err;
318         _dataOffset = _io.read_ubyte(_handle, &err);
319         if (err)
320             return false;
321         
322         _cmapType = _io.read_ubyte(_handle, &err);
323         if (err || _cmapType > 1)
324             return false; // only RGB or indexed allowed
325 
326         _imageType = _io.read_ubyte(_handle, &err);
327         if (err) return false;
328 
329         if (_cmapType == 1) // has colormap
330         {
331             if (_imageType != 1 && _imageType != 9)
332                 return false;
333 
334             _paletteStart = _io.read_ushort_LE(_handle, &err);
335             if (err)
336                 return false;
337             _paletteLen = _io.read_ushort_LE(_handle, &err);
338             if (err)
339                 return false;
340 
341             if (_paletteLen == 0)
342                 return false; // no entry in palette
343 
344             _cmapSize = _io.read_ubyte(_handle, &err); // check bits per palette color entry
345             if (err) 
346                 return false;
347 
348             if ( (_cmapSize != 8) && (_cmapSize != 15) && (_cmapSize != 16) && (_cmapSize != 24) && (_cmapSize != 32) ) 
349             {
350                 return false;
351             }
352 
353             if (!_io.skipBytes(_handle, 4)) // image x and y origin
354                 return false;
355         }
356         else // no colormap
357         {
358             // grey and RGB, RLE or not
359             if ( (_imageType != 2) && (_imageType != 3) && (_imageType != 10) && (_imageType != 11) )
360                 return false;
361 
362             if (!_io.skipBytes(_handle, 9))
363                 return false;
364         }
365 
366         _width = _io.read_ushort_LE(_handle, &err);
367         if (err) return false;
368         _height = _io.read_ushort_LE(_handle, &err);
369         if (err) return false;
370         if (_width < 1 || _height < 1)
371             return false; // Apparently size 0 not allowed
372 
373         _bpp = _io.read_ubyte(_handle, &err); //   bits per pixel
374         if (err) return false; 
375 
376         if ( (_cmapType == 1) && (_bpp != 8) && (_bpp != 16) ) // for colormapped images, bpp is size of an index
377             return false;
378         if ( (_bpp != 8) && (_bpp != 15) && (_bpp != 16) && (_bpp != 24) && (_bpp != 32) )
379             return false;
380 
381         return true; // OK, this is maybe a TGA
382     }
383 
384     ubyte* decodeImage(int* outComponents)
385     {
386         // Bit of post-processing.
387         _isRLE = false;
388         if (_imageType >= 8) // 10 and 11 become 2 and 3
389         {
390             _imageType -= 8;
391             _isRLE = true;
392         }
393 
394         bool err;
395         _inverted = _io.read_ubyte(_handle, &err); //   bits per pixel
396         if (err) 
397             return null; 
398         _inverted = 1 - ((_inverted >> 5) & 1);
399 
400         bool isIndexed = _cmapType != 0;
401 
402         //   If I'm paletted, then I'll use the number of bits from the palette
403         int components;
404         if (isIndexed)
405             components = stbi__tga_get_comp(_cmapSize, 0, &_rgb16);
406         else 
407             components = stbi__tga_get_comp(_bpp, (_imageType == 3), &_rgb16);
408         assert(components != 0); // should have been taken care of by earlier getImageInfo()
409 
410         if (!_io.skipBytes(_handle, _dataOffset))
411             return null;
412 
413         long allocationSize = (cast(long)_width)*_height * components;
414         if (allocationSize > GAMUT_MAX_IMAGE_BYTES)
415             return null;
416         if (allocationSize >= cast(ulong)(size_t.max))
417             return null;
418 
419         ubyte* data = cast(ubyte*) malloc(cast(size_t)allocationSize);
420         ubyte* palette = null;
421 
422         if ( !isIndexed && !_isRLE && !_rgb16 ) 
423         {
424             for (int i = 0; i < _height; ++i) 
425             {
426                 int row = _inverted ? _height - i - 1 : i;
427                 ubyte* prow = data + row * _width * components;
428                 size_t bytes = _width * components;
429                 if (bytes != _io.read(prow, 1, bytes, _handle))
430                 {
431                     free(data);
432                     return null;
433                 }
434             }
435         } 
436         else
437         {
438             // Is there a palette?
439             if (isIndexed)
440             {
441                 if (!_io.skipBytes(_handle, _paletteStart))
442                     goto errored;
443 
444                 palette = cast(ubyte*) malloc(_paletteLen * components); // PERF: could be allocated as extra bytes
445 
446                 if (_rgb16) 
447                 {
448                     ubyte* pal_entry = palette;
449                     assert(components == 3);
450                     for (int i = 0; i < _paletteLen; ++i) 
451                     {
452                         stbi__tga_read_rgb16(pal_entry, &err);
453                         if (err)
454                             goto errored;
455                         pal_entry += components;
456                     }
457                 } 
458                 else
459                 {
460                     // Read whole palette at once.
461                     size_t bytes = _paletteLen * components;
462                     if (bytes != _io.read(palette, 1, bytes, _handle))
463                         goto errored_with_palette;
464                 }
465             }
466 
467             int RLE_count = 0;
468             int RLE_repeating = 0;
469             int read_next_pixel = 1;
470             ubyte[4] raw_data;
471 
472             // Load the data
473             for (int i = 0; i < _width * _height; ++i)
474             {
475                 //   if I'm in RLE mode, do I need to get a RLE stbi__pngchunk?
476                 if (_isRLE)
477                 {
478                     if (RLE_count == 0)
479                     {
480                         //   yep, get the next byte as a RLE command
481                         int RLE_cmd = _io.read_ubyte(_handle, &err);
482                         if (err)
483                             goto errored_with_palette;
484 
485                         RLE_count = 1 + (RLE_cmd & 127);
486                         RLE_repeating = RLE_cmd >> 7;
487                         read_next_pixel = 1;
488                     } 
489                     else if ( !RLE_repeating )
490                     {
491                         read_next_pixel = 1;
492                     }
493                 } 
494                 else
495                 {
496                     read_next_pixel = 1;
497                 }
498 
499                 //   OK, if I need to read a pixel, do it now
500                 if ( read_next_pixel )
501                 {
502                     //   load however much data we did have
503                     if (isIndexed)
504                     {
505                         // read in index, then perform the lookup
506                         int pal_idx;
507                         if (_bpp == 8)
508                             pal_idx = _io.read_ubyte(_handle, &err);
509                         else
510                             pal_idx = _io.read_ushort_LE(_handle, &err);
511                         if (err)
512                             goto errored_with_palette;
513 
514                         if (pal_idx >= _paletteLen) 
515                         {
516                             // invalid index
517                             pal_idx = 0;
518                         }
519                         pal_idx *= components;
520                         for (int j = 0; j < components; ++j) 
521                         {
522                             raw_data[j] = palette[pal_idx + j];
523                         }
524                     } 
525                     else if (_rgb16) 
526                     {
527                         assert(components == 3);
528                         stbi__tga_read_rgb16(raw_data.ptr, &err);
529                         if (err)
530                             goto errored_with_palette;
531                     } 
532                     else 
533                     {
534                         //   read in the data raw
535                         for (int j = 0; j < components; ++j) 
536                         {
537                             raw_data[j] = _io.read_ubyte(_handle, &err);
538                             if (err)
539                                 goto errored_with_palette;
540                         }
541                     }
542                     //   clear the reading flag for the next pixel
543                     read_next_pixel = 0;
544                 } // end of reading a pixel
545 
546                 // copy data
547                 for (int j = 0; j < components; ++j)
548                 {
549                     data[i * components + j] = raw_data[j];
550                 }
551 
552                 //   in case we're in RLE mode, keep counting down
553                 --RLE_count;
554             }
555             
556             //   do I need to invert the image?
557             if (_inverted)
558             {
559                 for (int j = 0; j * 2 < _height; ++j)
560                 {
561                     int index1 = j * _width * components;
562                     int index2 = (_height - 1 - j) * _width * components;
563                     for (int i = _width * components; i > 0; --i)
564                     {
565                         ubyte temp = data[index1];
566                         data[index1] = data[index2];
567                         data[index2] = temp;
568                         ++index1;
569                         ++index2;
570                     }
571                 }
572             }
573             free(palette);
574         }
575 
576         // swap RB - if the source data was RGB16, it already is in the right order
577         if (components >= 3 && !_rgb16)
578         {
579             ubyte* tga_pixel = data;
580             for (int i = 0; i < _width * _height; ++i)
581             {
582                 ubyte temp = tga_pixel[0];
583                 tga_pixel[0] = tga_pixel[2];
584                 tga_pixel[2] = temp;
585                 tga_pixel += components;
586             }
587         }
588 
589         *outComponents = components;
590         return data;
591 
592         errored_with_palette:
593             free(palette);
594 
595         errored:
596             free(data);
597             return null;
598     }
599 
600 
601     int stbi__tga_get_comp(int bits_per_pixel, int is_grey, bool* is_rgb16)
602     {
603         // only RGB or RGBA (incl. 16bit) or grey allowed
604         *is_rgb16 = false;
605         switch(bits_per_pixel) 
606         {
607         case 8:  
608             return 1;
609         case 16: 
610             if(is_grey) 
611                 return 2;
612             goto case 15;
613         case 15: 
614             *is_rgb16 = true;
615             return 3;
616         case 24: 
617             goto case 32;
618         case 32: 
619             return bits_per_pixel / 8;
620         default: 
621             return 0;
622         }
623     }
624 
625     void stbi__tga_read_rgb16(ubyte* outColor, bool* err)
626     {
627         ushort px = _io.read_ushort_LE(_handle, err);
628         if (*err)
629             return;
630         
631         ushort fiveBitMask = 31;
632 
633         // we have 3 channels with 5bits each
634         int r = (px >> 10) & fiveBitMask;
635         int g = (px >> 5) & fiveBitMask;
636         int b = px & fiveBitMask;
637         // Note that this saves the data in RGB(A) order, so it doesn't need to be swapped later
638         outColor[0] = cast(ubyte)((r * 255)/31);
639         outColor[1] = cast(ubyte)((g * 255)/31);
640         outColor[2] = cast(ubyte)((b * 255)/31);
641 
642         // some people claim that the most significant bit might be used for alpha
643         // (possibly if an alpha-bit is set in the "image descriptor byte")
644         // but that only made 16bit test images completely translucent..
645         // so let's treat all 15 and 16bit TGAs as RGB with no alpha.
646     }
647 }