1/*
2 * wrrle.c
3 *
4 * Copyright (C) 1991-1996, 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 write output images in RLE format.
9 * The Utah Raster Toolkit library is required (version 3.1 or later).
10 *
11 * These routines may need modification for non-Unix environments or
12 * specialized applications.  As they stand, they assume output to
13 * an ordinary stdio stream.
14 *
15 * Based on code contributed by Mike Lijewski,
16 * with updates from Robert Hutchinson.
17 */
18
19#include "cdjpeg.h"		/* Common decls for cjpeg/djpeg applications */
20
21#ifdef RLE_SUPPORTED
22
23/* rle.h is provided by the Utah Raster Toolkit. */
24
25#include <rle.h>
26
27/*
28 * We assume that JSAMPLE has the same representation as rle_pixel,
29 * to wit, "unsigned char".  Hence we can't cope with 12- or 16-bit samples.
30 */
31
32#if BITS_IN_JSAMPLE != 8
33  Sorry, this code only copes with 8-bit JSAMPLEs. /* deliberate syntax err */
34#endif
35
36
37/*
38 * Since RLE stores scanlines bottom-to-top, we have to invert the image
39 * from JPEG's top-to-bottom order.  To do this, we save the outgoing data
40 * in a virtual array during put_pixel_row calls, then actually emit the
41 * RLE file during finish_output.
42 */
43
44
45/*
46 * For now, if we emit an RLE color map then it is always 256 entries long,
47 * though not all of the entries need be used.
48 */
49
50#define CMAPBITS	8
51#define CMAPLENGTH	(1<<(CMAPBITS))
52
53typedef struct {
54  struct djpeg_dest_struct pub; /* public fields */
55
56  jvirt_sarray_ptr image;	/* virtual array to store the output image */
57  rle_map *colormap;	 	/* RLE-style color map, or NULL if none */
58  rle_pixel **rle_row;		/* To pass rows to rle_putrow() */
59
60} rle_dest_struct;
61
62typedef rle_dest_struct * rle_dest_ptr;
63
64/* Forward declarations */
65METHODDEF(void) rle_put_pixel_rows
66    JPP((j_decompress_ptr cinfo, djpeg_dest_ptr dinfo,
67	 JDIMENSION rows_supplied));
68
69
70/*
71 * Write the file header.
72 *
73 * In this module it's easier to wait till finish_output to write anything.
74 */
75
76METHODDEF(void)
77start_output_rle (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo)
78{
79  rle_dest_ptr dest = (rle_dest_ptr) dinfo;
80  size_t cmapsize;
81  int i, ci;
82#ifdef PROGRESS_REPORT
83  cd_progress_ptr progress = (cd_progress_ptr) cinfo->progress;
84#endif
85
86  /*
87   * Make sure the image can be stored in RLE format.
88   *
89   * - RLE stores image dimensions as *signed* 16 bit integers.  JPEG
90   *   uses unsigned, so we have to check the width.
91   *
92   * - Colorspace is expected to be grayscale or RGB.
93   *
94   * - The number of channels (components) is expected to be 1 (grayscale/
95   *   pseudocolor) or 3 (truecolor/directcolor).
96   *   (could be 2 or 4 if using an alpha channel, but we aren't)
97   */
98
99  if (cinfo->output_width > 32767 || cinfo->output_height > 32767)
100    ERREXIT2(cinfo, JERR_RLE_DIMENSIONS, cinfo->output_width,
101	     cinfo->output_height);
102
103  if (cinfo->out_color_space != JCS_GRAYSCALE &&
104      cinfo->out_color_space != JCS_RGB)
105    ERREXIT(cinfo, JERR_RLE_COLORSPACE);
106
107  if (cinfo->output_components != 1 && cinfo->output_components != 3)
108    ERREXIT1(cinfo, JERR_RLE_TOOMANYCHANNELS, cinfo->num_components);
109
110  /* Convert colormap, if any, to RLE format. */
111
112  dest->colormap = NULL;
113
114  if (cinfo->quantize_colors) {
115    /* Allocate storage for RLE-style cmap, zero any extra entries */
116    cmapsize = cinfo->out_color_components * CMAPLENGTH * SIZEOF(rle_map);
117    dest->colormap = (rle_map *) (*cinfo->mem->alloc_small)
118      ((j_common_ptr) cinfo, JPOOL_IMAGE, cmapsize);
119    MEMZERO(dest->colormap, cmapsize);
120
121    /* Save away data in RLE format --- note 8-bit left shift! */
122    /* Shifting would need adjustment for JSAMPLEs wider than 8 bits. */
123    for (ci = 0; ci < cinfo->out_color_components; ci++) {
124      for (i = 0; i < cinfo->actual_number_of_colors; i++) {
125        dest->colormap[ci * CMAPLENGTH + i] =
126          GETJSAMPLE(cinfo->colormap[ci][i]) << 8;
127      }
128    }
129  }
130
131  /* Set the output buffer to the first row */
132  dest->pub.buffer = (*cinfo->mem->access_virt_sarray)
133    ((j_common_ptr) cinfo, dest->image, (JDIMENSION) 0, (JDIMENSION) 1, TRUE);
134  dest->pub.buffer_height = 1;
135
136  dest->pub.put_pixel_rows = rle_put_pixel_rows;
137
138#ifdef PROGRESS_REPORT
139  if (progress != NULL) {
140    progress->total_extra_passes++;  /* count file writing as separate pass */
141  }
142#endif
143}
144
145
146/*
147 * Write some pixel data.
148 *
149 * This routine just saves the data away in a virtual array.
150 */
151
152METHODDEF(void)
153rle_put_pixel_rows (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo,
154		    JDIMENSION rows_supplied)
155{
156  rle_dest_ptr dest = (rle_dest_ptr) dinfo;
157
158  if (cinfo->output_scanline < cinfo->output_height) {
159    dest->pub.buffer = (*cinfo->mem->access_virt_sarray)
160      ((j_common_ptr) cinfo, dest->image,
161       cinfo->output_scanline, (JDIMENSION) 1, TRUE);
162  }
163}
164
165/*
166 * Finish up at the end of the file.
167 *
168 * Here is where we really output the RLE file.
169 */
170
171METHODDEF(void)
172finish_output_rle (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo)
173{
174  rle_dest_ptr dest = (rle_dest_ptr) dinfo;
175  rle_hdr header;		/* Output file information */
176  rle_pixel **rle_row, *red, *green, *blue;
177  JSAMPROW output_row;
178  char cmapcomment[80];
179  int row, col;
180  int ci;
181#ifdef PROGRESS_REPORT
182  cd_progress_ptr progress = (cd_progress_ptr) cinfo->progress;
183#endif
184
185  /* Initialize the header info */
186  header = *rle_hdr_init(NULL);
187  header.rle_file = dest->pub.output_file;
188  header.xmin     = 0;
189  header.xmax     = cinfo->output_width  - 1;
190  header.ymin     = 0;
191  header.ymax     = cinfo->output_height - 1;
192  header.alpha    = 0;
193  header.ncolors  = cinfo->output_components;
194  for (ci = 0; ci < cinfo->output_components; ci++) {
195    RLE_SET_BIT(header, ci);
196  }
197  if (cinfo->quantize_colors) {
198    header.ncmap   = cinfo->out_color_components;
199    header.cmaplen = CMAPBITS;
200    header.cmap    = dest->colormap;
201    /* Add a comment to the output image with the true colormap length. */
202    sprintf(cmapcomment, "color_map_length=%d", cinfo->actual_number_of_colors);
203    rle_putcom(cmapcomment, &header);
204  }
205
206  /* Emit the RLE header and color map (if any) */
207  rle_put_setup(&header);
208
209  /* Now output the RLE data from our virtual array.
210   * We assume here that (a) rle_pixel is represented the same as JSAMPLE,
211   * and (b) we are not on a machine where FAR pointers differ from regular.
212   */
213
214#ifdef PROGRESS_REPORT
215  if (progress != NULL) {
216    progress->pub.pass_limit = cinfo->output_height;
217    progress->pub.pass_counter = 0;
218    (*progress->pub.progress_monitor) ((j_common_ptr) cinfo);
219  }
220#endif
221
222  if (cinfo->output_components == 1) {
223    for (row = cinfo->output_height-1; row >= 0; row--) {
224      rle_row = (rle_pixel **) (*cinfo->mem->access_virt_sarray)
225        ((j_common_ptr) cinfo, dest->image,
226	 (JDIMENSION) row, (JDIMENSION) 1, FALSE);
227      rle_putrow(rle_row, (int) cinfo->output_width, &header);
228#ifdef PROGRESS_REPORT
229      if (progress != NULL) {
230        progress->pub.pass_counter++;
231        (*progress->pub.progress_monitor) ((j_common_ptr) cinfo);
232      }
233#endif
234    }
235  } else {
236    for (row = cinfo->output_height-1; row >= 0; row--) {
237      rle_row = (rle_pixel **) dest->rle_row;
238      output_row = * (*cinfo->mem->access_virt_sarray)
239        ((j_common_ptr) cinfo, dest->image,
240	 (JDIMENSION) row, (JDIMENSION) 1, FALSE);
241      red = rle_row[0];
242      green = rle_row[1];
243      blue = rle_row[2];
244      for (col = cinfo->output_width; col > 0; col--) {
245        *red++ = GETJSAMPLE(*output_row++);
246        *green++ = GETJSAMPLE(*output_row++);
247        *blue++ = GETJSAMPLE(*output_row++);
248      }
249      rle_putrow(rle_row, (int) cinfo->output_width, &header);
250#ifdef PROGRESS_REPORT
251      if (progress != NULL) {
252        progress->pub.pass_counter++;
253        (*progress->pub.progress_monitor) ((j_common_ptr) cinfo);
254      }
255#endif
256    }
257  }
258
259#ifdef PROGRESS_REPORT
260  if (progress != NULL)
261    progress->completed_extra_passes++;
262#endif
263
264  /* Emit file trailer */
265  rle_puteof(&header);
266  fflush(dest->pub.output_file);
267  if (ferror(dest->pub.output_file))
268    ERREXIT(cinfo, JERR_FILE_WRITE);
269}
270
271
272/*
273 * The module selection routine for RLE format output.
274 */
275
276GLOBAL(djpeg_dest_ptr)
277jinit_write_rle (j_decompress_ptr cinfo)
278{
279  rle_dest_ptr dest;
280
281  /* Create module interface object, fill in method pointers */
282  dest = (rle_dest_ptr)
283      (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
284                                  SIZEOF(rle_dest_struct));
285  dest->pub.start_output = start_output_rle;
286  dest->pub.finish_output = finish_output_rle;
287
288  /* Calculate output image dimensions so we can allocate space */
289  jpeg_calc_output_dimensions(cinfo);
290
291  /* Allocate a work array for output to the RLE library. */
292  dest->rle_row = (*cinfo->mem->alloc_sarray)
293    ((j_common_ptr) cinfo, JPOOL_IMAGE,
294     cinfo->output_width, (JDIMENSION) cinfo->output_components);
295
296  /* Allocate a virtual array to hold the image. */
297  dest->image = (*cinfo->mem->request_virt_sarray)
298    ((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE,
299     (JDIMENSION) (cinfo->output_width * cinfo->output_components),
300     cinfo->output_height, (JDIMENSION) 1);
301
302  return (djpeg_dest_ptr) dest;
303}
304
305#endif /* RLE_SUPPORTED */
306