]> de.git.xonotic.org Git - xonotic/netradiant.git/blobdiff - tools/quake3/common/imagelib.c
ported over the 1.5 branch version of q3map2 which is newer
[xonotic/netradiant.git] / tools / quake3 / common / imagelib.c
index 7a0d22cf2ffbb0084f42a8f2041f02f45ac81d47..f575202355f10b0258a31a3ec9ffc0dd843a07bd 100644 (file)
@@ -21,6 +21,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 
 // imagelib.c
 
+#include "inout.h"
 #include "cmdlib.h"
 #include "imagelib.h"
 #include "vfs.h"
@@ -884,233 +885,242 @@ typedef struct _TargaHeader {
        unsigned char   pixel_size, attributes;
 } TargaHeader;
 
+void TargaError(TargaHeader *t, const char *message)
+{
+       Sys_Printf("%s\n:TargaHeader:\nuint8 id_length = %i;\nuint8 colormap_type = %i;\nuint8 image_type = %i;\nuint16 colormap_index = %i;\nuint16 colormap_length = %i;\nuint8 colormap_size = %i;\nuint16 x_origin = %i;\nuint16 y_origin = %i;\nuint16 width = %i;\nuint16 height = %i;\nuint8 pixel_size = %i;\nuint8 attributes = %i;\n", message, t->id_length, t->colormap_type, t->image_type, t->colormap_index, t->colormap_length, t->colormap_size, t->x_origin, t->y_origin, t->width, t->height, t->pixel_size, t->attributes);
+}
+
 /*
 =============
 LoadTGABuffer
 =============
 */
