]>
Commit | Line | Data |
---|---|---|
1 | #include "image.h" | |
2 | #include "io.h" | |
3 | #include "log.h" | |
4 | #include "iolayer.h" | |
5 | ||
6 | #include <stdlib.h> | |
7 | #include <errno.h> | |
8 | ||
9 | ||
10 | /* | |
11 | =head1 NAME | |
12 | ||
13 | tga.c - implements reading and writing targa files, uses io layer. | |
14 | ||
15 | =head1 SYNOPSIS | |
16 | ||
17 | io_glue *ig = io_new_fd( fd ); | |
18 | i_img *im = i_readtga_wiol(ig, -1); // no limit on how much is read | |
19 | // or | |
20 | io_glue *ig = io_new_fd( fd ); | |
21 | return_code = i_writetga_wiol(im, ig); | |
22 | ||
23 | =head1 DESCRIPTION | |
24 | ||
25 | tga.c implements the basic functions to read and write portable targa | |
26 | files. It uses the iolayer and needs either a seekable source or an | |
27 | entire memory mapped buffer. | |
28 | ||
29 | =head1 FUNCTION REFERENCE | |
30 | ||
31 | Some of these functions are internal. | |
32 | ||
33 | =over | |
34 | ||
35 | =cut | |
36 | */ | |
37 | ||
38 | ||
39 | ||
40 | ||
41 | typedef struct { | |
42 | char idlength; | |
43 | char colourmaptype; | |
44 | char datatypecode; | |
45 | short int colourmaporigin; | |
46 | short int colourmaplength; | |
47 | char colourmapdepth; | |
48 | short int x_origin; | |
49 | short int y_origin; | |
50 | short width; | |
51 | short height; | |
52 | char bitsperpixel; | |
53 | char imagedescriptor; | |
54 | } tga_header; | |
55 | ||
56 | ||
57 | typedef enum { NoInit, Raw, Rle } rle_state; | |
58 | ||
59 | typedef struct { | |
60 | int compressed; | |
61 | int bytepp; | |
62 | rle_state state; | |
63 | unsigned char cval[4]; | |
64 | int len; | |
65 | unsigned char hdr; | |
66 | io_glue *ig; | |
67 | } tga_source; | |
68 | ||
69 | ||
70 | typedef struct { | |
71 | int compressed; | |
72 | int bytepp; | |
73 | io_glue *ig; | |
74 | } tga_dest; | |
75 | ||
76 | ||
77 | ||
78 | /* | |
79 | =item bpp_to_bytes(bpp) | |
80 | ||
81 | Convert bits per pixel into bytes per pixel | |
82 | ||
83 | bpp - bits per pixel | |
84 | ||
85 | =cut | |
86 | */ | |
87 | ||
88 | ||
89 | static | |
90 | int | |
91 | bpp_to_bytes(unsigned int bpp) { | |
92 | switch (bpp) { | |
93 | case 8: | |
94 | return 1; | |
95 | case 15: | |
96 | case 16: | |
97 | return 2; | |
98 | case 24: | |
99 | return 3; | |
100 | case 32: | |
101 | return 4; | |
102 | } | |
103 | return 0; | |
104 | } | |
105 | ||
106 | ||
107 | ||
108 | /* | |
109 | =item bpp_to_channels(bpp) | |
110 | ||
111 | Convert bits per pixel into channels in the image | |
112 | ||
113 | bpp - bits per pixel | |
114 | ||
115 | =cut | |
116 | */ | |
117 | ||
118 | static | |
119 | int | |
120 | bpp_to_channels(unsigned int bpp) { | |
121 | switch (bpp) { | |
122 | case 8: | |
123 | return 1; | |
124 | case 15: | |
125 | return 3; | |
126 | case 16: | |
127 | return 4; | |
128 | case 24: | |
129 | return 3; | |
130 | case 32: | |
131 | return 4; | |
132 | } | |
133 | return 0; | |
134 | } | |
135 | ||
136 | ||
137 | ||
138 | /* | |
139 | * Packing functions - used for (un)packing | |
140 | * datastructures into raw bytes. | |
141 | */ | |
142 | ||
143 | ||
144 | /* | |
145 | =item color_unpack(buf, bytepp, val) | |
146 | ||
147 | Unpacks bytes into colour structures, for 2 byte type the first byte | |
148 | coming from the file will actually be GGGBBBBB, and the second will be | |
149 | ARRRRRGG. "A" represents an attribute bit. The 3 byte entry contains | |
150 | 1 byte each of blue, green, and red. The 4 byte entry contains 1 byte | |
151 | each of blue, green, red, and attribute. | |
152 | ||
153 | buf - pointer to data | |
154 | bytepp - bytes per pixel | |
155 | val - pointer to color to store to | |
156 | ||
157 | =cut | |
158 | */ | |
159 | ||
160 | static | |
161 | void | |
162 | color_unpack(unsigned char *buf, int bytepp, i_color *val) { | |
163 | switch (bytepp) { | |
164 | case 1: | |
165 | val->gray.gray_color = buf[0]; | |
166 | break; | |
167 | case 2: | |
168 | val->rgba.r = (buf[1] & 0x7c) << 1; | |
169 | val->rgba.g = ((buf[1] & 0x03) << 6) | ((buf[0] & 0xe0) >> 2); | |
170 | val->rgba.b = (buf[0] & 0x1f) << 3; | |
171 | val->rgba.a = (buf[1] & 0x80) ? 255 : 0; | |
172 | val->rgba.r |= val->rgba.r >> 5; | |
173 | val->rgba.g |= val->rgba.g >> 5; | |
174 | val->rgba.b |= val->rgba.b >> 5; | |
175 | break; | |
176 | case 3: | |
177 | val->rgb.b = buf[0]; | |
178 | val->rgb.g = buf[1]; | |
179 | val->rgb.r = buf[2]; | |
180 | break; | |
181 | case 4: | |
182 | val->rgba.b = buf[0]; | |
183 | val->rgba.g = buf[1]; | |
184 | val->rgba.r = buf[2]; | |
185 | val->rgba.a = buf[3]; | |
186 | break; | |
187 | } | |
188 | } | |
189 | ||
190 | ||
191 | ||
192 | /* | |
193 | =item color_pack | |
194 | ||
195 | Packs a colour into an array of bytes, for 2 byte type the first byte | |
196 | will be GGGBBBBB, and the second will be ARRRRRGG. "A" represents an | |
197 | attribute bit. The 3 byte entry contains 1 byte each of blue, green, | |
198 | and red. The 4 byte entry contains 1 byte each of blue, green, red, | |
199 | and attribute. | |
200 | ||
201 | buf - destination buffer | |
202 | bitspp - bits per pixel | |
203 | val - color to pack | |
204 | ||
205 | =cut | |
206 | */ | |
207 | ||
208 | static | |
209 | void | |
210 | color_pack(unsigned char *buf, int bitspp, i_color *val) { | |
211 | switch (bitspp) { | |
212 | case 8: | |
213 | buf[0] = val->gray.gray_color; | |
214 | break; | |
215 | case 15: | |
216 | buf[0] = (val->rgba.b >> 3); | |
217 | buf[0] |= (val->rgba.g & 0x38) << 2; | |
218 | buf[1] = (val->rgba.r & 0xf8)>> 1; | |
219 | buf[1] |= (val->rgba.g >> 6); | |
220 | case 16: | |
221 | buf[1] |= val->rgba.a & 0x80; | |
222 | break; | |
223 | case 24: | |
224 | buf[0] = val->rgb.b; | |
225 | buf[1] = val->rgb.g; | |
226 | buf[2] = val->rgb.r; | |
227 | break; | |
228 | case 32: | |
229 | buf[0] = val->rgba.b; | |
230 | buf[1] = val->rgba.g; | |
231 | buf[2] = val->rgba.r; | |
232 | buf[3] = val->rgba.a; | |
233 | break; | |
234 | } | |
235 | } | |
236 | ||
237 | ||
238 | /* | |
239 | =item find_repeat | |
240 | ||
241 | Helper function for rle compressor to find the next triple repeat of the | |
242 | same pixel value in buffer. | |
243 | ||
244 | buf - buffer | |
245 | length - number of pixel values in buffer | |
246 | bytepp - number of bytes in a pixel value | |
247 | ||
248 | =cut | |
249 | */ | |
250 | ||
251 | static | |
252 | int | |
253 | find_repeat(unsigned char *buf, int length, int bytepp) { | |
254 | int i = 0; | |
255 | ||
256 | while(i<length-1) { | |
257 | if(memcmp(buf+i*bytepp, buf+(i+1)*bytepp, bytepp) == 0) { | |
258 | if (i == length-2) return -1; | |
259 | if (memcmp(buf+(i+1)*bytepp, buf+(i+2)*bytepp,bytepp) == 0) | |
260 | return i; | |
261 | else i++; | |
262 | } | |
263 | i++; | |
264 | } | |
265 | return -1; | |
266 | } | |
267 | ||
268 | ||
269 | /* | |
270 | =item find_span | |
271 | ||
272 | Helper function for rle compressor to find the length of a span where | |
273 | the same pixel value is in the buffer. | |
274 | ||
275 | buf - buffer | |
276 | length - number of pixel values in buffer | |
277 | bytepp - number of bytes in a pixel value | |
278 | ||
279 | =cut | |
280 | */ | |
281 | ||
282 | static | |
283 | int | |
284 | find_span(unsigned char *buf, int length, int bytepp) { | |
285 | int i = 0; | |
286 | while(i<length) { | |
287 | if(memcmp(buf, buf+(i*bytepp), bytepp) != 0) return i; | |
288 | i++; | |
289 | } | |
290 | return length; | |
291 | } | |
292 | ||
293 | ||
294 | /* | |
295 | =item tga_header_unpack(header, headbuf) | |
296 | ||
297 | Unpacks the header structure into from buffer and stores | |
298 | in the header structure. | |
299 | ||
300 | header - header structure | |
301 | headbuf - buffer to unpack from | |
302 | ||
303 | =cut | |
304 | */ | |
305 | ||
306 | static | |
307 | void | |
308 | tga_header_unpack(tga_header *header, unsigned char headbuf[18]) { | |
309 | header->idlength = headbuf[0]; | |
310 | header->colourmaptype = headbuf[1]; | |
311 | header->datatypecode = headbuf[2]; | |
312 | header->colourmaporigin = (headbuf[4] << 8) + headbuf[3]; | |
313 | header->colourmaplength = (headbuf[6] << 8) + headbuf[5]; | |
314 | header->colourmapdepth = headbuf[7]; | |
315 | header->x_origin = (headbuf[9] << 8) + headbuf[8]; | |
316 | header->y_origin = (headbuf[11] << 8) + headbuf[10]; | |
317 | header->width = (headbuf[13] << 8) + headbuf[12]; | |
318 | header->height = (headbuf[15] << 8) + headbuf[14]; | |
319 | header->bitsperpixel = headbuf[16]; | |
320 | header->imagedescriptor = headbuf[17]; | |
321 | } | |
322 | ||
323 | ||
324 | /* | |
325 | =item tga_header_pack(header, headbuf) | |
326 | ||
327 | Packs header structure into buffer for writing. | |
328 | ||
329 | header - header structure | |
330 | headbuf - buffer to pack into | |
331 | ||
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 | */ | |
371 | ||
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; | |
390 | { | |
391 | static cnt = 0; | |
392 | printf("%04d %s: %d\n", cnt++, s->state==Rle?"RLE":"RAW", s->len); | |
393 | } | |
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]; | |
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 | ||
415 | ||
416 | ||
417 | ||
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 | |
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) { | |
446 | unsigned char clen = (tlen>128) ? 128 : tlen; | |
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); | |
456 | if (tlen <3) continue; | |
457 | while (tlen) { | |
458 | unsigned char clen = (tlen>128) ? 128 : tlen; | |
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 | ||
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. | |
480 | ||
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 | */ | |
488 | ||
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 | ||
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 | */ | |
527 | ||
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 | ||
550 | ||
551 | ||
552 | /* | |
553 | =item i_readtga_wiol(ig, length) | |
554 | ||
555 | Read in an image from the iolayer data source and return the image structure to it. | |
556 | Returns NULL on error. | |
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) { | |
567 | i_img* img = NULL; | |
568 | int x, y, i; | |
569 | int width, height, channels; | |
570 | int mapped; | |
571 | char *idstring = NULL; | |
572 | ||
573 | tga_source src; | |
574 | tga_header header; | |
575 | unsigned char headbuf[18]; | |
576 | unsigned char *databuf; | |
577 | unsigned char *reorderbuf; | |
578 | ||
579 | i_color *linebuf = NULL; | |
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 | ||
591 | tga_header_unpack(&header, headbuf); | |
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 | ||
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 | } | |
612 | } | |
613 | ||
614 | width = header.width; | |
615 | height = header.height; | |
616 | ||
617 | /* Set tags here */ | |
618 | ||
619 | switch (header.datatypecode) { | |
620 | case 0: /* No data in image */ | |
621 | i_push_error(0, "Targa image contains no image data"); | |
622 | if (idstring) myfree(idstring); | |
623 | return NULL; | |
624 | break; | |
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 */ | |
629 | if (header.bitsperpixel != 8) { | |
630 | i_push_error(0, "Targa: mapped/grayscale image's bpp is not 8, unsupported."); | |
631 | if (idstring) myfree(idstring); | |
632 | return NULL; | |
633 | } | |
634 | src.bytepp = 1; | |
635 | break; | |
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."); | |
641 | if (idstring) myfree(idstring); | |
642 | return NULL; | |
643 | break; | |
644 | case 32: /* Compressed color-mapped, Huffman, Delta and runlength */ | |
645 | case 33: /* Compressed color-mapped, Huffman, Delta and runlength */ | |
646 | i_push_error(0, "Unsupported Targa (Huffman/delta/rle/quadtree) subformat is not supported"); | |
647 | if (idstring) myfree(idstring); | |
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"); | |
652 | if (idstring) myfree(idstring); | |
653 | return NULL; | |
654 | break; | |
655 | } | |
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"); | |
676 | if (idstring) myfree(idstring); | |
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 | ||
690 | if (idstring) { | |
691 | i_tags_add(&img->tags, "tga_idstring", 0, idstring, header.idlength, 0); | |
692 | myfree(idstring); | |
693 | } | |
694 | ||
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"); | |
702 | if (idstring) myfree(idstring); | |
703 | if (img) i_img_destroy(img); | |
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); | |
715 | if (img) i_img_destroy(img); | |
716 | return NULL; | |
717 | } | |
718 | if (mapped && header.colourmaporigin) for(x=0; x<width; x++) databuf[x] -= header.colourmaporigin; | |
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); | |
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); | |
730 | return img; | |
731 | } | |
732 | ||
733 | ||
734 | ||
735 | /* | |
736 | =item i_writetga_wiol(img, ig) | |
737 | ||
738 | Writes an image in targa format. Returns 0 on error. | |
739 | ||
740 | img - image to store | |
741 | ig - io_glue object | |
742 | ||
743 | =cut | |
744 | */ | |
745 | ||
746 | undef_int | |
747 | i_writetga_wiol(i_img *img, io_glue *ig, int wierdpack, int compress, char *idstring, size_t idlen) { | |
748 | static int rgb_chan[] = { 2, 1, 0, 3 }; | |
749 | tga_header header; | |
750 | tga_dest dest; | |
751 | unsigned char headbuf[18]; | |
752 | unsigned int bitspp; | |
753 | ||
754 | int mapped; | |
755 | ||
756 | /* parameters */ | |
757 | ||
758 | /* | |
759 | int compress = 1; | |
760 | char *idstring = "testing"; | |
761 | int wierdpack = 0; | |
762 | */ | |
763 | ||
764 | idlen = strlen(idstring); | |
765 | mapped = img->type == i_palette_type; | |
766 | ||
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)); | |
769 | mm_log((1, "virtual %d, paletted %d\n", img->virtual, mapped)); | |
770 | mm_log((1, "channels %d\n", img->channels)); | |
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 | } | |
796 | ||
797 | io_glue_commit_types(ig); | |
798 | ||
799 | header.idlength; | |
800 | header.idlength = idlen; | |
801 | header.colourmaptype = mapped ? 1 : 0; | |
802 | header.datatypecode = mapped ? 1 : img->channels == 1 ? 3 : 2; | |
803 | header.datatypecode += compress ? 8 : 0; | |
804 | mm_log((1, "datatypecode %d\n", header.datatypecode)); | |
805 | header.colourmaporigin = 0; | |
806 | header.colourmaplength = mapped ? i_colorcount(img) : 0; | |
807 | header.colourmapdepth = mapped ? bitspp : 0; | |
808 | header.x_origin = 0; | |
809 | header.y_origin = 0; | |
810 | header.width = img->xsize; | |
811 | header.height = img->ysize; | |
812 | header.bitsperpixel = mapped ? 8 : bitspp; | |
813 | header.imagedescriptor = (1<<5); /* normal order instead of upside down */ | |
814 | ||
815 | tga_header_pack(&header, headbuf); | |
816 | ||
817 | if (ig->writecb(ig, &headbuf, sizeof(headbuf)) != sizeof(headbuf)) { | |
818 | i_push_error(errno, "could not write targa header"); | |
819 | return 0; | |
820 | } | |
821 | ||
822 | if (idlen) { | |
823 | if (ig->writecb(ig, idstring, idlen) != idlen) { | |
824 | i_push_error(errno, "could not write targa idstring"); | |
825 | return 0; | |
826 | } | |
827 | } | |
828 | ||
829 | /* Make this into a constructor? */ | |
830 | dest.compressed = compress; | |
831 | dest.bytepp = mapped ? 1 : bpp_to_bytes(bitspp); | |
832 | dest.ig = ig; | |
833 | ||
834 | mm_log((1, "dest.compressed = %d\n", dest.compressed)); | |
835 | mm_log((1, "dest.bytepp = %d\n", dest.bytepp)); | |
836 | ||
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) { | |
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; | |
846 | } | |
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); | |
852 | tga_dest_write(&dest, vals, img->xsize); | |
853 | } | |
854 | myfree(vals); | |
855 | } | |
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 | ||
863 | for(y=0; y<img->ysize; y++) { | |
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); | |
867 | } | |
868 | myfree(buf); | |
869 | myfree(vals); | |
870 | } | |
871 | return 1; | |
872 | } | |
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 | */ |