| /*====================================================================* |
| - 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; |
| } |
| |
| |