]> git.imager.perl.org - imager.git/blob - tga.c
fill out the large sample support docs
[imager.git] / tga.c
1 #include "imageri.h"
2 #include "log.h"
3 #include "iolayer.h"
4
5 #include <stdlib.h>
6 #include <errno.h>
7
8
9 /*
10 =head1 NAME
11
12 tga.c - implements reading and writing targa files, uses io layer.
13
14 =head1 SYNOPSIS
15
16    io_glue *ig = io_new_fd( fd );
17    i_img *im   = i_readtga_wiol(ig, -1); // no limit on how much is read
18    // or 
19    io_glue *ig = io_new_fd( fd );
20    return_code = i_writetga_wiol(im, ig); 
21
22 =head1 DESCRIPTION
23
24 tga.c implements the basic functions to read and write portable targa
25 files.  It uses the iolayer and needs either a seekable source or an
26 entire memory mapped buffer.
27
28 =head1 FUNCTION REFERENCE
29
30 Some of these functions are internal.
31
32 =over
33
34 =cut
35 */
36
37
38
39
40 typedef struct {
41   unsigned char  idlength;
42   char  colourmaptype;
43   char  datatypecode;
44   short int colourmaporigin;
45   short int colourmaplength;
46   char  colourmapdepth;
47   short int x_origin;
48   short int y_origin;
49   short width;
50   short height;
51   char  bitsperpixel;
52   char  imagedescriptor;
53 } tga_header;
54
55
56 typedef enum { NoInit, Raw, Rle } rle_state;
57
58 typedef struct {
59   int compressed;
60   int bytepp;
61   rle_state state;
62   unsigned char cval[4];
63   int len;
64   unsigned char hdr;
65   io_glue *ig;
66 } tga_source;
67
68
69 typedef struct {
70   int compressed;
71   int bytepp;
72   io_glue *ig;
73 } tga_dest;
74
75
76
77 /*
78 =item bpp_to_bytes(bpp)
79
80 Convert bits per pixel into bytes per pixel
81
82    bpp - bits per pixel
83
84 =cut
85 */
86
87
88 static
89 int
90 bpp_to_bytes(unsigned int bpp) {
91   switch (bpp) {
92   case 8:
93     return 1;
94   case 15:
95   case 16:
96     return 2;
97   case 24:
98     return 3;
99   case 32:
100     return 4;
101   }
102   return 0;
103 }
104
105
106
107 /*
108 =item bpp_to_channels(bpp)
109
110 Convert bits per pixel into channels in the image
111
112    bpp - bits per pixel
113
114 =cut
115 */
116
117 static
118 int
119 bpp_to_channels(unsigned int bpp) {
120   switch (bpp) {
121   case 8:
122     return 1;
123   case 15:
124     return 3;
125   case 16:
126     return 4;
127   case 24:
128     return 3;
129   case 32:
130     return 4;
131   }
132   return 0;
133 }
134
135
136
137 /* 
138  * Packing functions - used for (un)packing
139  * datastructures into raw bytes.
140  */
141
142
143 /*
144 =item color_unpack(buf, bytepp, val)
145
146 Unpacks bytes into colour structures, for 2 byte type the first byte
147 coming from the file will actually be GGGBBBBB, and the second will be
148 ARRRRRGG.  "A" represents an attribute bit.  The 3 byte entry contains
149 1 byte each of blue, green, and red.  The 4 byte entry contains 1 byte
150 each of blue, green, red, and attribute.
151
152    buf - pointer to data
153    bytepp - bytes per pixel
154    val - pointer to color to store to
155
156 =cut
157 */
158
159 static
160 void
161 color_unpack(unsigned char *buf, int bytepp, i_color *val) {
162   switch (bytepp) {
163   case 1:
164     val->gray.gray_color = buf[0];
165     break;
166   case 2:
167     val->rgba.r = (buf[1] & 0x7c) << 1;
168     val->rgba.g = ((buf[1] & 0x03) << 6) | ((buf[0] & 0xe0) >> 2);
169     val->rgba.b = (buf[0] & 0x1f) << 3;
170     val->rgba.a = (buf[1] & 0x80) ? 255 : 0;
171     val->rgba.r |= val->rgba.r >> 5;
172     val->rgba.g |= val->rgba.g >> 5;
173     val->rgba.b |= val->rgba.b >> 5;
174     break;
175   case 3:
176     val->rgb.b = buf[0];
177     val->rgb.g = buf[1];
178     val->rgb.r = buf[2];
179     break;
180   case 4:
181     val->rgba.b = buf[0];
182     val->rgba.g = buf[1];
183     val->rgba.r = buf[2];
184     val->rgba.a = buf[3];
185     break;
186   }
187 }
188
189
190
191 /*
192 =item color_pack
193
194 Packs a colour into an array of bytes, for 2 byte type the first byte
195 will be GGGBBBBB, and the second will be ARRRRRGG.  "A" represents an
196 attribute bit.  The 3 byte entry contains 1 byte each of blue, green,
197 and red.  The 4 byte entry contains 1 byte each of blue, green, red,
198 and attribute.
199
200     buf - destination buffer
201     bitspp - bits per pixel
202     val - color to pack
203
204 =cut
205 */
206
207 static
208 void
209 color_pack(unsigned char *buf, int bitspp, i_color *val) {
210   switch (bitspp) {
211   case 8:
212     buf[0] = val->gray.gray_color;
213     break;
214   case 15:
215     buf[0]  = (val->rgba.b >> 3);
216     buf[0] |= (val->rgba.g & 0x38) << 2;
217     buf[1]  = (val->rgba.r & 0xf8)>> 1;
218     buf[1] |= (val->rgba.g >> 6);
219   case 16:
220     buf[1] |=  val->rgba.a & 0x80;
221     break;
222   case 24:
223     buf[0] = val->rgb.b;
224     buf[1] = val->rgb.g;
225     buf[2] = val->rgb.r;
226     break;
227   case 32:
228     buf[0] = val->rgba.b;
229     buf[1] = val->rgba.g;
230     buf[2] = val->rgba.r;
231     buf[3] = val->rgba.a;
232     break;
233   }
234 }
235
236
237 /*
238 =item find_repeat
239
240 Helper function for rle compressor to find the next triple repeat of the 
241 same pixel value in buffer.
242
243     buf - buffer
244     length - number of pixel values in buffer
245     bytepp - number of bytes in a pixel value
246
247 =cut
248 */
249
250 static
251 int
252 find_repeat(unsigned char *buf, int length, int bytepp) {
253   int i = 0;
254   
255   while(i<length-1) {
256     if(memcmp(buf+i*bytepp, buf+(i+1)*bytepp, bytepp) == 0) {
257       if (i == length-2) return -1;
258       if (memcmp(buf+(i+1)*bytepp, buf+(i+2)*bytepp,bytepp) == 0)  
259         return i;
260       else i++;
261     }
262     i++;
263   }
264   return -1;
265 }
266
267
268 /*
269 =item find_span
270
271 Helper function for rle compressor to find the length of a span where
272 the same pixel value is in the buffer.
273
274     buf - buffer
275     length - number of pixel values in buffer
276     bytepp - number of bytes in a pixel value
277
278 =cut
279 */
280
281 static
282 int
283 find_span(unsigned char *buf, int length, int bytepp) {
284   int i = 0;
285   while(i<length) {
286     if(memcmp(buf, buf+(i*bytepp), bytepp) != 0) return i;
287     i++;
288   }
289   return length;
290 }
291
292
293 /*
294 =item tga_header_unpack(header, headbuf)
295
296 Unpacks the header structure into from buffer and stores
297 in the header structure.
298
299     header - header structure
300     headbuf - buffer to unpack from
301
302 =cut
303 */
304
305 static
306 void
307 tga_header_unpack(tga_header *header, unsigned char headbuf[18]) {
308   header->idlength        = headbuf[0];
309   header->colourmaptype   = headbuf[1];
310   header->datatypecode    = headbuf[2];
311   header->colourmaporigin = (headbuf[4] << 8) + headbuf[3];
312   header->colourmaplength = (headbuf[6] << 8) + headbuf[5];
313   header->colourmapdepth  = headbuf[7];
314   header->x_origin        = (headbuf[9] << 8) + headbuf[8];
315   header->y_origin        = (headbuf[11] << 8) + headbuf[10];
316   header->width           = (headbuf[13] << 8) + headbuf[12];
317   header->height          = (headbuf[15] << 8) + headbuf[14];
318   header->bitsperpixel    = headbuf[16];
319   header->imagedescriptor = headbuf[17];
320 }
321
322
323 /* this function should never produce diagnostics to stdout, maybe to the logfile */
324 int
325 tga_header_verify(unsigned char headbuf[18]) {
326   tga_header header;
327   tga_header_unpack(&header, headbuf);
328   switch (header.datatypecode) { 
329   default:
330     /*printf("bad typecode!\n");*/
331     return 0;
332   case 1:  /* Uncompressed, color-mapped images */ 
333   case 3:  /* Uncompressed, grayscale images    */ 
334   case 9:  /* Compressed,   color-mapped images */ 
335   case 11: /* Compressed,   grayscale images    */ 
336     if (header.bitsperpixel != 8)
337       return 0;
338     break;
339   case 0:
340   case 2:  /* Uncompressed, rgb images          */ 
341   case 10: /* Compressed,   rgb images          */ 
342     if (header.bitsperpixel != 15 && header.bitsperpixel != 16
343         && header.bitsperpixel != 24 && header.bitsperpixel != 23)
344       return 0;
345     break;
346         }
347
348   switch (header.colourmaptype) { 
349   default:
350     /*printf("bad colourmaptype!\n");*/
351     return 0;
352   case 1:
353     if (header.datatypecode != 1 && header.datatypecode != 9)
354       return 0; /* only get a color map on a color mapped image */
355   case 0:
356         break;
357         }
358
359   switch (header.colourmapdepth) {
360   default:
361     return 0;
362   case 0: /* can be 0 if no colour map */
363   case 15:
364   case 16:
365   case 24:
366   case 32:
367     break;
368   }
369   
370   return 1;
371 }
372
373
374 /*
375 =item tga_header_pack(header, headbuf)
376
377 Packs header structure into buffer for writing.
378
379     header - header structure
380     headbuf - buffer to pack into
381
382 =cut
383 */
384
385 static
386 void
387 tga_header_pack(tga_header *header, unsigned char headbuf[18]) {
388   headbuf[0] = header->idlength;
389   headbuf[1] = header->colourmaptype;
390   headbuf[2] = header->datatypecode;
391   headbuf[3] = header->colourmaporigin & 0xff;
392   headbuf[4] = header->colourmaporigin >> 8;
393   headbuf[5] = header->colourmaplength & 0xff;
394   headbuf[6] = header->colourmaplength >> 8;
395   headbuf[7] = header->colourmapdepth;
396   headbuf[8] = header->x_origin & 0xff;
397   headbuf[9] = header->x_origin >> 8;
398   headbuf[10] = header->y_origin & 0xff;
399   headbuf[11] = header->y_origin >> 8;
400   headbuf[12] = header->width & 0xff;
401   headbuf[13] = header->width >> 8;
402   headbuf[14] = header->height & 0xff;
403   headbuf[15] = header->height >> 8;
404   headbuf[16] = header->bitsperpixel;
405   headbuf[17] = header->imagedescriptor;
406 }
407
408
409 /*
410 =item tga_source_read(s, buf, pixels)
411
412 Reads pixel number of pixels from source s into buffer buf.  Takes
413 care of decompressing the stream if needed.
414
415     s - data source 
416     buf - destination buffer
417     pixels - number of pixels to put into buffer
418
419 =cut
420 */
421
422 static
423 int
424 tga_source_read(tga_source *s, unsigned char *buf, size_t pixels) {
425   int cp = 0, j, k;
426   if (!s->compressed) {
427     if (s->ig->readcb(s->ig, buf, pixels*s->bytepp) != pixels*s->bytepp) return 0;
428     return 1;
429   }
430   
431   while(cp < pixels) {
432     int ml;
433     if (s->len == 0) s->state = NoInit;
434     switch (s->state) {
435     case NoInit:
436       if (s->ig->readcb(s->ig, &s->hdr, 1) != 1) return 0;
437
438       s->len = (s->hdr &~(1<<7))+1;
439       s->state = (s->hdr & (1<<7)) ? Rle : Raw;
440       {
441 /*
442         static cnt = 0;
443         printf("%04d %s: %d\n", cnt++, s->state==Rle?"RLE":"RAW", s->len);
444  */
445      }
446       if (s->state == Rle && s->ig->readcb(s->ig, s->cval, s->bytepp) != s->bytepp) return 0;
447
448       break;
449     case Rle:
450       ml = i_min(s->len, pixels-cp);
451       for(k=0; k<ml; k++) for(j=0; j<s->bytepp; j++) 
452         buf[(cp+k)*s->bytepp+j] = s->cval[j];
453       cp     += ml;
454       s->len -= ml;
455       break;
456     case Raw:
457       ml = i_min(s->len, pixels-cp);
458       if (s->ig->readcb(s->ig, buf+cp*s->bytepp, ml*s->bytepp) != ml*s->bytepp) return 0;
459       cp     += ml;
460       s->len -= ml;
461       break;
462     }
463   }
464   return 1;
465 }
466
467
468
469
470 /*
471 =item tga_dest_write(s, buf, pixels)
472
473 Writes pixels from buf to destination s.  Takes care of compressing if the
474 destination is compressed.
475
476     s - data destination
477     buf - source buffer
478     pixels - number of pixels to put write to destination
479
480 =cut
481 */
482
483 static
484 int
485 tga_dest_write(tga_dest *s, unsigned char *buf, size_t pixels) {
486   int cp = 0;
487
488   if (!s->compressed) {
489     if (s->ig->writecb(s->ig, buf, pixels*s->bytepp) != pixels*s->bytepp) return 0;
490     return 1;
491   }
492   
493   while(cp < pixels) {
494     int tlen;
495     int nxtrip = find_repeat(buf+cp*s->bytepp, pixels-cp, s->bytepp);
496     tlen = (nxtrip == -1) ? pixels-cp : nxtrip;
497     while(tlen) {
498       unsigned char clen = (tlen>128) ? 128 : tlen;
499       clen--;
500       if (s->ig->writecb(s->ig, &clen, 1) != 1) return 0;
501       clen++;
502       if (s->ig->writecb(s->ig, buf+cp*s->bytepp, clen*s->bytepp) != clen*s->bytepp) return 0;
503       tlen -= clen;
504       cp += clen;
505     }
506     if (cp >= pixels) break;
507     tlen = find_span(buf+cp*s->bytepp, pixels-cp, s->bytepp);
508     if (tlen <3) continue;
509     while (tlen) {
510       unsigned char clen = (tlen>128) ? 128 : tlen;
511       clen = (clen - 1) | 0x80;
512       if (s->ig->writecb(s->ig, &clen, 1) != 1) return 0;
513       clen = (clen & ~0x80) + 1;
514       if (s->ig->writecb(s->ig, buf+cp*s->bytepp, s->bytepp) != s->bytepp) return 0;
515       tlen -= clen;
516       cp += clen;
517     }
518   }
519   return 1;
520 }
521
522
523
524
525
526
527 /*
528 =item tga_palette_read(ig, img, bytepp, colourmaplength)
529
530 Reads the colormap from a tga file and stores in the paletted image
531 structure.
532
533     ig - iolayer data source
534     img - image structure
535     bytepp - bytes per pixel
536     colourmaplength - number of colours in colourmap
537
538 =cut
539 */
540
541 static
542 int
543 tga_palette_read(io_glue *ig, i_img *img, int bytepp, int colourmaplength) {
544   int i;
545   size_t palbsize;
546   unsigned char *palbuf;
547   i_color val;
548
549   palbsize = colourmaplength*bytepp;
550   palbuf   = mymalloc(palbsize);
551   
552   if (ig->readcb(ig, palbuf, palbsize) != palbsize) {
553     i_push_error(errno, "could not read targa colourmap");
554     return 0;
555   }
556   
557   /* populate the palette of the new image */
558   for(i=0; i<colourmaplength; i++) {
559     color_unpack(palbuf+i*bytepp, bytepp, &val);
560     i_addcolors(img, &val, 1);
561   }
562   myfree(palbuf);
563   return 1;
564 }
565
566
567 /*
568 =item tga_palette_write(ig, img, bitspp, colourmaplength)
569
570 Stores the colormap of an image in the destination ig.
571
572     ig - iolayer data source
573     img - image structure
574     bitspp - bits per pixel in colourmap
575     colourmaplength - number of colours in colourmap
576
577 =cut
578 */
579
580 static
581 int
582 tga_palette_write(io_glue *ig, i_img *img, int bitspp, int colourmaplength) {
583   int i;
584   int bytepp = bpp_to_bytes(bitspp);
585   size_t palbsize = i_colorcount(img)*bytepp;
586   unsigned char *palbuf = mymalloc(palbsize);
587   
588   for(i=0; i<colourmaplength; i++) {
589     i_color val;
590     i_getcolors(img, i, &val, 1);
591     color_pack(palbuf+i*bytepp, bitspp, &val);
592   }
593   
594   if (ig->writecb(ig, palbuf, palbsize) != palbsize) {
595     i_push_error(errno, "could not write targa colourmap");
596     return 0;
597   }
598   myfree(palbuf);
599   return 1;
600 }
601
602
603
604 /*
605 =item i_readtga_wiol(ig, length)
606
607 Read in an image from the iolayer data source and return the image structure to it.
608 Returns NULL on error.
609
610    ig     - io_glue object
611    length - maximum length to read from data source, before closing it -1 
612             signifies no limit.
613
614 =cut
615 */
616
617 i_img *
618 i_readtga_wiol(io_glue *ig, int length) {
619   i_img* img = NULL;
620   int x, y;
621   int width, height, channels;
622   int mapped;
623   char *idstring = NULL;
624
625   tga_source src;
626   tga_header header;
627   unsigned char headbuf[18];
628   unsigned char *databuf;
629
630   i_color *linebuf = NULL;
631   i_clear_error();
632
633   mm_log((1,"i_readtga(ig %p, length %d)\n", ig, length));
634   
635   io_glue_commit_types(ig);
636
637   if (ig->readcb(ig, &headbuf, 18) != 18) {
638     i_push_error(errno, "could not read targa header");
639     return NULL;
640   }
641
642   tga_header_unpack(&header, headbuf);
643
644   mm_log((1,"Id length:         %d\n",header.idlength));
645   mm_log((1,"Colour map type:   %d\n",header.colourmaptype));
646   mm_log((1,"Image type:        %d\n",header.datatypecode));
647   mm_log((1,"Colour map offset: %d\n",header.colourmaporigin));
648   mm_log((1,"Colour map length: %d\n",header.colourmaplength));
649   mm_log((1,"Colour map depth:  %d\n",header.colourmapdepth));
650   mm_log((1,"X origin:          %d\n",header.x_origin));
651   mm_log((1,"Y origin:          %d\n",header.y_origin));
652   mm_log((1,"Width:             %d\n",header.width));
653   mm_log((1,"Height:            %d\n",header.height));
654   mm_log((1,"Bits per pixel:    %d\n",header.bitsperpixel));
655   mm_log((1,"Descriptor:        %d\n",header.imagedescriptor));
656
657   if (header.idlength) {
658     idstring = mymalloc(header.idlength+1);
659     if (ig->readcb(ig, idstring, header.idlength) != header.idlength) {
660       i_push_error(errno, "short read on targa idstring");
661       return NULL;
662     }
663   }
664
665   width = header.width;
666   height = header.height;
667
668   
669   /* Set tags here */
670   
671   switch (header.datatypecode) {
672   case 0: /* No data in image */
673     i_push_error(0, "Targa image contains no image data");
674     if (idstring) myfree(idstring);
675     return NULL;
676     break;
677   case 1:  /* Uncompressed, color-mapped images */
678   case 9:  /* Compressed,   color-mapped images */
679   case 3:  /* Uncompressed, grayscale images    */
680   case 11: /* Compressed,   grayscale images    */
681     if (header.bitsperpixel != 8) {
682       i_push_error(0, "Targa: mapped/grayscale image's bpp is not 8, unsupported.");
683       if (idstring) myfree(idstring);
684       return NULL;
685     }
686     src.bytepp = 1;
687     break;
688   case 2:  /* Uncompressed, rgb images          */
689   case 10: /* Compressed,   rgb images          */
690     if ((src.bytepp = bpp_to_bytes(header.bitsperpixel)))
691       break;
692     i_push_error(0, "Targa: direct color image's bpp is not 15/16/24/32 - unsupported.");
693     if (idstring) myfree(idstring);
694     return NULL;
695     break;
696   case 32: /* Compressed color-mapped, Huffman, Delta and runlength */
697   case 33: /* Compressed color-mapped, Huffman, Delta and runlength */
698     i_push_error(0, "Unsupported Targa (Huffman/delta/rle/quadtree) subformat is not supported");
699     if (idstring) myfree(idstring);
700     return NULL;
701     break;
702   default: /* All others which we don't know which might be */
703     i_push_error(0, "Unknown targa format");
704     if (idstring) myfree(idstring);
705     return NULL;
706     break;
707   }
708   
709   src.state = NoInit;
710   src.len = 0;
711   src.ig = ig;
712   src.compressed = !!(header.datatypecode & (1<<3));
713
714   /* Determine number of channels */
715   
716   mapped = 1;
717   switch (header.datatypecode) {
718   case 2:  /* Uncompressed, rgb images          */
719   case 10: /* Compressed,   rgb images          */
720     mapped = 0;
721   case 1:  /* Uncompressed, color-mapped images */
722   case 9:  /* Compressed,   color-mapped images */
723     if ((channels = bpp_to_channels(mapped ? 
724                                    header.colourmapdepth : 
725                                    header.bitsperpixel))) break;
726     i_push_error(0, "Targa Image has none of 15/16/24/32 pixel layout");
727     if (idstring) myfree(idstring);
728     return NULL;
729     break;
730   case 3:  /* Uncompressed, grayscale images    */
731   case 11: /* Compressed,   grayscale images    */
732     mapped = 0;
733     channels = 1;
734     break;
735   default:
736     i_push_error(0, "invalid or unsupported datatype code");
737     return NULL;
738   }
739
740   if (!i_int_check_image_file_limits(width, height, channels, 
741                                      sizeof(i_sample_t))) {
742     mm_log((1, "i_readtga_wiol: image size exceeds limits\n"));
743     return NULL;
744   }
745   
746   img = mapped ? 
747     i_img_pal_new(width, height, channels, 256) :
748     i_img_empty_ch(NULL, width, height, channels);
749
750   if (!img) {
751     if (idstring) 
752       myfree(idstring);
753     return NULL;
754   }
755   
756   if (idstring) {
757     i_tags_add(&img->tags, "tga_idstring", 0, idstring, header.idlength, 0);
758     myfree(idstring);
759   }
760
761   if (mapped &&
762       !tga_palette_read(ig,
763                         img,
764                         bpp_to_bytes(header.colourmapdepth),
765                         header.colourmaplength)
766       ) {
767     i_push_error(0, "Targa Image has none of 15/16/24/32 pixel layout");
768     if (idstring) myfree(idstring);
769     if (img) i_img_destroy(img);
770     return NULL;
771   }
772   
773   /* Allocate buffers */
774   databuf = mymalloc(width*src.bytepp);
775   if (!mapped) linebuf = mymalloc(width*sizeof(i_color));
776   
777   for(y=0; y<height; y++) {
778     if (!tga_source_read(&src, databuf, width)) {
779       i_push_error(errno, "read for targa data failed");
780       if (linebuf) myfree(linebuf);
781       myfree(databuf);
782       if (img) i_img_destroy(img);
783       return NULL;
784     }
785     if (mapped && header.colourmaporigin) for(x=0; x<width; x++) databuf[x] -= header.colourmaporigin;
786     if (mapped) i_ppal(img, 0, width, header.imagedescriptor & (1<<5) ? y : height-1-y, databuf);
787     else {
788       for(x=0; x<width; x++) color_unpack(databuf+x*src.bytepp, src.bytepp, linebuf+x);
789       i_plin(img, 0, width, header.imagedescriptor & (1<<5) ? y : height-1-y, linebuf);
790     }
791   }
792   myfree(databuf);
793   if (linebuf) myfree(linebuf);
794   
795   i_tags_add(&img->tags, "i_format", 0, "tga", -1, 0);
796   i_tags_addn(&img->tags, "tga_bitspp", 0, mapped?header.colourmapdepth:header.bitsperpixel);
797   if (src.compressed) i_tags_addn(&img->tags, "compressed", 0, 1);
798   return img;
799 }
800
801
802
803 /*
804 =item i_writetga_wiol(img, ig)
805
806 Writes an image in targa format.  Returns 0 on error.
807
808    img    - image to store
809    ig     - io_glue object
810
811 =cut
812 */
813
814 undef_int
815 i_writetga_wiol(i_img *img, io_glue *ig, int wierdpack, int compress, char *idstring, size_t idlen) {
816   tga_header header;
817   tga_dest dest;
818   unsigned char headbuf[18];
819   unsigned int bitspp;
820   
821   int mapped;
822
823   /* parameters */
824
825   /*
826     int compress = 1;
827     char *idstring = "testing";
828     int wierdpack = 0;
829   */
830
831   idlen = strlen(idstring);
832   mapped = img->type == i_palette_type;
833
834   mm_log((1,"i_writetga_wiol(img %p, ig %p, idstring %p, idlen %d, wierdpack %d, compress %d)\n",
835           img, ig, idstring, idlen, wierdpack, compress));
836   mm_log((1, "virtual %d, paletted %d\n", img->virtual, mapped));
837   mm_log((1, "channels %d\n", img->channels));
838   
839   i_clear_error();
840   io_glue_commit_types(ig);
841   
842   switch (img->channels) {
843   case 1:
844     bitspp = 8;
845     if (wierdpack) {
846       mm_log((1,"wierdpack option ignored for 1 channel images\n"));
847       wierdpack=0;
848     }
849     break;
850   case 2:
851     i_push_error(0, "Cannot store 2 channel image in targa format");
852     return 0;
853     break;
854   case 3:
855     bitspp = wierdpack ? 15 : 24;
856     break;
857   case 4:
858     bitspp = wierdpack ? 16 : 32;
859     break;
860   default:
861     i_push_error(0, "Targa only handles 1,3 and 4 channel images.");
862     return 0;
863   }
864
865   header.idlength = idlen;
866   header.colourmaptype   = mapped ? 1 : 0;
867   header.datatypecode    = mapped ? 1 : img->channels == 1 ? 3 : 2;
868   header.datatypecode   += compress ? 8 : 0;
869   mm_log((1, "datatypecode %d\n", header.datatypecode));
870   header.colourmaporigin = 0;
871   header.colourmaplength = mapped ? i_colorcount(img) : 0;
872   header.colourmapdepth  = mapped ? bitspp : 0;
873   header.x_origin        = 0;
874   header.y_origin        = 0;
875   header.width           = img->xsize;
876   header.height          = img->ysize;
877   header.bitsperpixel    = mapped ? 8 : bitspp;
878   header.imagedescriptor = (1<<5); /* normal order instead of upside down */
879
880   tga_header_pack(&header, headbuf);
881
882   if (ig->writecb(ig, &headbuf, sizeof(headbuf)) != sizeof(headbuf)) {
883     i_push_error(errno, "could not write targa header");
884     return 0;
885   }
886
887   if (idlen) {
888     if (ig->writecb(ig, idstring, idlen) != idlen) {
889       i_push_error(errno, "could not write targa idstring");
890       return 0;
891     }
892   }
893   
894   /* Make this into a constructor? */
895   dest.compressed = compress;
896   dest.bytepp     = mapped ? 1 : bpp_to_bytes(bitspp);
897   dest.ig         = ig;
898
899   mm_log((1, "dest.compressed = %d\n", dest.compressed));
900   mm_log((1, "dest.bytepp = %d\n", dest.bytepp));
901
902   if (img->type == i_palette_type) {
903     if (!tga_palette_write(ig, img, bitspp, i_colorcount(img))) return 0;
904     
905     if (!img->virtual && !dest.compressed) {
906       if (ig->writecb(ig, img->idata, img->bytes) != img->bytes) {
907         i_push_error(errno, "could not write targa image data");
908         return 0;
909       }
910     } else {
911       int y;
912       i_palidx *vals = mymalloc(sizeof(i_palidx)*img->xsize);
913       for(y=0; y<img->ysize; y++) {
914         i_gpal(img, 0, img->xsize, y, vals);
915         tga_dest_write(&dest, vals, img->xsize);
916       }
917       myfree(vals);
918     }
919   } else { /* direct type */
920     int x, y;
921     int bytepp = wierdpack ? 2 : bpp_to_bytes(bitspp);
922     int lsize = bytepp * img->xsize;
923     i_color *vals = mymalloc(img->xsize*sizeof(i_color));
924     unsigned char *buf = mymalloc(lsize);
925     
926     for(y=0; y<img->ysize; y++) {
927       i_glin(img, 0, img->xsize, y, vals);
928       for(x=0; x<img->xsize; x++) color_pack(buf+x*bytepp, bitspp, vals+x);
929       tga_dest_write(&dest, buf, img->xsize);
930     }
931     myfree(buf);
932     myfree(vals);
933   }
934
935   ig->closecb(ig);
936
937   return 1;
938 }
939
940 /*
941 =back
942
943 =head1 AUTHOR
944
945 Arnar M. Hrafnkelsson <addi@umich.edu>
946
947 =head1 SEE ALSO
948
949 Imager(3)
950
951 =cut
952 */