]> git.imager.perl.org - imager.git/blobdiff - tga.c
i_img_info() (C API) no longer tries to handle a NULL image object pointer.
[imager.git] / tga.c
diff --git a/tga.c b/tga.c
index b26081091b26811f41e6631d410f7d40a161bd8e..42e66621f79cc4fb9e9aac6f28f914ca3a64dab8 100644 (file)
--- a/tga.c
+++ b/tga.c
@@ -46,8 +46,8 @@ typedef struct {
   char  colourmapdepth;
   short int x_origin;
   short int y_origin;
-  short width;
-  short height;
+  int width;
+  int height;
   char  bitsperpixel;
   char  imagedescriptor;
 } tga_header;
@@ -57,7 +57,7 @@ typedef enum { NoInit, Raw, Rle } rle_state;
 
 typedef struct {
   int compressed;
-  int bytepp;
+  size_t bytepp;
   rle_state state;
   unsigned char cval[4];
   int len;
@@ -72,7 +72,7 @@ typedef struct {
   io_glue *ig;
 } tga_dest;
 
-
+#define TGA_MAX_DIM 0xFFFF
 
 /*
 =item bpp_to_bytes(bpp)
@@ -86,7 +86,7 @@ Convert bits per pixel into bytes per pixel
 
 
 static
-int
+size_t
 bpp_to_bytes(unsigned int bpp) {
   switch (bpp) {
   case 8:
@@ -107,27 +107,31 @@ bpp_to_bytes(unsigned int bpp) {
 /*
 =item bpp_to_channels(bpp)
 
-Convert bits per pixel into channels in the image
+Convert bits per pixel and the number of attribute bits into channels
+in the image
 
    bpp - bits per pixel
+   attr_bit_count - number of attribute bits
 
 =cut
 */
 
 static
 int
-bpp_to_channels(unsigned int bpp) {
+bpp_to_channels(unsigned int bpp, int attr_bit_count) {
   switch (bpp) {
   case 8:
     return 1;
+  case 16:
+    if (attr_bit_count == 1)
+      return 4;
   case 15:
     return 3;
-  case 16:
-    return 4;
+  case 32:
+    if (attr_bit_count == 8)
+      return 4;
   case 24:
     return 3;
-  case 32:
-    return 4;
   }
   return 0;
 }
@@ -167,7 +171,7 @@ color_unpack(unsigned char *buf, int bytepp, i_color *val) {
     val->rgba.r = (buf[1] & 0x7c) << 1;
     val->rgba.g = ((buf[1] & 0x03) << 6) | ((buf[0] & 0xe0) >> 2);
     val->rgba.b = (buf[0] & 0x1f) << 3;
-    val->rgba.a = (buf[1] & 0x80) ? 255 : 0;
+    val->rgba.a = (buf[1] & 0x80) ? 0 : 255;
     val->rgba.r |= val->rgba.r >> 5;
     val->rgba.g |= val->rgba.g >> 5;
     val->rgba.b |= val->rgba.b >> 5;
@@ -211,13 +215,18 @@ color_pack(unsigned char *buf, int bitspp, i_color *val) {
   case 8:
     buf[0] = val->gray.gray_color;
     break;
+  case 16:
+    buf[0]  = (val->rgba.b >> 3);
+    buf[0] |= (val->rgba.g & 0x38) << 2;
+    buf[1]  = (val->rgba.r & 0xf8)>> 1;
+    buf[1] |= (val->rgba.g >> 6);
+    buf[1] |=  val->rgba.a > 0x7f ? 0 : 0x80;
+    break;
   case 15:
     buf[0]  = (val->rgba.b >> 3);
     buf[0] |= (val->rgba.g & 0x38) << 2;
     buf[1]  = (val->rgba.r & 0xf8)>> 1;
     buf[1] |= (val->rgba.g >> 6);
-  case 16:
-    buf[1] |=  val->rgba.a & 0x80;
     break;
   case 24:
     buf[0] = val->rgb.b;
@@ -340,7 +349,7 @@ tga_header_verify(unsigned char headbuf[18]) {
   case 2:  /* Uncompressed, rgb images          */ 
   case 10: /* Compressed,   rgb images          */ 
     if (header.bitsperpixel != 15 && header.bitsperpixel != 16
-       && header.bitsperpixel != 24 && header.bitsperpixel != 23)
+       && header.bitsperpixel != 24 && header.bitsperpixel != 32)
       return 0;
     break;
        }
@@ -424,7 +433,7 @@ int
 tga_source_read(tga_source *s, unsigned char *buf, size_t pixels) {
   int cp = 0, j, k;
   if (!s->compressed) {
-    if (s->ig->readcb(s->ig, buf, pixels*s->bytepp) != pixels*s->bytepp) return 0;
+    if (i_io_read(s->ig, buf, pixels*s->bytepp) != pixels*s->bytepp) return 0;
     return 1;
   }
   
@@ -433,7 +442,7 @@ tga_source_read(tga_source *s, unsigned char *buf, size_t pixels) {
     if (s->len == 0) s->state = NoInit;
     switch (s->state) {
     case NoInit:
-      if (s->ig->readcb(s->ig, &s->hdr, 1) != 1) return 0;
+      if (i_io_read(s->ig, &s->hdr, 1) != 1) return 0;
 
       s->len = (s->hdr &~(1<<7))+1;
       s->state = (s->hdr & (1<<7)) ? Rle : Raw;
@@ -443,7 +452,7 @@ tga_source_read(tga_source *s, unsigned char *buf, size_t pixels) {
        printf("%04d %s: %d\n", cnt++, s->state==Rle?"RLE":"RAW", s->len);
  */
      }
-      if (s->state == Rle && s->ig->readcb(s->ig, s->cval, s->bytepp) != s->bytepp) return 0;
+      if (s->state == Rle && i_io_read(s->ig, s->cval, s->bytepp) != s->bytepp) return 0;
 
       break;
     case Rle:
@@ -455,7 +464,7 @@ tga_source_read(tga_source *s, unsigned char *buf, size_t pixels) {
       break;
     case Raw:
       ml = i_min(s->len, pixels-cp);
-      if (s->ig->readcb(s->ig, buf+cp*s->bytepp, ml*s->bytepp) != ml*s->bytepp) return 0;
+      if (i_io_read(s->ig, buf+cp*s->bytepp, ml*s->bytepp) != ml*s->bytepp) return 0;
       cp     += ml;
       s->len -= ml;
       break;
@@ -486,7 +495,7 @@ tga_dest_write(tga_dest *s, unsigned char *buf, size_t pixels) {
   int cp = 0;
 
   if (!s->compressed) {
-    if (s->ig->writecb(s->ig, buf, pixels*s->bytepp) != pixels*s->bytepp) return 0;
+    if (i_io_write(s->ig, buf, pixels*s->bytepp) != pixels*s->bytepp) return 0;
     return 1;
   }
   
@@ -497,9 +506,9 @@ tga_dest_write(tga_dest *s, unsigned char *buf, size_t pixels) {
     while(tlen) {
       unsigned char clen = (tlen>128) ? 128 : tlen;
       clen--;
-      if (s->ig->writecb(s->ig, &clen, 1) != 1) return 0;
+      if (i_io_write(s->ig, &clen, 1) != 1) return 0;
       clen++;
-      if (s->ig->writecb(s->ig, buf+cp*s->bytepp, clen*s->bytepp) != clen*s->bytepp) return 0;
+      if (i_io_write(s->ig, buf+cp*s->bytepp, clen*s->bytepp) != clen*s->bytepp) return 0;
       tlen -= clen;
       cp += clen;
     }
@@ -509,9 +518,9 @@ tga_dest_write(tga_dest *s, unsigned char *buf, size_t pixels) {
     while (tlen) {
       unsigned char clen = (tlen>128) ? 128 : tlen;
       clen = (clen - 1) | 0x80;
-      if (s->ig->writecb(s->ig, &clen, 1) != 1) return 0;
+      if (i_io_write(s->ig, &clen, 1) != 1) return 0;
       clen = (clen & ~0x80) + 1;
-      if (s->ig->writecb(s->ig, buf+cp*s->bytepp, s->bytepp) != s->bytepp) return 0;
+      if (i_io_write(s->ig, buf+cp*s->bytepp, s->bytepp) != s->bytepp) return 0;
       tlen -= clen;
       cp += clen;
     }
@@ -549,8 +558,8 @@ tga_palette_read(io_glue *ig, i_img *img, int bytepp, int colourmaplength) {
   palbsize = colourmaplength*bytepp;
   palbuf   = mymalloc(palbsize);
   
-  if (ig->readcb(ig, palbuf, palbsize) != palbsize) {
-    i_push_error(errno, "could not read targa colourmap");
+  if (i_io_read(ig, palbuf, palbsize) != palbsize) {
+    i_push_error(errno, "could not read targa colormap");
     return 0;
   }
   
@@ -581,7 +590,7 @@ static
 int
 tga_palette_write(io_glue *ig, i_img *img, int bitspp, int colourmaplength) {
   int i;
-  int bytepp = bpp_to_bytes(bitspp);
+  size_t bytepp = bpp_to_bytes(bitspp);
   size_t palbsize = i_colorcount(img)*bytepp;
   unsigned char *palbuf = mymalloc(palbsize);
   
@@ -591,8 +600,8 @@ tga_palette_write(io_glue *ig, i_img *img, int bitspp, int colourmaplength) {
     color_pack(palbuf+i*bytepp, bitspp, &val);
   }
   
-  if (ig->writecb(ig, palbuf, palbsize) != palbsize) {
-    i_push_error(errno, "could not write targa colourmap");
+  if (i_io_write(ig, palbuf, palbsize) != palbsize) {
+    i_push_error(errno, "could not write targa colormap");
     return 0;
   }
   myfree(palbuf);
@@ -632,9 +641,7 @@ i_readtga_wiol(io_glue *ig, int length) {
 
   mm_log((1,"i_readtga(ig %p, length %d)\n", ig, length));
   
-  io_glue_commit_types(ig);
-
-  if (ig->readcb(ig, &headbuf, 18) != 18) {
+  if (i_io_read(ig, &headbuf, 18) != 18) {
     i_push_error(errno, "could not read targa header");
     return NULL;
   }
@@ -655,8 +662,9 @@ i_readtga_wiol(io_glue *ig, int length) {
   mm_log((1,"Descriptor:        %d\n",header.imagedescriptor));
 
   if (header.idlength) {
+    /* max of 256, so this is safe */
     idstring = mymalloc(header.idlength+1);
-    if (ig->readcb(ig, idstring, header.idlength) != header.idlength) {
+    if (i_io_read(ig, idstring, header.idlength) != header.idlength) {
       i_push_error(errno, "short read on targa idstring");
       return NULL;
     }
@@ -722,7 +730,8 @@ i_readtga_wiol(io_glue *ig, int length) {
   case 9:  /* Compressed,   color-mapped images */
     if ((channels = bpp_to_channels(mapped ? 
                                   header.colourmapdepth : 
-                                  header.bitsperpixel))) break;
+                                  header.bitsperpixel,
+                                   header.imagedescriptor & 0xF))) break;
     i_push_error(0, "Targa Image has none of 15/16/24/32 pixel layout");
     if (idstring) myfree(idstring);
     return NULL;
@@ -732,6 +741,9 @@ i_readtga_wiol(io_glue *ig, int length) {
     mapped = 0;
     channels = 1;
     break;
+  default:
+    i_push_error(0, "invalid or unsupported datatype code");
+    return NULL;
   }
 
   if (!i_int_check_image_file_limits(width, height, channels, 
@@ -761,19 +773,21 @@ i_readtga_wiol(io_glue *ig, int length) {
                        bpp_to_bytes(header.colourmapdepth),
                        header.colourmaplength)
       ) {
-    i_push_error(0, "Targa Image has none of 15/16/24/32 pixel layout");
-    if (idstring) myfree(idstring);
+    /* tga_palette_read() sets a message */
     if (img) i_img_destroy(img);
     return NULL;
   }
   
   /* Allocate buffers */
+  /* width is max 0xffff, src.bytepp is max 4, so this is safe */
   databuf = mymalloc(width*src.bytepp);
+  /* similarly here */
   if (!mapped) linebuf = mymalloc(width*sizeof(i_color));
   
   for(y=0; y<height; y++) {
     if (!tga_source_read(&src, databuf, width)) {
       i_push_error(errno, "read for targa data failed");
+      if (linebuf) myfree(linebuf);
       myfree(databuf);
       if (img) i_img_destroy(img);
       return NULL;
@@ -813,6 +827,7 @@ i_writetga_wiol(i_img *img, io_glue *ig, int wierdpack, int compress, char *idst
   tga_dest dest;
   unsigned char headbuf[18];
   unsigned int bitspp;
+  unsigned int attr_bits = 0;
   
   int mapped;
 
@@ -827,13 +842,18 @@ i_writetga_wiol(i_img *img, io_glue *ig, int wierdpack, int compress, char *idst
   idlen = strlen(idstring);
   mapped = img->type == i_palette_type;
 
-  mm_log((1,"i_writetga_wiol(img %p, ig %p, idstring %p, idlen %d, wierdpack %d, compress %d)\n",
-         img, ig, idstring, idlen, wierdpack, compress));
+  mm_log((1,"i_writetga_wiol(img %p, ig %p, idstring %p, idlen %ld, wierdpack %d, compress %d)\n",
+         img, ig, idstring, (long)idlen, wierdpack, compress));
   mm_log((1, "virtual %d, paletted %d\n", img->virtual, mapped));
   mm_log((1, "channels %d\n", img->channels));
   
   i_clear_error();
-  
+
+  if (img->xsize > TGA_MAX_DIM || img->ysize > TGA_MAX_DIM) {
+    i_push_error(0, "image too large for TGA");
+    return 0;
+  }
+
   switch (img->channels) {
   case 1:
     bitspp = 8;
@@ -851,14 +871,13 @@ i_writetga_wiol(i_img *img, io_glue *ig, int wierdpack, int compress, char *idst
     break;
   case 4:
     bitspp = wierdpack ? 16 : 32;
+    attr_bits = wierdpack ? 1 : 8;
     break;
   default:
     i_push_error(0, "Targa only handles 1,3 and 4 channel images.");
     return 0;
   }
 
-  io_glue_commit_types(ig);
-  
   header.idlength = idlen;
   header.colourmaptype   = mapped ? 1 : 0;
   header.datatypecode    = mapped ? 1 : img->channels == 1 ? 3 : 2;
@@ -872,17 +891,17 @@ i_writetga_wiol(i_img *img, io_glue *ig, int wierdpack, int compress, char *idst
   header.width           = img->xsize;
   header.height          = img->ysize;
   header.bitsperpixel    = mapped ? 8 : bitspp;
-  header.imagedescriptor = (1<<5); /* normal order instead of upside down */
+  header.imagedescriptor = (1<<5) | attr_bits; /* normal order instead of upside down */
 
   tga_header_pack(&header, headbuf);
 
-  if (ig->writecb(ig, &headbuf, sizeof(headbuf)) != sizeof(headbuf)) {
+  if (i_io_write(ig, &headbuf, sizeof(headbuf)) != sizeof(headbuf)) {
     i_push_error(errno, "could not write targa header");
     return 0;
   }
 
   if (idlen) {
-    if (ig->writecb(ig, idstring, idlen) != idlen) {
+    if (i_io_write(ig, idstring, idlen) != idlen) {
       i_push_error(errno, "could not write targa idstring");
       return 0;
     }
@@ -900,7 +919,7 @@ i_writetga_wiol(i_img *img, io_glue *ig, int wierdpack, int compress, char *idst
     if (!tga_palette_write(ig, img, bitspp, i_colorcount(img))) return 0;
     
     if (!img->virtual && !dest.compressed) {
-      if (ig->writecb(ig, img->idata, img->bytes) != img->bytes) {
+      if (i_io_write(ig, img->idata, img->bytes) != img->bytes) {
        i_push_error(errno, "could not write targa image data");
        return 0;
       }
@@ -915,8 +934,8 @@ i_writetga_wiol(i_img *img, io_glue *ig, int wierdpack, int compress, char *idst
     }
   } else { /* direct type */
     int x, y;
-    int bytepp = wierdpack ? 2 : bpp_to_bytes(bitspp);
-    int lsize = bytepp * img->xsize;
+    size_t bytepp = wierdpack ? 2 : bpp_to_bytes(bitspp);
+    size_t lsize = bytepp * img->xsize;
     i_color *vals = mymalloc(img->xsize*sizeof(i_color));
     unsigned char *buf = mymalloc(lsize);
     
@@ -929,7 +948,8 @@ i_writetga_wiol(i_img *img, io_glue *ig, int wierdpack, int compress, char *idst
     myfree(vals);
   }
 
-  ig->closecb(ig);
+  if (i_io_close(ig))
+    return 0;
 
   return 1;
 }