minor documentation clarification
[imager.git] / tga.c
CommitLineData
1ec86afa
AMH
1#include "image.h"
2#include "io.h"
3#include "log.h"
4#include "iolayer.h"
5
6#include <stdlib.h>
7#include <errno.h>
8
9
10/*
11=head1 NAME
12
13tga.c - implements reading and writing targa files, uses io layer.
14
15=head1 SYNOPSIS
16
17 io_glue *ig = io_new_fd( fd );
18 i_img *im = i_readtga_wiol(ig, -1); // no limit on how much is read
19 // or
20 io_glue *ig = io_new_fd( fd );
21 return_code = i_writetga_wiol(im, ig);
22
23=head1 DESCRIPTION
24
25tga.c implements the basic functions to read and write portable targa
26files. It uses the iolayer and needs either a seekable source or an
27entire memory mapped buffer.
28
29=head1 FUNCTION REFERENCE
30
31Some of these functions are internal.
32
33=over 4
34
35=cut
36*/
37
38
1ec86afa
AMH
39
40
41typedef struct {
42 char idlength;
43 char colourmaptype;
44 char datatypecode;
45 short int colourmaporigin;
46 short int colourmaplength;
47 char colourmapdepth;
48 short int x_origin;
49 short int y_origin;
50 short width;
51 short height;
52 char bitsperpixel;
53 char imagedescriptor;
54} tga_header;
55
56
920aa4a5
AMH
57typedef struct {
58 int compressed;
59 int bytepp;
60 enum { NoInit, Raw, Rle } state;
61 unsigned char cval[4];
62 int len;
63 unsigned char hdr;
64 io_glue *ig;
65} tga_source;
66
67
68static
69int
70bpp_to_bytes(unsigned int bpp) {
71 switch (bpp) {
72 case 8:
73 return 1;
74 case 15:
75 case 16:
76 return 2;
77 case 24:
78 return 3;
79 case 32:
80 return 4;
81 }
82 return 0;
83}
84
85static
86int
87bpp_to_channels(unsigned int bpp) {
88 switch (bpp) {
89 case 8:
90 return 1;
91 case 15:
92 return 3;
93 case 16:
94 return 4;
95 case 24:
96 return 3;
97 case 32:
98 return 4;
99 }
100 return 0;
101}
102
103
104
105/* color_unpack
106
107Unpacks bytes into colours, for 2 byte type the first byte coming from
108the file will actually be GGGBBBBB, and the second will be ARRRRRGG.
109"A" represents an attribute bit. The 3 byte entry contains 1 byte
110each of blue, green, and red. The 4 byte entry contains 1 byte each
111of blue, green, red, and attribute.
112*/
113
114static
115void
116color_unpack(unsigned char *buf, int bytepp, i_color *val) {
117 switch (bytepp) {
118 case 1:
119 val->gray.gray_color = buf[0];
120 break;
121 case 2:
122 val->rgba.r = (buf[1] & 0x7c) << 1;
123 val->rgba.g = ((buf[1] & 0x03) << 6) | ((buf[0] & 0xe0) >> 2);
124 val->rgba.b = (buf[0] & 0x1f) << 3;
125 val->rgba.a = (buf[1] & 0x80);
126 break;
127 case 3:
128 val->rgb.b = buf[0];
129 val->rgb.g = buf[1];
130 val->rgb.r = buf[2];
131 break;
132 case 4:
133 val->rgba.b = buf[0];
134 val->rgba.g = buf[1];
135 val->rgba.r = buf[2];
136 val->rgba.a = buf[3];
137 break;
138 default:
139 }
140}
141
142/*
143 tga_source_read
144
145 Does not byte reorder, returns true on success, 0 otherwise
146*/
147
148static
149int
150tga_source_read(tga_source *s, unsigned char *buf, size_t pixels) {
151 int cp = 0, j, k;
152 if (!s->compressed) {
153 if (s->ig->readcb(s->ig, buf, pixels*s->bytepp) != pixels*s->bytepp) return 0;
154 return 1;
155 }
156
157 while(cp < pixels) {
158 int ml;
159 if (s->len == 0) s->state = NoInit;
160 switch (s->state) {
161 case NoInit:
162 if (s->ig->readcb(s->ig, &s->hdr, 1) != 1) return 0;
163
164 s->len = (s->hdr &~(1<<7))+1;
165 s->state = (s->hdr & (1<<7)) ? Rle : Raw;
166 if (s->state == Rle && s->ig->readcb(s->ig, s->cval, s->bytepp) != s->bytepp) return 0;
167
168 break;
169 case Rle:
170 ml = min(s->len, pixels-cp);
171 for(k=0; k<ml; k++) for(j=0; j<s->bytepp; j++)
172 buf[(cp+k)*s->bytepp+j] = s->cval[j];
173 // memset(buf+cp, s->cidx, ml);
174 cp += ml;
175 s->len -= ml;
176 break;
177 case Raw:
178 ml = min(s->len, pixels-cp);
179 if (s->ig->readcb(s->ig, buf+cp*s->bytepp, ml*s->bytepp) != ml*s->bytepp) return 0;
180 cp += ml;
181 s->len -= ml;
182 break;
183 }
184 }
185 return 1;
186}
187
188static
189int
190tga_palette_read(io_glue *ig, i_img *img, int bytepp, int colourmaplength) {
191 int i;
192 size_t palbsize;
193 unsigned char *palbuf;
194 i_color val;
195
196 palbsize = colourmaplength*bytepp;
197 palbuf = mymalloc(palbsize);
198
199 if (ig->readcb(ig, palbuf, palbsize) != palbsize) {
200 i_push_error(errno, "could not read targa colourmap");
201 return 0;
202 }
203
204 /* populate the palette of the new image */
205 for(i=0; i<colourmaplength; i++) {
206 color_unpack(palbuf+i*bytepp, bytepp, &val);
207 i_addcolors(img, &val, 1);
208 }
209 myfree(palbuf);
210 return 1;
211}
212
213
214
215
1ec86afa
AMH
216
217/*
218=item i_readtga_wiol(ig, length)
219
220Retrieve an image and stores in the iolayer object. Returns NULL on fatal error.
221
222 ig - io_glue object
223 length - maximum length to read from data source, before closing it -1
224 signifies no limit.
225
226=cut
227*/
228
229i_img *
230i_readtga_wiol(io_glue *ig, int length) {
231 i_img* img;
7c58edfc 232 int x, y, i;
1ec86afa 233 int width, height, channels;
920aa4a5 234 int mapped;
1ec86afa 235 char *idstring;
1ec86afa 236
920aa4a5 237 tga_source src;
1ec86afa 238 tga_header header;
1ec86afa 239 unsigned char headbuf[18];
7c58edfc 240 unsigned char *databuf;
920aa4a5
AMH
241 unsigned char *reorderbuf;
242
243 i_color *linebuf = NULL;
1ec86afa
AMH
244 i_clear_error();
245
246 mm_log((1,"i_readtga(ig %p, length %d)\n", ig, length));
247
248 io_glue_commit_types(ig);
249
250 if (ig->readcb(ig, &headbuf, 18) != 18) {
251 i_push_error(errno, "could not read targa header");
252 return NULL;
253 }
254
255 header.idlength = headbuf[0];
256 header.colourmaptype = headbuf[1];
257 header.datatypecode = headbuf[2];
920aa4a5
AMH
258 header.colourmaporigin = (headbuf[4] << 8) + headbuf[3];
259 header.colourmaplength = (headbuf[6] << 8) + headbuf[5];
1ec86afa 260 header.colourmapdepth = headbuf[7];
920aa4a5
AMH
261 header.x_origin = (headbuf[9] << 8) + headbuf[8];
262 header.y_origin = (headbuf[11] << 8) + headbuf[10];
1ec86afa
AMH
263 header.width = (headbuf[13] << 8) + headbuf[12];
264 header.height = (headbuf[15] << 8) + headbuf[14];
265 header.bitsperpixel = headbuf[16];
266 header.imagedescriptor = headbuf[17];
267
268 mm_log((1,"Id length: %d\n",header.idlength));
269 mm_log((1,"Colour map type: %d\n",header.colourmaptype));
270 mm_log((1,"Image type: %d\n",header.datatypecode));
271 mm_log((1,"Colour map offset: %d\n",header.colourmaporigin));
272 mm_log((1,"Colour map length: %d\n",header.colourmaplength));
273 mm_log((1,"Colour map depth: %d\n",header.colourmapdepth));
274 mm_log((1,"X origin: %d\n",header.x_origin));
275 mm_log((1,"Y origin: %d\n",header.y_origin));
276 mm_log((1,"Width: %d\n",header.width));
277 mm_log((1,"Height: %d\n",header.height));
278 mm_log((1,"Bits per pixel: %d\n",header.bitsperpixel));
279 mm_log((1,"Descriptor: %d\n",header.imagedescriptor));
280
281
282 if (header.idlength) {
283 idstring = mymalloc(header.idlength+1);
284 if (ig->readcb(ig, idstring, header.idlength) != header.idlength) {
285 i_push_error(errno, "short read on targa idstring");
286 return NULL;
287 }
288 myfree(idstring); /* Move this later, once this is stored in a tag */
289 }
290
291 width = header.width;
292 height = header.height;
293
294 /* Set tags here */
920aa4a5 295
1ec86afa 296 switch (header.datatypecode) {
1ec86afa
AMH
297 case 0: /* No data in image */
298 i_push_error(0, "Targa image contains no image data");
299 return NULL;
300 break;
920aa4a5
AMH
301 case 1: /* Uncompressed, color-mapped images */
302 case 9: /* Compressed, color-mapped images */
303 case 3: /* Uncompressed, grayscale images */
304 case 11: /* Compressed, grayscale images */
1ec86afa 305 if (header.bitsperpixel != 8) {
920aa4a5 306 i_push_error(0, "Targa: mapped/grayscale image's bpp is not 8, unsupported.");
1ec86afa
AMH
307 return NULL;
308 }
920aa4a5 309 src.bytepp = 1;
1ec86afa 310 break;
920aa4a5
AMH
311 case 2: /* Uncompressed, rgb images */
312 case 10: /* Compressed, rgb images */
313 if ((src.bytepp = bpp_to_bytes(header.bitsperpixel)))
314 break;
315 i_push_error(0, "Targa: direct color image's bpp is not 15/16/24/32 - unsupported.");
1ec86afa
AMH
316 return NULL;
317 break;
318 case 32: /* Compressed color-mapped, Huffman, Delta and runlength */
1ec86afa 319 case 33: /* Compressed color-mapped, Huffman, Delta and runlength */
920aa4a5 320 i_push_error(0, "Unsupported Targa (Huffman/delta/rle/quadtree) subformat is not supported");
1ec86afa
AMH
321 return NULL;
322 break;
323 default: /* All others which we don't know which might be */
324 i_push_error(0, "Unknown targa format");
325 return NULL;
326 break;
327 }
920aa4a5
AMH
328
329 src.state = NoInit;
330 src.len = 0;
331 src.ig = ig;
332 src.compressed = !!(header.datatypecode & (1<<3));
333
334 /* Determine number of channels */
335
336 mapped = 1;
337 switch (header.datatypecode) {
338 int tbpp;
339 case 2: /* Uncompressed, rgb images */
340 case 10: /* Compressed, rgb images */
341 mapped = 0;
342 case 1: /* Uncompressed, color-mapped images */
343 case 9: /* Compressed, color-mapped images */
344 if ((channels = bpp_to_channels(mapped ?
345 header.colourmapdepth :
346 header.bitsperpixel))) break;
347 i_push_error(0, "Targa Image has none of 15/16/24/32 pixel layout");
348 return NULL;
349 break;
350 case 3: /* Uncompressed, grayscale images */
351 case 11: /* Compressed, grayscale images */
352 mapped = 0;
353 channels = 1;
354 break;
355 }
356
357 img = mapped ?
358 i_img_pal_new(width, height, channels, 256) :
359 i_img_empty_ch(NULL, width, height, channels);
360
361 if (mapped &&
362 !tga_palette_read(ig,
363 img,
364 bpp_to_bytes(header.colourmapdepth),
365 header.colourmaplength)
366 ) {
367 i_push_error(0, "Targa Image has none of 15/16/24/32 pixel layout");
368 return NULL;
369 }
370
371 /* Allocate buffers */
372 databuf = mymalloc(width*src.bytepp);
373 if (!mapped) linebuf = mymalloc(width*sizeof(i_color));
374
375 for(y=0; y<height; y++) {
376 if (!tga_source_read(&src, databuf, width)) {
377 i_push_error(errno, "read for targa data failed");
378 myfree(databuf);
379 return NULL;
380 }
381 if (mapped) i_ppal(img, 0, width, header.imagedescriptor & (1<<5) ? y : height-1-y, databuf);
382 else {
383 for(x=0; x<width; x++) color_unpack(databuf+x*src.bytepp, src.bytepp, linebuf+x);
384 i_plin(img, 0, width, header.imagedescriptor & (1<<5) ? y : height-1-y, linebuf);
385 }
386 }
387 myfree(databuf);
388 if (linebuf) myfree(linebuf);
389 return img;
1ec86afa
AMH
390}
391
392
393
394
395
396
397
398
399
400undef_int
401i_writetga_wiol(i_img *im, io_glue *ig) {
402 char header[255];
403 int rc;
404 writep write_func;
405
7c58edfc 406 mm_log((1,"i_writetga_wiol(im %p, ig %p)\n", im, ig));
1ec86afa
AMH
407 i_clear_error();
408
409 /* Add code to get the filename info from the iolayer */
410 /* Also add code to check for mmapped code */
411
412 io_glue_commit_types(ig);
413
7c58edfc
AMH
414 if (im->type == i_palette_type) {
415
416
417
418
419 }
420
421
422
423
424
425
426
427
1ec86afa
AMH
428 if (im->channels == 3) {
429 sprintf(header,"P6\n#CREATOR: Imager\n%d %d\n255\n",im->xsize,im->ysize);
430 if (ig->writecb(ig,header,strlen(header))<0) {
431 i_push_error(errno, "could not write ppm header");
432 mm_log((1,"i_writeppm: unable to write ppm header.\n"));
433 return(0);
434 }
435
436 if (!im->virtual && im->bits == i_8_bits && im->type == i_direct_type) {
437 rc = ig->writecb(ig,im->idata,im->bytes);
438 }
439 else {
440 unsigned char *data = mymalloc(3 * im->xsize);
441 if (data != NULL) {
442 int y = 0;
443 int x, ch;
444 unsigned char *p;
445 static int rgb_chan[3] = { 0, 1, 2 };
446
447 rc = 0;
448 while (y < im->ysize && rc >= 0) {
449 i_gsamp(im, 0, im->xsize, y, data, rgb_chan, 3);
450 rc = ig->writecb(ig, data, im->xsize * 3);
451 }
452 myfree(data);
453 }
454 else {
455 i_push_error(0, "Out of memory");
456 return 0;
457 }
458 }
459 if (rc<0) {
460 i_push_error(errno, "could not write ppm data");
461 mm_log((1,"i_writeppm: unable to write ppm data.\n"));
462 return(0);
463 }
464 }
465 else if (im->channels == 1) {
466 sprintf(header, "P5\n#CREATOR: Imager\n%d %d\n255\n",
467 im->xsize, im->ysize);
468 if (ig->writecb(ig,header, strlen(header)) < 0) {
469 i_push_error(errno, "could not write pgm header");
470 mm_log((1,"i_writeppm: unable to write pgm header.\n"));
471 return(0);
472 }
473
474 if (!im->virtual && im->bits == i_8_bits && im->type == i_direct_type) {
475 rc=ig->writecb(ig,im->idata,im->bytes);
476 }
477 else {
478 unsigned char *data = mymalloc(im->xsize);
479 if (data != NULL) {
480 int y = 0;
481 int x, ch;
482 int chan = 0;
483 unsigned char *p;
484
485 rc = 0;
486 while (y < im->ysize && rc >= 0) {
487 i_gsamp(im, 0, im->xsize, y, data, &chan, 1);
488 rc = ig->writecb(ig, data, im->xsize);
489 }
490 myfree(data);
491 }
492 else {
493 i_push_error(0, "Out of memory");
494 return 0;
495 }
496 }
497 if (rc<0) {
498 i_push_error(errno, "could not write pgm data");
499 mm_log((1,"i_writeppm: unable to write pgm data.\n"));
500 return(0);
501 }
502 }
503 else {
504 i_push_error(0, "can only save 1 or 3 channel images to pnm");
505 mm_log((1,"i_writeppm: ppm/pgm is 1 or 3 channel only (current image is %d)\n",im->channels));
506 return(0);
507 }
508
509 return(1);
510}