/* * jrdtarga.c * * Copyright (C) 1991, 1992, Thomas G. Lane. * This file is part of the Independent JPEG Group's software. * For conditions of distribution and use, see the accompanying README file. * * This file contains routines to read input images in Targa format. * * These routines may need modification for non-Unix environments or * specialized applications. As they stand, they assume input from * an ordinary stdio stream. They further assume that reading begins * at the start of the file; input_init may need work if the * user interface has already read some data (e.g., to determine that * the file is indeed Targa format). * * These routines are invoked via the methods get_input_row * and input_init/term. * * Based on code contributed by Lee Daniel Crocker. */ #include "jinclude.h" #ifdef TARGA_SUPPORTED /* Macros to deal with unsigned chars as efficiently as compiler allows */ #ifdef HAVE_UNSIGNED_CHAR typedef unsigned char U_CHAR; #define UCH(x) ((int) (x)) #else /* !HAVE_UNSIGNED_CHAR */ #ifdef CHAR_IS_UNSIGNED typedef char U_CHAR; #define UCH(x) ((int) (x)) #else typedef char U_CHAR; #define UCH(x) ((int) (x) & 0xFF) #endif #endif /* HAVE_UNSIGNED_CHAR */ #define ReadOK(file,buffer,len) (JFREAD(file,buffer,len) == ((size_t) (len))) static JSAMPARRAY colormap; /* Targa colormap (converted to my format) */ static big_sarray_ptr whole_image; /* Needed if funny input row order */ static long current_row; /* Current logical row number to read */ /* Pointer to routine to extract next Targa pixel from input file */ static void (*read_pixel) PP((compress_info_ptr cinfo)); /* Result of read_pixel is delivered here: */ static U_CHAR tga_pixel[4]; static int pixel_size; /* Bytes per Targa pixel (1 to 4) */ /* State info for reading RLE-coded pixels; both counts must be init to 0 */ static int block_count; /* # of pixels remaining in RLE block */ static int dup_pixel_count; /* # of times to duplicate previous pixel */ /* This saves the correct pixel-row-expansion method for preload_image */ static void (*get_pixel_row) PP((compress_info_ptr cinfo, JSAMPARRAY pixel_row)); /* For expanding 5-bit pixel values to 8-bit with best rounding */ static const UINT8 c5to8bits[32] = { 0, 8, 16, 24, 32, 41, 49, 57, 65, 74, 82, 90, 98, 106, 115, 123, 131, 139, 148, 156, 164, 172, 180, 189, 197, 205, 213, 222, 230, 238, 246, 255 }; LOCAL int read_byte (compress_info_ptr cinfo) /* Read next byte from Targa file */ { register FILE *infile = cinfo->input_file; register int c; if ((c = getc(infile)) == EOF) ERREXIT(cinfo->emethods, "Premature EOF in Targa file"); return c; } LOCAL void read_colormap (compress_info_ptr cinfo, int cmaplen, int mapentrysize) /* Read the colormap from a Targa file */ { int i; /* Presently only handles 24-bit BGR format */ if (mapentrysize != 24) ERREXIT(cinfo->emethods, "Unsupported Targa colormap format"); for (i = 0; i < cmaplen; i++) { colormap[2][i] = (JSAMPLE) read_byte(cinfo); colormap[1][i] = (JSAMPLE) read_byte(cinfo); colormap[0][i] = (JSAMPLE) read_byte(cinfo); } } /* * read_pixel methods: get a single pixel from Targa file into tga_pixel[] */ LOCAL void read_non_rle_pixel (compress_info_ptr cinfo) /* Read one Targa pixel from the input file; no RLE expansion */ { register FILE * infile = cinfo->input_file; register int i; for (i = 0; i < pixel_size; i++) { tga_pixel[i] = (U_CHAR) getc(infile); } } LOCAL void read_rle_pixel (compress_info_ptr cinfo) /* Read one Targa pixel from the input file, expanding RLE data as needed */ { register FILE * infile = cinfo->input_file; register int i; /* Duplicate previously read pixel? */ if (dup_pixel_count > 0) { dup_pixel_count--; return; } /* Time to read RLE block header? */ if (--block_count < 0) { /* decrement pixels remaining in block */ i = read_byte(cinfo); if (i & 0x80) { /* Start of duplicate-pixel block? */ dup_pixel_count = i & 0x7F; /* number of duplications after this one */ block_count = 0; /* then read new block header */ } else { block_count = i & 0x7F; /* number of pixels after this one */ } } /* Read next pixel */ for (i = 0; i < pixel_size; i++) { tga_pixel[i] = (U_CHAR) getc(infile); } } /* * Read one row of pixels. * * We provide several different versions depending on input file format. */ METHODDEF void get_8bit_gray_row (compress_info_ptr cinfo, JSAMPARRAY pixel_row) /* This version is for reading 8-bit grayscale pixels */ { register JSAMPROW ptr0; register long col; ptr0 = pixel_row[0]; for (col = cinfo->image_width; col > 0; col--) { (*read_pixel) (cinfo); /* Load next pixel into tga_pixel */ *ptr0++ = (JSAMPLE) UCH(tga_pixel[0]); } } METHODDEF void get_8bit_row (compress_info_ptr cinfo, JSAMPARRAY pixel_row) /* This version is for reading 8-bit colormap indexes */ { register int t; register JSAMPROW ptr0, ptr1, ptr2; register long col; ptr0 = pixel_row[0]; ptr1 = pixel_row[1]; ptr2 = pixel_row[2]; for (col = cinfo->image_width; col > 0; col--) { (*read_pixel) (cinfo); /* Load next pixel into tga_pixel */ t = UCH(tga_pixel[0]); *ptr0++ = colormap[0][t]; *ptr1++ = colormap[1][t]; *ptr2++ = colormap[2][t]; } } METHODDEF void get_16bit_row (compress_info_ptr cinfo, JSAMPARRAY pixel_row) /* This version is for reading 16-bit pixels */ { register int t; register JSAMPROW ptr0, ptr1, ptr2; register long col; ptr0 = pixel_row[0]; ptr1 = pixel_row[1]; ptr2 = pixel_row[2]; for (col = cinfo->image_width; col > 0; col--) { (*read_pixel) (cinfo); /* Load next pixel into tga_pixel */ t = UCH(tga_pixel[0]); t += UCH(tga_pixel[1]) << 8; /* We expand 5 bit data to 8 bit sample width. * The format of the 16-bit (LSB first) input word is * xRRRRRGGGGGBBBBB */ *ptr2++ = (JSAMPLE) c5to8bits[t & 0x1F]; t >>= 5; *ptr1++ = (JSAMPLE) c5to8bits[t & 0x1F]; t >>= 5; *ptr0++ = (JSAMPLE) c5to8bits[t & 0x1F]; } } METHODDEF void get_24bit_row (compress_info_ptr cinfo, JSAMPARRAY pixel_row) /* This version is for reading 24-bit pixels */ { register JSAMPROW ptr0, ptr1, ptr2; register long col; ptr0 = pixel_row[0]; ptr1 = pixel_row[1]; ptr2 = pixel_row[2]; for (col = cinfo->image_width; col > 0; col--) { (*read_pixel) (cinfo); /* Load next pixel into tga_pixel */ *ptr0++ = (JSAMPLE) UCH(tga_pixel[2]); /* convert BGR to RGB order */ *ptr1++ = (JSAMPLE) UCH(tga_pixel[1]); *ptr2++ = (JSAMPLE) UCH(tga_pixel[0]); } } /* * Targa also defines a 32-bit pixel format with order B,G,R,A. * We presently ignore the attribute byte, so the code for reading * these pixels is identical to the 24-bit routine above. * This works because the actual pixel length is only known to read_pixel. */ #define get_32bit_row get_24bit_row /* * This method is for re-reading the input data in standard top-down * row order. The entire image has already been read into whole_image * with proper conversion of pixel format, but it's in a funny row order. */ METHODDEF void get_memory_row (compress_info_ptr cinfo, JSAMPARRAY pixel_row) { JSAMPARRAY image_ptr; long source_row; /* Compute row of source that maps to current_row of normal order */ /* For now, assume image is bottom-up and not interlaced. */ /* NEEDS WORK to support interlaced images! */ source_row = cinfo->image_height - current_row - 1; /* Fetch that row from virtual array */ image_ptr = (*cinfo->emethods->access_big_sarray) (whole_image, source_row * cinfo->input_components, FALSE); jcopy_sample_rows(image_ptr, 0, pixel_row, 0, cinfo->input_components, cinfo->image_width); current_row++; } /* * This method loads the image into whole_image during the first call on * get_input_row. The get_input_row pointer is then adjusted to call * get_memory_row on subsequent calls. */ METHODDEF void preload_image (compress_info_ptr cinfo, JSAMPARRAY pixel_row) { JSAMPARRAY image_ptr; long row; /* Read the data into a virtual array in input-file row order */ for (row = 0; row < cinfo->image_height; row++) { (*cinfo->methods->progress_monitor) (cinfo, row, cinfo->image_height); image_ptr = (*cinfo->emethods->access_big_sarray) (whole_image, row * cinfo->input_components, TRUE); (*get_pixel_row) (cinfo, image_ptr); } cinfo->completed_passes++; /* Set up to read from the virtual array in unscrambled order */ cinfo->methods->get_input_row = get_memory_row; current_row = 0; /* And read the first row */ get_memory_row(cinfo, pixel_row); } /* * Read the file header; return image size and component count. */ METHODDEF void input_init (compress_info_ptr cinfo) { U_CHAR targaheader[18]; int idlen, cmaptype, subtype, flags, interlace_type, components; UINT16 width, height, maplen; boolean is_bottom_up; #define GET_2B(offset) ((unsigned int) UCH(targaheader[offset]) + \ (((unsigned int) UCH(targaheader[offset+1])) << 8)) if (! ReadOK(cinfo->input_file, targaheader, 18)) ERREXIT(cinfo->emethods, "Unexpected end of file"); /* Pretend "15-bit" pixels are 16-bit --- we ignore attribute bit anyway */ if (targaheader[16] == 15) targaheader[16] = 16; idlen = UCH(targaheader[0]); cmaptype = UCH(targaheader[1]); subtype = UCH(targaheader[2]); maplen = GET_2B(5); width = GET_2B(12); height = GET_2B(14); pixel_size = UCH(targaheader[16]) >> 3; flags = UCH(targaheader[17]); /* Image Descriptor byte */ is_bottom_up = ((flags & 0x20) == 0); /* bit 5 set => top-down */ interlace_type = flags >> 6; /* bits 6/7 are interlace code */ if (cmaptype > 1 || /* cmaptype must be 0 or 1 */ pixel_size < 1 || pixel_size > 4 || (UCH(targaheader[16]) & 7) != 0 || /* bits/pixel must be multiple of 8 */ interlace_type != 0) /* currently don't allow interlaced image */ ERREXIT(cinfo->emethods, "Invalid or unsupported Targa file"); if (subtype > 8) { /* It's an RLE-coded file */ read_pixel = read_rle_pixel; block_count = dup_pixel_count = 0; subtype -= 8; } else { /* Non-RLE file */ read_pixel = read_non_rle_pixel; } /* Now should have subtype 1, 2, or 3 */ components = 3; /* until proven different */ cinfo->in_color_space = CS_RGB; switch (subtype) { case 1: /* colormapped image */ if (pixel_size == 1 && cmaptype == 1) get_pixel_row = get_8bit_row; else ERREXIT(cinfo->emethods, "Invalid or unsupported Targa file"); TRACEMS2(cinfo->emethods, 1, "%ux%u colormapped Targa image", width, height); break; case 2: /* RGB image */ switch (pixel_size) { case 2: get_pixel_row = get_16bit_row; break; case 3: get_pixel_row = get_24bit_row; break; case 4: get_pixel_row = get_32bit_row; break; default: ERREXIT(cinfo->emethods, "Invalid or unsupported Targa file"); break; } TRACEMS2(cinfo->emethods, 1, "%ux%u RGB Targa image", width, height); break; case 3: /* Grayscale image */ components = 1; cinfo->in_color_space = CS_GRAYSCALE; if (pixel_size == 1) get_pixel_row = get_8bit_gray_row; else ERREXIT(cinfo->emethods, "Invalid or unsupported Targa file"); TRACEMS2(cinfo->emethods, 1, "%ux%u grayscale Targa image", width, height); break; default: ERREXIT(cinfo->emethods, "Invalid or unsupported Targa file"); break; } if (is_bottom_up) { whole_image = (*cinfo->emethods->request_big_sarray) ((long) width, (long) height * components, (long) components); cinfo->methods->get_input_row = preload_image; cinfo->total_passes++; /* count file reading as separate pass */ } else { whole_image = NULL; cinfo->methods->get_input_row = get_pixel_row; } while (idlen--) /* Throw away ID field */ (void) read_byte(cinfo); if (maplen > 0) { if (maplen > 256 || GET_2B(3) != 0) ERREXIT(cinfo->emethods, "Colormap too large"); /* Allocate space to store the colormap */ colormap = (*cinfo->emethods->alloc_small_sarray) ((long) maplen, 3L); /* and read it from the file */ read_colormap(cinfo, (int) maplen, UCH(targaheader[7])); } else { if (cmaptype) /* but you promised a cmap! */ ERREXIT(cinfo->emethods, "Invalid or unsupported Targa file"); colormap = NULL; } cinfo->input_components = components; cinfo->image_width = width; cinfo->image_height = height; cinfo->data_precision = 8; /* always, even if 12-bit JSAMPLEs */ } /* * Finish up at the end of the file. */ METHODDEF void input_term (compress_info_ptr cinfo) { /* no work (we let free_all release the workspace) */ } /* * The method selection routine for Targa format input. * Note that this must be called by the user interface before calling * jpeg_compress. If multiple input formats are supported, the * user interface is responsible for discovering the file format and * calling the appropriate method selection routine. */ GLOBAL void jselrtarga (compress_info_ptr cinfo) { cinfo->methods->input_init = input_init; /* cinfo->methods->get_input_row is set by input_init */ cinfo->methods->input_term = input_term; } #endif /* TARGA_SUPPORTED */