From d7a1b90de57fbeaa187eb2d7875eda8760474ba4 Mon Sep 17 00:00:00 2001
From: Stijn Buys <ingar@osirion.org>
Date: Thu, 27 Mar 2008 20:49:11 +0000
Subject: TGA RLE

---
 src/render/tga.cc | 104 ++++++++++++++++++++++++++++++++++++++++++++++++------
 1 file 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));
-- 
cgit v1.2.3