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