• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * rdtarga.c
3  *
4  * This file was part of the Independent JPEG Group's software:
5  * Copyright (C) 1991-1996, Thomas G. Lane.
6  * It was modified by The libjpeg-turbo Project to include only code relevant
7  * to libjpeg-turbo.
8  * For conditions of distribution and use, see the accompanying README.ijg
9  * file.
10  *
11  * This file contains routines to read input images in Targa format.
12  *
13  * These routines may need modification for non-Unix environments or
14  * specialized applications.  As they stand, they assume input from
15  * an ordinary stdio stream.  They further assume that reading begins
16  * at the start of the file; start_input may need work if the
17  * user interface has already read some data (e.g., to determine that
18  * the file is indeed Targa format).
19  *
20  * Based on code contributed by Lee Daniel Crocker.
21  */
22 
23 #include "cdjpeg.h"             /* Common decls for cjpeg/djpeg applications */
24 
25 #ifdef TARGA_SUPPORTED
26 
27 
28 /* Macros to deal with unsigned chars as efficiently as compiler allows */
29 
30 #ifdef HAVE_UNSIGNED_CHAR
31 typedef unsigned char U_CHAR;
32 #define UCH(x)  ((int) (x))
33 #else /* !HAVE_UNSIGNED_CHAR */
34 #ifdef __CHAR_UNSIGNED__
35 typedef char U_CHAR;
36 #define UCH(x)  ((int) (x))
37 #else
38 typedef char U_CHAR;
39 #define UCH(x)  ((int) (x) & 0xFF)
40 #endif
41 #endif /* HAVE_UNSIGNED_CHAR */
42 
43 
44 #define ReadOK(file,buffer,len) (JFREAD(file,buffer,len) == ((size_t) (len)))
45 
46 
47 /* Private version of data source object */
48 
49 typedef struct _tga_source_struct *tga_source_ptr;
50 
51 typedef struct _tga_source_struct {
52   struct cjpeg_source_struct pub; /* public fields */
53 
54   j_compress_ptr cinfo;         /* back link saves passing separate parm */
55 
56   JSAMPARRAY colormap;          /* Targa colormap (converted to my format) */
57 
58   jvirt_sarray_ptr whole_image; /* Needed if funny input row order */
59   JDIMENSION current_row;       /* Current logical row number to read */
60 
61   /* Pointer to routine to extract next Targa pixel from input file */
62   void (*read_pixel) (tga_source_ptr sinfo);
63 
64   /* Result of read_pixel is delivered here: */
65   U_CHAR tga_pixel[4];
66 
67   int pixel_size;               /* Bytes per Targa pixel (1 to 4) */
68 
69   /* State info for reading RLE-coded pixels; both counts must be init to 0 */
70   int block_count;              /* # of pixels remaining in RLE block */
71   int dup_pixel_count;          /* # of times to duplicate previous pixel */
72 
73   /* This saves the correct pixel-row-expansion method for preload_image */
74   JDIMENSION (*get_pixel_rows) (j_compress_ptr cinfo, cjpeg_source_ptr sinfo);
75 } tga_source_struct;
76 
77 
78 /* For expanding 5-bit pixel values to 8-bit with best rounding */
79 
80 static const UINT8 c5to8bits[32] = {
81     0,   8,  16,  25,  33,  41,  49,  58,
82    66,  74,  82,  90,  99, 107, 115, 123,
83   132, 140, 148, 156, 165, 173, 181, 189,
84   197, 206, 214, 222, 230, 239, 247, 255
85 };
86 
87 
88 
89 LOCAL(int)
read_byte(tga_source_ptr sinfo)90 read_byte (tga_source_ptr sinfo)
91 /* Read next byte from Targa file */
92 {
93   register FILE *infile = sinfo->pub.input_file;
94   register int c;
95 
96   if ((c = getc(infile)) == EOF)
97     ERREXIT(sinfo->cinfo, JERR_INPUT_EOF);
98   return c;
99 }
100 
101 
102 LOCAL(void)
read_colormap(tga_source_ptr sinfo,int cmaplen,int mapentrysize)103 read_colormap (tga_source_ptr sinfo, int cmaplen, int mapentrysize)
104 /* Read the colormap from a Targa file */
105 {
106   int i;
107 
108   /* Presently only handles 24-bit BGR format */
109   if (mapentrysize != 24)
110     ERREXIT(sinfo->cinfo, JERR_TGA_BADCMAP);
111 
112   for (i = 0; i < cmaplen; i++) {
113     sinfo->colormap[2][i] = (JSAMPLE) read_byte(sinfo);
114     sinfo->colormap[1][i] = (JSAMPLE) read_byte(sinfo);
115     sinfo->colormap[0][i] = (JSAMPLE) read_byte(sinfo);
116   }
117 }
118 
119 
120 /*
121  * read_pixel methods: get a single pixel from Targa file into tga_pixel[]
122  */
123 
124 METHODDEF(void)
read_non_rle_pixel(tga_source_ptr sinfo)125 read_non_rle_pixel (tga_source_ptr sinfo)
126 /* Read one Targa pixel from the input file; no RLE expansion */
127 {
128   register FILE *infile = sinfo->pub.input_file;
129   register int i;
130 
131   for (i = 0; i < sinfo->pixel_size; i++) {
132     sinfo->tga_pixel[i] = (U_CHAR) getc(infile);
133   }
134 }
135 
136 
137 METHODDEF(void)
read_rle_pixel(tga_source_ptr sinfo)138 read_rle_pixel (tga_source_ptr sinfo)
139 /* Read one Targa pixel from the input file, expanding RLE data as needed */
140 {
141   register FILE *infile = sinfo->pub.input_file;
142   register int i;
143 
144   /* Duplicate previously read pixel? */
145   if (sinfo->dup_pixel_count > 0) {
146     sinfo->dup_pixel_count--;
147     return;
148   }
149 
150   /* Time to read RLE block header? */
151   if (--sinfo->block_count < 0) { /* decrement pixels remaining in block */
152     i = read_byte(sinfo);
153     if (i & 0x80) {             /* Start of duplicate-pixel block? */
154       sinfo->dup_pixel_count = i & 0x7F; /* number of dups after this one */
155       sinfo->block_count = 0;   /* then read new block header */
156     } else {
157       sinfo->block_count = i & 0x7F; /* number of pixels after this one */
158     }
159   }
160 
161   /* Read next pixel */
162   for (i = 0; i < sinfo->pixel_size; i++) {
163     sinfo->tga_pixel[i] = (U_CHAR) getc(infile);
164   }
165 }
166 
167 
168 /*
169  * Read one row of pixels.
170  *
171  * We provide several different versions depending on input file format.
172  */
173 
174 
175 METHODDEF(JDIMENSION)
get_8bit_gray_row(j_compress_ptr cinfo,cjpeg_source_ptr sinfo)176 get_8bit_gray_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
177 /* This version is for reading 8-bit grayscale pixels */
178 {
179   tga_source_ptr source = (tga_source_ptr) sinfo;
180   register JSAMPROW ptr;
181   register JDIMENSION col;
182 
183   ptr = source->pub.buffer[0];
184   for (col = cinfo->image_width; col > 0; col--) {
185     (*source->read_pixel) (source); /* Load next pixel into tga_pixel */
186     *ptr++ = (JSAMPLE) UCH(source->tga_pixel[0]);
187   }
188   return 1;
189 }
190 
191 METHODDEF(JDIMENSION)
get_8bit_row(j_compress_ptr cinfo,cjpeg_source_ptr sinfo)192 get_8bit_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
193 /* This version is for reading 8-bit colormap indexes */
194 {
195   tga_source_ptr source = (tga_source_ptr) sinfo;
196   register int t;
197   register JSAMPROW ptr;
198   register JDIMENSION col;
199   register JSAMPARRAY colormap = source->colormap;
200 
201   ptr = source->pub.buffer[0];
202   for (col = cinfo->image_width; col > 0; col--) {
203     (*source->read_pixel) (source); /* Load next pixel into tga_pixel */
204     t = UCH(source->tga_pixel[0]);
205     *ptr++ = colormap[0][t];
206     *ptr++ = colormap[1][t];
207     *ptr++ = colormap[2][t];
208   }
209   return 1;
210 }
211 
212 METHODDEF(JDIMENSION)
get_16bit_row(j_compress_ptr cinfo,cjpeg_source_ptr sinfo)213 get_16bit_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
214 /* This version is for reading 16-bit pixels */
215 {
216   tga_source_ptr source = (tga_source_ptr) sinfo;
217   register int t;
218   register JSAMPROW ptr;
219   register JDIMENSION col;
220 
221   ptr = source->pub.buffer[0];
222   for (col = cinfo->image_width; col > 0; col--) {
223     (*source->read_pixel) (source); /* Load next pixel into tga_pixel */
224     t = UCH(source->tga_pixel[0]);
225     t += UCH(source->tga_pixel[1]) << 8;
226     /* We expand 5 bit data to 8 bit sample width.
227      * The format of the 16-bit (LSB first) input word is
228      *     xRRRRRGGGGGBBBBB
229      */
230     ptr[2] = (JSAMPLE) c5to8bits[t & 0x1F];
231     t >>= 5;
232     ptr[1] = (JSAMPLE) c5to8bits[t & 0x1F];
233     t >>= 5;
234     ptr[0] = (JSAMPLE) c5to8bits[t & 0x1F];
235     ptr += 3;
236   }
237   return 1;
238 }
239 
240 METHODDEF(JDIMENSION)
get_24bit_row(j_compress_ptr cinfo,cjpeg_source_ptr sinfo)241 get_24bit_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
242 /* This version is for reading 24-bit pixels */
243 {
244   tga_source_ptr source = (tga_source_ptr) sinfo;
245   register JSAMPROW ptr;
246   register JDIMENSION col;
247 
248   ptr = source->pub.buffer[0];
249   for (col = cinfo->image_width; col > 0; col--) {
250     (*source->read_pixel) (source); /* Load next pixel into tga_pixel */
251     *ptr++ = (JSAMPLE) UCH(source->tga_pixel[2]); /* change BGR to RGB order */
252     *ptr++ = (JSAMPLE) UCH(source->tga_pixel[1]);
253     *ptr++ = (JSAMPLE) UCH(source->tga_pixel[0]);
254   }
255   return 1;
256 }
257 
258 /*
259  * Targa also defines a 32-bit pixel format with order B,G,R,A.
260  * We presently ignore the attribute byte, so the code for reading
261  * these pixels is identical to the 24-bit routine above.
262  * This works because the actual pixel length is only known to read_pixel.
263  */
264 
265 #define get_32bit_row  get_24bit_row
266 
267 
268 /*
269  * This method is for re-reading the input data in standard top-down
270  * row order.  The entire image has already been read into whole_image
271  * with proper conversion of pixel format, but it's in a funny row order.
272  */
273 
274 METHODDEF(JDIMENSION)
get_memory_row(j_compress_ptr cinfo,cjpeg_source_ptr sinfo)275 get_memory_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
276 {
277   tga_source_ptr source = (tga_source_ptr) sinfo;
278   JDIMENSION source_row;
279 
280   /* Compute row of source that maps to current_row of normal order */
281   /* For now, assume image is bottom-up and not interlaced. */
282   /* NEEDS WORK to support interlaced images! */
283   source_row = cinfo->image_height - source->current_row - 1;
284 
285   /* Fetch that row from virtual array */
286   source->pub.buffer = (*cinfo->mem->access_virt_sarray)
287     ((j_common_ptr) cinfo, source->whole_image,
288      source_row, (JDIMENSION) 1, FALSE);
289 
290   source->current_row++;
291   return 1;
292 }
293 
294 
295 /*
296  * This method loads the image into whole_image during the first call on
297  * get_pixel_rows.  The get_pixel_rows pointer is then adjusted to call
298  * get_memory_row on subsequent calls.
299  */
300 
301 METHODDEF(JDIMENSION)
preload_image(j_compress_ptr cinfo,cjpeg_source_ptr sinfo)302 preload_image (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
303 {
304   tga_source_ptr source = (tga_source_ptr) sinfo;
305   JDIMENSION row;
306   cd_progress_ptr progress = (cd_progress_ptr) cinfo->progress;
307 
308   /* Read the data into a virtual array in input-file row order. */
309   for (row = 0; row < cinfo->image_height; row++) {
310     if (progress != NULL) {
311       progress->pub.pass_counter = (long) row;
312       progress->pub.pass_limit = (long) cinfo->image_height;
313       (*progress->pub.progress_monitor) ((j_common_ptr) cinfo);
314     }
315     source->pub.buffer = (*cinfo->mem->access_virt_sarray)
316       ((j_common_ptr) cinfo, source->whole_image, row, (JDIMENSION) 1, TRUE);
317     (*source->get_pixel_rows) (cinfo, sinfo);
318   }
319   if (progress != NULL)
320     progress->completed_extra_passes++;
321 
322   /* Set up to read from the virtual array in unscrambled order */
323   source->pub.get_pixel_rows = get_memory_row;
324   source->current_row = 0;
325   /* And read the first row */
326   return get_memory_row(cinfo, sinfo);
327 }
328 
329 
330 /*
331  * Read the file header; return image size and component count.
332  */
333 
334 METHODDEF(void)
start_input_tga(j_compress_ptr cinfo,cjpeg_source_ptr sinfo)335 start_input_tga (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
336 {
337   tga_source_ptr source = (tga_source_ptr) sinfo;
338   U_CHAR targaheader[18];
339   int idlen, cmaptype, subtype, flags, interlace_type, components;
340   unsigned int width, height, maplen;
341   boolean is_bottom_up;
342 
343 #define GET_2B(offset)  ((unsigned int) UCH(targaheader[offset]) + \
344                          (((unsigned int) UCH(targaheader[offset+1])) << 8))
345 
346   if (! ReadOK(source->pub.input_file, targaheader, 18))
347     ERREXIT(cinfo, JERR_INPUT_EOF);
348 
349   /* Pretend "15-bit" pixels are 16-bit --- we ignore attribute bit anyway */
350   if (targaheader[16] == 15)
351     targaheader[16] = 16;
352 
353   idlen = UCH(targaheader[0]);
354   cmaptype = UCH(targaheader[1]);
355   subtype = UCH(targaheader[2]);
356   maplen = GET_2B(5);
357   width = GET_2B(12);
358   height = GET_2B(14);
359   source->pixel_size = UCH(targaheader[16]) >> 3;
360   flags = UCH(targaheader[17]); /* Image Descriptor byte */
361 
362   is_bottom_up = ((flags & 0x20) == 0); /* bit 5 set => top-down */
363   interlace_type = flags >> 6;  /* bits 6/7 are interlace code */
364 
365   if (cmaptype > 1 ||           /* cmaptype must be 0 or 1 */
366       source->pixel_size < 1 || source->pixel_size > 4 ||
367       (UCH(targaheader[16]) & 7) != 0 || /* bits/pixel must be multiple of 8 */
368       interlace_type != 0 ||      /* currently don't allow interlaced image */
369       width == 0 || height == 0)  /* image width/height must be non-zero */
370     ERREXIT(cinfo, JERR_TGA_BADPARMS);
371 
372   if (subtype > 8) {
373     /* It's an RLE-coded file */
374     source->read_pixel = read_rle_pixel;
375     source->block_count = source->dup_pixel_count = 0;
376     subtype -= 8;
377   } else {
378     /* Non-RLE file */
379     source->read_pixel = read_non_rle_pixel;
380   }
381 
382   /* Now should have subtype 1, 2, or 3 */
383   components = 3;               /* until proven different */
384   cinfo->in_color_space = JCS_RGB;
385 
386   switch (subtype) {
387   case 1:                       /* Colormapped image */
388     if (source->pixel_size == 1 && cmaptype == 1)
389       source->get_pixel_rows = get_8bit_row;
390     else
391       ERREXIT(cinfo, JERR_TGA_BADPARMS);
392     TRACEMS2(cinfo, 1, JTRC_TGA_MAPPED, width, height);
393     break;
394   case 2:                       /* RGB image */
395     switch (source->pixel_size) {
396     case 2:
397       source->get_pixel_rows = get_16bit_row;
398       break;
399     case 3:
400       source->get_pixel_rows = get_24bit_row;
401       break;
402     case 4:
403       source->get_pixel_rows = get_32bit_row;
404       break;
405     default:
406       ERREXIT(cinfo, JERR_TGA_BADPARMS);
407       break;
408     }
409     TRACEMS2(cinfo, 1, JTRC_TGA, width, height);
410     break;
411   case 3:                       /* Grayscale image */
412     components = 1;
413     cinfo->in_color_space = JCS_GRAYSCALE;
414     if (source->pixel_size == 1)
415       source->get_pixel_rows = get_8bit_gray_row;
416     else
417       ERREXIT(cinfo, JERR_TGA_BADPARMS);
418     TRACEMS2(cinfo, 1, JTRC_TGA_GRAY, width, height);
419     break;
420   default:
421     ERREXIT(cinfo, JERR_TGA_BADPARMS);
422     break;
423   }
424 
425   if (is_bottom_up) {
426     /* Create a virtual array to buffer the upside-down image. */
427     source->whole_image = (*cinfo->mem->request_virt_sarray)
428       ((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE,
429        (JDIMENSION) width * components, (JDIMENSION) height, (JDIMENSION) 1);
430     if (cinfo->progress != NULL) {
431       cd_progress_ptr progress = (cd_progress_ptr) cinfo->progress;
432       progress->total_extra_passes++; /* count file input as separate pass */
433     }
434     /* source->pub.buffer will point to the virtual array. */
435     source->pub.buffer_height = 1; /* in case anyone looks at it */
436     source->pub.get_pixel_rows = preload_image;
437   } else {
438     /* Don't need a virtual array, but do need a one-row input buffer. */
439     source->whole_image = NULL;
440     source->pub.buffer = (*cinfo->mem->alloc_sarray)
441       ((j_common_ptr) cinfo, JPOOL_IMAGE,
442        (JDIMENSION) width * components, (JDIMENSION) 1);
443     source->pub.buffer_height = 1;
444     source->pub.get_pixel_rows = source->get_pixel_rows;
445   }
446 
447   while (idlen--)               /* Throw away ID field */
448     (void) read_byte(source);
449 
450   if (maplen > 0) {
451     if (maplen > 256 || GET_2B(3) != 0)
452       ERREXIT(cinfo, JERR_TGA_BADCMAP);
453     /* Allocate space to store the colormap */
454     source->colormap = (*cinfo->mem->alloc_sarray)
455       ((j_common_ptr) cinfo, JPOOL_IMAGE, (JDIMENSION) maplen, (JDIMENSION) 3);
456     /* and read it from the file */
457     read_colormap(source, (int) maplen, UCH(targaheader[7]));
458   } else {
459     if (cmaptype)               /* but you promised a cmap! */
460       ERREXIT(cinfo, JERR_TGA_BADPARMS);
461     source->colormap = NULL;
462   }
463 
464   cinfo->input_components = components;
465   cinfo->data_precision = 8;
466   cinfo->image_width = width;
467   cinfo->image_height = height;
468 }
469 
470 
471 /*
472  * Finish up at the end of the file.
473  */
474 
475 METHODDEF(void)
finish_input_tga(j_compress_ptr cinfo,cjpeg_source_ptr sinfo)476 finish_input_tga (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
477 {
478   /* no work */
479 }
480 
481 
482 /*
483  * The module selection routine for Targa format input.
484  */
485 
486 GLOBAL(cjpeg_source_ptr)
jinit_read_targa(j_compress_ptr cinfo)487 jinit_read_targa (j_compress_ptr cinfo)
488 {
489   tga_source_ptr source;
490 
491   /* Create module interface object */
492   source = (tga_source_ptr)
493       (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
494                                   sizeof(tga_source_struct));
495   source->cinfo = cinfo;        /* make back link for subroutines */
496   /* Fill in method ptrs, except get_pixel_rows which start_input sets */
497   source->pub.start_input = start_input_tga;
498   source->pub.finish_input = finish_input_tga;
499 
500   return (cjpeg_source_ptr) source;
501 }
502 
503 #endif /* TARGA_SUPPORTED */
504