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