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