]> git.imager.perl.org - imager.git/blobdiff - tga.c
- more information on gif library versions in README and Makefile.PL
[imager.git] / tga.c
diff --git a/tga.c b/tga.c
index bb233ea6319864baa6b47d04d39cc03bf68d2e0c..04f684c8065f1e39f18ae3b673b45a8c40b4bbee 100644 (file)
--- a/tga.c
+++ b/tga.c
@@ -1,5 +1,4 @@
 #include "image.h"
 #include "image.h"
-#include "io.h"
 #include "log.h"
 #include "iolayer.h"
 
 #include "log.h"
 #include "iolayer.h"
 
@@ -30,7 +29,7 @@ entire memory mapped buffer.
 
 Some of these functions are internal.
 
 
 Some of these functions are internal.
 
-=over 4
+=over
 
 =cut
 */
 
 =cut
 */
@@ -75,6 +74,17 @@ typedef struct {
 
 
 
 
 
 
+/*
+=item bpp_to_bytes(bpp)
+
+Convert bits per pixel into bytes per pixel
+
+   bpp - bits per pixel
+
+=cut
+*/
+
+
 static
 int
 bpp_to_bytes(unsigned int bpp) {
 static
 int
 bpp_to_bytes(unsigned int bpp) {
@@ -92,6 +102,18 @@ bpp_to_bytes(unsigned int bpp) {
   return 0;
 }
 
   return 0;
 }
 
+
+
+/*
+=item bpp_to_channels(bpp)
+
+Convert bits per pixel into channels in the image
+
+   bpp - bits per pixel
+
+=cut
+*/
+
 static
 int
 bpp_to_channels(unsigned int bpp) {
 static
 int
 bpp_to_channels(unsigned int bpp) {
@@ -115,16 +137,23 @@ bpp_to_channels(unsigned int bpp) {
 /* 
  * Packing functions - used for (un)packing
  * datastructures into raw bytes.
 /* 
  * Packing functions - used for (un)packing
  * datastructures into raw bytes.
-*/
+ */
+
 
 
+/*
+=item color_unpack(buf, bytepp, val)
+
+Unpacks bytes into colour structures, for 2 byte type the first byte
+coming from the file will actually be GGGBBBBB, and the second will be
+ARRRRRGG.  "A" represents an attribute bit.  The 3 byte entry contains
+1 byte each of blue, green, and red.  The 4 byte entry contains 1 byte
+each of blue, green, red, and attribute.
 
 
-/* color_unpack
+   buf - pointer to data
+   bytepp - bytes per pixel
+   val - pointer to color to store to
 
 
-Unpacks bytes into colours, for 2 byte type the first byte coming from
-the file will actually be GGGBBBBB, and the second will be ARRRRRGG.
-"A" represents an attribute bit.  The 3 byte entry contains 1 byte
-each of blue, green, and red.  The 4 byte entry contains 1 byte each
-of blue, green, red, and attribute.
+=cut
 */
 
 static
 */
 
 static
@@ -154,19 +183,25 @@ color_unpack(unsigned char *buf, int bytepp, i_color *val) {
     val->rgba.r = buf[2];
     val->rgba.a = buf[3];
     break;
     val->rgba.r = buf[2];
     val->rgba.a = buf[3];
     break;
-  default:
   }
 }
 
 
 
   }
 }
 
 
 
-/* color_pack
+/*
+=item color_pack
 
 
-Packs colous into bytes, for 2 byte type the first byte will be
-GGGBBBBB, and the second will be ARRRRRGG.  "A" represents an
+Packs a colour into an array of bytes, for 2 byte type the first byte
+will be GGGBBBBB, and the second will be ARRRRRGG.  "A" represents an
 attribute bit.  The 3 byte entry contains 1 byte each of blue, green,
 and red.  The 4 byte entry contains 1 byte each of blue, green, red,
 and attribute.
 attribute bit.  The 3 byte entry contains 1 byte each of blue, green,
 and red.  The 4 byte entry contains 1 byte each of blue, green, red,
 and attribute.
+
+    buf - destination buffer
+    bitspp - bits per pixel
+    val - color to pack
+
+=cut
 */
 
 static
 */
 
 static
@@ -195,13 +230,22 @@ color_pack(unsigned char *buf, int bitspp, i_color *val) {
     buf[2] = val->rgba.r;
     buf[3] = val->rgba.a;
     break;
     buf[2] = val->rgba.r;
     buf[3] = val->rgba.a;
     break;
-  default:
   }
   }
-  //  printf("%d  %3d %3d %3d\n", bitspp, val->rgb.r, val->rgb.g, val->rgb.b);
 }
 
 
 }
 
 
