libjpeg-turbo/jquant1.c

389 lines
13 KiB
C
Raw Normal View History

/*
* 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 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, 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++) {
/* Compute j'th output value (out of nci) for component */
val = (j * MAXJSAMPLE + (nci-1)/2) / (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 */
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 */
colorindex[i][j] = (JSAMPLE) (val * blksize);
}
}
/* 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] = (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;
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++ = (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 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] = (JSAMPLE) pixcode;
/* propagate error to adjacent pixels */
for (ci = 0; ci < nc; ci++) {
val = thisrowerr[ci] - (FSERROR) 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] = (JSAMPLE) pixcode;
/* propagate error to adjacent pixels */
for (ci = 0; ci < nc; ci++) {
val = thisrowerr[ci] - (FSERROR) 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 */