[COMMITTED] TGALoader enhancement to handle compressed TGA files

Here is a patch for the TGALoader class that will read compressed TGA images (at the moment it only reads uncompressed ones).  I originally posted it somewhere else . . . my bad.




Index: src/com/jme/image/util/TGALoader.java
===================================================================
--- src/com/jme/image/util/TGALoader.java   (revision 4081)
+++ src/com/jme/image/util/TGALoader.java   (working copy)
@@ -127,8 +127,8 @@
         // open a stream to the file
         BufferedInputStream bis = new BufferedInputStream(fis, 8192);
         DataInputStream dis = new DataInputStream(bis);
+        boolean createAlpha=false;
         
-       
         //


Start Reading the TGA header
//
         // length of the image id (1 byte)
         int idLength = dis.readUnsignedByte();
@@ -214,13 +214,13 @@
         if ((pixelDepth == 32) || (exp32)) {
             rawData = new byte[width * height * 4];
             dl = 4;
+            createAlpha=true;
         } else {
             rawData = new byte[width * height * 3];
             dl = 3;
         }
         int rawDataIndex = 0;
 
-       
         if (imageType == TYPE_TRUECOLOR) {
             byte red = 0;
             byte green = 0;
@@ -283,9 +283,145 @@
                     }
                 }
             else throw new JmeException("Unsupported TGA true color depth: "+pixelDepth);
