Preliminary support for writing targa files. Compression not working yet.
[imager.git] / tga.c
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 4
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 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
68 static
69 int
70 bpp_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
85 static
86 int
87 bpp_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
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.
112 */
113
114 static
115 void
116 color_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
148 static
149 int
150 tga_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
188 static
189 int
190 tga_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
216
217 /*
218 =item i_readtga_wiol(ig, length)
219
220 Retrieve 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
229 i_img *
230 i_readtga_wiol(io_glue *ig, int length) {
231   i_img* img;
232   int x, y, i;
233   int width, height, channels;
234   int mapped;
235   char *idstring;
236
237   tga_source src;
238   tga_header header;
239   unsigned char headbuf[18];
240   unsigned char *databuf;
241   unsigned char *reorderbuf;
242
243   i_color *linebuf = NULL;
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];
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];
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 */
295   
296   switch (header.datatypecode) {
297   case 0: /* No data in image */
298     i_push_error(0, "Targa image contains no image data");
299     return NULL;
300     break;
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.");
307       return NULL;
308     }
309     src.bytepp = 1;
310     break;
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.");
316     return NULL;
317     break;
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");
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   }
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 && 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);
383     else {
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);
386     }
387   }
388   myfree(databuf);
389   if (linebuf) myfree(linebuf);
390   return img;
391 }
392
393
394
395
396
397
398
399
400
401 undef_int
402 i_writetga_wiol(i_img *img, io_glue *ig) {
403   int rc;
404   static int rgb_chan[] = { 2, 1, 0, 3 };
405   tga_header header;
406   unsigned char headbuf[18];
407   unsigned char *data;
408   int compress = 0;
409   char *idstring = "testing";
410   int idlen = strlen(idstring);
411   int mapped = img->type == i_palette_type;
412
413   mm_log((1,"i_writetga_wiol(img %p, ig %p)\n", img, ig));
414   i_clear_error();
415
416   io_glue_commit_types(ig);
417
418   mm_log((1, "virtual %d, paletted %d\n", img->virtual, mapped));
419   mm_log((1, "channels %d\n", img->channels));
420
421   header.idlength;
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;
430   header.x_origin        = 0;
431   header.y_origin        = 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 */
436
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;
455
456   if (ig->writecb(ig, &headbuf, sizeof(headbuf)) != sizeof(headbuf)) {
457     i_push_error(errno, "could not write targa header");
458     return 0;
459   }
460
461   if (idlen) {
462     if (ig->writecb(ig, idstring, idlen) != idlen) {
463       i_push_error(errno, "could not write targa idstring");
464       return 0;
465     }
466   }
467
468   
469   if (img->type == i_palette_type) {
470     /* write palette */
471     if (!img->virtual) {
472       if (ig->writecb(ig, img->idata, img->bytes) != img->bytes) {
473         i_push_error(errno, "could not write targa image data");
474         return 0;
475       }
476     } else {
477       int y;
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");
483           myfree(vals);
484           return 0;
485         }
486       }
487       myfree(vals);
488     }
489   } else {
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");
496         myfree(data);
497         return 0;
498       }
499     }
500     myfree(data);
501   }
502   return 1;
503 }