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