Commit | Line | Data |
---|---|---|
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 | ||
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 | ||
b8c2033e | 33 | =over |
1ec86afa AMH |
34 | |
35 | =cut | |
36 | */ | |
37 | ||
38 | ||
1ec86afa AMH |
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 | ||
76ff75b8 AMH |
57 | typedef enum { NoInit, Raw, Rle } rle_state; |
58 | ||
920aa4a5 AMH |
59 | typedef 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 |
70 | typedef 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 | ||
81 | Convert bits per pixel into bytes per pixel | |
82 | ||
83 | bpp - bits per pixel | |
84 | ||
85 | =cut | |
86 | */ | |
87 | ||
88 | ||
920aa4a5 AMH |
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 | ||
febba01f AMH |
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 | ||
920aa4a5 AMH |
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 | ||
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 |
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. | |
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 | ||
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; | |
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; | |
920aa4a5 AMH |
187 | } |
188 | } | |
189 | ||
76ff75b8 AMH |
190 | |
191 | ||
febba01f AMH |
192 | /* |
193 | =item color_pack | |
76ff75b8 | 194 | |
febba01f AMH |
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 | |
76ff75b8 AMH |
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. | |
febba01f AMH |
200 | |
201 | buf - destination buffer | |
202 | bitspp - bits per pixel | |
203 | val - color to pack | |
204 | ||
205 | =cut | |
920aa4a5 AMH |
206 | */ |
207 | ||
76ff75b8 AMH |
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; | |
76ff75b8 | 234 | } |
76ff75b8 AMH |
235 | } |
236 | ||
237 | ||
febba01f AMH |
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 | |
76ff75b8 | 247 | |
febba01f AMH |
248 | =cut |
249 | */ | |
76ff75b8 AMH |
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 | ||
febba01f AMH |
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 | ||
76ff75b8 AMH |
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 | ||
febba01f AMH |
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 | =item tga_header_pack(header, headbuf) | |
326 | ||
327 | Packs header structure into buffer for writing. | |
76ff75b8 | 328 | |
febba01f AMH |
329 | header - header structure |
330 | headbuf - buffer to pack into | |
76ff75b8 | 331 | |
febba01f AMH |
332 | =cut |
333 | */ | |
334 | ||
335 | static | |
336 | void | |
337 | tga_header_pack(tga_header *header, unsigned char headbuf[18]) { | |
338 | headbuf[0] = header->idlength; | |
339 | headbuf[1] = header->colourmaptype; | |
340 | headbuf[2] = header->datatypecode; | |
341 | headbuf[3] = header->colourmaporigin & 0xff; | |
342 | headbuf[4] = header->colourmaporigin >> 8; | |
343 | headbuf[5] = header->colourmaplength & 0xff; | |
344 | headbuf[6] = header->colourmaplength >> 8; | |
345 | headbuf[7] = header->colourmapdepth; | |
346 | headbuf[8] = header->x_origin & 0xff; | |
347 | headbuf[9] = header->x_origin >> 8; | |
348 | headbuf[10] = header->y_origin & 0xff; | |
349 | headbuf[11] = header->y_origin >> 8; | |
350 | headbuf[12] = header->width & 0xff; | |
351 | headbuf[13] = header->width >> 8; | |
352 | headbuf[14] = header->height & 0xff; | |
353 | headbuf[15] = header->height >> 8; | |
354 | headbuf[16] = header->bitsperpixel; | |
355 | headbuf[17] = header->imagedescriptor; | |
356 | } | |
357 | ||
358 | ||
359 | /* | |
360 | =item tga_source_read(s, buf, pixels) | |
361 | ||
362 | Reads pixel number of pixels from source s into buffer buf. Takes | |
363 | care of decompressing the stream if needed. | |
364 | ||
365 | s - data source | |
366 | buf - destination buffer | |
367 | pixels - number of pixels to put into buffer | |
368 | ||
369 | =cut | |
370 | */ | |
76ff75b8 | 371 | |
920aa4a5 AMH |
372 | static |
373 | int | |
374 | tga_source_read(tga_source *s, unsigned char *buf, size_t pixels) { | |
375 | int cp = 0, j, k; | |
376 | if (!s->compressed) { | |
377 | if (s->ig->readcb(s->ig, buf, pixels*s->bytepp) != pixels*s->bytepp) return 0; | |
378 | return 1; | |
379 | } | |
380 | ||
381 | while(cp < pixels) { | |
382 | int ml; | |
383 | if (s->len == 0) s->state = NoInit; | |
384 | switch (s->state) { | |
385 | case NoInit: | |
386 | if (s->ig->readcb(s->ig, &s->hdr, 1) != 1) return 0; | |
387 | ||
388 | s->len = (s->hdr &~(1<<7))+1; | |
389 | s->state = (s->hdr & (1<<7)) ? Rle : Raw; | |
9a88a5e6 AMH |
390 | { |
391 | static cnt = 0; | |
392 | printf("%04d %s: %d\n", cnt++, s->state==Rle?"RLE":"RAW", s->len); | |
393 | } | |
920aa4a5 AMH |
394 | if (s->state == Rle && s->ig->readcb(s->ig, s->cval, s->bytepp) != s->bytepp) return 0; |
395 | ||
396 | break; | |
397 | case Rle: | |
398 | ml = min(s->len, pixels-cp); | |
399 | for(k=0; k<ml; k++) for(j=0; j<s->bytepp; j++) | |
400 | buf[(cp+k)*s->bytepp+j] = s->cval[j]; | |
920aa4a5 AMH |
401 | cp += ml; |
402 | s->len -= ml; | |
403 | break; | |
404 | case Raw: | |
405 | ml = min(s->len, pixels-cp); | |
406 | if (s->ig->readcb(s->ig, buf+cp*s->bytepp, ml*s->bytepp) != ml*s->bytepp) return 0; | |
407 | cp += ml; | |
408 | s->len -= ml; | |
409 | break; | |
410 | } | |
411 | } | |
412 | return 1; | |
413 | } | |
414 | ||
76ff75b8 AMH |
415 | |
416 | ||
76ff75b8 | 417 | |
febba01f AMH |
418 | /* |
419 | =item tga_dest_write(s, buf, pixels) | |
420 | ||
421 | Writes pixels from buf to destination s. Takes care of compressing if the | |
422 | destination is compressed. | |
423 | ||
424 | s - data destination | |
425 | buf - source buffer | |
426 | pixels - number of pixels to put write to destination | |
427 | ||
428 | =cut | |
76ff75b8 AMH |
429 | */ |
430 | ||
431 | static | |
432 | int | |
433 | tga_dest_write(tga_dest *s, unsigned char *buf, size_t pixels) { | |
434 | int cp = 0, j, k; | |
435 | ||
436 | if (!s->compressed) { | |
437 | if (s->ig->writecb(s->ig, buf, pixels*s->bytepp) != pixels*s->bytepp) return 0; | |
438 | return 1; | |
439 | } | |
440 | ||
441 | while(cp < pixels) { | |
442 | int tlen; | |
443 | int nxtrip = find_repeat(buf+cp*s->bytepp, pixels-cp, s->bytepp); | |
444 | tlen = (nxtrip == -1) ? pixels-cp : nxtrip; | |
445 | while(tlen) { | |
9a88a5e6 | 446 | unsigned char clen = (tlen>128) ? 128 : tlen; |
76ff75b8 AMH |
447 | clen--; |
448 | if (s->ig->writecb(s->ig, &clen, 1) != 1) return 0; | |
449 | clen++; | |
450 | if (s->ig->writecb(s->ig, buf+cp*s->bytepp, clen*s->bytepp) != clen*s->bytepp) return 0; | |
451 | tlen -= clen; | |
452 | cp += clen; | |
453 | } | |
454 | if (cp >= pixels) break; | |
455 | tlen = find_span(buf+cp*s->bytepp, pixels-cp, s->bytepp); | |
07d70837 | 456 | if (tlen <3) continue; |
76ff75b8 | 457 | while (tlen) { |
9a88a5e6 | 458 | unsigned char clen = (tlen>128) ? 128 : tlen; |
76ff75b8 AMH |
459 | clen = (clen - 1) | 0x80; |
460 | if (s->ig->writecb(s->ig, &clen, 1) != 1) return 0; | |
461 | clen = (clen & ~0x80) + 1; | |
462 | if (s->ig->writecb(s->ig, buf+cp*s->bytepp, s->bytepp) != s->bytepp) return 0; | |
463 | tlen -= clen; | |
464 | cp += clen; | |
465 | } | |
466 | } | |
467 | return 1; | |
468 | } | |
469 | ||
470 | ||
471 | ||
472 | ||
473 | ||
474 | ||
febba01f AMH |
475 | /* |
476 | =item tga_palette_read(ig, img, bytepp, colourmaplength) | |
477 | ||
478 | Reads the colormap from a tga file and stores in the paletted image | |
479 | structure. | |
76ff75b8 | 480 | |
febba01f AMH |
481 | ig - iolayer data source |
482 | img - image structure | |
483 | bytepp - bytes per pixel | |
484 | colourmaplength - number of colours in colourmap | |
485 | ||
486 | =cut | |
487 | */ | |
76ff75b8 | 488 | |
920aa4a5 AMH |
489 | static |
490 | int | |
491 | tga_palette_read(io_glue *ig, i_img *img, int bytepp, int colourmaplength) { | |
492 | int i; | |
493 | size_t palbsize; | |
494 | unsigned char *palbuf; | |
495 | i_color val; | |
496 | ||
497 | palbsize = colourmaplength*bytepp; | |
498 | palbuf = mymalloc(palbsize); | |
499 | ||
500 | if (ig->readcb(ig, palbuf, palbsize) != palbsize) { | |
501 | i_push_error(errno, "could not read targa colourmap"); | |
502 | return 0; | |
503 | } | |
504 | ||
505 | /* populate the palette of the new image */ | |
506 | for(i=0; i<colourmaplength; i++) { | |
507 | color_unpack(palbuf+i*bytepp, bytepp, &val); | |
508 | i_addcolors(img, &val, 1); | |
509 | } | |
510 | myfree(palbuf); | |
511 | return 1; | |
512 | } | |
513 | ||
514 | ||
febba01f AMH |
515 | /* |
516 | =item tga_palette_write(ig, img, bitspp, colourmaplength) | |
517 | ||
518 | Stores the colormap of an image in the destination ig. | |
519 | ||
520 | ig - iolayer data source | |
521 | img - image structure | |
522 | bitspp - bits per pixel in colourmap | |
523 | colourmaplength - number of colours in colourmap | |
524 | ||
525 | =cut | |
526 | */ | |
920aa4a5 | 527 | |
76ff75b8 AMH |
528 | static |
529 | int | |
530 | tga_palette_write(io_glue *ig, i_img *img, int bitspp, int colourmaplength) { | |
531 | int i; | |
532 | int bytepp = bpp_to_bytes(bitspp); | |
533 | size_t palbsize = i_colorcount(img)*bytepp; | |
534 | unsigned char *palbuf = mymalloc(palbsize); | |
535 | ||
536 | for(i=0; i<colourmaplength; i++) { | |
537 | i_color val; | |
538 | i_getcolors(img, i, &val, 1); | |
539 | color_pack(palbuf+i*bytepp, bitspp, &val); | |
540 | } | |
541 | ||
542 | if (ig->writecb(ig, palbuf, palbsize) != palbsize) { | |
543 | i_push_error(errno, "could not write targa colourmap"); | |
544 | return 0; | |
545 | } | |
546 | myfree(palbuf); | |
547 | return 1; | |
548 | } | |
549 | ||
920aa4a5 | 550 | |
1ec86afa AMH |
551 | |
552 | /* | |
553 | =item i_readtga_wiol(ig, length) | |
554 | ||
febba01f AMH |
555 | Read in an image from the iolayer data source and return the image structure to it. |
556 | Returns NULL on error. | |
1ec86afa AMH |
557 | |
558 | ig - io_glue object | |
559 | length - maximum length to read from data source, before closing it -1 | |
560 | signifies no limit. | |
561 | ||
562 | =cut | |
563 | */ | |
564 | ||
565 | i_img * | |
566 | i_readtga_wiol(io_glue *ig, int length) { | |
07d70837 | 567 | i_img* img = NULL; |
7c58edfc | 568 | int x, y, i; |
1ec86afa | 569 | int width, height, channels; |
920aa4a5 | 570 | int mapped; |
07d70837 | 571 | char *idstring = NULL; |
1ec86afa | 572 | |
920aa4a5 | 573 | tga_source src; |
1ec86afa | 574 | tga_header header; |
1ec86afa | 575 | unsigned char headbuf[18]; |
7c58edfc | 576 | unsigned char *databuf; |
920aa4a5 AMH |
577 | unsigned char *reorderbuf; |
578 | ||
579 | i_color *linebuf = NULL; | |
1ec86afa AMH |
580 | i_clear_error(); |
581 | ||
582 | mm_log((1,"i_readtga(ig %p, length %d)\n", ig, length)); | |
583 | ||
584 | io_glue_commit_types(ig); | |
585 | ||
586 | if (ig->readcb(ig, &headbuf, 18) != 18) { | |
587 | i_push_error(errno, "could not read targa header"); | |
588 | return NULL; | |
589 | } | |
590 | ||
76ff75b8 | 591 | tga_header_unpack(&header, headbuf); |
1ec86afa AMH |
592 | |
593 | mm_log((1,"Id length: %d\n",header.idlength)); | |
594 | mm_log((1,"Colour map type: %d\n",header.colourmaptype)); | |
595 | mm_log((1,"Image type: %d\n",header.datatypecode)); | |
596 | mm_log((1,"Colour map offset: %d\n",header.colourmaporigin)); | |
597 | mm_log((1,"Colour map length: %d\n",header.colourmaplength)); | |
598 | mm_log((1,"Colour map depth: %d\n",header.colourmapdepth)); | |
599 | mm_log((1,"X origin: %d\n",header.x_origin)); | |
600 | mm_log((1,"Y origin: %d\n",header.y_origin)); | |
601 | mm_log((1,"Width: %d\n",header.width)); | |
602 | mm_log((1,"Height: %d\n",header.height)); | |
603 | mm_log((1,"Bits per pixel: %d\n",header.bitsperpixel)); | |
604 | mm_log((1,"Descriptor: %d\n",header.imagedescriptor)); | |
605 | ||
1ec86afa AMH |
606 | if (header.idlength) { |
607 | idstring = mymalloc(header.idlength+1); | |
608 | if (ig->readcb(ig, idstring, header.idlength) != header.idlength) { | |
609 | i_push_error(errno, "short read on targa idstring"); | |
610 | return NULL; | |
611 | } | |
1ec86afa AMH |
612 | } |
613 | ||
614 | width = header.width; | |
615 | height = header.height; | |
616 | ||
617 | /* Set tags here */ | |
920aa4a5 | 618 | |
1ec86afa | 619 | switch (header.datatypecode) { |
1ec86afa AMH |
620 | case 0: /* No data in image */ |
621 | i_push_error(0, "Targa image contains no image data"); | |
07d70837 | 622 | if (idstring) myfree(idstring); |
1ec86afa AMH |
623 | return NULL; |
624 | break; | |
920aa4a5 AMH |
625 | case 1: /* Uncompressed, color-mapped images */ |
626 | case 9: /* Compressed, color-mapped images */ | |
627 | case 3: /* Uncompressed, grayscale images */ | |
628 | case 11: /* Compressed, grayscale images */ | |
1ec86afa | 629 | if (header.bitsperpixel != 8) { |
920aa4a5 | 630 | i_push_error(0, "Targa: mapped/grayscale image's bpp is not 8, unsupported."); |
07d70837 | 631 | if (idstring) myfree(idstring); |
1ec86afa AMH |
632 | return NULL; |
633 | } | |
920aa4a5 | 634 | src.bytepp = 1; |
1ec86afa | 635 | break; |
920aa4a5 AMH |
636 | case 2: /* Uncompressed, rgb images */ |
637 | case 10: /* Compressed, rgb images */ | |
638 | if ((src.bytepp = bpp_to_bytes(header.bitsperpixel))) | |
639 | break; | |
640 | i_push_error(0, "Targa: direct color image's bpp is not 15/16/24/32 - unsupported."); | |
07d70837 | 641 | if (idstring) myfree(idstring); |
1ec86afa AMH |
642 | return NULL; |
643 | break; | |
644 | case 32: /* Compressed color-mapped, Huffman, Delta and runlength */ | |
1ec86afa | 645 | case 33: /* Compressed color-mapped, Huffman, Delta and runlength */ |
920aa4a5 | 646 | i_push_error(0, "Unsupported Targa (Huffman/delta/rle/quadtree) subformat is not supported"); |
07d70837 | 647 | if (idstring) myfree(idstring); |
1ec86afa AMH |
648 | return NULL; |
649 | break; | |
650 | default: /* All others which we don't know which might be */ | |
651 | i_push_error(0, "Unknown targa format"); | |
07d70837 | 652 | if (idstring) myfree(idstring); |
1ec86afa AMH |
653 | return NULL; |
654 | break; | |
655 | } | |
920aa4a5 AMH |
656 | |
657 | src.state = NoInit; | |
658 | src.len = 0; | |
659 | src.ig = ig; | |
660 | src.compressed = !!(header.datatypecode & (1<<3)); | |
661 | ||
662 | /* Determine number of channels */ | |
663 | ||
664 | mapped = 1; | |
665 | switch (header.datatypecode) { | |
666 | int tbpp; | |
667 | case 2: /* Uncompressed, rgb images */ | |
668 | case 10: /* Compressed, rgb images */ | |
669 | mapped = 0; | |
670 | case 1: /* Uncompressed, color-mapped images */ | |
671 | case 9: /* Compressed, color-mapped images */ | |
672 | if ((channels = bpp_to_channels(mapped ? | |
673 | header.colourmapdepth : | |
674 | header.bitsperpixel))) break; | |
675 | i_push_error(0, "Targa Image has none of 15/16/24/32 pixel layout"); | |
07d70837 | 676 | if (idstring) myfree(idstring); |
920aa4a5 AMH |
677 | return NULL; |
678 | break; | |
679 | case 3: /* Uncompressed, grayscale images */ | |
680 | case 11: /* Compressed, grayscale images */ | |
681 | mapped = 0; | |
682 | channels = 1; | |
683 | break; | |
684 | } | |
685 | ||
686 | img = mapped ? | |
687 | i_img_pal_new(width, height, channels, 256) : | |
688 | i_img_empty_ch(NULL, width, height, channels); | |
689 | ||
07d70837 AMH |
690 | if (idstring) { |
691 | i_tags_add(&img->tags, "tga_idstring", 0, idstring, header.idlength, 0); | |
4dfa5522 | 692 | myfree(idstring); |
07d70837 AMH |
693 | } |
694 | ||
920aa4a5 AMH |
695 | if (mapped && |
696 | !tga_palette_read(ig, | |
697 | img, | |
698 | bpp_to_bytes(header.colourmapdepth), | |
699 | header.colourmaplength) | |
700 | ) { | |
701 | i_push_error(0, "Targa Image has none of 15/16/24/32 pixel layout"); | |
07d70837 AMH |
702 | if (idstring) myfree(idstring); |
703 | if (img) i_img_destroy(img); | |
920aa4a5 AMH |
704 | return NULL; |
705 | } | |
706 | ||
707 | /* Allocate buffers */ | |
708 | databuf = mymalloc(width*src.bytepp); | |
709 | if (!mapped) linebuf = mymalloc(width*sizeof(i_color)); | |
710 | ||
711 | for(y=0; y<height; y++) { | |
712 | if (!tga_source_read(&src, databuf, width)) { | |
713 | i_push_error(errno, "read for targa data failed"); | |
714 | myfree(databuf); | |
07d70837 | 715 | if (img) i_img_destroy(img); |
920aa4a5 AMH |
716 | return NULL; |
717 | } | |
7456c26c | 718 | if (mapped && header.colourmaporigin) for(x=0; x<width; x++) databuf[x] -= header.colourmaporigin; |
920aa4a5 AMH |
719 | if (mapped) i_ppal(img, 0, width, header.imagedescriptor & (1<<5) ? y : height-1-y, databuf); |
720 | else { | |
721 | for(x=0; x<width; x++) color_unpack(databuf+x*src.bytepp, src.bytepp, linebuf+x); | |
722 | i_plin(img, 0, width, header.imagedescriptor & (1<<5) ? y : height-1-y, linebuf); | |
723 | } | |
724 | } | |
725 | myfree(databuf); | |
726 | if (linebuf) myfree(linebuf); | |
07d70837 AMH |
727 | |
728 | i_tags_addn(&img->tags, "tga_bitspp", 0, mapped?header.colourmapdepth:header.bitsperpixel); | |
729 | if (src.compressed) i_tags_addn(&img->tags, "compressed", 0, 1); | |
920aa4a5 | 730 | return img; |
1ec86afa AMH |
731 | } |
732 | ||
733 | ||
734 | ||
febba01f AMH |
735 | /* |
736 | =item i_writetga_wiol(img, ig) | |
1ec86afa | 737 | |
febba01f | 738 | Writes an image in targa format. Returns 0 on error. |
1ec86afa | 739 | |
febba01f AMH |
740 | img - image to store |
741 | ig - io_glue object | |
1ec86afa | 742 | |
febba01f AMH |
743 | =cut |
744 | */ | |
1ec86afa AMH |
745 | |
746 | undef_int | |
febba01f | 747 | i_writetga_wiol(i_img *img, io_glue *ig, int wierdpack, int compress, char *idstring, size_t idlen) { |
7456c26c AMH |
748 | static int rgb_chan[] = { 2, 1, 0, 3 }; |
749 | tga_header header; | |
76ff75b8 | 750 | tga_dest dest; |
7456c26c | 751 | unsigned char headbuf[18]; |
76ff75b8 AMH |
752 | unsigned int bitspp; |
753 | ||
76ff75b8 | 754 | int mapped; |
1ec86afa | 755 | |
76ff75b8 | 756 | /* parameters */ |
febba01f AMH |
757 | |
758 | /* | |
759 | int compress = 1; | |
760 | char *idstring = "testing"; | |
761 | int wierdpack = 0; | |
762 | */ | |
1ec86afa | 763 | |
76ff75b8 AMH |
764 | idlen = strlen(idstring); |
765 | mapped = img->type == i_palette_type; | |
1ec86afa | 766 | |
febba01f AMH |
767 | mm_log((1,"i_writetga_wiol(img %p, ig %p, idstring %p, idlen %d, wierdpack %d, compress %d)\n", |
768 | img, ig, idstring, idlen, wierdpack, compress)); | |
7456c26c AMH |
769 | mm_log((1, "virtual %d, paletted %d\n", img->virtual, mapped)); |
770 | mm_log((1, "channels %d\n", img->channels)); | |
76ff75b8 AMH |
771 | |
772 | i_clear_error(); | |
773 | ||
774 | switch (img->channels) { | |
775 | case 1: | |
776 | bitspp = 8; | |
777 | if (wierdpack) { | |
778 | mm_log((1,"wierdpack option ignored for 1 channel images\n")); | |
779 | wierdpack=0; | |
780 | } | |
781 | break; | |
782 | case 2: | |
783 | i_push_error(0, "Cannot store 2 channel image in targa format"); | |
784 | return 0; | |
785 | break; | |
786 | case 3: | |
787 | bitspp = wierdpack ? 15 : 24; | |
788 | break; | |
789 | case 4: | |
790 | bitspp = wierdpack ? 16 : 32; | |
791 | break; | |
792 | default: | |
793 | i_push_error(0, "Targa only handles 1,3 and 4 channel images."); | |
794 | return 0; | |
795 | } | |
7456c26c | 796 | |
76ff75b8 AMH |
797 | io_glue_commit_types(ig); |
798 | ||
7456c26c AMH |
799 | header.idlength; |
800 | header.idlength = idlen; | |
801 | header.colourmaptype = mapped ? 1 : 0; | |
802 | header.datatypecode = mapped ? 1 : img->channels == 1 ? 3 : 2; | |
7456c26c | 803 | header.datatypecode += compress ? 8 : 0; |
76ff75b8 | 804 | mm_log((1, "datatypecode %d\n", header.datatypecode)); |
7456c26c AMH |
805 | header.colourmaporigin = 0; |
806 | header.colourmaplength = mapped ? i_colorcount(img) : 0; | |
76ff75b8 | 807 | header.colourmapdepth = mapped ? bitspp : 0; |
7456c26c AMH |
808 | header.x_origin = 0; |
809 | header.y_origin = 0; | |
810 | header.width = img->xsize; | |
811 | header.height = img->ysize; | |
76ff75b8 | 812 | header.bitsperpixel = mapped ? 8 : bitspp; |
7456c26c AMH |
813 | header.imagedescriptor = (1<<5); /* normal order instead of upside down */ |
814 | ||
76ff75b8 | 815 | tga_header_pack(&header, headbuf); |
7456c26c AMH |
816 | |
817 | if (ig->writecb(ig, &headbuf, sizeof(headbuf)) != sizeof(headbuf)) { | |
818 | i_push_error(errno, "could not write targa header"); | |
819 | return 0; | |
7c58edfc AMH |
820 | } |
821 | ||
7456c26c AMH |
822 | if (idlen) { |
823 | if (ig->writecb(ig, idstring, idlen) != idlen) { | |
824 | i_push_error(errno, "could not write targa idstring"); | |
825 | return 0; | |
1ec86afa AMH |
826 | } |
827 | } | |
7456c26c | 828 | |
76ff75b8 AMH |
829 | /* Make this into a constructor? */ |
830 | dest.compressed = compress; | |
831 | dest.bytepp = mapped ? 1 : bpp_to_bytes(bitspp); | |
832 | dest.ig = ig; | |
c95846c2 | 833 | |
76ff75b8 AMH |
834 | mm_log((1, "dest.compressed = %d\n", dest.compressed)); |
835 | mm_log((1, "dest.bytepp = %d\n", dest.bytepp)); | |
c95846c2 | 836 | |
76ff75b8 AMH |
837 | if (img->type == i_palette_type) { |
838 | int i; | |
839 | int bytepp = bpp_to_bytes(bitspp); | |
840 | if (!tga_palette_write(ig, img, bitspp, i_colorcount(img))) return 0; | |
841 | ||
842 | if (!img->virtual && !dest.compressed) { | |
7456c26c AMH |
843 | if (ig->writecb(ig, img->idata, img->bytes) != img->bytes) { |
844 | i_push_error(errno, "could not write targa image data"); | |
845 | return 0; | |
1ec86afa | 846 | } |
7456c26c AMH |
847 | } else { |
848 | int y; | |
849 | i_palidx *vals = mymalloc(sizeof(i_palidx)*img->xsize); | |
850 | for(y=0; y<img->ysize; y++) { | |
851 | i_gpal(img, 0, img->xsize, y, vals); | |
76ff75b8 | 852 | tga_dest_write(&dest, vals, img->xsize); |
1ec86afa | 853 | } |
7456c26c | 854 | myfree(vals); |
1ec86afa | 855 | } |
76ff75b8 AMH |
856 | } else { /* direct type */ |
857 | int x, y; | |
858 | int bytepp = wierdpack ? 2 : bpp_to_bytes(bitspp); | |
859 | int lsize = bytepp * img->xsize; | |
860 | i_color *vals = mymalloc(img->xsize*sizeof(i_color)); | |
861 | unsigned char *buf = mymalloc(lsize); | |
862 | ||
7456c26c | 863 | for(y=0; y<img->ysize; y++) { |
76ff75b8 AMH |
864 | i_glin(img, 0, img->xsize, y, vals); |
865 | for(x=0; x<img->xsize; x++) color_pack(buf+x*bytepp, bitspp, vals+x); | |
866 | tga_dest_write(&dest, buf, img->xsize); | |
1ec86afa | 867 | } |
76ff75b8 AMH |
868 | myfree(buf); |
869 | myfree(vals); | |
1ec86afa | 870 | } |
7456c26c | 871 | return 1; |
1ec86afa | 872 | } |
b8c2033e AMH |
873 | |
874 | /* | |
875 | =back | |
876 | ||
877 | =head1 AUTHOR | |
878 | ||
879 | Arnar M. Hrafnkelsson <addi@umich.edu> | |
880 | ||
881 | =head1 SEE ALSO | |
882 | ||
883 | Imager(3) | |
884 | ||
885 | =cut | |
886 | */ |