blob: 6e61a6ca3efc02ab4dac460d1ee41e2c724cf3b7 [file] [log] [blame]
/*====================================================================*
- Copyright (C) 2001 Leptonica. All rights reserved.
- This software is distributed in the hope that it will be
- useful, but with NO WARRANTY OF ANY KIND.
- No author or distributor accepts responsibility to anyone for the
- consequences of using this software, or for whether it serves any
- particular purpose or works at all, unless he or she says so in
- writing. Everyone is granted permission to copy, modify and
- redistribute this source code, for commercial or non-commercial
- purposes, with the following restrictions: (1) the origin of this
- source code must not be misrepresented; (2) modified versions must
- be plainly marked as such; and (3) this notice may not be removed
- or altered from any source or modified source distribution.
*====================================================================*/
/*
* grayquantlow.c
*
* Thresholding from 8 bpp to 1 bpp
*
* Floyd-Steinberg dithering to binary
* void ditherToBinaryLow()
* void ditherToBinaryLineLow()
*
* Simple (pixelwise) binarization
* void thresholdToBinaryLow()
* void thresholdToBinaryLineLow()
*
* A slower version of Floyd-Steinberg dithering that uses LUTs
* void ditherToBinaryLUTLow()
* void ditherToBinaryLineLUTLow()
* l_int32 make8To1DitherTables()
*
* Thresholding from 8 bpp to 2 bpp
*
* Floyd-Steinberg-like dithering to 2 bpp
* void ditherTo2bppLow()
* void ditherTo2bppLineLow()
* l_int32 make8To2DitherTables()
*
* Simple thresholding to 2 bpp
* void thresholdTo2bppLow()
*
* Thresholding from 8 bpp to 4 bpp
*
* Simple thresholding to 4 bpp
* void thresholdTo4bppLow()
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "allheaders.h"
#ifndef NO_CONSOLE_IO
#define DEBUG_UNROLLING 0
#endif /* ~NO_CONSOLE_IO */
/*------------------------------------------------------------------*
* Binarization by Floyd-Steinberg Dithering *
*------------------------------------------------------------------*/
/*
* ditherToBinaryLow()
*
* See comments in pixDitherToBinary() in binarize.c
*/
void
ditherToBinaryLow(l_uint32 *datad,
l_int32 w,
l_int32 h,
l_int32 wpld,
l_uint32 *datas,
l_int32 wpls,
l_uint32 *bufs1,
l_uint32 *bufs2,
l_int32 lowerclip,
l_int32 upperclip)
{
l_int32 i;
l_uint32 *lined;
/* do all lines except last line */
memcpy(bufs2, datas, 4 * wpls); /* prime the buffer */
for (i = 0; i < h - 1; i++) {
memcpy(bufs1, bufs2, 4 * wpls);
memcpy(bufs2, datas + (i + 1) * wpls, 4 * wpls);
lined = datad + i * wpld;
ditherToBinaryLineLow(lined, w, bufs1, bufs2, lowerclip, upperclip, 0);
}
/* do last line */
memcpy(bufs1, bufs2, 4 * wpls);
lined = datad + (h - 1) * wpld;
ditherToBinaryLineLow(lined, w, bufs1, bufs2, lowerclip, upperclip, 1);
return;
}
/*
* ditherToBinaryLineLow()
*
* Input: lined (ptr to beginning of dest line
* w (width of image in pixels)
* bufs1 (buffer of current source line)
* bufs2 (buffer of next source line)
* lowerclip (lower clip distance to black)
* upperclip (upper clip distance to white)
* lastlineflag (0 if not last dest line, 1 if last dest line)
* Return: void
*
* Dispatches FS error diffusion dithering for
* a single line of the image. If lastlineflag == 0,
* both source buffers are used; otherwise, only bufs1
* is used. We use source buffers because the error
* is propagated into them, and we don't want to change
* the input src image.
*
* We break dithering out line by line to make it
* easier to combine functions like interpolative
* scaling and error diffusion dithering, as such a
* combination of operations obviates the need to
* generate a 2x grayscale image as an intermediary.
*/
void
ditherToBinaryLineLow(l_uint32 *lined,
l_int32 w,
l_uint32 *bufs1,
l_uint32 *bufs2,
l_int32 lowerclip,
l_int32 upperclip,
l_int32 lastlineflag)
{
l_int32 j;
l_int32 oval, eval;
l_uint8 fval1, fval2, rval, bval, dval;
if (lastlineflag == 0) {
for (j = 0; j < w - 1; j++) {
oval = GET_DATA_BYTE(bufs1, j);
if (oval > 127) { /* binarize to OFF */
if ((eval = 255 - oval) > upperclip) {
/* subtract from neighbors */
fval1 = (3 * eval) / 8;
fval2 = eval / 4;
rval = GET_DATA_BYTE(bufs1, j + 1);
rval = L_MAX(0, rval - fval1);
SET_DATA_BYTE(bufs1, j + 1, rval);
bval = GET_DATA_BYTE(bufs2, j);
bval = L_MAX(0, bval - fval1);
SET_DATA_BYTE(bufs2, j, bval);
dval = GET_DATA_BYTE(bufs2, j + 1);
dval = L_MAX(0, dval - fval2);
SET_DATA_BYTE(bufs2, j + 1, dval);
}
}
else { /* oval <= 127; binarize to ON */
SET_DATA_BIT(lined, j); /* ON pixel */
if (oval > lowerclip) {
/* add to neighbors */
fval1 = (3 * oval) / 8;
fval2 = oval / 4;
rval = GET_DATA_BYTE(bufs1, j + 1);
rval = L_MIN(255, rval + fval1);
SET_DATA_BYTE(bufs1, j + 1, rval);
bval = GET_DATA_BYTE(bufs2, j);
bval = L_MIN(255, bval + fval1);
SET_DATA_BYTE(bufs2, j, bval);
dval = GET_DATA_BYTE(bufs2, j + 1);
dval = L_MIN(255, dval + fval2);
SET_DATA_BYTE(bufs2, j + 1, dval);
}
}
}
/* do last column: j = w - 1 */
oval = GET_DATA_BYTE(bufs1, j);
if (oval > 127) { /* binarize to OFF */
if ((eval = 255 - oval) > upperclip) {
/* subtract from neighbors */
fval1 = (3 * eval) / 8;
bval = GET_DATA_BYTE(bufs2, j);
bval = L_MAX(0, bval - fval1);
SET_DATA_BYTE(bufs2, j, bval);
}
}
else { /*oval <= 127; binarize to ON */
SET_DATA_BIT(lined, j); /* ON pixel */
if (oval > lowerclip) {
/* add to neighbors */
fval1 = (3 * oval) / 8;
bval = GET_DATA_BYTE(bufs2, j);
bval = L_MIN(255, bval + fval1);
SET_DATA_BYTE(bufs2, j, bval);
}
}
}
else { /* lastlineflag == 1 */
for (j = 0; j < w - 1; j++) {
oval = GET_DATA_BYTE(bufs1, j);
if (oval > 127) { /* binarize to OFF */
if ((eval = 255 - oval) > upperclip) {
/* subtract from neighbors */
fval1 = (3 * eval) / 8;
rval = GET_DATA_BYTE(bufs1, j + 1);
rval = L_MAX(0, rval - fval1);
SET_DATA_BYTE(bufs1, j + 1, rval);
}
}
else { /* oval <= 127; binarize to ON */
SET_DATA_BIT(lined, j); /* ON pixel */
if (oval > lowerclip) {
/* add to neighbors */
fval1 = (3 * oval) / 8;
rval = GET_DATA_BYTE(bufs1, j + 1);
rval = L_MIN(255, rval + fval1);
SET_DATA_BYTE(bufs1, j + 1, rval);
}
}
}
/* do last pixel: (i, j) = (h - 1, w - 1) */
oval = GET_DATA_BYTE(bufs1, j);
if (oval < 128)
SET_DATA_BIT(lined, j); /* ON pixel */
}
return;
}
/*------------------------------------------------------------------*
* Simple binarization with fixed threshold *
*------------------------------------------------------------------*/
/*
* thresholdToBinaryLow()
*
* If the source pixel is less than thresh,
* the dest will be 1; otherwise, it will be 0
*/
void
thresholdToBinaryLow(l_uint32 *datad,
l_int32 w,
l_int32 h,
l_int32 wpld,
l_uint32 *datas,
l_int32 d,
l_int32 wpls,
l_int32 thresh)
{
l_int32 i;
l_uint32 *lines, *lined;
for (i = 0; i < h; i++) {
lines = datas + i * wpls;
lined = datad + i * wpld;
thresholdToBinaryLineLow(lined, w, lines, d, thresh);
}
return;
}
/*
* thresholdToBinaryLineLow()
*
*/
void
thresholdToBinaryLineLow(l_uint32 *lined,
l_int32 w,
l_uint32 *lines,
l_int32 d,
l_int32 thresh)
{
l_int32 j, k, gval, scount, dcount;
l_uint32 sword, dword;
PROCNAME("thresholdToBinaryLineLow");
switch (d)
{
case 4:
/* Unrolled as 4 source words, 1 dest word */
for (j = 0, scount = 0, dcount = 0; j + 31 < w; j += 32) {
dword = 0;
for (k = 0; k < 4; k++) {
sword = lines[scount++];
dword <<= 8;
gval = (sword >> 28) & 0xf;
/* Trick used here and below: if gval < thresh then
* gval - thresh < 0, so its high-order bit is 1, and
* ((gval - thresh) >> 31) & 1 == 1; likewise, if
* gval >= thresh, then ((gval - thresh) >> 31) & 1 == 0
* Doing it this way avoids a random (and thus easily
* mispredicted) branch on each pixel. */
dword |= ((gval - thresh) >> 24) & 128;
gval = (sword >> 24) & 0xf;
dword |= ((gval - thresh) >> 25) & 64;
gval = (sword >> 20) & 0xf;
dword |= ((gval - thresh) >> 26) & 32;
gval = (sword >> 16) & 0xf;
dword |= ((gval - thresh) >> 27) & 16;
gval = (sword >> 12) & 0xf;
dword |= ((gval - thresh) >> 28) & 8;
gval = (sword >> 8) & 0xf;
dword |= ((gval - thresh) >> 29) & 4;
gval = (sword >> 4) & 0xf;
dword |= ((gval - thresh) >> 30) & 2;
gval = sword & 0xf;
dword |= ((gval - thresh) >> 31) & 1;
}
lined[dcount++] = dword;
}
if (j < w) {
dword = 0;
for (; j < w; j++) {
if ((j & 7) == 0) {
sword = lines[scount++];
}
gval = (sword >> 28) & 0xf;
sword <<= 4;
dword |= (((gval - thresh) >> 31) & 1) << (31 - (j & 31));
}
lined[dcount] = dword;
}
#if DEBUG_UNROLLING
#define CHECK_BIT(a, b, c) if (GET_DATA_BIT(a, b) != c) { \
fprintf(stderr, "Error: mismatch at %d/%d(%d), %d vs %d\n", \
j, w, d, GET_DATA_BIT(a, b), c); }
for (j = 0; j < w; j++) {
gval = GET_DATA_QBIT(lines, j);
CHECK_BIT(lined, j, gval < thresh ? 1 : 0);
}
#endif
break;
case 8:
/* Unrolled as 8 source words, 1 dest word */
for (j = 0, scount = 0, dcount = 0; j + 31 < w; j += 32) {
dword = 0;
for (k = 0; k < 8; k++) {
sword = lines[scount++];
dword <<= 4;
gval = (sword >> 24) & 0xff;
dword |= ((gval - thresh) >> 28) & 8;
gval = (sword >> 16) & 0xff;
dword |= ((gval - thresh) >> 29) & 4;
gval = (sword >> 8) & 0xff;
dword |= ((gval - thresh) >> 30) & 2;
gval = sword & 0xff;
dword |= ((gval - thresh) >> 31) & 1;
}
lined[dcount++] = dword;
}
if (j < w) {
dword = 0;
for (; j < w; j++) {
if ((j & 3) == 0) {
sword = lines[scount++];
}
gval = (sword >> 24) & 0xff;
sword <<= 8;
dword |= (((gval - thresh) >> 31) & 1) << (31 - (j & 31));
}
lined[dcount] = dword;
}
#if DEBUG_UNROLLING
for (j = 0; j < w; j++) {
gval = GET_DATA_BYTE(lines, j);
CHECK_BIT(lined, j, gval < thresh ? 1 : 0);
}
#undef CHECK_BIT
#endif
break;
default:
ERROR_VOID("src depth not 4 or 8 bpp", procName);
break;
}
return;
}
/*---------------------------------------------------------------------*
* Alternate implementation of dithering that uses lookup tables. *
* This is analogous to the method used in dithering to 2 bpp. *
*---------------------------------------------------------------------*/
/*!
* ditherToBinaryLUTLow()
*
* Low-level function for doing Floyd-Steinberg error diffusion
* dithering from 8 bpp (datas) to 1 bpp (datad). Two source
* line buffers, bufs1 and bufs2, are provided, along with three
* 256-entry lookup tables: tabval gives the output pixel value,
* tab38 gives the extra (plus or minus) transferred to the pixels
* directly to the left and below, and tab14 gives the extra
* transferred to the diagonal below. The choice of 3/8 and 1/4
* is traditional but arbitrary when you use a lookup table; the
* only constraint is that the sum is 1. See other comments below.
*/
void
ditherToBinaryLUTLow(l_uint32 *datad,
l_int32 w,
l_int32 h,
l_int32 wpld,
l_uint32 *datas,
l_int32 wpls,
l_uint32 *bufs1,
l_uint32 *bufs2,
l_int32 *tabval,
l_int32 *tab38,
l_int32 *tab14)
{
l_int32 i;
l_uint32 *lined;
/* do all lines except last line */
memcpy(bufs2, datas, 4 * wpls); /* prime the buffer */
for (i = 0; i < h - 1; i++) {
memcpy(bufs1, bufs2, 4 * wpls);
memcpy(bufs2, datas + (i + 1) * wpls, 4 * wpls);
lined = datad + i * wpld;
ditherToBinaryLineLUTLow(lined, w, bufs1, bufs2,
tabval, tab38, tab14, 0);
}
/* do last line */
memcpy(bufs1, bufs2, 4 * wpls);
lined = datad + (h - 1) * wpld;
ditherToBinaryLineLUTLow(lined, w, bufs1, bufs2, tabval, tab38, tab14, 1);
return;
}
/*!
* ditherToBinaryLineLUTLow()
*
* Input: lined (ptr to beginning of dest line
* w (width of image in pixels)
* bufs1 (buffer of current source line)
* bufs2 (buffer of next source line)
* tabval (value to assign for current pixel)
* tab38 (excess value to give to neighboring 3/8 pixels)
* tab14 (excess value to give to neighboring 1/4 pixel)
* lastlineflag (0 if not last dest line, 1 if last dest line)
* Return: void
*/
void
ditherToBinaryLineLUTLow(l_uint32 *lined,
l_int32 w,
l_uint32 *bufs1,
l_uint32 *bufs2,
l_int32 *tabval,
l_int32 *tab38,
l_int32 *tab14,
l_int32 lastlineflag)
{
l_int32 j;
l_int32 oval, tab38val, tab14val;
l_uint8 rval, bval, dval;
if (lastlineflag == 0) {
for (j = 0; j < w - 1; j++) {
oval = GET_DATA_BYTE(bufs1, j);
if (tabval[oval])
SET_DATA_BIT(lined, j);
rval = GET_DATA_BYTE(bufs1, j + 1);
bval = GET_DATA_BYTE(bufs2, j);
dval = GET_DATA_BYTE(bufs2, j + 1);
tab38val = tab38[oval];
if (tab38val == 0)
continue;
tab14val = tab14[oval];
if (tab38val < 0) {
rval = L_MAX(0, rval + tab38val);
bval = L_MAX(0, bval + tab38val);
dval = L_MAX(0, dval + tab14val);
}
else {
rval = L_MIN(255, rval + tab38val);
bval = L_MIN(255, bval + tab38val);
dval = L_MIN(255, dval + tab14val);
}
SET_DATA_BYTE(bufs1, j + 1, rval);
SET_DATA_BYTE(bufs2, j, bval);
SET_DATA_BYTE(bufs2, j + 1, dval);
}
/* do last column: j = w - 1 */
oval = GET_DATA_BYTE(bufs1, j);
if (tabval[oval])
SET_DATA_BIT(lined, j);
bval = GET_DATA_BYTE(bufs2, j);
tab38val = tab38[oval];
if (tab38val < 0) {
bval = L_MAX(0, bval + tab38val);
SET_DATA_BYTE(bufs2, j, bval);
}
else if (tab38val > 0 ) {
bval = L_MIN(255, bval + tab38val);
SET_DATA_BYTE(bufs2, j, bval);
}
}
else { /* lastlineflag == 1 */
for (j = 0; j < w - 1; j++) {
oval = GET_DATA_BYTE(bufs1, j);
if (tabval[oval])
SET_DATA_BIT(lined, j);
rval = GET_DATA_BYTE(bufs1, j + 1);
tab38val = tab38[oval];
if (tab38val == 0)
continue;
if (tab38val < 0)
rval = L_MAX(0, rval + tab38val);
else
rval = L_MIN(255, rval + tab38val);
SET_DATA_BYTE(bufs1, j + 1, rval);
}
/* do last pixel: (i, j) = (h - 1, w - 1) */
oval = GET_DATA_BYTE(bufs1, j);
if (tabval[oval])
SET_DATA_BIT(lined, j);
}
return;
}
/*!
* make8To1DitherTables()
*
* Input: &tabval (value assigned to output pixel; 0 or 1)
* &tab38 (amount propagated to pixels left and below)
* &tab14 (amount propagated to pixel to left and down)
* lowerclip (values near 0 where the excess is not propagated)
* upperclip (values near 255 where the deficit is not propagated)
*
* Return: 0 if OK, 1 on error
*/
l_int32
make8To1DitherTables(l_int32 **ptabval,
l_int32 **ptab38,
l_int32 **ptab14,
l_int32 lowerclip,
l_int32 upperclip)
{
l_int32 i;
l_int32 *tabval, *tab38, *tab14;
PROCNAME("make8To1DitherTables");
if (!ptabval || !ptab38 || !ptab14)
return ERROR_INT("table ptrs not all defined", procName, 1);
/* 3 lookup tables: 1-bit value, (3/8)excess, and (1/4)excess */
if ((tabval = (l_int32 *)CALLOC(256, sizeof(l_int32))) == NULL)
return ERROR_INT("tabval not made", procName, 1);
if ((tab38 = (l_int32 *)CALLOC(256, sizeof(l_int32))) == NULL)
return ERROR_INT("tab38 not made", procName, 1);
if ((tab14 = (l_int32 *)CALLOC(256, sizeof(l_int32))) == NULL)
return ERROR_INT("tab14 not made", procName, 1);
*ptabval = tabval;
*ptab38 = tab38;
*ptab14 = tab14;
for (i = 0; i < 256; i++) {
if (i <= lowerclip) {
tabval[i] = 1;
tab38[i] = 0;
tab14[i] = 0;
}
else if (i < 128) {
tabval[i] = 1;
tab38[i] = (3 * i + 4) / 8;
tab14[i] = (i + 2) / 4;
}
else if (i < 255 - upperclip) {
tabval[i] = 0;
tab38[i] = (3 * (i - 255) + 4) / 8;
tab14[i] = ((i - 255) + 2) / 4;
}
else { /* i >= 255 - upperclip */
tabval[i] = 0;
tab38[i] = 0;
tab14[i] = 0;
}
}
return 0;
}
/*------------------------------------------------------------------*
* Dithering to 2 bpp *
*------------------------------------------------------------------*/
/*
* ditherTo2bppLow()
*
* Low-level function for doing Floyd-Steinberg error diffusion
* dithering from 8 bpp (datas) to 2 bpp (datad). Two source
* line buffers, bufs1 and bufs2, are provided, along with three
* 256-entry lookup tables: tabval gives the output pixel value,
* tab38 gives the extra (plus or minus) transferred to the pixels
* directly to the left and below, and tab14 gives the extra
* transferred to the diagonal below. The choice of 3/8 and 1/4
* is traditional but arbitrary when you use a lookup table; the
* only constraint is that the sum is 1. See other comments
* below and in grayquant.c.
*/
void
ditherTo2bppLow(l_uint32 *datad,
l_int32 w,
l_int32 h,
l_int32 wpld,
l_uint32 *datas,
l_int32 wpls,
l_uint32 *bufs1,
l_uint32 *bufs2,
l_int32 *tabval,
l_int32 *tab38,
l_int32 *tab14)
{
l_int32 i;
l_uint32 *lined;
/* do all lines except last line */
memcpy(bufs2, datas, 4 * wpls); /* prime the buffer */
for (i = 0; i < h - 1; i++) {
memcpy(bufs1, bufs2, 4 * wpls);
memcpy(bufs2, datas + (i + 1) * wpls, 4 * wpls);
lined = datad + i * wpld;
ditherTo2bppLineLow(lined, w, bufs1, bufs2, tabval, tab38, tab14, 0);
}
/* do last line */
memcpy(bufs1, bufs2, 4 * wpls);
lined = datad + (h - 1) * wpld;
ditherTo2bppLineLow(lined, w, bufs1, bufs2, tabval, tab38, tab14, 1);
return;
}
/*
* ditherTo2bppLineLow()
*
* Input: lined (ptr to beginning of dest line
* w (width of image in pixels)
* bufs1 (buffer of current source line)
* bufs2 (buffer of next source line)
* tabval (value to assign for current pixel)
* tab38 (excess value to give to neighboring 3/8 pixels)
* tab14 (excess value to give to neighboring 1/4 pixel)
* lastlineflag (0 if not last dest line, 1 if last dest line)
* Return: void
*
* Dispatches error diffusion dithering for
* a single line of the image. If lastlineflag == 0,
* both source buffers are used; otherwise, only bufs1
* is used. We use source buffers because the error
* is propagated into them, and we don't want to change
* the input src image.
*
* We break dithering out line by line to make it
* easier to combine functions like interpolative
* scaling and error diffusion dithering, as such a
* combination of operations obviates the need to
* generate a 2x grayscale image as an intermediary.
*/
void
ditherTo2bppLineLow(l_uint32 *lined,
l_int32 w,
l_uint32 *bufs1,
l_uint32 *bufs2,
l_int32 *tabval,
l_int32 *tab38,
l_int32 *tab14,
l_int32 lastlineflag)
{
l_int32 j;
l_int32 oval, tab38val, tab14val;
l_uint8 rval, bval, dval;
if (lastlineflag == 0) {
for (j = 0; j < w - 1; j++) {
oval = GET_DATA_BYTE(bufs1, j);
SET_DATA_DIBIT(lined, j, tabval[oval]);
rval = GET_DATA_BYTE(bufs1, j + 1);
bval = GET_DATA_BYTE(bufs2, j);
dval = GET_DATA_BYTE(bufs2, j + 1);
tab38val = tab38[oval];
tab14val = tab14[oval];
if (tab38val < 0) {
rval = L_MAX(0, rval + tab38val);
bval = L_MAX(0, bval + tab38val);
dval = L_MAX(0, dval + tab14val);
}
else {
rval = L_MIN(255, rval + tab38val);
bval = L_MIN(255, bval + tab38val);
dval = L_MIN(255, dval + tab14val);
}
SET_DATA_BYTE(bufs1, j + 1, rval);
SET_DATA_BYTE(bufs2, j, bval);
SET_DATA_BYTE(bufs2, j + 1, dval);
}
/* do last column: j = w - 1 */
oval = GET_DATA_BYTE(bufs1, j);
SET_DATA_DIBIT(lined, j, tabval[oval]);
bval = GET_DATA_BYTE(bufs2, j);
tab38val = tab38[oval];
if (tab38val < 0)
bval = L_MAX(0, bval + tab38val);
else
bval = L_MIN(255, bval + tab38val);
SET_DATA_BYTE(bufs2, j, bval);
}
else { /* lastlineflag == 1 */
for (j = 0; j < w - 1; j++) {
oval = GET_DATA_BYTE(bufs1, j);
SET_DATA_DIBIT(lined, j, tabval[oval]);
rval = GET_DATA_BYTE(bufs1, j + 1);
tab38val = tab38[oval];
if (tab38val < 0)
rval = L_MAX(0, rval + tab38val);
else
rval = L_MIN(255, rval + tab38val);
SET_DATA_BYTE(bufs1, j + 1, rval);
}
/* do last pixel: (i, j) = (h - 1, w - 1) */
oval = GET_DATA_BYTE(bufs1, j);
SET_DATA_DIBIT(lined, j, tabval[oval]);
}
return;
}
/*!
* make8To2DitherTables()
*
* Input: &tabval (value assigned to output pixel; 0, 1, 2 or 3)
* &tab38 (amount propagated to pixels left and below)
* &tab14 (amount propagated to pixel to left and down)
* cliptoblack (values near 0 where the excess is not propagated)
* cliptowhite (values near 255 where the deficit is not propagated)
*
* Return: 0 if OK, 1 on error
*/
l_int32
make8To2DitherTables(l_int32 **ptabval,
l_int32 **ptab38,
l_int32 **ptab14,
l_int32 cliptoblack,
l_int32 cliptowhite)
{
l_int32 i;
l_int32 *tabval, *tab38, *tab14;
PROCNAME("make8To2DitherTables");
/* 3 lookup tables: 2-bit value, (3/8)excess, and (1/4)excess */
if ((tabval = (l_int32 *)CALLOC(256, sizeof(l_int32))) == NULL)
return ERROR_INT("tabval not made", procName, 1);
if ((tab38 = (l_int32 *)CALLOC(256, sizeof(l_int32))) == NULL)
return ERROR_INT("tab38 not made", procName, 1);
if ((tab14 = (l_int32 *)CALLOC(256, sizeof(l_int32))) == NULL)
return ERROR_INT("tab14 not made", procName, 1);
*ptabval = tabval;
*ptab38 = tab38;
*ptab14 = tab14;
for (i = 0; i < 256; i++) {
if (i <= cliptoblack) {
tabval[i] = 0;
tab38[i] = 0;
tab14[i] = 0;
}
else if (i < 43) {
tabval[i] = 0;
tab38[i] = (3 * i + 4) / 8;
tab14[i] = (i + 2) / 4;
}
else if (i < 85) {
tabval[i] = 1;
tab38[i] = (3 * (i - 85) - 4) / 8;
tab14[i] = ((i - 85) - 2) / 4;
}
else if (i < 128) {
tabval[i] = 1;
tab38[i] = (3 * (i - 85) + 4) / 8;
tab14[i] = ((i - 85) + 2) / 4;
}
else if (i < 170) {
tabval[i] = 2;
tab38[i] = (3 * (i - 170) - 4) / 8;
tab14[i] = ((i - 170) - 2) / 4;
}
else if (i < 213) {
tabval[i] = 2;
tab38[i] = (3 * (i - 170) + 4) / 8;
tab14[i] = ((i - 170) + 2) / 4;
}
else if (i < 255 - cliptowhite) {
tabval[i] = 3;
tab38[i] = (3 * (i - 255) - 4) / 8;
tab14[i] = ((i - 255) - 2) / 4;
}
else { /* i >= 255 - cliptowhite */
tabval[i] = 3;
tab38[i] = 0;
tab14[i] = 0;
}
}
#if 0
for (i = 0; i < 256; i++)
fprintf(stderr, "tabval[%d] = %d, tab38[%d] = %d, tab14[%d] = %d\n",
i, tabval[i], i, tab38[i], i, tab14[i]);
#endif
return 0;
}
/*------------------------------------------------------------------*
* Simple thresholding to 2 bpp *
*------------------------------------------------------------------*/
/*
* thresholdTo2bppLow()
*
* Low-level function for thresholding from 8 bpp (datas) to
* 2 bpp (datad), using thresholds implicitly defined through @tab,
* a 256-entry lookup table that gives a 2-bit output value
* for each possible input.
*
* For each line, unroll the loop so that for each 32 bit src word,
* representing four consecutive 8-bit pixels, we compose one byte
* of output consisiting of four 2-bit pixels.
*/
void
thresholdTo2bppLow(l_uint32 *datad,
l_int32 h,
l_int32 wpld,
l_uint32 *datas,
l_int32 wpls,
l_int32 *tab)
{
l_uint8 sval1, sval2, sval3, sval4, dval;
l_int32 i, j, k;
l_uint32 *lines, *lined;
for (i = 0; i < h; i++) {
lines = datas + i * wpls;
lined = datad + i * wpld;
for (j = 0; j < wpls; j++) {
k = 4 * j;
sval1 = GET_DATA_BYTE(lines, k);
sval2 = GET_DATA_BYTE(lines, k + 1);
sval3 = GET_DATA_BYTE(lines, k + 2);
sval4 = GET_DATA_BYTE(lines, k + 3);
dval = (tab[sval1] << 6) | (tab[sval2] << 4) |
(tab[sval3] << 2) | tab[sval4];
SET_DATA_BYTE(lined, j, dval);
}
}
return;
}
/*------------------------------------------------------------------*
* Simple thresholding to 4 bpp *
*------------------------------------------------------------------*/
/*
* thresholdTo4bppLow()
*
* Low-level function for thresholding from 8 bpp (datas) to
* 4 bpp (datad), using thresholds implicitly defined through @tab,
* a 256-entry lookup table that gives a 4-bit output value
* for each possible input.
*
* For each line, unroll the loop so that for each 32 bit src word,
* representing four consecutive 8-bit pixels, we compose two bytes
* of output consisiting of four 4-bit pixels.
*/
void
thresholdTo4bppLow(l_uint32 *datad,
l_int32 h,
l_int32 wpld,
l_uint32 *datas,
l_int32 wpls,
l_int32 *tab)
{
l_uint8 sval1, sval2, sval3, sval4;
l_uint16 dval;
l_int32 i, j, k;
l_uint32 *lines, *lined;
for (i = 0; i < h; i++) {
lines = datas + i * wpls;
lined = datad + i * wpld;
for (j = 0; j < wpls; j++) {
k = 4 * j;
sval1 = GET_DATA_BYTE(lines, k);
sval2 = GET_DATA_BYTE(lines, k + 1);
sval3 = GET_DATA_BYTE(lines, k + 2);
sval4 = GET_DATA_BYTE(lines, k + 3);
dval = (tab[sval1] << 12) | (tab[sval2] << 8) |
(tab[sval3] << 4) | tab[sval4];
SET_DATA_TWO_BYTES(lined, j, dval);
}
}
return;
}