1/* 2 * rdrle.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 and 7 * information relevant 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 Utah RLE format. 12 * The Utah Raster Toolkit library is required (version 3.1 or later). 13 * 14 * These routines may need modification for non-Unix environments or 15 * specialized applications. As they stand, they assume input from 16 * an ordinary stdio stream. They further assume that reading begins 17 * at the start of the file; start_input may need work if the 18 * user interface has already read some data (e.g., to determine that 19 * the file is indeed RLE format). 20 * 21 * Based on code contributed by Mike Lijewski, 22 * with updates from Robert Hutchinson. 23 */ 24 25#include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */ 26 27#ifdef RLE_SUPPORTED 28 29/* rle.h is provided by the Utah Raster Toolkit. */ 30 31#include <rle.h> 32 33/* 34 * We assume that JSAMPLE has the same representation as rle_pixel, 35 * to wit, "unsigned char". Hence we can't cope with 12- or 16-bit samples. 36 */ 37 38#if BITS_IN_JSAMPLE != 8 39 Sorry, this code only copes with 8-bit JSAMPLEs. /* deliberate syntax err */ 40#endif 41 42/* 43 * We support the following types of RLE files: 44 * 45 * GRAYSCALE - 8 bits, no colormap 46 * MAPPEDGRAY - 8 bits, 1 channel colomap 47 * PSEUDOCOLOR - 8 bits, 3 channel colormap 48 * TRUECOLOR - 24 bits, 3 channel colormap 49 * DIRECTCOLOR - 24 bits, no colormap 50 * 51 * For now, we ignore any alpha channel in the image. 52 */ 53 54typedef enum 55 { GRAYSCALE, MAPPEDGRAY, PSEUDOCOLOR, TRUECOLOR, DIRECTCOLOR } rle_kind; 56 57 58/* 59 * Since RLE stores scanlines bottom-to-top, we have to invert the image 60 * to conform to JPEG's top-to-bottom order. To do this, we read the 61 * incoming image into a virtual array on the first get_pixel_rows call, 62 * then fetch the required row from the virtual array on subsequent calls. 63 */ 64 65typedef struct _rle_source_struct *rle_source_ptr; 66 67typedef struct _rle_source_struct { 68 struct cjpeg_source_struct pub; /* public fields */ 69 70 rle_kind visual; /* actual type of input file */ 71 jvirt_sarray_ptr image; /* virtual array to hold the image */ 72 JDIMENSION row; /* current row # in the virtual array */ 73 rle_hdr header; /* Input file information */ 74 rle_pixel **rle_row; /* holds a row returned by rle_getrow() */ 75 76} rle_source_struct; 77 78 79/* 80 * Read the file header; return image size and component count. 81 */ 82 83METHODDEF(void) 84start_input_rle (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) 85{ 86 rle_source_ptr source = (rle_source_ptr) sinfo; 87 JDIMENSION width, height; 88#ifdef PROGRESS_REPORT 89 cd_progress_ptr progress = (cd_progress_ptr) cinfo->progress; 90#endif 91 92 /* Use RLE library routine to get the header info */ 93 source->header = *rle_hdr_init(NULL); 94 source->header.rle_file = source->pub.input_file; 95 switch (rle_get_setup(&(source->header))) { 96 case RLE_SUCCESS: 97 /* A-OK */ 98 break; 99 case RLE_NOT_RLE: 100 ERREXIT(cinfo, JERR_RLE_NOT); 101 break; 102 case RLE_NO_SPACE: 103 ERREXIT(cinfo, JERR_RLE_MEM); 104 break; 105 case RLE_EMPTY: 106 ERREXIT(cinfo, JERR_RLE_EMPTY); 107 break; 108 case RLE_EOF: 109 ERREXIT(cinfo, JERR_RLE_EOF); 110 break; 111 default: 112 ERREXIT(cinfo, JERR_RLE_BADERROR); 113 break; 114 } 115 116 /* Figure out what we have, set private vars and return values accordingly */ 117 118 width = source->header.xmax - source->header.xmin + 1; 119 height = source->header.ymax - source->header.ymin + 1; 120 source->header.xmin = 0; /* realign horizontally */ 121 source->header.xmax = width-1; 122 123 cinfo->image_width = width; 124 cinfo->image_height = height; 125 cinfo->data_precision = 8; /* we can only handle 8 bit data */ 126 127 if (source->header.ncolors == 1 && source->header.ncmap == 0) { 128 source->visual = GRAYSCALE; 129 TRACEMS2(cinfo, 1, JTRC_RLE_GRAY, width, height); 130 } else if (source->header.ncolors == 1 && source->header.ncmap == 1) { 131 source->visual = MAPPEDGRAY; 132 TRACEMS3(cinfo, 1, JTRC_RLE_MAPGRAY, width, height, 133 1 << source->header.cmaplen); 134 } else if (source->header.ncolors == 1 && source->header.ncmap == 3) { 135 source->visual = PSEUDOCOLOR; 136 TRACEMS3(cinfo, 1, JTRC_RLE_MAPPED, width, height, 137 1 << source->header.cmaplen); 138 } else if (source->header.ncolors == 3 && source->header.ncmap == 3) { 139 source->visual = TRUECOLOR; 140 TRACEMS3(cinfo, 1, JTRC_RLE_FULLMAP, width, height, 141 1 << source->header.cmaplen); 142 } else if (source->header.ncolors == 3 && source->header.ncmap == 0) { 143 source->visual = DIRECTCOLOR; 144 TRACEMS2(cinfo, 1, JTRC_RLE, width, height); 145 } else 146 ERREXIT(cinfo, JERR_RLE_UNSUPPORTED); 147 148 if (source->visual == GRAYSCALE || source->visual == MAPPEDGRAY) { 149 cinfo->in_color_space = JCS_GRAYSCALE; 150 cinfo->input_components = 1; 151 } else { 152 cinfo->in_color_space = JCS_RGB; 153 cinfo->input_components = 3; 154 } 155 156 /* 157 * A place to hold each scanline while it's converted. 158 * (GRAYSCALE scanlines don't need converting) 159 */ 160 if (source->visual != GRAYSCALE) { 161 source->rle_row = (rle_pixel**) (*cinfo->mem->alloc_sarray) 162 ((j_common_ptr) cinfo, JPOOL_IMAGE, 163 (JDIMENSION) width, (JDIMENSION) cinfo->input_components); 164 } 165 166 /* request a virtual array to hold the image */ 167 source->image = (*cinfo->mem->request_virt_sarray) 168 ((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE, 169 (JDIMENSION) (width * source->header.ncolors), 170 (JDIMENSION) height, (JDIMENSION) 1); 171 172#ifdef PROGRESS_REPORT 173 if (progress != NULL) { 174 /* count file input as separate pass */ 175 progress->total_extra_passes++; 176 } 177#endif 178 179 source->pub.buffer_height = 1; 180} 181 182 183/* 184 * Read one row of pixels. 185 * Called only after load_image has read the image into the virtual array. 186 * Used for GRAYSCALE, MAPPEDGRAY, TRUECOLOR, and DIRECTCOLOR images. 187 */ 188 189METHODDEF(JDIMENSION) 190get_rle_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) 191{ 192 rle_source_ptr source = (rle_source_ptr) sinfo; 193 194 source->row--; 195 source->pub.buffer = (*cinfo->mem->access_virt_sarray) 196 ((j_common_ptr) cinfo, source->image, source->row, (JDIMENSION) 1, FALSE); 197 198 return 1; 199} 200 201/* 202 * Read one row of pixels. 203 * Called only after load_image has read the image into the virtual array. 204 * Used for PSEUDOCOLOR images. 205 */ 206 207METHODDEF(JDIMENSION) 208get_pseudocolor_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) 209{ 210 rle_source_ptr source = (rle_source_ptr) sinfo; 211 JSAMPROW src_row, dest_row; 212 JDIMENSION col; 213 rle_map *colormap; 214 int val; 215 216 colormap = source->header.cmap; 217 dest_row = source->pub.buffer[0]; 218 source->row--; 219 src_row = *(*cinfo->mem->access_virt_sarray) 220 ((j_common_ptr) cinfo, source->image, source->row, (JDIMENSION) 1, FALSE); 221 222 for (col = cinfo->image_width; col > 0; col--) { 223 val = GETJSAMPLE(*src_row++); 224 *dest_row++ = (JSAMPLE) (colormap[val ] >> 8); 225 *dest_row++ = (JSAMPLE) (colormap[val + 256] >> 8); 226 *dest_row++ = (JSAMPLE) (colormap[val + 512] >> 8); 227 } 228 229 return 1; 230} 231 232 233/* 234 * Load the image into a virtual array. We have to do this because RLE 235 * files start at the lower left while the JPEG standard has them starting 236 * in the upper left. This is called the first time we want to get a row 237 * of input. What we do is load the RLE data into the array and then call 238 * the appropriate routine to read one row from the array. Before returning, 239 * we set source->pub.get_pixel_rows so that subsequent calls go straight to 240 * the appropriate row-reading routine. 241 */ 242 243METHODDEF(JDIMENSION) 244load_image (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) 245{ 246 rle_source_ptr source = (rle_source_ptr) sinfo; 247 JDIMENSION row, col; 248 JSAMPROW scanline, red_ptr, green_ptr, blue_ptr; 249 rle_pixel **rle_row; 250 rle_map *colormap; 251 char channel; 252#ifdef PROGRESS_REPORT 253 cd_progress_ptr progress = (cd_progress_ptr) cinfo->progress; 254#endif 255 256 colormap = source->header.cmap; 257 rle_row = source->rle_row; 258 259 /* Read the RLE data into our virtual array. 260 * We assume here that rle_pixel is represented the same as JSAMPLE. 261 */ 262 RLE_CLR_BIT(source->header, RLE_ALPHA); /* don't read the alpha channel */ 263 264#ifdef PROGRESS_REPORT 265 if (progress != NULL) { 266 progress->pub.pass_limit = cinfo->image_height; 267 progress->pub.pass_counter = 0; 268 (*progress->pub.progress_monitor) ((j_common_ptr) cinfo); 269 } 270#endif 271 272 switch (source->visual) { 273 274 case GRAYSCALE: 275 case PSEUDOCOLOR: 276 for (row = 0; row < cinfo->image_height; row++) { 277 rle_row = (rle_pixel **) (*cinfo->mem->access_virt_sarray) 278 ((j_common_ptr) cinfo, source->image, row, (JDIMENSION) 1, TRUE); 279 rle_getrow(&source->header, rle_row); 280#ifdef PROGRESS_REPORT 281 if (progress != NULL) { 282 progress->pub.pass_counter++; 283 (*progress->pub.progress_monitor) ((j_common_ptr) cinfo); 284 } 285#endif 286 } 287 break; 288 289 case MAPPEDGRAY: 290 case TRUECOLOR: 291 for (row = 0; row < cinfo->image_height; row++) { 292 scanline = *(*cinfo->mem->access_virt_sarray) 293 ((j_common_ptr) cinfo, source->image, row, (JDIMENSION) 1, TRUE); 294 rle_row = source->rle_row; 295 rle_getrow(&source->header, rle_row); 296 297 for (col = 0; col < cinfo->image_width; col++) { 298 for (channel = 0; channel < source->header.ncolors; channel++) { 299 *scanline++ = (JSAMPLE) 300 (colormap[GETJSAMPLE(rle_row[channel][col]) + 256 * channel] >> 8); 301 } 302 } 303 304#ifdef PROGRESS_REPORT 305 if (progress != NULL) { 306 progress->pub.pass_counter++; 307 (*progress->pub.progress_monitor) ((j_common_ptr) cinfo); 308 } 309#endif 310 } 311 break; 312 313 case DIRECTCOLOR: 314 for (row = 0; row < cinfo->image_height; row++) { 315 scanline = *(*cinfo->mem->access_virt_sarray) 316 ((j_common_ptr) cinfo, source->image, row, (JDIMENSION) 1, TRUE); 317 rle_getrow(&source->header, rle_row); 318 319 red_ptr = rle_row[0]; 320 green_ptr = rle_row[1]; 321 blue_ptr = rle_row[2]; 322 323 for (col = cinfo->image_width; col > 0; col--) { 324 *scanline++ = *red_ptr++; 325 *scanline++ = *green_ptr++; 326 *scanline++ = *blue_ptr++; 327 } 328 329#ifdef PROGRESS_REPORT 330 if (progress != NULL) { 331 progress->pub.pass_counter++; 332 (*progress->pub.progress_monitor) ((j_common_ptr) cinfo); 333 } 334#endif 335 } 336 } 337 338#ifdef PROGRESS_REPORT 339 if (progress != NULL) 340 progress->completed_extra_passes++; 341#endif 342 343 /* Set up to call proper row-extraction routine in future */ 344 if (source->visual == PSEUDOCOLOR) { 345 source->pub.buffer = source->rle_row; 346 source->pub.get_pixel_rows = get_pseudocolor_row; 347 } else { 348 source->pub.get_pixel_rows = get_rle_row; 349 } 350 source->row = cinfo->image_height; 351 352 /* And fetch the topmost (bottommost) row */ 353 return (*source->pub.get_pixel_rows) (cinfo, sinfo); 354} 355 356 357/* 358 * Finish up at the end of the file. 359 */ 360 361METHODDEF(void) 362finish_input_rle (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) 363{ 364 /* no work */ 365} 366 367 368/* 369 * The module selection routine for RLE format input. 370 */ 371 372GLOBAL(cjpeg_source_ptr) 373jinit_read_rle (j_compress_ptr cinfo) 374{ 375 rle_source_ptr source; 376 377 /* Create module interface object */ 378 source = (rle_source_ptr) 379 (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, 380 sizeof(rle_source_struct)); 381 /* Fill in method ptrs */ 382 source->pub.start_input = start_input_rle; 383 source->pub.finish_input = finish_input_rle; 384 source->pub.get_pixel_rows = load_image; 385 386 return (cjpeg_source_ptr) source; 387} 388 389#endif /* RLE_SUPPORTED */ 390