summaryrefslogtreecommitdiff
path: root/jquant1.c
diff options
context:
space:
mode:
authorThomas G. Lane <tgl@netcom.com>1991-10-07 00:00:00 +0000
committerDRC <information@libjpeg-turbo.org>2015-07-29 15:18:11 -0500
commit2cbeb8abd92d5ad8a1bd415b51b3816213b15f31 (patch)
tree1d154fe137ed58037b3004b593dec05063896dd6 /jquant1.c
The Independent JPEG Group's JPEG software v1
Diffstat (limited to 'jquant1.c')
-rw-r--r--jquant1.c387
1 files changed, 387 insertions, 0 deletions
diff --git a/jquant1.c b/jquant1.c
new file mode 100644
index 0000000..49dfd97
--- /dev/null
+++ b/jquant1.c
@@ -0,0 +1,387 @@
+/*
+ * jquant1.c
+ *
+ * Copyright (C) 1991, 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 1-pass color quantization (color mapping) routines.
+ * These routines are invoked via the methods color_quantize
+ * and color_quant_init/term.
+ */
+
+#include "jinclude.h"
+
+#ifdef QUANT_1PASS_SUPPORTED
+
+
+/*
+ * This implementation is a fairly dumb, quick-and-dirty quantizer;
+ * it's here mostly so that we can start working on colormapped output formats.
+ *
+ * We quantize to a color map that is selected in advance of seeing the image;
+ * the map depends only on the requested number of colors (at least 8).
+ * The map consists of all combinations of Ncolors[j] color values for each
+ * component j; we choose Ncolors[] based on the requested # of colors.
+ * We always use 0 and MAXJSAMPLE in each color (hence the minimum 8 colors).
+ * Any additional color values are equally spaced between these limits.
+ *
+ * The result almost always needs dithering to look decent.
+ */
+
+#define MAX_COMPONENTS 4 /* max components I can handle */
+
+static int total_colors; /* Number of distinct output colors */
+static int Ncolors[MAX_COMPONENTS]; /* # of values alloced to each component */
+/* total_colors is the product of the Ncolors[] values */
+
+static JSAMPARRAY colormap; /* The actual color map */
+/* colormap[i][j] = value of i'th color component for output pixel value j */
+
+static JSAMPARRAY colorindex; /* Precomputed mapping for speed */
+/* colorindex[i][j] = index of color closest to pixel value j in component i,
+ * premultiplied so that the correct mapped value for a pixel (r,g,b) is:
+ * colorindex[0][r] + colorindex[1][g] + colorindex[2][b]
+ */
+
+
+/* Declarations for Floyd-Steinberg dithering.
+ * Errors are accumulated into the arrays evenrowerrs[] and oddrowerrs[],
+ * each of which have #colors * (#columns + 2) entries (so that first/last
+ * pixels need not be special cases). These have resolutions of 1/16th of
+ * a pixel count. The error at a given pixel is propagated to its unprocessed
+ * neighbors using the standard F-S fractions,
+ * ... (here) 7/16
+ * 3/16 5/16 1/16
+ * We work left-to-right on even rows, right-to-left on odd rows.
+ *
+ * Note: on a wide image, we might not have enough room in a PC's near data
+ * segment to hold the error arrays; so they are allocated with alloc_medium.
+ */
+
+#ifdef EIGHT_BIT_SAMPLES
+typedef short FSERROR; /* 16 bits should be enough */
+#else
+typedef INT32 FSERROR; /* may need more than 16 bits? */
+#endif
+
+typedef FSERROR FAR *FSERRPTR; /* pointer to error array (in FAR storage!) */
+
+static FSERRPTR evenrowerrs, oddrowerrs; /* current-row and next-row errors */
+static boolean on_odd_row; /* flag to remember which row we are on */
+
+
+/*
+ * Initialize for one-pass color quantization.
+ */
+
+METHODDEF void
+color_quant_init (decompress_info_ptr cinfo)
+{
+ int max_colors = cinfo->desired_number_of_colors;
+ int i,j,k, ntc, nci, blksize, blkdist, ptr, val;
+
+ if (cinfo->color_out_comps > MAX_COMPONENTS)
+ ERREXIT1(cinfo->emethods, "Cannot quantize more than %d color components",
+ MAX_COMPONENTS);
+ if (max_colors > (MAXJSAMPLE+1))
+ ERREXIT1(cinfo->emethods, "Cannot request more than %d quantized colors",
+ MAXJSAMPLE+1);
+
+ /* Initialize to 2 color values for each component */
+ total_colors = 1;
+ for (i = 0; i < cinfo->color_out_comps; i++) {
+ Ncolors[i] = 2;
+ total_colors *= 2;
+ }
+ if (total_colors > max_colors)
+ ERREXIT1(cinfo->emethods, "Cannot quantize to fewer than %d colors",
+ total_colors);
+
+ /* Increase the number of color values until requested limit is reached. */
+ /* Note that for standard RGB color space, we will have at least as many */
+ /* red values as green, and at least as many green values as blue. */
+ i = 0; /* component index to increase next */
+ /* test calculates ntc = new total_colors if Ncolors[i] is incremented */
+ while ((ntc = (total_colors / Ncolors[i]) * (Ncolors[i]+1)) <= max_colors) {
+ Ncolors[i]++; /* OK, apply the increment */
+ total_colors = ntc;
+ i++; /* advance to next component */
+ if (i >= cinfo->color_out_comps) i = 0;
+ }
+
+ /* Report selected color counts */
+ if (cinfo->color_out_comps == 3)
+ TRACEMS4(cinfo->emethods, 1, "Quantizing to %d = %d*%d*%d colors",
+ total_colors, Ncolors[0], Ncolors[1], Ncolors[2]);
+ else
+ TRACEMS1(cinfo->emethods, 1, "Quantizing to %d colors", total_colors);
+
+ /* Allocate and fill in the colormap and color index. */
+ /* The colors are ordered in the map in standard row-major order, */
+ /* i.e. rightmost (highest-indexed) color changes most rapidly. */
+
+ colormap = (*cinfo->emethods->alloc_small_sarray)
+ ((long) total_colors, (long) cinfo->color_out_comps);
+ colorindex = (*cinfo->emethods->alloc_small_sarray)
+ ((long) (MAXJSAMPLE+1), (long) cinfo->color_out_comps);
+
+ /* blksize is number of adjacent repeated entries for a component */
+ /* blkdist is distance between groups of identical entries for a component */
+ blkdist = total_colors;
+
+ for (i = 0; i < cinfo->color_out_comps; i++) {
+ /* fill in colormap entries for i'th color component */
+ nci = Ncolors[i]; /* # of distinct values for this color */
+ blksize = blkdist / nci;
+ for (j = 0; j < nci; j++) {
+ val = (j * MAXJSAMPLE + (nci-1)/2) / (nci-1); /* j'th value of color */
+ for (ptr = j * blksize; ptr < total_colors; ptr += blkdist) {
+ /* fill in blksize entries beginning at ptr */
+ for (k = 0; k < blksize; k++)
+ colormap[i][ptr+k] = val;
+ }
+ }
+ blkdist = blksize; /* blksize of this color is blkdist of next */
+
+ /* fill in colorindex entries for i'th color component */
+ for (j = 0; j <= MAXJSAMPLE; j++) {
+ /* compute index of color closest to pixel value j */
+ val = (j * (nci-1) + CENTERJSAMPLE) / MAXJSAMPLE;
+ /* premultiply so that no multiplication needed in main processing */
+ val *= blksize;
+ colorindex[i][j] = val;
+ }
+ }
+
+ /* Pass the colormap to the output module. Note that the output */
+ /* module is allowed to save this pointer and use the map during */
+ /* any put_pixel_rows call! */
+ (*cinfo->methods->put_color_map) (cinfo, total_colors, colormap);
+
+ /* Allocate Floyd-Steinberg workspace if necessary */
+ if (cinfo->use_dithering) {
+ size_t arraysize = (cinfo->image_width + 2L) * cinfo->color_out_comps
+ * SIZEOF(FSERROR);
+
+ evenrowerrs = (FSERRPTR) (*cinfo->emethods->alloc_medium) (arraysize);
+ oddrowerrs = (FSERRPTR) (*cinfo->emethods->alloc_medium) (arraysize);
+ /* we only need to zero the forward contribution for current row. */
+ jzero_far((void FAR *) evenrowerrs, arraysize);
+ on_odd_row = FALSE;
+ }
+
+}
+
+
+/*
+ * Map some rows of pixels to the output colormapped representation.
+ */
+
+METHODDEF void
+color_quantize (decompress_info_ptr cinfo, int num_rows,
+ JSAMPIMAGE input_data, JSAMPARRAY output_data)
+/* General case, no dithering */
+{
+ register int pixcode, ci;
+ register long col;
+ register int row;
+ register long widthm1 = cinfo->image_width - 1;
+ register int nc = cinfo->color_out_comps;
+
+ for (row = 0; row < num_rows; row++) {
+ for (col = widthm1; col >= 0; col--) {
+ pixcode = 0;
+ for (ci = 0; ci < nc; ci++) {
+ pixcode += GETJSAMPLE(colorindex[ci]
+ [GETJSAMPLE(input_data[ci][row][col])]);
+ }
+ output_data[row][col] = pixcode;
+ }
+ }
+}
+
+
+METHODDEF void
+color_quantize3 (decompress_info_ptr cinfo, int num_rows,
+ JSAMPIMAGE input_data, JSAMPARRAY output_data)
+/* Fast path for color_out_comps==3, no dithering */
+{
+ register int pixcode;
+ register JSAMPROW ptr0, ptr1, ptr2, ptrout;
+ register long col;
+ register int row;
+ register long width = cinfo->image_width;
+
+ for (row = 0; row < num_rows; row++) {
+ ptr0 = input_data[0][row];
+ ptr1 = input_data[1][row];
+ ptr2 = input_data[2][row];
+ ptrout = output_data[row];
+ for (col = width; col > 0; col--) {
+ pixcode = GETJSAMPLE(colorindex[0][GETJSAMPLE(*ptr0++)]);
+ pixcode += GETJSAMPLE(colorindex[1][GETJSAMPLE(*ptr1++)]);
+ pixcode += GETJSAMPLE(colorindex[2][GETJSAMPLE(*ptr2++)]);
+ *ptrout++ = pixcode;
+ }
+ }
+}
+
+
+METHODDEF void
+color_quantize_dither (decompress_info_ptr cinfo, int num_rows,
+ JSAMPIMAGE input_data, JSAMPARRAY output_data)
+/* General case, with Floyd-Steinberg dithering */
+{
+ register int pixcode, ci;
+ register FSERROR val;
+ register FSERRPTR thisrowerr, nextrowerr;
+ register long col;
+ register int row;
+ register long width = cinfo->image_width;
+ register int nc = cinfo->color_out_comps;
+
+ for (row = 0; row < num_rows; row++) {
+ if (on_odd_row) {
+ /* work right to left in this row */
+ thisrowerr = oddrowerrs + width*nc;
+ nextrowerr = evenrowerrs + width*nc;
+ for (ci = 0; ci < nc; ci++) /* need only initialize this one entry */
+ nextrowerr[ci] = 0;
+ for (col = width - 1; col >= 0; col--) {
+ /* select the output pixel value */
+ pixcode = 0;
+ for (ci = 0; ci < nc; ci++) {
+ /* compute pixel value + accumulated error */
+ val = (((FSERROR) GETJSAMPLE(input_data[ci][row][col])) << 4)
+ + thisrowerr[ci];
+ if (val < 0) val = 0; /* must watch for range overflow! */
+ else {
+ val += 8; /* divide by 16 with proper rounding */
+ val >>= 4;
+ if (val > MAXJSAMPLE) val = MAXJSAMPLE;
+ }
+ thisrowerr[ci] = val; /* save for error propagation */
+ pixcode += GETJSAMPLE(colorindex[ci][val]);
+ }
+ output_data[row][col] = pixcode;
+ /* propagate error to adjacent pixels */
+ for (ci = 0; ci < nc; ci++) {
+ val = thisrowerr[ci] - GETJSAMPLE(colormap[ci][pixcode]);
+ thisrowerr[ci-nc] += val * 7;
+ nextrowerr[ci+nc] += val * 3;
+ nextrowerr[ci ] += val * 5;
+ nextrowerr[ci-nc] = val; /* not +=, since not initialized yet */
+ }
+ thisrowerr -= nc; /* advance error ptrs to next pixel entry */
+ nextrowerr -= nc;
+ }
+ on_odd_row = FALSE;
+ } else {
+ /* work left to right in this row */
+ thisrowerr = evenrowerrs + nc;
+ nextrowerr = oddrowerrs + nc;
+ for (ci = 0; ci < nc; ci++) /* need only initialize this one entry */
+ nextrowerr[ci] = 0;
+ for (col = 0; col < width; col++) {
+ /* select the output pixel value */
+ pixcode = 0;
+ for (ci = 0; ci < nc; ci++) {
+ /* compute pixel value + accumulated error */
+ val = (((FSERROR) GETJSAMPLE(input_data[ci][row][col])) << 4)
+ + thisrowerr[ci];
+ if (val < 0) val = 0; /* must watch for range overflow! */
+ else {
+ val += 8; /* divide by 16 with proper rounding */
+ val >>= 4;
+ if (val > MAXJSAMPLE) val = MAXJSAMPLE;
+ }
+ thisrowerr[ci] = val; /* save for error propagation */
+ pixcode += GETJSAMPLE(colorindex[ci][val]);
+ }
+ output_data[row][col] = pixcode;
+ /* propagate error to adjacent pixels */
+ for (ci = 0; ci < nc; ci++) {
+ val = thisrowerr[ci] - GETJSAMPLE(colormap[ci][pixcode]);
+ thisrowerr[ci+nc] += val * 7;
+ nextrowerr[ci-nc] += val * 3;
+ nextrowerr[ci ] += val * 5;
+ nextrowerr[ci+nc] = val; /* not +=, since not initialized yet */
+ }
+ thisrowerr += nc; /* advance error ptrs to next pixel entry */
+ nextrowerr += nc;
+ }
+ on_odd_row = TRUE;
+ }
+ }
+}
+
+
+/*
+ * Finish up at the end of the file.
+ */
+
+METHODDEF void
+color_quant_term (decompress_info_ptr cinfo)
+{
+ /* We can't free the colormap until now, since output module may use it! */
+ (*cinfo->emethods->free_small_sarray)
+ (colormap, (long) cinfo->color_out_comps);
+ (*cinfo->emethods->free_small_sarray)
+ (colorindex, (long) cinfo->color_out_comps);
+ if (cinfo->use_dithering) {
+ (*cinfo->emethods->free_medium) ((void FAR *) evenrowerrs);
+ (*cinfo->emethods->free_medium) ((void FAR *) oddrowerrs);
+ }
+}
+
+
+/*
+ * Prescan some rows of pixels.
+ * Not used in one-pass case.
+ */
+
+METHODDEF void
+color_quant_prescan (decompress_info_ptr cinfo, int num_rows,
+ JSAMPIMAGE image_data)
+{
+ ERREXIT(cinfo->emethods, "Should not get here!");
+}
+
+
+/*
+ * Do two-pass quantization.
+ * Not used in one-pass case.
+ */
+
+METHODDEF void
+color_quant_doit (decompress_info_ptr cinfo, quantize_caller_ptr source_method)
+{
+ ERREXIT(cinfo->emethods, "Should not get here!");
+}
+
+
+/*
+ * The method selection routine for 1-pass color quantization.
+ */
+
+GLOBAL void
+jsel1quantize (decompress_info_ptr cinfo)
+{
+ if (! cinfo->two_pass_quantize) {
+ cinfo->methods->color_quant_init = color_quant_init;
+ if (cinfo->use_dithering) {
+ cinfo->methods->color_quantize = color_quantize_dither;
+ } else {
+ if (cinfo->color_out_comps == 3)
+ cinfo->methods->color_quantize = color_quantize3;
+ else
+ cinfo->methods->color_quantize = color_quantize;
+ }
+ cinfo->methods->color_quant_prescan = color_quant_prescan;
+ cinfo->methods->color_quant_doit = color_quant_doit;
+ cinfo->methods->color_quant_term = color_quant_term;
+ }
+}
+
+#endif /* QUANT_1PASS_SUPPORTED */