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 }