stop ft1.x support dropping descenders
[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;
7c58edfc 75 int x, y, i;
1ec86afa
AMH
76 int width, height, channels;
77 char *idstring;
1ec86afa
AMH
78
79 tga_header header;
80 size_t palbsize;
81 unsigned char *palbuf;
82 unsigned char headbuf[18];
7c58edfc
AMH
83 unsigned char *databuf;
84 i_color *linebuf;
85
1ec86afa
AMH
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 */
7c58edfc 188 databuf = mymalloc(width);
1ec86afa 189 for(y=height-1;y>=0;y--) {
7c58edfc 190 if (ig->readcb(ig, databuf, width) != width) {
1ec86afa 191 i_push_error(errno, "read for targa data failed");
7c58edfc 192 myfree(databuf);
1ec86afa
AMH
193 return NULL;
194 }
7c58edfc 195 i_ppal(img, 0, width, y, databuf);
1ec86afa
AMH
196 }
197
198 return img;
199 break;
200
201 case 2: /* Uncompressed, RGB images */
202 mm_log((1, "Uncompressed RGB image\n"));
7c58edfc
AMH
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 }
1ec86afa 213 channels = 3;
7c58edfc
AMH
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;
1ec86afa
AMH
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
280undef_int
281i_writetga_wiol(i_img *im, io_glue *ig) {
282 char header[255];
283 int rc;
284 writep write_func;
285
7c58edfc 286 mm_log((1,"i_writetga_wiol(im %p, ig %p)\n", im, ig));
1ec86afa
AMH
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
7c58edfc
AMH
294 if (im->type == i_palette_type) {
295
296
297
298
299 }
300
301
302
303
304
305
306
307
1ec86afa
AMH
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}