13 tga.c - implements reading and writing targa files, uses io layer.
17 io_glue *ig = io_new_fd( fd );
18 i_img *im = i_readtga_wiol(ig, -1); // no limit on how much is read
20 io_glue *ig = io_new_fd( fd );
21 return_code = i_writetga_wiol(im, ig);
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.
29 =head1 FUNCTION REFERENCE
31 Some of these functions are internal.
45 short int colourmaporigin;
46 short int colourmaplength;
60 enum { NoInit, Raw, Rle } state;
61 unsigned char cval[4];
70 bpp_to_bytes(unsigned int bpp) {
87 bpp_to_channels(unsigned int bpp) {
107 Unpacks bytes into colours, for 2 byte type the first byte coming from
108 the file will actually be GGGBBBBB, and the second will be ARRRRRGG.
109 "A" represents an attribute bit. The 3 byte entry contains 1 byte
110 each of blue, green, and red. The 4 byte entry contains 1 byte each
111 of blue, green, red, and attribute.
116 color_unpack(unsigned char *buf, int bytepp, i_color *val) {
119 val->gray.gray_color = buf[0];
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);
133 val->rgba.b = buf[0];
134 val->rgba.g = buf[1];
135 val->rgba.r = buf[2];
136 val->rgba.a = buf[3];
145 Does not byte reorder, returns true on success, 0 otherwise
150 tga_source_read(tga_source *s, unsigned char *buf, size_t pixels) {
152 if (!s->compressed) {
153 if (s->ig->readcb(s->ig, buf, pixels*s->bytepp) != pixels*s->bytepp) return 0;
159 if (s->len == 0) s->state = NoInit;
162 if (s->ig->readcb(s->ig, &s->hdr, 1) != 1) return 0;
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;
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);
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;
190 tga_palette_read(io_glue *ig, i_img *img, int bytepp, int colourmaplength) {
193 unsigned char *palbuf;
196 palbsize = colourmaplength*bytepp;
197 palbuf = mymalloc(palbsize);
199 if (ig->readcb(ig, palbuf, palbsize) != palbsize) {
200 i_push_error(errno, "could not read targa colourmap");
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);
218 =item i_readtga_wiol(ig, length)
220 Retrieve an image and stores in the iolayer object. Returns NULL on fatal error.
223 length - maximum length to read from data source, before closing it -1
230 i_readtga_wiol(io_glue *ig, int length) {
233 int width, height, channels;
239 unsigned char headbuf[18];
240 unsigned char *databuf;
241 unsigned char *reorderbuf;
243 i_color *linebuf = NULL;
246 mm_log((1,"i_readtga(ig %p, length %d)\n", ig, length));
248 io_glue_commit_types(ig);
250 if (ig->readcb(ig, &headbuf, 18) != 18) {
251 i_push_error(errno, "could not read targa header");
255 header.idlength = headbuf[0];
256 header.colourmaptype = headbuf[1];
257 header.datatypecode = headbuf[2];
258 header.colourmaporigin = (headbuf[4] << 8) + headbuf[3];
259 header.colourmaplength = (headbuf[6] << 8) + headbuf[5];
260 header.colourmapdepth = headbuf[7];
261 header.x_origin = (headbuf[9] << 8) + headbuf[8];
262 header.y_origin = (headbuf[11] << 8) + headbuf[10];
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];
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));
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");
288 myfree(idstring); /* Move this later, once this is stored in a tag */
291 width = header.width;
292 height = header.height;
296 switch (header.datatypecode) {
297 case 0: /* No data in image */
298 i_push_error(0, "Targa image contains no image data");
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 */
305 if (header.bitsperpixel != 8) {
306 i_push_error(0, "Targa: mapped/grayscale image's bpp is not 8, unsupported.");
311 case 2: /* Uncompressed, rgb images */
312 case 10: /* Compressed, rgb images */
313 if ((src.bytepp = bpp_to_bytes(header.bitsperpixel)))
315 i_push_error(0, "Targa: direct color image's bpp is not 15/16/24/32 - unsupported.");
318 case 32: /* Compressed color-mapped, Huffman, Delta and runlength */
319 case 33: /* Compressed color-mapped, Huffman, Delta and runlength */
320 i_push_error(0, "Unsupported Targa (Huffman/delta/rle/quadtree) subformat is not supported");
323 default: /* All others which we don't know which might be */
324 i_push_error(0, "Unknown targa format");
332 src.compressed = !!(header.datatypecode & (1<<3));
334 /* Determine number of channels */
337 switch (header.datatypecode) {
339 case 2: /* Uncompressed, rgb images */
340 case 10: /* Compressed, rgb images */
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");
350 case 3: /* Uncompressed, grayscale images */
351 case 11: /* Compressed, grayscale images */
358 i_img_pal_new(width, height, channels, 256) :
359 i_img_empty_ch(NULL, width, height, channels);
362 !tga_palette_read(ig,
364 bpp_to_bytes(header.colourmapdepth),
365 header.colourmaplength)
367 i_push_error(0, "Targa Image has none of 15/16/24/32 pixel layout");
371 /* Allocate buffers */
372 databuf = mymalloc(width*src.bytepp);
373 if (!mapped) linebuf = mymalloc(width*sizeof(i_color));
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");
381 if (mapped && header.colourmaporigin) for(x=0; x<width; x++) databuf[x] -= header.colourmaporigin;
382 if (mapped) i_ppal(img, 0, width, header.imagedescriptor & (1<<5) ? y : height-1-y, databuf);
384 for(x=0; x<width; x++) color_unpack(databuf+x*src.bytepp, src.bytepp, linebuf+x);
385 i_plin(img, 0, width, header.imagedescriptor & (1<<5) ? y : height-1-y, linebuf);
389 if (linebuf) myfree(linebuf);
402 i_writetga_wiol(i_img *img, io_glue *ig) {
404 static int rgb_chan[] = { 2, 1, 0, 3 };
406 unsigned char headbuf[18];
409 char *idstring = "testing";
410 int idlen = strlen(idstring);
411 int mapped = img->type == i_palette_type;
413 mm_log((1,"i_writetga_wiol(img %p, ig %p)\n", img, ig));
416 io_glue_commit_types(ig);
418 mm_log((1, "virtual %d, paletted %d\n", img->virtual, mapped));
419 mm_log((1, "channels %d\n", img->channels));
422 header.idlength = idlen;
423 header.colourmaptype = mapped ? 1 : 0;
424 header.datatypecode = mapped ? 1 : img->channels == 1 ? 3 : 2;
425 mm_log((1, "datatypecode %d\n", header.datatypecode));
426 header.datatypecode += compress ? 8 : 0;
427 header.colourmaporigin = 0;
428 header.colourmaplength = mapped ? i_colorcount(img) : 0;
429 header.colourmapdepth = mapped ? img->channels*8 : 0;
432 header.width = img->xsize;
433 header.height = img->ysize;
434 header.bitsperpixel = mapped ? 8 : 8*img->channels;
435 header.imagedescriptor = (1<<5); /* normal order instead of upside down */
437 headbuf[0] = header.idlength;
438 headbuf[1] = header.colourmaptype;
439 headbuf[2] = header.datatypecode;
440 headbuf[3] = header.colourmaporigin & 0xff;
441 headbuf[4] = header.colourmaporigin >> 8;
442 headbuf[5] = header.colourmaplength & 0xff;
443 headbuf[6] = header.colourmaplength >> 8;
444 headbuf[7] = header.colourmapdepth;
445 headbuf[8] = header.x_origin & 0xff;
446 headbuf[9] = header.x_origin >> 8;
447 headbuf[10] = header.y_origin & 0xff;
448 headbuf[11] = header.y_origin >> 8;
449 headbuf[12] = header.width & 0xff;
450 headbuf[13] = header.width >> 8;
451 headbuf[14] = header.height & 0xff;
452 headbuf[15] = header.height >> 8;
453 headbuf[16] = header.bitsperpixel;
454 headbuf[17] = header.imagedescriptor;
456 if (ig->writecb(ig, &headbuf, sizeof(headbuf)) != sizeof(headbuf)) {
457 i_push_error(errno, "could not write targa header");
462 if (ig->writecb(ig, idstring, idlen) != idlen) {
463 i_push_error(errno, "could not write targa idstring");
469 if (img->type == i_palette_type) {
472 if (ig->writecb(ig, img->idata, img->bytes) != img->bytes) {
473 i_push_error(errno, "could not write targa image data");
478 i_palidx *vals = mymalloc(sizeof(i_palidx)*img->xsize);
479 for(y=0; y<img->ysize; y++) {
480 i_gpal(img, 0, img->xsize, y, vals);
481 if (ig->writecb(ig, vals, img->xsize) != img->xsize) {
482 i_push_error(errno, "could not write targa data to file");
490 int y, lsize = img->channels * img->xsize;
491 data = mymalloc(lsize);
492 for(y=0; y<img->ysize; y++) {
493 i_gsamp(img, 0, img->xsize, y, data, rgb_chan, img->channels);
494 if ( ig->writecb(ig, data, lsize) != lsize ) {
495 i_push_error(errno, "could not write targa data to file");