Adds reading capabilities for certain variants of targa, writer code has not been
[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
39#define BSIZ 1024
40
41
42
43typedef 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
63Retrieve 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
72i_img *
73i_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
248undef_int
249i_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}