+/*
+=item find_repeat
+
+Helper function for rle compressor to find the next triple repeat of the 
+same pixel value in buffer.
+
+    buf - buffer
+    length - number of pixel values in buffer
+    bytepp - number of bytes in a pixel value
 
 
+=cut
+*/
 
 static
 int
 
 static
 int
@@ -221,6 +265,19 @@ find_repeat(unsigned char *buf, int length, int bytepp) {
 }
 
 
 }
 
 
+/*
+=item find_span
+
+Helper function for rle compressor to find the length of a span where
+the same pixel value is in the buffer.
+
+    buf - buffer
+    length - number of pixel values in buffer
+    bytepp - number of bytes in a pixel value
+
+=cut
+*/
+
 static
 int
 find_span(unsigned char *buf, int length, int bytepp) {
 static
 int
 find_span(unsigned char *buf, int length, int bytepp) {
@@ -233,8 +290,115 @@ find_span(unsigned char *buf, int length, int bytepp) {
 }
 
 
 }
 
 
+/*
+=item tga_header_unpack(header, headbuf)
+
+Unpacks the header structure into from buffer and stores
+in the header structure.
+
+    header - header structure
+    headbuf - buffer to unpack from
+
+=cut
+*/
+
+static
+void
+tga_header_unpack(tga_header *header, unsigned char headbuf[18]) {
+  header->idlength        = headbuf[0];
+  header->colourmaptype   = headbuf[1];
+  header->datatypecode    = headbuf[2];
+  header->colourmaporigin = (headbuf[4] << 8) + headbuf[3];
+  header->colourmaplength = (headbuf[6] << 8) + headbuf[5];
+  header->colourmapdepth  = headbuf[7];
+  header->x_origin        = (headbuf[9] << 8) + headbuf[8];
+  header->y_origin        = (headbuf[11] << 8) + headbuf[10];
+  header->width           = (headbuf[13] << 8) + headbuf[12];
+  header->height          = (headbuf[15] << 8) + headbuf[14];
+  header->bitsperpixel    = headbuf[16];
+  header->imagedescriptor = headbuf[17];
+}
+
+
+
+int
+tga_header_verify(unsigned char headbuf[18]) {
+  tga_header header;
+  tga_header_unpack(&header, headbuf);
+  switch (header.datatypecode) { 
+  default:
+               printf("bad typecode!\n");
+    return 0;
+  case 0:
+  case 1:  /* Uncompressed, color-mapped images */ 
+  case 2:  /* Uncompressed, rgb images          */ 
+  case 3:  /* Uncompressed, grayscale images    */ 
+  case 9:  /* Compressed,   color-mapped images */ 
+  case 10: /* Compressed,   rgb images          */ 
+  case 11: /* Compressed,   grayscale images    */ 
+         break;
+       }
+
+  switch (header.colourmaptype) { 
+  default:
+               printf("bad colourmaptype!\n");
+    return 0;
+  case 0:
+  case 1:
+       break;
+       }
+  
+  return 1;
+}
+
+
+/*
+=item tga_header_pack(header, headbuf)
+
+Packs header structure into buffer for writing.
+
+    header - header structure
+    headbuf - buffer to pack into
+
+=cut
+*/
+
+static
+void
+tga_header_pack(tga_header *header, unsigned char headbuf[18]) {
+  headbuf[0] = header->idlength;
+  headbuf[1] = header->colourmaptype;
+  headbuf[2] = header->datatypecode;
+  headbuf[3] = header->colourmaporigin & 0xff;
+  headbuf[4] = header->colourmaporigin >> 8;
+  headbuf[5] = header->colourmaplength & 0xff;
+  headbuf[6] = header->colourmaplength >> 8;
+  headbuf[7] = header->colourmapdepth;
+  headbuf[8] = header->x_origin & 0xff;
+  headbuf[9] = header->x_origin >> 8;
+  headbuf[10] = header->y_origin & 0xff;
+  headbuf[11] = header->y_origin >> 8;
+  headbuf[12] = header->width & 0xff;
+  headbuf[13] = header->width >> 8;
+  headbuf[14] = header->height & 0xff;
+  headbuf[15] = header->height >> 8;
+  headbuf[16] = header->bitsperpixel;
+  headbuf[17] = header->imagedescriptor;
+}
+
+
+/*
+=item tga_source_read(s, buf, pixels)
+
+Reads pixel number of pixels from source s into buffer buf.  Takes
+care of decompressing the stream if needed.
 
 
+    s - data source 
+    buf - destination buffer
+    pixels - number of pixels to put into buffer
 
 
+=cut
+*/
 
 static
 int
 
 static
 int
@@ -254,19 +418,24 @@ tga_source_read(tga_source *s, unsigned char *buf, size_t pixels) {
 
       s->len = (s->hdr &~(1<<7))+1;
       s->state = (s->hdr & (1<<7)) ? Rle : Raw;
 
       s->len = (s->hdr &~(1<<7))+1;
       s->state = (s->hdr & (1<<7)) ? Rle : Raw;
+      {
+/*
+       static cnt = 0;
+       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;
 
       break;
     case Rle:
       if (s->state == Rle && s->ig->readcb(s->ig, s->cval, s->bytepp) != s->bytepp) return 0;
 
       break;
     case Rle:
-      ml = min(s->len, pixels-cp);
+      ml = i_min(s->len, pixels-cp);
       for(k=0; k<ml; k++) for(j=0; j<s->bytepp; j++) 
        buf[(cp+k)*s->bytepp+j] = s->cval[j];
       for(k=0; k<ml; k++) for(j=0; j<s->bytepp; j++) 
        buf[(cp+k)*s->bytepp+j] = s->cval[j];
-      //      memset(buf+cp, s->cidx, ml);
       cp     += ml;
       s->len -= ml;
       break;
     case Raw:
       cp     += ml;
       s->len -= ml;
       break;
     case Raw:
-      ml = min(s->len, pixels-cp);
+      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;
       cp     += ml;
       s->len -= ml;
       if (s->ig->readcb(s->ig, buf+cp*s->bytepp, ml*s->bytepp) != ml*s->bytepp) return 0;
       cp     += ml;
       s->len -= ml;
@@ -278,13 +447,18 @@ tga_source_read(tga_source *s, unsigned char *buf, size_t pixels) {
 
 
 
 
 
 
-/* 
-   tga_dest_write
-   
-   Note that it is possible for length to be more than 0 and the state
-   still be noinit.  That just means that there isn't enough data yet to
-   determine the next packet type.
 
 
+/*
+=item tga_dest_write(s, buf, pixels)
+
+Writes pixels from buf to destination s.  Takes care of compressing if the
+destination is compressed.
+
+    s - data destination
+    buf - source buffer
+    pixels - number of pixels to put write to destination
+
+=cut
 */
 
 static
 */
 
 static
@@ -302,7 +476,7 @@ tga_dest_write(tga_dest *s, unsigned char *buf, size_t pixels) {
     int nxtrip = find_repeat(buf+cp*s->bytepp, pixels-cp, s->bytepp);
     tlen = (nxtrip == -1) ? pixels-cp : nxtrip;
     while(tlen) {
     int nxtrip = find_repeat(buf+cp*s->bytepp, pixels-cp, s->bytepp);
     tlen = (nxtrip == -1) ? pixels-cp : nxtrip;
     while(tlen) {
-      int clen = (tlen>128) ? 128 : tlen;
+      unsigned char clen = (tlen>128) ? 128 : tlen;
       clen--;
       if (s->ig->writecb(s->ig, &clen, 1) != 1) return 0;
       clen++;
       clen--;
       if (s->ig->writecb(s->ig, &clen, 1) != 1) return 0;
       clen++;
@@ -312,8 +486,9 @@ tga_dest_write(tga_dest *s, unsigned char *buf, size_t pixels) {
     }
     if (cp >= pixels) break;
     tlen = find_span(buf+cp*s->bytepp, pixels-cp, s->bytepp);
     }
     if (cp >= pixels) break;
     tlen = find_span(buf+cp*s->bytepp, pixels-cp, s->bytepp);
+    if (tlen <3) continue;
     while (tlen) {
     while (tlen) {
-      int clen = (tlen>128) ? 128 : tlen;
+      unsigned char clen = (tlen>128) ? 128 : tlen;
       clen = (clen - 1) | 0x80;
       if (s->ig->writecb(s->ig, &clen, 1) != 1) return 0;
       clen = (clen & ~0x80) + 1;
       clen = (clen - 1) | 0x80;
       if (s->ig->writecb(s->ig, &clen, 1) != 1) return 0;
       clen = (clen & ~0x80) + 1;
@@ -330,7 +505,19 @@ tga_dest_write(tga_dest *s, unsigned char *buf, size_t pixels) {
 
 
 
 
 
 
+/*
+=item tga_palette_read(ig, img, bytepp, colourmaplength)
 
 
+Reads the colormap from a tga file and stores in the paletted image
+structure.
+
+    ig - iolayer data source
+    img - image structure
+    bytepp - bytes per pixel
+    colourmaplength - number of colours in colourmap
+
+=cut
+*/
 
 static
 int
 
 static
 int
@@ -358,6 +545,18 @@ tga_palette_read(io_glue *ig, i_img *img, int bytepp, int colourmaplength) {
 }
 
 
 }
 
 
+/*
+=item tga_palette_write(ig, img, bitspp, colourmaplength)
+
+Stores the colormap of an image in the destination ig.
+
+    ig - iolayer data source
+    img - image structure
+    bitspp - bits per pixel in colourmap
+    colourmaplength - number of colours in colourmap
+
+=cut
+*/
 
 static
 int
 
 static
 int
@@ -381,53 +580,13 @@ tga_palette_write(io_glue *ig, i_img *img, int bitspp, int colourmaplength) {
   return 1;
 }
 
   return 1;
 }
 
-static
-void
-tga_header_unpack(tga_header *header, unsigned char headbuf[18]) {
-  header->idlength        = headbuf[0];
-  header->colourmaptype   = headbuf[1];
-  header->datatypecode    = headbuf[2];
-  header->colourmaporigin = (headbuf[4] << 8) + headbuf[3];
-  header->colourmaplength = (headbuf[6] << 8) + headbuf[5];
-  header->colourmapdepth  = headbuf[7];
-  header->x_origin        = (headbuf[9] << 8) + headbuf[8];
-  header->y_origin        = (headbuf[11] << 8) + headbuf[10];
-  header->width           = (headbuf[13] << 8) + headbuf[12];
-  header->height          = (headbuf[15] << 8) + headbuf[14];
-  header->bitsperpixel    = headbuf[16];
-  header->imagedescriptor = headbuf[17];
-}
-
-
-static
-void
-tga_header_pack(tga_header *header, unsigned char headbuf[18]) {
-  headbuf[0] = header->idlength;
-  headbuf[1] = header->colourmaptype;
-  headbuf[2] = header->datatypecode;
-  headbuf[3] = header->colourmaporigin & 0xff;
-  headbuf[4] = header->colourmaporigin >> 8;
-  headbuf[5] = header->colourmaplength & 0xff;
-  headbuf[6] = header->colourmaplength >> 8;
-  headbuf[7] = header->colourmapdepth;
-  headbuf[8] = header->x_origin & 0xff;
-  headbuf[9] = header->x_origin >> 8;
-  headbuf[10] = header->y_origin & 0xff;
-  headbuf[11] = header->y_origin >> 8;
-  headbuf[12] = header->width & 0xff;
-  headbuf[13] = header->width >> 8;
-  headbuf[14] = header->height & 0xff;
-  headbuf[15] = header->height >> 8;
-  headbuf[16] = header->bitsperpixel;
-  headbuf[17] = header->imagedescriptor;
-}
-
 
 
 /*
 =item i_readtga_wiol(ig, length)
 
 
 
 /*
 =item i_readtga_wiol(ig, length)
 
-Retrieve an image and stores in the iolayer object. Returns NULL on fatal error.
+Read in an image from the iolayer data source and return the image structure to it.
+Returns NULL on error.
 
    ig     - io_glue object
    length - maximum length to read from data source, before closing it -1 
 
    ig     - io_glue object
    length - maximum length to read from data source, before closing it -1 
@@ -438,11 +597,11 @@ Retrieve an image and stores in the iolayer object. Returns NULL on fatal error.
 
 i_img *
 i_readtga_wiol(io_glue *ig, int length) {
 
 i_img *
 i_readtga_wiol(io_glue *ig, int length) {
-  i_img* img;
+  i_img* img = NULL;
   int x, y, i;
   int width, height, channels;
   int mapped;
   int x, y, i;
   int width, height, channels;
   int mapped;
-  char *idstring;
+  char *idstring = NULL;
 
   tga_source src;
   tga_header header;
 
   tga_source src;
   tga_header header;
@@ -483,7 +642,6 @@ i_readtga_wiol(io_glue *ig, int length) {
       i_push_error(errno, "short read on targa idstring");
       return NULL;
     }
       i_push_error(errno, "short read on targa idstring");
       return NULL;
     }
-    myfree(idstring); /* Move this later, once this is stored in a tag */
   }
 
   width = header.width;
   }
 
   width = header.width;
@@ -494,6 +652,7 @@ i_readtga_wiol(io_glue *ig, int length) {
   switch (header.datatypecode) {
   case 0: /* No data in image */
     i_push_error(0, "Targa image contains no image data");
   switch (header.datatypecode) {
   case 0: /* No data in image */
     i_push_error(0, "Targa image contains no image data");
+    if (idstring) myfree(idstring);
     return NULL;
     break;
   case 1:  /* Uncompressed, color-mapped images */
     return NULL;
     break;
   case 1:  /* Uncompressed, color-mapped images */
@@ -502,6 +661,7 @@ i_readtga_wiol(io_glue *ig, int length) {
   case 11: /* Compressed,   grayscale images    */
     if (header.bitsperpixel != 8) {
       i_push_error(0, "Targa: mapped/grayscale image's bpp is not 8, unsupported.");
   case 11: /* Compressed,   grayscale images    */
     if (header.bitsperpixel != 8) {
       i_push_error(0, "Targa: mapped/grayscale image's bpp is not 8, unsupported.");
+      if (idstring) myfree(idstring);
       return NULL;
     }
     src.bytepp = 1;
       return NULL;
     }
     src.bytepp = 1;
@@ -511,15 +671,18 @@ i_readtga_wiol(io_glue *ig, int length) {
     if ((src.bytepp = bpp_to_bytes(header.bitsperpixel)))
       break;
     i_push_error(0, "Targa: direct color image's bpp is not 15/16/24/32 - unsupported.");
     if ((src.bytepp = bpp_to_bytes(header.bitsperpixel)))
       break;
     i_push_error(0, "Targa: direct color image's bpp is not 15/16/24/32 - unsupported.");
+    if (idstring) myfree(idstring);
     return NULL;
     break;
   case 32: /* Compressed color-mapped, Huffman, Delta and runlength */
   case 33: /* Compressed color-mapped, Huffman, Delta and runlength */
     i_push_error(0, "Unsupported Targa (Huffman/delta/rle/quadtree) subformat is not supported");
     return NULL;
     break;
   case 32: /* Compressed color-mapped, Huffman, Delta and runlength */
   case 33: /* Compressed color-mapped, Huffman, Delta and runlength */
     i_push_error(0, "Unsupported Targa (Huffman/delta/rle/quadtree) subformat is not supported");
+    if (idstring) myfree(idstring);
     return NULL;
     break;
   default: /* All others which we don't know which might be */
     i_push_error(0, "Unknown targa format");
     return NULL;
     break;
   default: /* All others which we don't know which might be */
     i_push_error(0, "Unknown targa format");
+    if (idstring) myfree(idstring);
     return NULL;
     break;
   }
     return NULL;
     break;
   }
@@ -543,6 +706,7 @@ i_readtga_wiol(io_glue *ig, int length) {
                                   header.colourmapdepth : 
                                   header.bitsperpixel))) break;
     i_push_error(0, "Targa Image has none of 15/16/24/32 pixel layout");
                                   header.colourmapdepth : 
                                   header.bitsperpixel))) break;
     i_push_error(0, "Targa Image has none of 15/16/24/32 pixel layout");
+    if (idstring) myfree(idstring);
     return NULL;
     break;
   case 3:  /* Uncompressed, grayscale images    */
     return NULL;
     break;
   case 3:  /* Uncompressed, grayscale images    */
@@ -556,6 +720,11 @@ i_readtga_wiol(io_glue *ig, int length) {
     i_img_pal_new(width, height, channels, 256) :
     i_img_empty_ch(NULL, width, height, channels);
   
     i_img_pal_new(width, height, channels, 256) :
     i_img_empty_ch(NULL, width, height, channels);
   
+  if (idstring) {
+    i_tags_add(&img->tags, "tga_idstring", 0, idstring, header.idlength, 0);
+    myfree(idstring);
+  }
+
   if (mapped &&
       !tga_palette_read(ig,
                        img,
   if (mapped &&
       !tga_palette_read(ig,
                        img,
@@ -563,6 +732,8 @@ i_readtga_wiol(io_glue *ig, int length) {
                        header.colourmaplength)
       ) {
     i_push_error(0, "Targa Image has none of 15/16/24/32 pixel layout");
                        header.colourmaplength)
       ) {
     i_push_error(0, "Targa Image has none of 15/16/24/32 pixel layout");
+    if (idstring) myfree(idstring);
+    if (img) i_img_destroy(img);
     return NULL;
   }
   
     return NULL;
   }
   
@@ -574,6 +745,7 @@ i_readtga_wiol(io_glue *ig, int length) {
     if (!tga_source_read(&src, databuf, width)) {
       i_push_error(errno, "read for targa data failed");
       myfree(databuf);
     if (!tga_source_read(&src, databuf, width)) {
       i_push_error(errno, "read for targa data failed");
       myfree(databuf);
+      if (img) i_img_destroy(img);
       return NULL;
     }
     if (mapped && header.colourmaporigin) for(x=0; x<width; x++) databuf[x] -= header.colourmaporigin;
       return NULL;
     }
     if (mapped && header.colourmaporigin) for(x=0; x<width; x++) databuf[x] -= header.colourmaporigin;
@@ -585,38 +757,48 @@ i_readtga_wiol(io_glue *ig, int length) {
   }
   myfree(databuf);
   if (linebuf) myfree(linebuf);
   }
   myfree(databuf);
   if (linebuf) myfree(linebuf);
+  
+  i_tags_addn(&img->tags, "tga_bitspp", 0, mapped?header.colourmapdepth:header.bitsperpixel);
+  if (src.compressed) i_tags_addn(&img->tags, "compressed", 0, 1);
   return img;
 }
 
 
 
   return img;
 }
 
 
 
+/*
+=item i_writetga_wiol(img, ig)
 
 
+Writes an image in targa format.  Returns 0 on error.
 
 
+   img    - image to store
+   ig     - io_glue object
 
 
-
-
+=cut
+*/
 
 undef_int
 
 undef_int
-i_writetga_wiol(i_img *img, io_glue *ig) {
+i_writetga_wiol(i_img *img, io_glue *ig, int wierdpack, int compress, char *idstring, size_t idlen) {
   static int rgb_chan[] = { 2, 1, 0, 3 };
   tga_header header;
   tga_dest dest;
   unsigned char headbuf[18];
   static int rgb_chan[] = { 2, 1, 0, 3 };
   tga_header header;
   tga_dest dest;
   unsigned char headbuf[18];
-  //  unsigned char *data;
   unsigned int bitspp;
   
   unsigned int bitspp;
   
-  int idlen;
   int mapped;
 
   /* parameters */
   int mapped;
 
   /* parameters */
-  int compress = 1;
-  char *idstring = "testing";
-  int wierdpack = 0;
+
+  /*
+    int compress = 1;
+    char *idstring = "testing";
+    int wierdpack = 0;
+  */
 
   idlen = strlen(idstring);
   mapped = img->type == i_palette_type;
 
 
   idlen = strlen(idstring);
   mapped = img->type == i_palette_type;
 
-  mm_log((1,"i_writetga_wiol(img %p, ig %p)\n", img, ig));
+  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, "virtual %d, paletted %d\n", img->virtual, mapped));
   mm_log((1, "channels %d\n", img->channels));
   
   mm_log((1, "virtual %d, paletted %d\n", img->virtual, mapped));
   mm_log((1, "channels %d\n", img->channels));
   
@@ -719,5 +901,22 @@ i_writetga_wiol(i_img *img, io_glue *ig) {
     myfree(buf);
     myfree(vals);
   }
     myfree(buf);
     myfree(vals);
   }
+
+  ig->closecb(ig);
+
   return 1;
 }
   return 1;
 }
+
+/*
+=back
+
+=head1 AUTHOR
+
+Arnar M. Hrafnkelsson <addi@umich.edu>
+
+=head1 SEE ALSO
+
+Imager(3)
+
+=cut
+*/