1 /*
2 * A simple TGA loader
3 *
4 */
5
6 #include <config.h>
7 #ifdef SIMAGE_TGA_SUPPORT
8
9 #include <simage_tga.h>
10 #include <stdio.h>
11 #include <assert.h>
12 #include <string.h>
13 #include <stdlib.h>
14
15 /* */
16 /* Supported types: */
17 /* */
18 /* 1 (Uncompressed, color-mapped images) */
19 /* 2 (RGB uncompressed) */
20 /* 9 RLE color-mapped */
21 /* 10 RLE RGB */
22 /* */
23
24
25 #define ERR_NO_ERROR 0
26 #define ERR_OPEN 1
27 #define ERR_READ 2
28 #define ERR_MEM 3
29 #define ERR_UNSUPPORTED 4
30
31 static int tgaerror = ERR_NO_ERROR;
32 int
simage_tga_error(char * buffer,int buflen)33 simage_tga_error(char * buffer, int buflen)
34 {
35 switch (tgaerror) {
36 case ERR_OPEN:
37 strncpy(buffer, "TGA loader: Error opening file", buflen);
38 break;
39 case ERR_READ:
40 strncpy(buffer, "TGA loader: Error reading file", buflen);
41 break;
42 case ERR_MEM:
43 strncpy(buffer, "TGA loader: Out of memory error", buflen);
44 break;
45 }
46 return tgaerror;
47 }
48
49 /* TODO: */
50 /* - bottom-up images */
51 /* - huffman, delta encoding */
52 static void
convert_16_to_24(const unsigned char * const src,unsigned char * const dest)53 convert_16_to_24(const unsigned char * const src, unsigned char * const dest)
54 {
55 /* RGB for opengl, lo-hi 16 bit for TGA */
56 unsigned int t0 = src[0];
57 unsigned int t1 = src[1];
58 dest[0] = (unsigned char) (t0 & 0x1f) << 2; /* r */
59 dest[1] = (unsigned char) (t1 & 0x7c) >> 2; /* g */
60 dest[2] = (unsigned char) ((t1 & 0x3)<<3) | ((t0&0xe)>>5); /*b */
61 }
62
63 static void
convert_16_to_32(const unsigned char * const src,unsigned char * const dest)64 convert_16_to_32(const unsigned char * const src, unsigned char * const dest)
65 {
66 /* RGBA for opengl, lo-hi 16 bit for TGA */
67 unsigned int t0 = src[0];
68 unsigned int t1 = src[1];
69 dest[0] = (unsigned char) (t0 & 0x1f) << 2; /* r */
70 dest[1] = (unsigned char) (t1 & 0x7c) >> 2; /* g */
71 dest[2] = (unsigned char) ((t1 & 0x3)<<3) | ((t0&0xe)>>5); /*b */
72 dest[3] = (t1&0x70)?255:0; /* a */
73 }
74
75 static void
convert_24_to_24(const unsigned char * const src,unsigned char * const dest)76 convert_24_to_24(const unsigned char * const src, unsigned char * const dest)
77 {
78 /* RGB for opengl */
79 /* BGR for TGA */
80 dest[0] = src[2];
81 dest[1] = src[1];
82 dest[2] = src[0];
83 }
84
85 static void
convert_32_to_32(const unsigned char * const src,unsigned char * const dest)86 convert_32_to_32(const unsigned char * const src, unsigned char * const dest)
87 {
88 /* opengl image format is RGBA, not ARGB */
89 /* TGA image format is BGRA for 32 bit */
90 dest[0] = src[2];
91 dest[1] = src[1];
92 dest[2] = src[0];
93 dest[3] = src[3];
94 }
95
96 static void
convert_data(const unsigned char * const src,unsigned char * const dest,const int x,const int srcformat,const int destformat)97 convert_data(const unsigned char * const src, unsigned char * const dest,
98 const int x, const int srcformat,
99 const int destformat)
100 {
101 if (srcformat == 2) {
102 if (destformat == 3)
103 convert_16_to_24(src+x*srcformat,
104 dest+x*destformat);
105 else {
106 assert(destformat == 4);
107 convert_16_to_32(src+x*srcformat,
108 dest+x*destformat);
109 }
110 }
111 else if (srcformat == 3) {
112 assert(destformat == 3);
113 convert_24_to_24(src+x*srcformat,
114 dest+x*destformat);
115 }
116 else {
117 assert(srcformat == 4 && destformat == 4);
118 convert_32_to_32(src+x*srcformat,
119 dest+x*destformat);
120 }
121 }
122
123 /* Intel byte order workaround */
getInt16(unsigned char * ptr)124 static int getInt16(unsigned char *ptr)
125 {
126 int res = ptr[0];
127 int tmp = ptr[1];
128 return res | (tmp<<8);
129 }
130
131 /* */
132 /* decode a new rle packet */
133 /* */
134 static unsigned char *
rle_new_packet(unsigned char * src,int * rleRemaining,int * rleIsCompressed,unsigned char * rleCurrent,const int rleEntrySize)135 rle_new_packet(unsigned char * src,
136 int * rleRemaining,
137 int * rleIsCompressed,
138 unsigned char *rleCurrent,
139 const int rleEntrySize)
140 {
141 int i;
142 unsigned char code;
143
144 code = *src++;
145
146 *rleRemaining = (code & 127) + 1; /* number of bytes left in this packet */
147 if (code & 128) { /* rle */
148 *rleIsCompressed = 1;
149 for (i = 0; i < rleEntrySize; i++)
150 rleCurrent[i] = *src++;
151 }
152 else { /* uncompressed */
153 *rleIsCompressed = 0;
154 }
155 return src;
156 }
157
158 /* */
159 /* decode the # of specified bytes */
160 /* */
161 static unsigned char *
rle_decode(unsigned char * src,unsigned char * dest,const int numbytes,int * rleRemaining,int * rleIsCompressed,unsigned char * rleCurrent,const int rleEntrySize)162 rle_decode(unsigned char * src,
163 unsigned char *dest,
164 const int numbytes,
165 int * rleRemaining,
166 int * rleIsCompressed,
167 unsigned char *rleCurrent,
168 const int rleEntrySize)
169 {
170 int i;
171 int remain, compress, size;
172 unsigned char *stop = dest + numbytes;
173
174 size = rleEntrySize;
175 remain = *rleRemaining;
176 compress = *rleIsCompressed;
177
178 while (dest < stop) {
179 if (remain == 0) {/* start new packet */
180 src = rle_new_packet(src, &remain, &compress,
181 rleCurrent, rleEntrySize);
182 }
183 if (compress) {
184 for (i = 0; i < size; i++) {
185 *dest++ = rleCurrent[i];
186 }
187 }
188 else {
189 for (i = 0; i < size; i++) {
190 *dest++ = *src++;
191 }
192 }
193 remain--;
194 }
195 *rleRemaining = remain;
196 *rleIsCompressed = compress;
197 return src;
198 }
199
200 unsigned char *
simage_tga_load(const char * filename,int * width_ret,int * height_ret,int * numComponents_ret)201 simage_tga_load(const char *filename,
202 int *width_ret,
203 int *height_ret,
204 int *numComponents_ret)
205 {
206 FILE * fp;
207 unsigned char header[18];
208 int type;
209 int width;
210 int height;
211 int depth;
212 int flags;
213 int format;
214 unsigned char *colormap;
215 int indexsize;
216 int rleIsCompressed;
217 int rleRemaining;
218 int rleEntrySize;
219 unsigned char rleCurrent[4];
220 unsigned char *buffer;
221 unsigned char *dest;
222 int bpr;
223 unsigned char *linebuf;
224
225 tgaerror = ERR_NO_ERROR; /* clear error */
226
227 fp = fopen(filename, "rb");
228 if (!fp) {
229 tgaerror = ERR_OPEN;
230 return NULL;
231 }
232
233 if (fread(header, 1, 18, fp) != 18) {
234 tgaerror = ERR_READ;
235 fclose(fp);
236 return NULL;
237 }
238
239 type = header[2];
240 width = getInt16(&header[12]);
241 height = getInt16(&header[14]);
242 depth = header[16] >> 3;
243 flags = header[17];
244
245 /* check for reasonable values in case this is not a tga file */
246 if ((type != 2 && type != 10) ||
247 (width < 0 || width > 4096) ||
248 (height < 0 || height > 4096) ||
249 (depth < 2 || depth > 4)) {
250 tgaerror = ERR_UNSUPPORTED;
251 fclose(fp);
252 return NULL;
253 }
254
255 if (header[0]) /* skip identification field */
256 fseek(fp, header[0], SEEK_CUR);
257
258 colormap = NULL;
259 if (header[1] == 1) { /* there is a colormap */
260 int len = getInt16(&header[5]);
261 indexsize = header[7]>>3;
262 colormap = (unsigned char *)malloc(len*indexsize);
263 fread(colormap, 1, len*indexsize, fp);
264 }
265
266 if (depth == 2) { /* 16 bits */
267 if (flags & 1) format = 4;
268 else format = 3;
269 }
270 else format = depth;
271
272 /* SoDebugError::postInfo("simage_tga_load", "TARGA file: %d %d %d %d %d\n", */
273 /* type, width, height, depth, format); */
274
275 rleIsCompressed = 0;
276 rleRemaining = 0;
277 rleEntrySize = depth;
278 buffer = (unsigned char*)malloc(width*height*format);
279 dest = buffer;
280 bpr = format * width;
281 linebuf = (unsigned char *)malloc(width*depth);
282
283 switch(type) {
284 case 1: /* colormap, uncompressed */
285 {
286 /* FIXME: write code */
287 /* should never get here because simage_tga_identify returns 0 */
288 /* for this filetype */
289 /* I need example files in this format to write the code... */
290 tgaerror = ERR_UNSUPPORTED;
291 }
292 break;
293 case 2: /* RGB, uncompressed */
294 {
295 int x, y;
296 for (y = 0; y < height; y++) {
297 if (fread(linebuf, 1, width*depth, fp) != (unsigned int)width*depth) {
298 tgaerror = ERR_READ;
299 break;
300 }
301 for (x = 0; x < width; x++) {
302 convert_data(linebuf, dest, x, depth, format);
303 }
304 dest += bpr;
305 }
306 }
307 break;
308 case 9: /* colormap, compressed */
309 {
310 /* FIXME: write code */
311 /* should never get here because simage_tga_identify returns 0 */
312 /* for this filetype */
313 /* I need example files in this format to write the code... */
314 tgaerror = ERR_UNSUPPORTED;
315 }
316 break;
317 case 10: /* RGB, compressed */
318 {
319 int size, x, y;
320 unsigned char *buf;
321 unsigned char *src;
322 int pos = ftell(fp);
323 fseek(fp, 0, SEEK_END);
324 size = ftell(fp) - pos;
325 fseek(fp, pos, SEEK_SET);
326 buf = (unsigned char *)malloc(size);
327 if (buf == NULL) {
328 tgaerror = ERR_MEM;
329 break;
330 }
331 src = buf;
332 if (fread(buf, 1, size, fp) != (unsigned int) size) {
333 tgaerror = ERR_READ;
334 break;
335 }
336 for (y = 0; y < height; y++) {
337 src = rle_decode(src, linebuf, width*depth, &rleRemaining,
338 &rleIsCompressed, rleCurrent, rleEntrySize);
339 assert(src <= buf + size);
340 for (x = 0; x < width; x++) {
341 convert_data(linebuf, dest, x, depth, format);
342 }
343 dest += bpr;
344 }
345 if (buf) free(buf);
346 }
347 break;
348 default:
349 tgaerror = ERR_UNSUPPORTED;
350 }
351
352 if (linebuf) free(linebuf);
353 fclose(fp);
354
355 if (tgaerror) {
356 if (buffer) free(buffer);
357 return NULL;
358 }
359
360 *width_ret = width;
361 *height_ret = height;
362 *numComponents_ret = format;
363
364 return buffer;
365 }
366
367
368 int
simage_tga_identify(const char * filename,const unsigned char * buf,int headerlen)369 simage_tga_identify(const char *filename,
370 const unsigned char *buf,
371 int headerlen)
372 {
373 const char * ptr;
374 if (headerlen < 18) return 0;
375 ptr = strrchr(filename, '.');
376 if (!ptr) return 0; /* TGA files must end with .tga|.TGA */
377
378 if (strcmp(ptr, ".tga") && strcmp(ptr, ".TGA")) return 0;
379
380 if (buf[1] == 1 && buf[2] == 1 && buf[17] < 64) {
381 /* SoDebugError::postInfo("simage_tga_identify", */
382 /* "TARGA colormap file: %s\n", filename); */
383 return 0;
384 }
385 if ((buf[1] == 0 || buf[1] == 1) && buf[2] == 2 && buf[17] < 64) return 1;
386 if (buf[1] == 1 && buf[2] == 9 && buf[17] < 64) {
387 /* SoDebugError::postInfo("simage_tga_identity", */
388 /* "TARGA RLE and colormap file: %s\n", filename); */
389
390 /* will soon be supported */
391 return 0;
392 }
393 if ((buf[1] == 0 || buf[1] == 1) && buf[2] == 10 && buf[17] < 64) {
394 /* RLE and RGB */
395 return 1;
396 }
397 else { /* unsupported */
398 /* SoDebugError::postInfo("simage_tga_identify", */
399 /* "Unsupported TARGA type.\n"); */
400 }
401 /* not a TGA, or not supported type */
402 return 0;
403 }
404
405 #endif /* SIMAGE_TGA_SUPPORT */
406