/* * jquant1.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 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 /* * The main purpose of 1-pass quantization is to provide a fast, if not very * high quality, colormapped output capability. A 2-pass quantizer usually * gives better visual quality; however, for quantized grayscale output this * quantizer is perfectly adequate. Dithering is highly recommended with this * quantizer, though you can turn it off if you really want to. * * This implementation quantizes in the output colorspace. This has a couple * of disadvantages: each pixel must be individually color-converted, and if * the color conversion includes gamma correction then quantization is done in * a nonlinear space, which is less desirable. The major advantage is that * with the usual output color spaces (RGB, grayscale) an orthogonal grid of * representative colors can be used, thus permitting the very simple and fast * color lookup scheme used here. The standard JPEG colorspace (YCbCr) cannot * be effectively handled this way, because only about a quarter of an * orthogonal grid would fall within the gamut of realizable colors. Another * advantage is that when the user wants quantized grayscale output from a * color JPEG file, this quantizer can provide a high-quality result with no * special hacking. * * The gamma-correction problem could be eliminated by adjusting the grid * spacing to counteract the gamma correction applied by color_convert. * At this writing, gamma correction is not implemented by jdcolor, so * nothing is done here. * * In 1-pass quantization the colormap must be chosen in advance of seeing the * image. We use a map consisting of all combinations of Ncolors[i] color * values for the i'th component. The Ncolors[] values are chosen so that * their product, the total number of colors, is no more than that requested. * (In most cases, the product will be somewhat less.) * * Since the colormap is orthogonal, the representative value for each color * component can be determined without considering the other components; * then these indexes can be combined into a colormap index by a standard * N-dimensional-array-subscript calculation. Most of the arithmetic involved * can be precalculated and stored in the lookup table colorindex[]. * colorindex[i][j] maps pixel value j in component i to the nearest * representative value (grid plane) for that component; this index is * multiplied by the array stride for component i, so that the * index of the colormap entry closest to a given pixel value is just * sum( colorindex[component-number][pixel-component-value] ) * Aside from being fast, this scheme allows for variable spacing between * representative values with no additional lookup cost. */ #define MAX_COMPONENTS 4 /* max components I can handle */ 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 as described above. Since colormap indexes must fit into * JSAMPLEs, the entries of this array will too. */ static JSAMPARRAY input_buffer; /* color conversion workspace */ /* Since our input data is presented in the JPEG colorspace, we have to call * color_convert to get it into the output colorspace. input_buffer is a * one-row-high workspace for the result of color_convert. */ /* Declarations for Floyd-Steinberg dithering. * * Errors are accumulated into the arrays evenrowerrs[] and oddrowerrs[]. * 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. * * In each of the xxxrowerrs[] arrays, indexing is [component#][position]. * We provide (#columns + 2) entries per component; the extra entry at each * end saves us from special-casing the first and last pixels. * In evenrowerrs[], the entries for a component are stored left-to-right, but * in oddrowerrs[] they are stored right-to-left. This means we always * process the current row's error entries in increasing order and the next * row's error entries in decreasing order, regardless of whether we are * working L-to-R or R-to-L in the pixel data! * * 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 INT16 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[MAX_COMPONENTS]; /* errors for even rows */ static FSERRPTR oddrowerrs[MAX_COMPONENTS]; /* errors for odd rows */ static boolean on_odd_row; /* flag to remember which row we are on */ /* * Policy-making subroutines for color_quant_init: these routines determine * the colormap to be used. The rest of the module only assumes that the * colormap is orthogonal. * * * select_ncolors decides how to divvy up the available colors * among the components. * * output_value defines the set of representative values for a component. * * largest_input_value defines the mapping from input values to * representative values for a component. * Note that the latter two routines may impose different policies for * different components, though this is not currently done. */ LOCAL int select_ncolors (decompress_info_ptr cinfo, int Ncolors[]) /* Determine allocation of desired colors to components, */ /* and fill in Ncolors[] array to indicate choice. */ /* Return value is total number of colors (product of Ncolors[] values). */ { int nc = cinfo->color_out_comps; /* number of color components */ int max_colors = cinfo->desired_number_of_colors; int total_colors, iroot, i; long temp; boolean changed; /* We can allocate at least the nc'th root of max_colors per component. */ /* Compute floor(nc'th root of max_colors). */ iroot = 1; do { iroot++; temp = iroot; /* set temp = iroot ** nc */ for (i = 1; i < nc; i++) temp *= iroot; } while (temp <= (long) max_colors); /* repeat till iroot exceeds root */ iroot--; /* now iroot = floor(root) */ /* Must have at least 2 color values per component */ if (iroot < 2) ERREXIT1(cinfo->emethods, "Cannot quantize to fewer than %d colors", (int) temp); if (cinfo->out_color_space == CS_RGB && nc == 3) { /* We provide a special policy for quantizing in RGB space. * If 256 colors are requested, we allocate 8 red, 8 green, 4 blue levels; * this corresponds to the common 3/3/2-bit scheme. For other totals, * the counts are set so that the number of colors allocated to each * component are roughly in the proportion R 3, G 4, B 2. * For low color counts, it's easier to hardwire the optimal choices * than try to tweak the algorithm to generate them. */ if (max_colors == 256) { Ncolors[0] = 8; Ncolors[1] = 8; Ncolors[2] = 4; return 256; } if (max_colors < 12) { /* Fixed mapping for 8 colors */ Ncolors[0] = Ncolors[1] = Ncolors[2] = 2; } else if (max_colors < 18) { /* Fixed mapping for 12 colors */ Ncolors[0] = 2; Ncolors[1] = 3; Ncolors[2] = 2; } else if (max_colors < 24) { /* Fixed mapping for 18 colors */ Ncolors[0] = 3; Ncolors[1] = 3; Ncolors[2] = 2; } else if (max_colors < 27) { /* Fixed mapping for 24 colors */ Ncolors[0] = 3; Ncolors[1] = 4; Ncolors[2] = 2; } else if (max_colors < 36) { /* Fixed mapping for 27 colors */ Ncolors[0] = 3; Ncolors[1] = 3; Ncolors[2] = 3; } else { /* these weights are readily derived with a little algebra */ Ncolors[0] = (iroot * 266) >> 8; /* R weight is 1.0400 */ Ncolors[1] = (iroot * 355) >> 8; /* G weight is 1.3867 */ Ncolors[2] = (iroot * 177) >> 8; /* B weight is 0.6934 */ } total_colors = Ncolors[0] * Ncolors[1] * Ncolors[2]; /* The above computation produces "floor" values, so we may be able to * increment the count for one or more components without exceeding * max_colors. We try in the order B, G, R. */ do { changed = FALSE; for (i = 2; i >= 0; i--) { /* calculate new total_colors if Ncolors[i] is incremented */ temp = total_colors / Ncolors[i]; temp *= Ncolors[i]+1; /* done in long arith to avoid oflo */ if (temp <= (long) max_colors) { Ncolors[i]++; /* OK, apply the increment */ total_colors = (int) temp; changed = TRUE; } } } while (changed); /* loop until no increment is possible */ } else { /* For any colorspace besides RGB, treat all the components equally. */ /* Initialize to iroot color values for each component */ total_colors = 1; for (i = 0; i < nc; i++) { Ncolors[i] = iroot; total_colors *= iroot; } /* We may be able to increment the count for one or more components without * exceeding max_colors, though we know not all can be incremented. */ for (i = 0; i < nc; i++) { /* calculate new total_colors if Ncolors[i] is incremented */ temp = total_colors / Ncolors[i]; temp *= Ncolors[i]+1; /* done in long arith to avoid oflo */ if (temp > (long) max_colors) break; /* won't fit, done */ Ncolors[i]++; /* OK, apply the increment */ total_colors = (int) temp; } } return total_colors; } LOCAL int output_value (decompress_info_ptr cinfo, int ci, int j, int maxj) /* Return j'th output value, where j will range from 0 to maxj */ /* The output values must fall in 0..MAXJSAMPLE in increasing order */ { /* We always provide values 0 and MAXJSAMPLE for each component; * any additional values are equally spaced between these limits. * (Forcing the upper and lower values to the limits ensures that * dithering can't produce a color outside the selected gamut.) */ return (int) (((INT32) j * MAXJSAMPLE + maxj/2) / maxj); } LOCAL int largest_input_value (decompress_info_ptr cinfo, int ci, int j, int maxj) /* Return largest input value that should map to j'th output value */ /* Must have largest(j=0) >= 0, and largest(j=maxj) >= MAXJSAMPLE */ { /* Breakpoints are halfway between values returned by output_value */ return (int) (((INT32) (2*j + 1) * MAXJSAMPLE + maxj) / (2*maxj)); } /* * Initialize for one-pass color quantization. */ METHODDEF void color_quant_init (decompress_info_ptr cinfo) { int total_colors; /* Number of distinct output colors */ int Ncolors[MAX_COMPONENTS]; /* # of values alloced to each component */ int i,j,k, nci, blksize, blkdist, ptr, val; /* Make sure my internal arrays won't overflow */ if (cinfo->num_components > MAX_COMPONENTS || cinfo->color_out_comps > MAX_COMPONENTS) ERREXIT1(cinfo->emethods, "Cannot quantize more than %d color components", MAX_COMPONENTS); /* Make sure colormap indexes can be represented by JSAMPLEs */ if (cinfo->desired_number_of_colors > (MAXJSAMPLE+1)) ERREXIT1(cinfo->emethods, "Cannot request more than %d quantized colors", MAXJSAMPLE+1); /* Select number of colors for each component */ total_colors = select_ncolors(cinfo, Ncolors); /* 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++) { /* Compute j'th output value (out of nci) for component */ val = output_value(cinfo, i, j, nci-1); /* Fill in all colormap entries that have this value of this component */ 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] = (JSAMPLE) val; } } blkdist = blksize; /* blksize of this color is blkdist of next */ /* fill in colorindex entries for i'th color component */ /* in loop, val = index of current output value, */ /* and k = largest j that maps to current val */ val = 0; k = largest_input_value(cinfo, i, 0, nci-1); for (j = 0; j <= MAXJSAMPLE; j++) { while (j > k) /* advance val if past boundary */ k = largest_input_value(cinfo, i, ++val, nci-1); /* premultiply so that no multiplication needed in main processing */ colorindex[i][j] = (JSAMPLE) (val * blksize); } } /* Pass the colormap to the output module. */ /* NB: the output module may continue to use the colormap until shutdown. */ cinfo->colormap = colormap; cinfo->actual_number_of_colors = total_colors; (*cinfo->methods->put_color_map) (cinfo, total_colors, colormap); /* Allocate workspace to hold one row of color-converted data */ input_buffer = (*cinfo->emethods->alloc_small_sarray) (cinfo->image_width, (long) cinfo->color_out_comps); /* Allocate Floyd-Steinberg workspace if necessary */ if (cinfo->use_dithering) { size_t arraysize = (size_t) ((cinfo->image_width + 2L) * SIZEOF(FSERROR)); for (i = 0; i < cinfo->color_out_comps; i++) { evenrowerrs[i] = (FSERRPTR) (*cinfo->emethods->alloc_medium) (arraysize); oddrowerrs[i] = (FSERRPTR) (*cinfo->emethods->alloc_medium) (arraysize); /* we only need to zero the forward contribution for current row. */ jzero_far((void FAR *) evenrowerrs[i], arraysize); } on_odd_row = FALSE; } } /* * Subroutines for color conversion methods. */ LOCAL void do_color_conversion (decompress_info_ptr cinfo, JSAMPIMAGE input_data, int row) /* Convert the indicated row of the input data into output colorspace */ /* in input_buffer. This requires a little trickery since color_convert */ /* expects to deal with 3-D arrays; fortunately we can fake it out */ /* at fairly low cost. */ { short ci; JSAMPARRAY input_hack[MAX_COMPONENTS]; JSAMPARRAY output_hack[MAX_COMPONENTS]; /* create JSAMPIMAGE pointing at specified row of input_data */ for (ci = 0; ci < cinfo->num_components; ci++) input_hack[ci] = input_data[ci] + row; /* Create JSAMPIMAGE pointing at input_buffer */ for (ci = 0; ci < cinfo->color_out_comps; ci++) output_hack[ci] = &(input_buffer[ci]); (*cinfo->methods->color_convert) (cinfo, 1, cinfo->image_width, input_hack, output_hack); } /* * 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 JSAMPROW ptrout; register long col; int row; long width = cinfo->image_width; register int nc = cinfo->color_out_comps; for (row = 0; row < num_rows; row++) { do_color_conversion(cinfo, input_data, row); ptrout = output_data[row]; for (col = 0; col < width; col++) { pixcode = 0; for (ci = 0; ci < nc; ci++) { pixcode += GETJSAMPLE(colorindex[ci] [GETJSAMPLE(input_buffer[ci][col])]); } *ptrout++ = (JSAMPLE) 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; int row; long width = cinfo->image_width; for (row = 0; row < num_rows; row++) { do_color_conversion(cinfo, input_data, row); ptr0 = input_buffer[0]; ptr1 = input_buffer[1]; ptr2 = input_buffer[2]; 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++ = (JSAMPLE) 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 FSERROR val; FSERROR two_val; register FSERRPTR thisrowerr, nextrowerr; register JSAMPROW input_ptr; register JSAMPROW output_ptr; JSAMPLE *range_limit = cinfo->sample_range_limit; JSAMPROW colorindex_ci; JSAMPROW colormap_ci; register int pixcode; int dir; /* 1 for left-to-right, -1 for right-to-left */ int ci; int nc = cinfo->color_out_comps; int row; long col_counter; long width = cinfo->image_width; SHIFT_TEMPS for (row = 0; row < num_rows; row++) { do_color_conversion(cinfo, input_data, row); /* Initialize output values to 0 so can process components separately */ jzero_far((void FAR *) output_data[row], (size_t) (width * SIZEOF(JSAMPLE))); for (ci = 0; ci < nc; ci++) { if (on_odd_row) { /* work right to left in this row */ dir = -1; input_ptr = input_buffer[ci] + (width-1); output_ptr = output_data[row] + (width-1); thisrowerr = oddrowerrs[ci] + 1; nextrowerr = evenrowerrs[ci] + width; } else { /* work left to right in this row */ dir = 1; input_ptr = input_buffer[ci]; output_ptr = output_data[row]; thisrowerr = evenrowerrs[ci] + 1; nextrowerr = oddrowerrs[ci] + width; } colorindex_ci = colorindex[ci]; colormap_ci = colormap[ci]; *nextrowerr = 0; /* need only initialize this one entry */ for (col_counter = width; col_counter > 0; col_counter--) { /* Get accumulated error for this component, round to integer. * RIGHT_SHIFT rounds towards minus infinity, so adding 8 is correct * for either sign of the error value. */ val = RIGHT_SHIFT(*thisrowerr + 8, 4); /* Compute pixel value + error compensation, range-limit to * 0..MAXJSAMPLE. Note max error value is +- MAXJSAMPLE. */ val = GETJSAMPLE(range_limit[GETJSAMPLE(*input_ptr) + val]); /* Select output value, accumulate into output code for this pixel */ pixcode = GETJSAMPLE(*output_ptr) + GETJSAMPLE(colorindex_ci[val]); *output_ptr = (JSAMPLE) pixcode; /* Compute actual representation error at this pixel */ /* Note: we can do this even though we don't yet have the final */ /* value of pixcode, because the colormap is orthogonal. */ val -= GETJSAMPLE(colormap_ci[pixcode]); /* Propagate error to (same component of) adjacent pixels */ /* Remember that nextrowerr entries are in reverse order! */ two_val = val * 2; nextrowerr[-1] = val; /* not +=, since not initialized yet */ val += two_val; /* form error * 3 */ nextrowerr[ 1] += val; val += two_val; /* form error * 5 */ nextrowerr[ 0] += val; val += two_val; /* form error * 7 */ thisrowerr[ 1] += val; input_ptr += dir; /* advance input ptr to next column */ output_ptr += dir; /* advance output ptr to next column */ thisrowerr++; /* cur-row error ptr advances to right */ nextrowerr--; /* next-row error ptr advances to left */ } } on_odd_row = (on_odd_row ? FALSE : TRUE); } } /* * Finish up at the end of the file. */ METHODDEF void color_quant_term (decompress_info_ptr cinfo) { /* no work (we let free_all release the workspace) */ /* Note that we *mustn't* free the colormap before free_all, */ /* since output module may use it! */ } /* * 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, JSAMPARRAY workspace) { 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 */