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