diff options
-rw-r--r-- | src/render/tga.cc | 104 |
1 files changed, 94 insertions, 10 deletions
diff --git a/src/render/tga.cc b/src/render/tga.cc index c7f4fc1..d8e2216 100644 --- a/src/render/tga.cc +++ b/src/render/tga.cc @@ -65,7 +65,7 @@ Image *TGA::load(const char *filename) return 0; } - // byte 0 - image ID field lenght + // byte 0 - image ID field length unsigned int tga_idlength = header[0]; // byte 1 - color map type @@ -233,10 +233,10 @@ void TGA::save(const char *filename, Image & image) unsigned char header[18]; memset(header, 0, sizeof(header)); - // byte 0 - image ID field lenght = 0 (no image ID field present) + // byte 0 - image ID field length = 0 (no image ID field present) // byte 1 - color map type = 0 (no palette present) - // byte 2 - image type = 2 (truecolor without RLE) - header[2] = TGA_TRUECOLOR; + // byte 2 - image type = 10 (truecolor RLE encoded) + header[2] = TGA_TRUECOLOR_RLE; // byte 3-11 - palette data (not used) // byte 12+13 - image width header[12] = (image.width() & 0xff); @@ -254,20 +254,104 @@ void TGA::save(const char *filename, Image & image) // write image data // TGA has the R and B channels switched - unsigned char buf[image.channels()]; + unsigned char pixel_data[image.channels()]; + unsigned char block_data[image.channels()*128]; + unsigned char rle_packet; + bool compress = false; + size_t block_length = 0; + for (int y = image.height()-1; y >= 0; y--) { for (size_t x = 0; x < image.width(); x++) { - buf[0] = *image[y*image.width()*image.channels()+x*image.channels()+2]; - buf[1] = *image[y*image.width()*image.channels()+x*image.channels()+1]; - buf[2] = *image[y*image.width()*image.channels()+x*image.channels()]; + size_t index = y*image.width()*image.channels() + x * image.channels(); + pixel_data[0] = *image[index+2]; + pixel_data[1] = *image[index+1]; + pixel_data[2] = *image[index]; if (image.channels() == 4) - buf[3] = *image[y*image.width()*image.channels()+x*image.channels()+3]; + pixel_data[3] = *image[index+3]; + + if (block_length == 0) { + memcpy(block_data, pixel_data, image.channels()); + block_length++; + compress = false; + } else { + if (!compress) { + + // uncompressed block and pixel_data differs from the last pixel + if (memcmp(&block_data[(block_length-1)*image.channels()], pixel_data, image.channels()) != 0) { + // append pixel + memcpy(&block_data[block_length*image.channels()], pixel_data, image.channels()); + + block_length++; + } else { + + // uncompressed block and pixel data is identical + if (block_length > 1 ) { + // write the uncompressed block + rle_packet = block_length - 2; + ofs.write((char *)&rle_packet, 1); + ofs.write((char *)block_data, (block_length-1) * image.channels()); + block_length = 1; + } + memcpy(block_data, pixel_data, image.channels()); + block_length++; + compress = true; + } + + } else { + + // compressed block and pixel data is identical + if (memcmp(block_data, pixel_data, image.channels()) == 0) { + block_length++; + + } else { + + // compressed block and pixel data differs + if (block_length > 1) { + // write the compressed block + rle_packet = block_length + 127; + ofs.write((char *)&rle_packet, 1); + ofs.write((char *)block_data, image.channels()); + block_length = 0; + } + memcpy(&block_data[block_length * image.channels()], pixel_data, image.channels()); + block_length++; + compress = false; + } + } + } + + if (block_length == 128) { + rle_packet = block_length - 1; + if (!compress) { + ofs.write((char *)&rle_packet, 1); + ofs.write((char *)block_data, 128 * image.channels()); + } else { + rle_packet += 128; + ofs.write((char *)&rle_packet, 1); + ofs.write((char *)block_data, image.channels()); + } - ofs.write((char *)buf, image.channels()); + block_length = 0; + compress = false; + } + } + } + + // write remaining bytes + if (block_length) { + rle_packet = block_length - 1; + if (!compress) { + ofs.write((char *)&rle_packet, 1); + ofs.write((char *)block_data, block_length * image.channels()); + } else { + rle_packet += 128; + ofs.write((char *)&rle_packet, 1); + ofs.write((char *)block_data, image.channels()); } } + // write footer (optional, but the specification recommends it) char footer[26]; memset(footer, 0, sizeof(footer)); |