+
+
+        } else if( imageType == TYPE_TRUECOLOR_RLE ){
+            byte red = 0;
+            byte green = 0;
+            byte blue = 0;
+            byte alpha = 0;
             
-           
-        } else if (imageType == TYPE_COLORMAPPED) {
+            // Faster than doing a 16-or-24-or-32 check on each individual pixel,
+            // just make a seperate loop for each.
+            if (pixelDepth == 32) {
+               for (int i = 0; i <= (height - 1); ++i) {
+                    if (!flip)
+                        rawDataIndex = (height - 1 - i) * width * dl;
+
+                    for (int j = 0; j < width; ++j) {
+                       // Get the number of pixels the next chunk covers (either packed or unpacked)
+                       int count = dis.readByte();
+                       if( (count & 0x80) != 0 ){
+                          // Its an RLE packed block - use the following 1 pixel for the next <count> pixels
+                          count &= 0x07f;
+                         j+=count;
+                          blue = dis.readByte();
+                         green = dis.readByte();
+                         red = dis.readByte();
+                         alpha = dis.readByte();
+                         while ( count-- >= 0) {
+                            rawData[rawDataIndex++] = red;
+                            rawData[rawDataIndex++] = green;
+                              rawData[rawDataIndex++] = blue;
+                              rawData[rawDataIndex++] = alpha;
+                          }
+                        } else {
+                           // Its not RLE packed, but the next <count> pixels are raw.
+                         j+=count;
+                         while ( count-- >= 0) {
+                             blue = dis.readByte();
+                            green = dis.readByte();
+                            red = dis.readByte();
+                            alpha = dis.readByte();
+                            rawData[rawDataIndex++] = red;
+                            rawData[rawDataIndex++] = green;
+                            rawData[rawDataIndex++] = blue;
+                            rawData[rawDataIndex++] = alpha;
+                           }
+                       }
+                    }
+                }
+            }
+            else if (pixelDepth == 24) {
+               for (int i = 0; i <= (height - 1); i++) {
+                  if (!flip)
+                     rawDataIndex = (height - 1 - i) * width * dl;
+                  for (int j = 0; j < width; ++j) {
+                       // Get the number of pixels the next chunk covers (either packed or unpacked)
+                       int count = dis.readByte();
+                       if( (count & 0x80) != 0 ){
+                          // Its an RLE packed block - use the following 1 pixel for the next <count> pixels
+                          count &= 0x07f;
+                         j+=count;
+                          blue = dis.readByte();
+                         green = dis.readByte();
+                         red = dis.readByte();
+                         while ( count-- >= 0) {
+                           rawData[rawDataIndex++] = red;
+                           rawData[rawDataIndex++] = green;
+                           rawData[rawDataIndex++] = blue;
+                           if (createAlpha) {
+                              rawData[rawDataIndex++] = (byte) 255;
+                           }
+                          }
+                        } else {
+                           // Its not RLE packed, but the next <count> pixels are raw.
+                         j+=count;
+                         while ( count-- >= 0) {
+                             blue = dis.readByte();
+                            green = dis.readByte();
+                            red = dis.readByte();
+                            rawData[rawDataIndex++] = red;
+                            rawData[rawDataIndex++] = green;
+                            rawData[rawDataIndex++] = blue;
+                           if (createAlpha) {
+                              rawData[rawDataIndex++] = (byte) 255;
+                           }
+                           }
+                       }
+                  }
+               }
+            }
+            else if (pixelDepth == 16) {
+                byte[] data = new byte[2];
+                float scalar = 255f/31f;
+                for (int i = 0; i <= (height - 1); i++) {
+                    if (!flip)
+                        rawDataIndex = (height - 1 - i) * width * dl;
+                    for (int j = 0; j < width; j++) {
+                       // Get the number of pixels the next chunk covers (either packed or unpacked)
+                       int count = dis.readByte();
+                       if( (count & 0x80) != 0 ){
+                          // Its an RLE packed block - use the following 1 pixel for the next <count> pixels
+                          count &= 0x07f;
+                         j+=count;
+                           data[1] = dis.readByte();
+                           data[0] = dis.readByte();
+                           blue = (byte)(int)(getBitsAsByte(data, 1, 5) * scalar);
+                           green = (byte)(int)(getBitsAsByte(data, 6, 5) * scalar);
+                           red = (byte)(int)(getBitsAsByte(data, 11, 5) * scalar);
+                         while ( count-- >= 0) {
+                           rawData[rawDataIndex++] = red;
+                           rawData[rawDataIndex++] = green;
+                           rawData[rawDataIndex++] = blue;
+                           if (createAlpha) {
+                              rawData[rawDataIndex++] = (byte) 255;
+                           }
+                          }
+                       } else {
+                          // Its not RLE packed, but the next <count> pixels are raw.
+                          j+=count;
+                          while ( count-- >= 0) {
+                               data[1] = dis.readByte();
+                               data[0] = dis.readByte();
+                               blue = (byte)(int)(getBitsAsByte(data, 1, 5) * scalar);
+                               green = (byte)(int)(getBitsAsByte(data, 6, 5) * scalar);
+                               red = (byte)(int)(getBitsAsByte(data, 11, 5) * scalar);
+                             rawData[rawDataIndex++] = red;
+                             rawData[rawDataIndex++] = green;
+                             rawData[rawDataIndex++] = blue;
+                             if (createAlpha) {
+                                rawData[rawDataIndex++] = (byte) 255;
+                             }
+                          }
+                       }
+                    }
+                }
+            }
+            else throw new JmeException("Unsupported TGA true color depth: "+pixelDepth);
+
+        }
+        else if (imageType == TYPE_COLORMAPPED) {
             int bytesPerIndex = pixelDepth / 8;
             
             if (bytesPerIndex == 1) {

Wow, now that's a patch :smiley:



How much testing have you done with this?

Awesome!

A lot people complained about textures failing to load and this was often the problem.

Well, its not had a huge amount of testing. I originally wrote it to import some textures from Quake maps, and I generated some "testcard" like patterns and they all seemed to come thru it ok. I converted all the textures for a Quake map (using paint.net) to compressed TGA's and they all worked - so I'd say I've tried it with about 30ish textures in total. Not a massive amount, but its pretty simple stuff, just run length encoding really, so odds are if it works on that many it should be pretty reliable.



There are other TGA modes that I didn't bother with as I doubt anyone uses colour mapped or black and white images these days.


If there are no problems with this patch, could someone commit it for me?

committed (rev 4094), would you mind testing this JOC?

Did have a moment of panic there when it didn't work, but after a quick clean and rebuild all appears fine.

My level test worked with all compressed TGA textures looking ok.



Thanks basixs :slight_smile:

awesome, marking this thread as committed :slight_smile: