1 /*
2 * jrdtarga.c
3 *
4 * Copyright (C) 1991, 1992, Thomas G. Lane.
5 * This file is part of the Independent JPEG Group's software.
6 * For conditions of distribution and use, see the accompanying README file.
7 *
8 * This file contains routines to read input images in Targa format.
9 *
10 * These routines may need modification for non-Unix environments or
11 * specialized applications. As they stand, they assume input from
12 * an ordinary stdio stream. They further assume that reading begins
13 * at the start of the file; input_init may need work if the
14 * user interface has already read some data (e.g., to determine that
15 * the file is indeed Targa format).
16 *
17 * These routines are invoked via the methods get_input_row
18 * and input_init/term.
19 *
20 * Based on code contributed by Lee Daniel Crocker.
21 */
22
23 #include "jinclude.h"
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_IS_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 static JSAMPARRAY colormap; /* Targa colormap (converted to my format) */
48
49 static big_sarray_ptr whole_image; /* Needed if funny input row order */
50 static long current_row; /* Current logical row number to read */
51
52 /* Pointer to routine to extract next Targa pixel from input file */
53 static void (*read_pixel) PP((compress_info_ptr cinfo));
54
55 /* Result of read_pixel is delivered here: */
56 static U_CHAR tga_pixel[4];
57
58 static int pixel_size; /* Bytes per Targa pixel (1 to 4) */
59
60 /* State info for reading RLE-coded pixels; both counts must be init to 0 */
61 static int block_count; /* # of pixels remaining in RLE block */
62 static int dup_pixel_count; /* # of times to duplicate previous pixel */
63
64 /* This saves the correct pixel-row-expansion method for preload_image */
65 static void (*get_pixel_row) PP((compress_info_ptr cinfo,
66 JSAMPARRAY pixel_row));
67
68
69 /* For expanding 5-bit pixel values to 8-bit with best rounding */
70
71 static const UINT8 c5to8bits[32] = {
72 0, 8, 16, 24, 32, 41, 49, 57,
73 65, 74, 82, 90, 98, 106, 115, 123,
74 131, 139, 148, 156, 164, 172, 180, 189,
75 197, 205, 213, 222, 230, 238, 246, 255
76 };
77
78
79
80 LOCAL int
read_byte(compress_info_ptr cinfo)81 read_byte (compress_info_ptr cinfo)
82 /* Read next byte from Targa file */
83 {
84 register FILE *infile = cinfo->input_file;
85 register int c;
86
87 if ((c = getc(infile)) == EOF)
88 ERREXIT(cinfo->emethods, "Premature EOF in Targa file");
89 return c;
90 }
91
92
93 LOCAL void
read_colormap(compress_info_ptr cinfo,int cmaplen,int mapentrysize)94 read_colormap (compress_info_ptr cinfo, int cmaplen, int mapentrysize)
95 /* Read the colormap from a Targa file */
96 {
97 int i;
98
99 /* Presently only handles 24-bit BGR format */
100 if (mapentrysize != 24)
101 ERREXIT(cinfo->emethods, "Unsupported Targa colormap format");
102
103 for (i = 0; i < cmaplen; i++) {
104 colormap[2][i] = (JSAMPLE) read_byte(cinfo);
105 colormap[1][i] = (JSAMPLE) read_byte(cinfo);
106 colormap[0][i] = (JSAMPLE) read_byte(cinfo);
107 }
108 }
109
110
111 /*
112 * read_pixel methods: get a single pixel from Targa file into tga_pixel[]
113 */
114
115 LOCAL void
read_non_rle_pixel(compress_info_ptr cinfo)116 read_non_rle_pixel (compress_info_ptr cinfo)
117 /* Read one Targa pixel from the input file; no RLE expansion */
118 {
119 register FILE * infile = cinfo->input_file;
120 register int i;
121
122 for (i = 0; i < pixel_size; i++) {
123 tga_pixel[i] = (U_CHAR) getc(infile);
124 }
125 }
126
127
128 LOCAL void
read_rle_pixel(compress_info_ptr cinfo)129 read_rle_pixel (compress_info_ptr cinfo)
130 /* Read one Targa pixel from the input file, expanding RLE data as needed */
131 {
132 register FILE * infile = cinfo->input_file;
133 register int i;
134
135 /* Duplicate previously read pixel? */
136 if (dup_pixel_count > 0) {
137 dup_pixel_count--;
138 return;
139 }
140
141 /* Time to read RLE block header? */
142 if (--block_count < 0) { /* decrement pixels remaining in block */
143 i = read_byte(cinfo);
144 if (i & 0x80) { /* Start of duplicate-pixel block? */
145 dup_pixel_count = i & 0x7F; /* number of duplications after this one */
146 block_count = 0; /* then read new block header */
147 } else {
148 block_count = i & 0x7F; /* number of pixels after this one */
149 }
150 }
151
152 /* Read next pixel */
153 for (i = 0; i < pixel_size; i++) {
154 tga_pixel[i] = (U_CHAR) getc(infile);
155 }
156 }
157
158
159 /*
160 * Read one row of pixels.
161 *
162 * We provide several different versions depending on input file format.
163 */
164
165
166 METHODDEF void
get_8bit_gray_row(compress_info_ptr cinfo,JSAMPARRAY pixel_row)167 get_8bit_gray_row (compress_info_ptr cinfo, JSAMPARRAY pixel_row)
168 /* This version is for reading 8-bit grayscale pixels */
169 {
170 register JSAMPROW ptr0;
171 register long col;
172
173 ptr0 = pixel_row[0];
174 for (col = cinfo->image_width; col > 0; col--) {
175 (*read_pixel) (cinfo); /* Load next pixel into tga_pixel */
176 *ptr0++ = (JSAMPLE) UCH(tga_pixel[0]);
177 }
178 }
179
180 METHODDEF void
get_8bit_row(compress_info_ptr cinfo,JSAMPARRAY pixel_row)181 get_8bit_row (compress_info_ptr cinfo, JSAMPARRAY pixel_row)
182 /* This version is for reading 8-bit colormap indexes */
183 {
184 register int t;
185 register JSAMPROW ptr0, ptr1, ptr2;
186 register long col;
187
188 ptr0 = pixel_row[0];
189 ptr1 = pixel_row[1];
190 ptr2 = pixel_row[2];
191 for (col = cinfo->image_width; col > 0; col--) {
192 (*read_pixel) (cinfo); /* Load next pixel into tga_pixel */
193 t = UCH(tga_pixel[0]);
194 *ptr0++ = colormap[0][t];
195 *ptr1++ = colormap[1][t];
196 *ptr2++ = colormap[2][t];
197 }
198 }
199
200 METHODDEF void
get_16bit_row(compress_info_ptr cinfo,JSAMPARRAY pixel_row)201 get_16bit_row (compress_info_ptr cinfo, JSAMPARRAY pixel_row)
202 /* This version is for reading 16-bit pixels */
203 {
204 register int t;
205 register JSAMPROW ptr0, ptr1, ptr2;
206 register long col;
207
208 ptr0 = pixel_row[0];
209 ptr1 = pixel_row[1];
210 ptr2 = pixel_row[2];
211 for (col = cinfo->image_width; col > 0; col--) {
212 (*read_pixel) (cinfo); /* Load next pixel into tga_pixel */
213 t = UCH(tga_pixel[0]);
214 t += UCH(tga_pixel[1]) << 8;
215 /* We expand 5 bit data to 8 bit sample width.
216 * The format of the 16-bit (LSB first) input word is
217 * xRRRRRGGGGGBBBBB
218 */
219 *ptr2++ = (JSAMPLE) c5to8bits[t & 0x1F];
220 t >>= 5;
221 *ptr1++ = (JSAMPLE) c5to8bits[t & 0x1F];
222 t >>= 5;
223 *ptr0++ = (JSAMPLE) c5to8bits[t & 0x1F];
224 }
225 }
226
227 METHODDEF void
get_24bit_row(compress_info_ptr cinfo,JSAMPARRAY pixel_row)228 get_24bit_row (compress_info_ptr cinfo, JSAMPARRAY pixel_row)
229 /* This version is for reading 24-bit pixels */
230 {
231 register JSAMPROW ptr0, ptr1, ptr2;
232 register long col;
233
234 ptr0 = pixel_row[0];
235 ptr1 = pixel_row[1];
236 ptr2 = pixel_row[2];
237 for (col = cinfo->image_width; col > 0; col--) {
238 (*read_pixel) (cinfo); /* Load next pixel into tga_pixel */
239 *ptr0++ = (JSAMPLE) UCH(tga_pixel[2]); /* convert BGR to RGB order */
240 *ptr1++ = (JSAMPLE) UCH(tga_pixel[1]);
241 *ptr2++ = (JSAMPLE) UCH(tga_pixel[0]);
242 }
243 }
244
245 /*
246 * Targa also defines a 32-bit pixel format with order B,G,R,A.
247 * We presently ignore the attribute byte, so the code for reading
248 * these pixels is identical to the 24-bit routine above.
249 * This works because the actual pixel length is only known to read_pixel.
250 */
251
252 #define get_32bit_row get_24bit_row
253
254
255 /*
256 * This method is for re-reading the input data in standard top-down
257 * row order. The entire image has already been read into whole_image
258 * with proper conversion of pixel format, but it's in a funny row order.
259 */
260
261 METHODDEF void
get_memory_row(compress_info_ptr cinfo,JSAMPARRAY pixel_row)262 get_memory_row (compress_info_ptr cinfo, JSAMPARRAY pixel_row)
263 {
264 JSAMPARRAY image_ptr;
265 long source_row;
266
267 /* Compute row of source that maps to current_row of normal order */
268 /* For now, assume image is bottom-up and not interlaced. */
269 /* NEEDS WORK to support interlaced images! */
270 source_row = cinfo->image_height - current_row - 1;
271
272 /* Fetch that row from virtual array */
273 image_ptr = (*cinfo->emethods->access_big_sarray)
274 (whole_image, source_row * cinfo->input_components, FALSE);
275
276 jcopy_sample_rows(image_ptr, 0, pixel_row, 0,
277 cinfo->input_components, cinfo->image_width);
278
279 current_row++;
280 }
281
282
283 /*
284 * This method loads the image into whole_image during the first call on
285 * get_input_row. The get_input_row pointer is then adjusted to call
286 * get_memory_row on subsequent calls.
287 */
288
289 METHODDEF void
preload_image(compress_info_ptr cinfo,JSAMPARRAY pixel_row)290 preload_image (compress_info_ptr cinfo, JSAMPARRAY pixel_row)
291 {
292 JSAMPARRAY image_ptr;
293 long row;
294
295 /* Read the data into a virtual array in input-file row order */
296 for (row = 0; row < cinfo->image_height; row++) {
297 (*cinfo->methods->progress_monitor) (cinfo, row, cinfo->image_height);
298 image_ptr = (*cinfo->emethods->access_big_sarray)
299 (whole_image, row * cinfo->input_components, TRUE);
300 (*get_pixel_row) (cinfo, image_ptr);
301 }
302 cinfo->completed_passes++;
303
304 /* Set up to read from the virtual array in unscrambled order */
305 cinfo->methods->get_input_row = get_memory_row;
306 current_row = 0;
307 /* And read the first row */
308 get_memory_row(cinfo, pixel_row);
309 }
310
311
312 /*
313 * Read the file header; return image size and component count.
314 */
315
316 METHODDEF void
input_init(compress_info_ptr cinfo)317 input_init (compress_info_ptr cinfo)
318 {
319 U_CHAR targaheader[18];
320 int idlen, cmaptype, subtype, flags, interlace_type, components;
321 UINT16 width, height, maplen;
322 boolean is_bottom_up;
323
324 #define GET_2B(offset) ((unsigned int) UCH(targaheader[offset]) + \
325 (((unsigned int) UCH(targaheader[offset+1])) << 8))
326
327 if (! ReadOK(cinfo->input_file, targaheader, 18))
328 ERREXIT(cinfo->emethods, "Unexpected end of file");
329
330 /* Pretend "15-bit" pixels are 16-bit --- we ignore attribute bit anyway */
331 if (targaheader[16] == 15)
332 targaheader[16] = 16;
333
334 idlen = UCH(targaheader[0]);
335 cmaptype = UCH(targaheader[1]);
336 subtype = UCH(targaheader[2]);
337 maplen = GET_2B(5);
338 width = GET_2B(12);
339 height = GET_2B(14);
340 pixel_size = UCH(targaheader[16]) >> 3;
341 flags = UCH(targaheader[17]); /* Image Descriptor byte */
342
343 is_bottom_up = ((flags & 0x20) == 0); /* bit 5 set => top-down */
344 interlace_type = flags >> 6; /* bits 6/7 are interlace code */
345
346 if (cmaptype > 1 || /* cmaptype must be 0 or 1 */
347 pixel_size < 1 || pixel_size > 4 ||
348 (UCH(targaheader[16]) & 7) != 0 || /* bits/pixel must be multiple of 8 */
349 interlace_type != 0) /* currently don't allow interlaced image */
350 ERREXIT(cinfo->emethods, "Invalid or unsupported Targa file");
351
352 if (subtype > 8) {
353 /* It's an RLE-coded file */
354 read_pixel = read_rle_pixel;
355 block_count = dup_pixel_count = 0;
356 subtype -= 8;
357 } else {
358 /* Non-RLE file */
359 read_pixel = read_non_rle_pixel;
360 }
361
362 /* Now should have subtype 1, 2, or 3 */
363 components = 3; /* until proven different */
364 cinfo->in_color_space = CS_RGB;
365
366 switch (subtype) {
367 case 1: /* colormapped image */
368 if (pixel_size == 1 && cmaptype == 1)
369 get_pixel_row = get_8bit_row;
370 else
371 ERREXIT(cinfo->emethods, "Invalid or unsupported Targa file");
372 TRACEMS2(cinfo->emethods, 1, "%ux%u colormapped Targa image",
373 width, height);
374 break;
375 case 2: /* RGB image */
376 switch (pixel_size) {
377 case 2:
378 get_pixel_row = get_16bit_row;
379 break;
380 case 3:
381 get_pixel_row = get_24bit_row;
382 break;
383 case 4:
384 get_pixel_row = get_32bit_row;
385 break;
386 default:
387 ERREXIT(cinfo->emethods, "Invalid or unsupported Targa file");
388 break;
389 }
390 TRACEMS2(cinfo->emethods, 1, "%ux%u RGB Targa image",
391 width, height);
392 break;
393 case 3: /* Grayscale image */
394 components = 1;
395 cinfo->in_color_space = CS_GRAYSCALE;
396 if (pixel_size == 1)
397 get_pixel_row = get_8bit_gray_row;
398 else
399 ERREXIT(cinfo->emethods, "Invalid or unsupported Targa file");
400 TRACEMS2(cinfo->emethods, 1, "%ux%u grayscale Targa image",
401 width, height);
402 break;
403 default:
404 ERREXIT(cinfo->emethods, "Invalid or unsupported Targa file");
405 break;
406 }
407
408 if (is_bottom_up) {
409 whole_image = (*cinfo->emethods->request_big_sarray)
410 ((long) width, (long) height * components,
411 (long) components);
412 cinfo->methods->get_input_row = preload_image;
413 cinfo->total_passes++; /* count file reading as separate pass */
414 } else {
415 whole_image = NULL;
416 cinfo->methods->get_input_row = get_pixel_row;
417 }
418
419 while (idlen--) /* Throw away ID field */
420 (void) read_byte(cinfo);
421
422 if (maplen > 0) {
423 if (maplen > 256 || GET_2B(3) != 0)
424 ERREXIT(cinfo->emethods, "Colormap too large");
425 /* Allocate space to store the colormap */
426 colormap = (*cinfo->emethods->alloc_small_sarray)
427 ((long) maplen, 3L);
428 /* and read it from the file */
429 read_colormap(cinfo, (int) maplen, UCH(targaheader[7]));
430 } else {
431 if (cmaptype) /* but you promised a cmap! */
432 ERREXIT(cinfo->emethods, "Invalid or unsupported Targa file");
433 colormap = NULL;
434 }
435
436 cinfo->input_components = components;
437 cinfo->image_width = width;
438 cinfo->image_height = height;
439 cinfo->data_precision = 8; /* always, even if 12-bit JSAMPLEs */
440 }
441
442
443 /*
444 * Finish up at the end of the file.
445 */
446
447 METHODDEF void
input_term(compress_info_ptr cinfo)448 input_term (compress_info_ptr cinfo)
449 {
450 /* no work (we let free_all release the workspace) */
451 }
452
453
454 /*
455 * The method selection routine for Targa format input.
456 * Note that this must be called by the user interface before calling
457 * jpeg_compress. If multiple input formats are supported, the
458 * user interface is responsible for discovering the file format and
459 * calling the appropriate method selection routine.
460 */
461
462 GLOBAL void
jselrtarga(compress_info_ptr cinfo)463 jselrtarga (compress_info_ptr cinfo)
464 {
465 cinfo->methods->input_init = input_init;
466 /* cinfo->methods->get_input_row is set by input_init */
467 cinfo->methods->input_term = input_term;
468 }
469
470 #endif /* TARGA_SUPPORTED */
471