399 lines
13 KiB
C++
399 lines
13 KiB
C++
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
*
|
|
* The contents of this file are subject to the Netscape Public
|
|
* License Version 1.1 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.mozilla.org/NPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS
|
|
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
|
* implied. See the License for the specific language governing
|
|
* rights and limitations under the License.
|
|
*
|
|
* The Original Code is mozilla.org code.
|
|
*
|
|
* The Initial Developer of the Original Code is Netscape
|
|
* Communications Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1998 Netscape Communications Corporation. All
|
|
* Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
*/
|
|
|
|
#include "if.h"
|
|
#include "il.h"
|
|
|
|
/* This is a lame hack to get around a problem with boolean on Solaris 2.3 */
|
|
#if defined(__sun) && defined(__sparc) && defined(__svr4__) && (OSMINOR == 3)
|
|
#define HAVE_BOOLEAN
|
|
#undef MUST_UNDEF_HAVE_BOOLEAN_AFTER_INCLUDES
|
|
#endif
|
|
|
|
#include "jpeglib.h"
|
|
#include "jerror.h"
|
|
#include "nsCRT.h"
|
|
|
|
/* cope with brain-damaged compilers that don't make sizeof return a size_t */
|
|
#ifdef SIZEOF
|
|
#undef SIZEOF
|
|
#endif
|
|
#define SIZEOF(object) ((size_t) sizeof(object))
|
|
|
|
/* just in case someone needs it */
|
|
#ifdef RIGHT_SHIFT_IS_UNSIGNED
|
|
#define SHIFT_TEMPS INT32 shift_temp;
|
|
#define RIGHT_SHIFT(x,shft) \
|
|
((shift_temp = (x)) < 0 ? \
|
|
(shift_temp >> (shft)) | ((~((INT32) 0)) << (32-(shft))) : \
|
|
(shift_temp >> (shft)))
|
|
#else
|
|
#define SHIFT_TEMPS
|
|
#define RIGHT_SHIFT(x,shft) ((x) >> (shft))
|
|
#endif
|
|
|
|
|
|
/* BEGIN code adapted from jpeg library */
|
|
|
|
typedef INT32 FSERROR; /* 16 bits should be enough */
|
|
typedef int LOCFSERROR; /* use 'int' for calculation temps */
|
|
|
|
typedef FSERROR FAR *FSERRPTR; /* pointer to error array (in FAR storage!) */
|
|
|
|
typedef struct my_cquantize_str {
|
|
/* Variables for Floyd-Steinberg dithering */
|
|
FSERRPTR fserrors[3]; /* accumulated errors */
|
|
boolean on_odd_row; /* flag to remember which row we are on */
|
|
} my_cquantize;
|
|
|
|
typedef my_cquantize *my_cquantize_ptr;
|
|
|
|
static JSAMPLE *the_sample_range_limit = NULL;
|
|
|
|
/* allocate and fill in the sample_range_limit table */
|
|
int
|
|
il_setup_quantize(void)
|
|
{
|
|
JSAMPLE *table;
|
|
int i;
|
|
|
|
if(the_sample_range_limit)
|
|
return TRUE;
|
|
|
|
/* lost for ever */
|
|
table = (JSAMPLE *)PR_MALLOC((5 * (MAXJSAMPLE+1) + CENTERJSAMPLE) * SIZEOF(JSAMPLE));
|
|
if (!table)
|
|
{
|
|
ILTRACE(1,("il: range limit table lossage"));
|
|
return FALSE;
|
|
}
|
|
|
|
table += (MAXJSAMPLE+1); /* allow negative subscripts of simple table */
|
|
the_sample_range_limit = table;
|
|
|
|
/* First segment of "simple" table: limit[x] = 0 for x < 0 */
|
|
nsCRT::zero(table - (MAXJSAMPLE+1), (MAXJSAMPLE+1) * SIZEOF(JSAMPLE));
|
|
|
|
/* Main part of "simple" table: limit[x] = x */
|
|
for (i = 0; i <= MAXJSAMPLE; i++)
|
|
table[i] = (JSAMPLE) i;
|
|
|
|
table += CENTERJSAMPLE; /* Point to where post-IDCT table starts */
|
|
|
|
/* End of simple table, rest of first half of post-IDCT table */
|
|
for (i = CENTERJSAMPLE; i < 2*(MAXJSAMPLE+1); i++)
|
|
table[i] = MAXJSAMPLE;
|
|
|
|
/* Second half of post-IDCT table */
|
|
nsCRT::zero(table + (2 * (MAXJSAMPLE+1)),
|
|
(2 * (MAXJSAMPLE+1) - CENTERJSAMPLE) * SIZEOF(JSAMPLE));
|
|
nsCRT::memcpy(table + (4 * (MAXJSAMPLE+1) - CENTERJSAMPLE),
|
|
the_sample_range_limit, CENTERJSAMPLE * SIZEOF(JSAMPLE));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/* Must remain idempotent. Also used to make sure that the ic->quantize has the same
|
|
colorSpace info as the rest of ic. */
|
|
int
|
|
il_init_quantize(il_container *ic)
|
|
{
|
|
size_t arraysize;
|
|
int i, j;
|
|
my_cquantize_ptr cquantize;
|
|
|
|
if (ic->quantize)
|
|
il_free_quantize(ic);
|
|
|
|
ic->quantize = PR_NEWZAP(my_cquantize);
|
|
if (!ic->quantize)
|
|
{
|
|
loser:
|
|
ILTRACE(0,("il: MEM il_init_quantize"));
|
|
return FALSE;
|
|
}
|
|
|
|
cquantize = (my_cquantize_ptr) ic->quantize;
|
|
arraysize = (size_t) ((ic->image->header.width + 2) * SIZEOF(FSERROR));
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
cquantize->fserrors[i] = (FSERRPTR) PR_Calloc(1, arraysize);
|
|
if (!cquantize->fserrors[i])
|
|
{
|
|
/* ran out of memory part way thru */
|
|
for (j = 0; j < i; j++)
|
|
{
|
|
if (cquantize->fserrors[j])
|
|
{
|
|
PR_FREEIF(cquantize->fserrors[j]);
|
|
cquantize->fserrors[j]=0;
|
|
}
|
|
}
|
|
if (cquantize)
|
|
{
|
|
PR_FREEIF(cquantize);
|
|
ic->quantize = 0;
|
|
}
|
|
goto loser;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
** Free up quantizer information attached to ic. If this is the last
|
|
** quantizer then free up the sample range limit table.
|
|
*/
|
|
void
|
|
il_free_quantize(il_container *ic)
|
|
{
|
|
my_cquantize_ptr cquantize = (my_cquantize_ptr) ic->quantize;
|
|
int i;
|
|
|
|
if (cquantize)
|
|
{
|
|
#ifdef DEBUG
|
|
if (il_debug > 5)
|
|
ILTRACE(1,("il: 0x%x: free quantize", ic));
|
|
#endif
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
if (cquantize->fserrors[i])
|
|
{
|
|
PR_FREEIF(cquantize->fserrors[i]);
|
|
cquantize->fserrors[i] = 0;
|
|
}
|
|
}
|
|
|
|
PR_FREEIF(cquantize);
|
|
ic->quantize = 0;
|
|
}
|
|
}
|
|
|
|
|
|
/* floyd-steinberg dithering */
|
|
|
|
#ifdef XP_MAC
|
|
#ifndef powerc
|
|
#pragma peephole on
|
|
#endif
|
|
#endif
|
|
|
|
void
|
|
il_quantize_fs_dither(il_container *ic, const PRUint8 *mask,
|
|
const PRUint8 *input_buf, int x_offset,
|
|
PRUint8 XP_HUGE *output_buf, int width)
|
|
{
|
|
my_cquantize_ptr cquantize;
|
|
register LOCFSERROR r_cur, g_cur, b_cur; /* current error or pixel
|
|
value */
|
|
LOCFSERROR r_belowerr, g_belowerr, b_belowerr; /* error for pixel below
|
|
cur */
|
|
LOCFSERROR r_bpreverr, g_bpreverr, b_bpreverr; /* error for below/prev
|
|
col */
|
|
LOCFSERROR r_bnexterr, g_bnexterr, b_bnexterr; /* error for below/next
|
|
col */
|
|
LOCFSERROR delta;
|
|
FSERRPTR r_errorptr, g_errorptr, b_errorptr; /* fserrors[] at column
|
|
before current */
|
|
const JSAMPLE* input_ptr;
|
|
JSAMPLE XP_HUGE * output_ptr;
|
|
IL_ColorMap *cmap = &ic->image->header.color_space->cmap;
|
|
IL_RGB *map = cmap->map; /* The colormap array. */
|
|
IL_RGB *map_entry; /* Current entry in the colormap. */
|
|
PRUint8 *lookup_table = (PRUint8 *)cmap->table; /* Lookup table for the colormap. */
|
|
const PRUint8 *maskp;
|
|
PRUint8 map_index;
|
|
int dir; /* 1 for left-to-right, -1 for right-to-left */
|
|
JDIMENSION col;
|
|
JSAMPLE *range_limit = the_sample_range_limit;
|
|
SHIFT_TEMPS
|
|
|
|
cquantize = (my_cquantize_ptr) ic->quantize;
|
|
|
|
output_buf += x_offset;
|
|
|
|
/* Initialize output values to 0 so can process components separately */
|
|
if (mask) {
|
|
output_ptr = output_buf;
|
|
maskp = mask;
|
|
for (col = width; col > 0; col--)
|
|
*output_ptr++ &= ~*maskp++;
|
|
} else {
|
|
nsCRT::zero((void XP_HUGE *) output_buf,
|
|
(size_t) (width * SIZEOF(JSAMPLE)));
|
|
}
|
|
|
|
input_ptr = input_buf;
|
|
output_ptr = output_buf;
|
|
maskp = mask;
|
|
if (cquantize->on_odd_row) {
|
|
int total_offset;
|
|
|
|
/* work right to left in this row */
|
|
input_ptr += 3 * width - 1; /* so point to the blue sample of the
|
|
rightmost pixel */
|
|
output_ptr += width-1;
|
|
dir = -1;
|
|
/* => entry after last column */
|
|
total_offset = x_offset + (width + 1);
|
|
r_errorptr = cquantize->fserrors[0] + total_offset;
|
|
g_errorptr = cquantize->fserrors[1] + total_offset;
|
|
b_errorptr = cquantize->fserrors[2] + total_offset;
|
|
maskp += (width - 1);
|
|
}
|
|
else {
|
|
/* work left to right in this row */
|
|
dir = 1;
|
|
/* => entry before first column */
|
|
r_errorptr = cquantize->fserrors[0] + x_offset;
|
|
g_errorptr = cquantize->fserrors[1] + x_offset;
|
|
b_errorptr = cquantize->fserrors[2] + x_offset;
|
|
}
|
|
|
|
/* Preset error values: no error propagated to first pixel from left */
|
|
r_cur = g_cur = b_cur = 0;
|
|
|
|
/* and no error propagated to row below yet */
|
|
r_belowerr = g_belowerr = b_belowerr = 0;
|
|
r_bpreverr = g_bpreverr = b_bpreverr = 0;
|
|
|
|
for (col = width; col > 0; col--) {
|
|
/* cur holds the error propagated from the previous pixel on the
|
|
* current line. Add the error propagated from the previous line
|
|
* to form the complete error correction term for this pixel, and
|
|
* round the error term (which is expressed * 16) to an integer.
|
|
* RIGHT_SHIFT rounds towards minus infinity, so adding 8 is correct
|
|
* for either sign of the error value.
|
|
* Note: errorptr points to *previous* column's array entry.
|
|
*/
|
|
r_cur = RIGHT_SHIFT(r_cur + r_errorptr[dir] + 8, 4);
|
|
g_cur = RIGHT_SHIFT(g_cur + g_errorptr[dir] + 8, 4);
|
|
b_cur = RIGHT_SHIFT(b_cur + b_errorptr[dir] + 8, 4);
|
|
|
|
/* Form pixel value + error, and range-limit to 0..MAXJSAMPLE.
|
|
* The maximum error is +- MAXJSAMPLE; this sets the required size
|
|
* of the range_limit array.
|
|
*/
|
|
if (dir > 0) {
|
|
r_cur += GETJSAMPLE(*input_ptr);
|
|
r_cur = GETJSAMPLE(range_limit[r_cur]);
|
|
input_ptr++;
|
|
g_cur += GETJSAMPLE(*input_ptr);
|
|
g_cur = GETJSAMPLE(range_limit[g_cur]);
|
|
input_ptr++;
|
|
b_cur += GETJSAMPLE(*input_ptr);
|
|
b_cur = GETJSAMPLE(range_limit[b_cur]);
|
|
input_ptr++;
|
|
}
|
|
else {
|
|
b_cur += GETJSAMPLE(*input_ptr);
|
|
b_cur = GETJSAMPLE(range_limit[b_cur]);
|
|
input_ptr--;
|
|
g_cur += GETJSAMPLE(*input_ptr);
|
|
g_cur = GETJSAMPLE(range_limit[g_cur]);
|
|
input_ptr--;
|
|
r_cur += GETJSAMPLE(*input_ptr);
|
|
r_cur = GETJSAMPLE(range_limit[r_cur]);
|
|
input_ptr--;
|
|
}
|
|
|
|
/* Select output value, accumulate into output code for this pixel */
|
|
map_index = COLORMAP_INDEX(lookup_table, r_cur, g_cur, b_cur);
|
|
if (mask) {
|
|
if (*maskp)
|
|
*output_ptr = map_index;
|
|
maskp += dir;
|
|
} else {
|
|
*output_ptr = map_index;
|
|
}
|
|
|
|
/* Compute the actual representation error at this pixel */
|
|
map_entry = map + map_index;
|
|
r_cur -= GETJSAMPLE(map_entry->red);
|
|
g_cur -= GETJSAMPLE(map_entry->green);
|
|
b_cur -= GETJSAMPLE(map_entry->blue);
|
|
|
|
/* Compute error fractions to be propagated to adjacent pixels.
|
|
* Add these into the running sums, and simultaneously shift the
|
|
* next-line error sums left by 1 column.
|
|
*/
|
|
r_bnexterr = r_cur;
|
|
delta = r_cur * 2;
|
|
r_cur += delta; /* form error * 3 */
|
|
r_errorptr[0] = (FSERROR) (r_bpreverr + r_cur);
|
|
r_cur += delta; /* form error * 5 */
|
|
r_bpreverr = r_belowerr + r_cur;
|
|
r_belowerr = r_bnexterr;
|
|
r_cur += delta; /* form error * 7 */
|
|
|
|
g_bnexterr = g_cur;
|
|
delta = g_cur * 2;
|
|
g_cur += delta; /* form error * 3 */
|
|
g_errorptr[0] = (FSERROR) (g_bpreverr + g_cur);
|
|
g_cur += delta; /* form error * 5 */
|
|
g_bpreverr = g_belowerr + g_cur;
|
|
g_belowerr = g_bnexterr;
|
|
g_cur += delta; /* form error * 7 */
|
|
|
|
b_bnexterr = b_cur;
|
|
delta = b_cur * 2;
|
|
b_cur += delta; /* form error * 3 */
|
|
b_errorptr[0] = (FSERROR) (b_bpreverr + b_cur);
|
|
b_cur += delta; /* form error * 5 */
|
|
b_bpreverr = b_belowerr + b_cur;
|
|
b_belowerr = b_bnexterr;
|
|
b_cur += delta; /* form error * 7 */
|
|
|
|
/* At this point cur contains the 7/16 error value to be propagated
|
|
* to the next pixel on the current line, and all the errors for the
|
|
* next line have been shifted over. We are therefore ready to move on.
|
|
* Note: the input_ptr has already been advanced.
|
|
*/
|
|
output_ptr += dir; /* advance output ptr to next column */
|
|
r_errorptr += dir; /* advance errorptr to current column */
|
|
g_errorptr += dir; /* advance errorptr to current column */
|
|
b_errorptr += dir; /* advance errorptr to current column */
|
|
}
|
|
|
|
/* Post-loop cleanup: we must unload the final error value into the
|
|
* final fserrors[] entry. Note we need not unload belowerr because
|
|
* it is for the dummy column before or after the actual array.
|
|
*/
|
|
r_errorptr[0] = (FSERROR) r_bpreverr; /* unload prev err into array */
|
|
g_errorptr[0] = (FSERROR) g_bpreverr; /* unload prev err into array */
|
|
b_errorptr[0] = (FSERROR) b_bpreverr; /* unload prev err into array */
|
|
|
|
cquantize->on_odd_row = (cquantize->on_odd_row ? FALSE : TRUE);
|
|
}
|
|
|
|
#ifdef XP_MAC
|
|
#ifndef powerc
|
|
#pragma peephole reset
|
|
#endif
|
|
#endif
|
|
|
|
/* END code adapted from jpeg library */
|
|
|