-void LoadTGABuffer ( byte *buffer, byte **pic, int *width, int *height)
+void LoadTGABuffer (const byte *f, const byte *enddata, byte **pic, int *width, int *height)
 {
-       int             columns, rows, numPixels;
-       byte    *pixbuf;
-       int             row, column;
-       byte    *buf_p;
-       TargaHeader     targa_header;
-       byte            *targa_rgba;
+       int x, y, row_inc, compressed, readpixelcount, red, green, blue, alpha, runlen, pindex, alphabits, image_width, image_height;
+       byte *pixbuf, *image_rgba;
+       const byte *fin;
+       unsigned char *p;
+       TargaHeader targa_header;
+       unsigned char palette[256*4];
 
        *pic = NULL;
 
-       buf_p = buffer;
+       // abort if it is too small to parse
+       if (enddata - f < 19)
+               return;
 
-       targa_header.id_length = *buf_p++;
-       targa_header.colormap_type = *buf_p++;
-       targa_header.image_type = *buf_p++;
-       
-       targa_header.colormap_index = LittleShort ( *(short *)buf_p );
-       buf_p += 2;
-       targa_header.colormap_length = LittleShort ( *(short *)buf_p );
-       buf_p += 2;
-       targa_header.colormap_size = *buf_p++;
-       targa_header.x_origin = LittleShort ( *(short *)buf_p );
-       buf_p += 2;
-       targa_header.y_origin = LittleShort ( *(short *)buf_p );
-       buf_p += 2;
-       targa_header.width = LittleShort ( *(short *)buf_p );
-       buf_p += 2;
-       targa_header.height = LittleShort ( *(short *)buf_p );
-       buf_p += 2;
-       targa_header.pixel_size = *buf_p++;
-       targa_header.attributes = *buf_p++;
-
-       if (targa_header.image_type!=2 
-               && targa_header.image_type!=10
-               && targa_header.image_type != 3 ) 
+       targa_header.id_length = f[0];
+       targa_header.colormap_type = f[1];
+       targa_header.image_type = f[2];
+
+       targa_header.colormap_index = f[3] + f[4] * 256;
+       targa_header.colormap_length = f[5] + f[6] * 256;
+       targa_header.colormap_size = f[7];
+       targa_header.x_origin = f[8] + f[9] * 256;
+       targa_header.y_origin = f[10] + f[11] * 256;
+       targa_header.width = image_width = f[12] + f[13] * 256;
+       targa_header.height = image_height = f[14] + f[15] * 256;
+
+       targa_header.pixel_size = f[16];
+       targa_header.attributes = f[17];
+
+       // advance to end of header
+       fin = f + 18;
+
+       // skip TARGA image comment (usually 0 bytes)
+       fin += targa_header.id_length;
+
+       // read/skip the colormap if present (note: according to the TARGA spec it
+       // can be present even on truecolor or greyscale images, just not used by
+       // the image data)
+       if (targa_header.colormap_type)
        {
-               Error("LoadTGA: Only type 2 (RGB), 3 (gray), and 10 (RGB) TGA images supported\n");
+               if (targa_header.colormap_length > 256)
+               {
+                       TargaError(&targa_header, "LoadTGA: only up to 256 colormap_length supported\n");
+                       return;
+               }
+               if (targa_header.colormap_index)
+               {
+                       TargaError(&targa_header, "LoadTGA: colormap_index not supported\n");
+                       return;
+               }
+               if (targa_header.colormap_size == 24)
+               {
+                       for (x = 0;x < targa_header.colormap_length;x++)
+                       {
+                               palette[x*4+2] = *fin++;
+                               palette[x*4+1] = *fin++;
+                               palette[x*4+0] = *fin++;
+                               palette[x*4+3] = 255;
+                       }
+               }
+               else if (targa_header.colormap_size == 32)
+               {
+                       for (x = 0;x < targa_header.colormap_length;x++)
+                       {
+                               palette[x*4+2] = *fin++;
+                               palette[x*4+1] = *fin++;
+                               palette[x*4+0] = *fin++;
+                               palette[x*4+3] = *fin++;
+                       }
+               }
+               else
+               {
+                       TargaError(&targa_header, "LoadTGA: Only 32 and 24 bit colormap_size supported\n");
+                       return;
+               }
        }
 
-       if ( targa_header.colormap_type != 0 )
+       // check our pixel_size restrictions according to image_type
+       if (targa_header.image_type == 2 || targa_header.image_type == 10)
        {
-               Error("LoadTGA: colormaps not supported\n" );
+               if (targa_header.pixel_size != 24 && targa_header.pixel_size != 32)
+               {
+                       TargaError(&targa_header, "LoadTGA: only 24bit and 32bit pixel sizes supported for type 2 and type 10 images\n");
+                       return;
+               }
+       }
+       else if (targa_header.image_type == 1 || targa_header.image_type == 9)
+       {
+               if (targa_header.pixel_size != 8)
+               {
+                       TargaError(&targa_header, "LoadTGA: only 8bit pixel size for type 1, 3, 9, and 11 images supported\n");
+                       return;
+               }
+       }
+       else if (targa_header.image_type == 3 || targa_header.image_type == 11)
+       {
+               if (targa_header.pixel_size != 8)
+               {
+                       TargaError(&targa_header, "LoadTGA: only 8bit pixel size for type 1, 3, 9, and 11 images supported\n");
+                       return;
+               }
+       }
+       else
+       {
+               TargaError(&targa_header, "LoadTGA: Only type 1, 2, 3, 9, 10, and 11 targa RGB images supported");
+               return;
        }
 
-       if ( ( targa_header.pixel_size != 32 && targa_header.pixel_size != 24 ) && targa_header.image_type != 3 )
+       if (targa_header.attributes & 0x10)
        {
-               Error("LoadTGA: Only 32 or 24 bit images supported (no colormaps)\n");
+               TargaError(&targa_header, "LoadTGA: origin must be in top left or bottom left, top right and bottom right are not supported\n");
+               return;
        }
 
-       columns = targa_header.width;
-       rows = targa_header.height;
-       numPixels = columns * rows;
+       // number of attribute bits per pixel, we only support 0 or 8
+       alphabits = targa_header.attributes & 0x0F;
+       if (alphabits != 8 && alphabits != 0)
+       {
+               TargaError(&targa_header, "LoadTGA: only 0 or 8 attribute (alpha) bits supported\n");
+               return;
+       }
 
-       if (width)
-               *width = columns;
-       if (height)
-               *height = rows;
+       image_rgba = safe_malloc(image_width * image_height * 4);
+       if (!image_rgba)
+       {
+               Sys_Printf("LoadTGA: not enough memory for %i by %i image\n", image_width, image_height);
+               return;
+       }
 
-       targa_rgba = safe_malloc (numPixels*4);
-       *pic = targa_rgba;
+       // If bit 5 of attributes isn't set, the image has been stored from bottom to top
+       if ((targa_header.attributes & 0x20) == 0)
+       {
+               pixbuf = image_rgba + (image_height - 1)*image_width*4;
+               row_inc = -image_width*4*2;
+       }
+       else
+       {
+               pixbuf = image_rgba;
+               row_inc = 0;
+       }
 
-       if (targa_header.id_length != 0)
-               buf_p += targa_header.id_length;  // skip TARGA image comment
-       
-       if ( targa_header.image_type==2 || targa_header.image_type == 3 )
-       { 
-               // Uncompressed RGB or gray scale image
-               for(row=rows-1; row>=0; row--) 
+       compressed = targa_header.image_type == 9 || targa_header.image_type == 10 || targa_header.image_type == 11;
+       x = 0;
+       y = 0;
+       red = green = blue = alpha = 255;
+       while (y < image_height)
+       {
+               // decoder is mostly the same whether it's compressed or not
+               readpixelcount = 1000000;
+               runlen = 1000000;
+               if (compressed && fin < enddata)
+               {
+                       runlen = *fin++;
+                       // high bit indicates this is an RLE compressed run
+                       if (runlen & 0x80)
+                               readpixelcount = 1;
+                       runlen = 1 + (runlen & 0x7f);
+               }
+
+               while((runlen--) && y < image_height)
                {
-                       pixbuf = targa_rgba + row*columns*4;
-                       for(column=0; column<columns; column++) 
+                       if (readpixelcount > 0)
                        {
-                               unsigned char red,green,blue,alphabyte;
-                               switch (targa_header.pixel_size) 
+                               readpixelcount--;
+                               red = green = blue = alpha = 255;
+                               if (fin < enddata)
                                {
-                                       
-                               case 8:
-                                       blue = *buf_p++;
-                                       green = blue;
-                                       red = blue;
-                                       *pixbuf++ = red;
-                                       *pixbuf++ = green;
-                                       *pixbuf++ = blue;
-                                       *pixbuf++ = 255;
-                                       break;
-
-                               case 24:
-                                       blue = *buf_p++;
-                                       green = *buf_p++;
-                                       red = *buf_p++;
-                                       *pixbuf++ = red;
-                                       *pixbuf++ = green;
-                                       *pixbuf++ = blue;
-                                       *pixbuf++ = 255;
-                                       break;
-                               case 32:
-                                       blue = *buf_p++;
-                                       green = *buf_p++;
-                                       red = *buf_p++;
-                                       alphabyte = *buf_p++;
-                                       *pixbuf++ = red;
-                                       *pixbuf++ = green;
-                                       *pixbuf++ = blue;
-                                       *pixbuf++ = alphabyte;
-                                       break;
-                               default:
-                                       //Error("LoadTGA: illegal pixel_size '%d' in file '%s'\n", targa_header.pixel_size, name );
-                                       break;
-                               }
-                       }
-               }
-       }
-       else if (targa_header.image_type==10) {   // Runlength encoded RGB images
-               unsigned char red,green,blue,alphabyte,packetHeader,packetSize,j;
-
-               red = 0;
-               green = 0;
-               blue = 0;
-               alphabyte = 0xff;
-
-               for(row=rows-1; row>=0; row--) {
-                       pixbuf = targa_rgba + row*columns*4;
-                       for(column=0; column<columns; ) {
-                               packetHeader= *buf_p++;
-                               packetSize = 1 + (packetHeader & 0x7f);
-                               if (packetHeader & 0x80) {        // run-length packet
-                                       switch (targa_header.pixel_size) {
-                                               case 24:
-                                                               blue = *buf_p++;
-                                                               green = *buf_p++;
-                                                               red = *buf_p++;
-                                                               alphabyte = 255;
-                                                               break;
-                                               case 32:
-                                                               blue = *buf_p++;
-                                                               green = *buf_p++;
-                                                               red = *buf_p++;
-                                                               alphabyte = *buf_p++;
-                                                               break;
-                                               default:
-                                                       //Error("LoadTGA: illegal pixel_size '%d' in file '%s'\n", targa_header.pixel_size, name );
-                                                       break;
-                                       }
-       
-                                       for(j=0;j<packetSize;j++) {
-                                               *pixbuf++=red;
-                                               *pixbuf++=green;
-                                               *pixbuf++=blue;
-                                               *pixbuf++=alphabyte;
-                                               column++;
-                                               if (column==columns) { // run spans across rows
-                                                       column=0;
-                                                       if (row>0)
-                                                               row--;
-                                                       else
-                                                               goto breakOut;
-                                                       pixbuf = targa_rgba + row*columns*4;
-                                               }
-                                       }
-                               }
-                               else {                            // non run-length packet
-                                       for(j=0;j<packetSize;j++) {
-                                               switch (targa_header.pixel_size) {
-                                                       case 24:
-                                                                       blue = *buf_p++;
-                                                                       green = *buf_p++;
-                                                                       red = *buf_p++;
-                                                                       *pixbuf++ = red;
-                                                                       *pixbuf++ = green;
-                                                                       *pixbuf++ = blue;
-                                                                       *pixbuf++ = 255;
-                                                                       break;
-                                                       case 32:
-                                                                       blue = *buf_p++;
-                                                                       green = *buf_p++;
-                                                                       red = *buf_p++;
-                                                                       alphabyte = *buf_p++;
-                                                                       *pixbuf++ = red;
-                                                                       *pixbuf++ = green;
-                                                                       *pixbuf++ = blue;
-                                                                       *pixbuf++ = alphabyte;
-                                                                       break;
-                                                       default:
-                                                               //Sysprintf("LoadTGA: illegal pixel_size '%d' in file '%s'\n", targa_header.pixel_size, name );
-                                                               break;
-                                               }
-                                               column++;
-                                               if (column==columns) { // pixel packet run spans across rows
-                                                       column=0;
-                                                       if (row>0)
-                                                               row--;
-                                                       else
-                                                               goto breakOut;
-                                                       pixbuf = targa_rgba + row*columns*4;
-                                               }                                               
+                                       switch(targa_header.image_type)
+                                       {
+                                       case 1:
+                                       case 9:
+                                               // colormapped
+                                               pindex = *fin++;
+                                               if (pindex >= targa_header.colormap_length)
+                                                       pindex = 0; // error
+                                               p = palette + pindex * 4;
+                                               red = p[0];
+                                               green = p[1];
+                                               blue = p[2];
+                                               alpha = p[3];
+                                               break;
+                                       case 2:
+                                       case 10:
+                                               // BGR or BGRA
+                                               blue = *fin++;
+                                               if (fin < enddata)
+                                                       green = *fin++;
+                                               if (fin < enddata)
+                                                       red = *fin++;
+                                               if (targa_header.pixel_size == 32 && fin < enddata)
+                                                       alpha = *fin++;
+                                               break;
+                                       case 3:
+                                       case 11:
+                                               // greyscale
+                                               red = green = blue = *fin++;
+                                               break;
                                        }
+                                       if (!alphabits)
+                                               alpha = 255;
                                }
                        }
-                       breakOut:;
-               }
-       }
-
-       // vertically flipped
-       if ( (targa_header.attributes & (1<<5)) ) {
-               int flip;
-               for (row = 0; row < .5f * rows; row++)
-               {
-                       for (column = 0; column < columns; column++)
+                       *pixbuf++ = red;
+                       *pixbuf++ = green;
+                       *pixbuf++ = blue;
+                       *pixbuf++ = alpha;
+                       x++;
+                       if (x == image_width)
                        {
-                               flip = *( (int*)targa_rgba + row * columns + column);
-                               *( (int*)targa_rgba + row * columns + column) = *( (int*)targa_rgba + ( ( rows - 1 ) - row ) * columns + column );
-                               *( (int*)targa_rgba + ( ( rows - 1 ) - row ) * columns + column ) = flip;
+                               // end of line, advance to next
+                               x = 0;
+                               y++;
+                               pixbuf += row_inc;
                        }
                }
        }
 
-       //free(buffer);
+       *pic = image_rgba;
+       if (width)
+               *width = image_width;
+       if (height)
+               *height = image_height;
 }
 
 
-
 /*
 =============
 LoadTGA
@@ -1119,17 +1129,17 @@ LoadTGA
 void LoadTGA (const char *name, byte **pixels, int *width, int *height)
 {
        byte                    *buffer;
-  int nLen;
+       int nLen;
        //
        // load the file
        //
        nLen = vfsLoadFile ( ( char * ) name, (void **)&buffer, 0);
-       if (nLen == -1) 
-  {
+       if (nLen == -1)
+       {
                Error ("Couldn't read %s", name);
-  }
+       }
 
-  LoadTGABuffer(buffer, pixels, width, height);
+       LoadTGABuffer(buffer, buffer + nLen, pixels, width, height);
 
 }