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