Adds reading capabilities for certain variants of targa, writer code has not been
[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 #define BSIZ 1024
40
41
42
43 typedef struct {
44   char  idlength;
45   char  colourmaptype;
46   char  datatypecode;
47   short int colourmaporigin;
48   short int colourmaplength;
49   char  colourmapdepth;
50   short int x_origin;
51   short int y_origin;
52   short width;
53   short height;
54   char  bitsperpixel;
55   char  imagedescriptor;
56 } tga_header;
57
58
59
60 /*
61 =item i_readtga_wiol(ig, length)
62
63 Retrieve an image and stores in the iolayer object. Returns NULL on fatal error.
64
65    ig     - io_glue object
66    length - maximum length to read from data source, before closing it -1 
67             signifies no limit.
68
69 =cut
70 */
71
72 i_img *
73 i_readtga_wiol(io_glue *ig, int length) {
74   i_img* img;
75   int type;
76   int y, i;
77   int width, height, channels;
78   char *idstring;
79   unsigned char *uc;
80   i_color val;
81
82   tga_header header;
83   size_t palbsize;
84   unsigned char *palbuf;
85   unsigned char headbuf[18];
86   unsigned char *linebuf;
87
88   i_clear_error();
89
90   mm_log((1,"i_readtga(ig %p, length %d)\n", ig, length));
91   
92   io_glue_commit_types(ig);
93
94   if (ig->readcb(ig, &headbuf, 18) != 18) {
95     i_push_error(errno, "could not read targa header");
96     return NULL;
97   }
98
99   header.idlength        = headbuf[0];
100   header.colourmaptype   = headbuf[1];
101   header.datatypecode    = headbuf[2];
102   header.colourmaporigin = headbuf[4] << 8 + headbuf[3];
103   header.colourmaplength = headbuf[6] << 8 + headbuf[5];
104   header.colourmapdepth  = headbuf[7];
105   header.x_origin        = headbuf[9] << 8 + headbuf[8];
106   header.y_origin        = headbuf[11] << 8 + headbuf[10];
107   header.width           = (headbuf[13] << 8) + headbuf[12];
108   header.height          = (headbuf[15] << 8) + headbuf[14];
109   header.bitsperpixel    = headbuf[16];
110   header.imagedescriptor = headbuf[17];
111
112   mm_log((1,"Id length:         %d\n",header.idlength));
113   mm_log((1,"Colour map type:   %d\n",header.colourmaptype));
114   mm_log((1,"Image type:        %d\n",header.datatypecode));
115   mm_log((1,"Colour map offset: %d\n",header.colourmaporigin));
116   mm_log((1,"Colour map length: %d\n",header.colourmaplength));
117   mm_log((1,"Colour map depth:  %d\n",header.colourmapdepth));
118   mm_log((1,"X origin:          %d\n",header.x_origin));
119   mm_log((1,"Y origin:          %d\n",header.y_origin));
120   mm_log((1,"Width:             %d\n",header.width));
121   mm_log((1,"Height:            %d\n",header.height));
122   mm_log((1,"Bits per pixel:    %d\n",header.bitsperpixel));
123   mm_log((1,"Descriptor:        %d\n",header.imagedescriptor));
124
125
126   if (header.idlength) {
127     idstring = mymalloc(header.idlength+1);
128     if (ig->readcb(ig, idstring, header.idlength) != header.idlength) {
129       i_push_error(errno, "short read on targa idstring");
130       return NULL;
131     }
132     myfree(idstring); /* Move this later, once this is stored in a tag */
133   }
134
135   width = header.width;
136   height = header.height;
137   
138   /* Set tags here */
139
140   /* Only RGB for now, eventually support all formats */
141   switch (header.datatypecode) {
142
143   case 0: /* No data in image */
144     i_push_error(0, "Targa image contains no image data");
145     return NULL;
146     break;
147
148   case 1: /* Uncompressed, color-mapped images */
149     if (header.imagedescriptor != 0) {
150       i_push_error(0, "Targa: Imagedescriptor not 0, not supported internal format");
151       return NULL;
152     }
153     
154     if (header.bitsperpixel != 8) {
155       i_push_error(0, "Targa: bpp is not 8, unsupported.");
156       return NULL;
157     }
158     
159     mm_log((1, "Uncompressed color-mapped image\n"));
160     if (header.colourmapdepth != 24) {
161       i_push_error(0, "Colourmap is not 24 bit");
162       return NULL;
163     }
164     channels = 3;
165
166     img = i_img_pal_new(width, height, channels, 256);
167
168     /* Read in the palette */
169
170     palbsize = header.colourmaplength*channels;
171     palbuf = mymalloc(palbsize);
172     
173     if (ig->readcb(ig, palbuf, palbsize) != palbsize) {
174       i_push_error(errno, "could not read targa colourmap");
175       return NULL;
176     }
177     
178     /* populate the palette of the new image */
179     for(i=0; i<header.colourmaplength; i++) {
180       i_color col;
181       col.rgba.r = palbuf[i*channels+2];
182       col.rgba.g = palbuf[i*channels+1];
183       col.rgba.b = palbuf[i*channels+0];
184       i_addcolors(img, &col, 1);
185     }
186     myfree(palbuf);
187
188
189     /* read image data in */
190     linebuf = mymalloc(width);
191     for(y=height-1;y>=0;y--) {
192       if (ig->readcb(ig, linebuf, width) != width) {
193         i_push_error(errno, "read for targa data failed");
194         myfree(linebuf);
195         return NULL;
196       }
197       i_ppal(img, 0, width, y, linebuf);
198     }
199     
200     return img;
201     break;
202     
203   case 2:  /* Uncompressed, RGB images */
204     mm_log((1, "Uncompressed RGB image\n"));
205     channels = 3;
206     /* Do stuff */
207     break;
208   case 3: /* Uncompressed, black and white images */
209     i_push_error(0, "Targa black and white subformat is not supported");
210     return NULL;
211     break;
212   case 9: /* Runlength encoded, color-mapped images */
213     i_push_error(0, "Targa runlength coded colormapped subformat is not supported");
214     return NULL;
215     break;
216   case 10: /* Runlength encoded, RGB images */
217     channels = 3;
218     /* Do stuff */
219     break;
220   case 11: /* Compressed, black and white images */
221     i_push_error(0, "Targa compressed black and white subformat is not supported");
222     return NULL;
223     break;
224   case 32: /* Compressed color-mapped, Huffman, Delta and runlength */
225     i_push_error(0, "Targa Huffman/delta/rle subformat is not supported");
226     return NULL;
227     break;
228   case 33: /* Compressed color-mapped, Huffman, Delta and runlength */
229     i_push_error(0, "Targa Huffman/delta/rle/quadtree subformat is not supported");
230     return NULL;
231     break;
232   default: /* All others which we don't know which might be */
233     i_push_error(0, "Unknown targa format");
234     return NULL;
235     break;
236   }
237   return NULL;
238 }
239
240
241
242
243
244
245
246
247
248 undef_int
249 i_writetga_wiol(i_img *im, io_glue *ig) {
250   char header[255];
251   int rc;
252   writep write_func;
253
254   mm_log((1,"i_writeppm(im %p, ig %p)\n", im, ig));
255   i_clear_error();
256
257   /* Add code to get the filename info from the iolayer */
258   /* Also add code to check for mmapped code */
259
260   io_glue_commit_types(ig);
261
262   if (im->channels == 3) {
263     sprintf(header,"P6\n#CREATOR: Imager\n%d %d\n255\n",im->xsize,im->ysize);
264     if (ig->writecb(ig,header,strlen(header))<0) {
265       i_push_error(errno, "could not write ppm header");
266       mm_log((1,"i_writeppm: unable to write ppm header.\n"));
267       return(0);
268     }
269
270     if (!im->virtual && im->bits == i_8_bits && im->type == i_direct_type) {
271       rc = ig->writecb(ig,im->idata,im->bytes);
272     }
273     else {
274       unsigned char *data = mymalloc(3 * im->xsize);
275       if (data != NULL) {
276         int y = 0;
277         int x, ch;
278         unsigned char *p;
279         static int rgb_chan[3] = { 0, 1, 2 };
280
281         rc = 0;
282         while (y < im->ysize && rc >= 0) {
283           i_gsamp(im, 0, im->xsize, y, data, rgb_chan, 3);
284           rc = ig->writecb(ig, data, im->xsize * 3);
285         }
286         myfree(data);
287       }
288       else {
289         i_push_error(0, "Out of memory");
290         return 0;
291       }
292     }
293     if (rc<0) {
294       i_push_error(errno, "could not write ppm data");
295       mm_log((1,"i_writeppm: unable to write ppm data.\n"));
296       return(0);
297     }
298   }
299   else if (im->channels == 1) {
300     sprintf(header, "P5\n#CREATOR: Imager\n%d %d\n255\n",
301             im->xsize, im->ysize);
302     if (ig->writecb(ig,header, strlen(header)) < 0) {
303       i_push_error(errno, "could not write pgm header");
304       mm_log((1,"i_writeppm: unable to write pgm header.\n"));
305       return(0);
306     }
307
308     if (!im->virtual && im->bits == i_8_bits && im->type == i_direct_type) {
309       rc=ig->writecb(ig,im->idata,im->bytes);
310     }
311     else {
312       unsigned char *data = mymalloc(im->xsize);
313       if (data != NULL) {
314         int y = 0;
315         int x, ch;
316         int chan = 0;
317         unsigned char *p;
318
319         rc = 0;
320         while (y < im->ysize && rc >= 0) {
321           i_gsamp(im, 0, im->xsize, y, data, &chan, 1);
322           rc = ig->writecb(ig, data, im->xsize);
323         }
324         myfree(data);
325       }
326       else {
327         i_push_error(0, "Out of memory");
328         return 0;
329       }
330     }
331     if (rc<0) {
332       i_push_error(errno, "could not write pgm data");
333       mm_log((1,"i_writeppm: unable to write pgm data.\n"));
334       return(0);
335     }
336   }
337   else {
338     i_push_error(0, "can only save 1 or 3 channel images to pnm");
339     mm_log((1,"i_writeppm: ppm/pgm is 1 or 3 channel only (current image is %d)\n",im->channels));
340     return(0);
341   }
342
343   return(1);
344 }