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