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