/* * CDE - Common Desktop Environment * * Copyright (c) 1993-2012, The Open Group. All rights reserved. * * These libraries and programs are free software; you can * redistribute them and/or modify them under the terms of the GNU * Lesser General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) * any later version. * * These libraries and programs are distributed in the hope that * they will be useful, but WITHOUT ANY WARRANTY; without even the * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public * License along with these libraries and programs; if not, write * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth * Floor, Boston, MA 02110-1301 USA */ /* $XConsortium: ilbigray.c /main/6 1996/09/24 17:12:45 drk $ */ /**--------------------------------------------------------------------- *** *** (c)Copyright 1991 Hewlett-Packard Co. *** *** RESTRICTED RIGHTS LEGEND *** Use, duplication, or disclosure by the U.S. Government is subject to *** restrictions as set forth in sub-paragraph (c)(1)(ii) of the Rights in *** Technical Data and Computer Software clause in DFARS 252.227-7013. *** Hewlett-Packard Company *** 3000 Hanover Street *** Palo Alto, CA 94304 U.S.A. *** Rights for non-DOD U.S. Government Departments and Agencies are as set *** forth in FAR 52.227-19(c)(1,2). *** ***-------------------------------------------------------------------*/ /* /ilc/ilbigray.c : Contains ilBitonalToGray(), which adds a pipe element to convert the pipe image (which must be IL_BITONAL) to gray scale. */ #include "ilint.h" #include "ilpipelem.h" #include "ilscaleint.h" #include "ilerrors.h" #ifdef LSB_BIT_ORDER extern void flip_bits(ilPtr start, unsigned n); #else # define flip_bits(s,n) /*EMPTY*/ #endif #define IL_FILTER_SIZE 3 /* size of one side of filter square */ #define IL_FILTER_SQUARE (IL_FILTER_SIZE*IL_FILTER_SIZE) /* square size of filter */ #define MIN_DESIRED_STRIP 16 /* arbitrary: desired size of strips */ /* Private data for pipe elements. See comments under ilInit9BitFilter() for a description of "filterValues". */ typedef struct { /* Data inited by ilScaleBitonalToGray() when pipe element added. */ ilByte filterValues [512]; /* should be first for efficiency! */ ilBool bitonalOutput; /* true: outputting bitonal, else gray */ int *pGrayThreshold; /* bitonal out only: ptr to threshold */ int defaultGrayThreshold; /* pGrayThreshold -> this if defaulting */ long srcWidth, srcHeight; /* size of src (pipe) image */ long dstWidth, dstHeight; /* size of dst (output) image */ long widthDiff, heightDiff; /* src - dst of width/height */ long srcBufferHeight; /* height of perm src image, else 0 */ /* Data inited by ilBiGrayInit() */ unsigned int grayThreshold; /* *pGrayThreshold for this pass */ long lineAcc; /* line accumulator for vert scaling */ long srcRowBytes; /* bytes/row of src image */ ilPtr pSrcPixels; /* ptr to start of src pixels */ long dstRowBytes; /* bytes/row of dst image */ ilPtr pDstPixels; /* ptr to start of dst pixels */ } ilBiGrayPrivRec, *ilBiGrayPrivPtr; /* --------------------------- ilInit9BitFilter ---------------------------- */ /* Init "*pTable", a 512 entry table, with the values to be indexed by a 9 bit number formed as follows: ----------- | 8 | 7 | 6 | <- top source line ----------- | 5 | 4 | 3 | <- bit "4" is logical center point ----------- | 2 | 1 | 0 | ----------- The bits are shifted into a 9 bit number, as above, then used as an index into the table. The value in entry 0xff (all bits on) should equal the max gray value ("nLevels" - 1). */ static void ilInit9BitFilter ( long srcWidth, long srcHeight, long dstWidth, long dstHeight, unsigned long nLevels, ilBool blackIsZero, ilByte *pTable ) { double filter [IL_FILTER_SQUARE]; double fraction, widthScale, heightScale, totalArea, widthSmall, heightSmall; long value, nLevelsM1; int i; /* Setup "filter" with the fractional (0..1) contribution of each pixel to the whole, in the bit order described above (bit 0 is the top-left pixel). The sum of all values should ~= 1.0. The portion of each pixel selected is based on a square of size "srcWidth/dstWidth" by "srcHeight/dstHeight", mapped over the source pixels, centered on pixel 4 (above). If the scale factor is greater than 3.0 in either direction, make it 3.0 (all pixels contribute equally in that direction. */ widthScale = (double)srcWidth / (double)dstWidth; if (widthScale > 3.0) widthScale = 3.0; heightScale = (double)srcHeight / (double)dstHeight; if (heightScale > 3.0) heightScale = 3.0; totalArea = widthScale * heightScale; /* The center pixel is a 1x1 square. The four corner pixels are overlayed by a rect of width or height (wh) = (wh - 1) / 2: the whole side is "wh", - 1 for center, / 2 cause there are 2. The four side pixels have width/height of corner pixels, but other dim = 1. */ filter [4] = 1.0 / totalArea; widthSmall = (widthScale - 1.0) / 2.0; heightSmall = (heightScale - 1.0) / 2.0; filter [0] = filter [2] = filter [6] = filter [8] = (widthSmall * heightSmall) / totalArea; filter [1] = filter [7] = heightSmall / totalArea; filter [3] = filter [5] = widthSmall / totalArea; /* Convert the filter to an array indexed by a 9 bit number: the filter bits as shown above, in order "876543210". If whitePixel is 0, add the filter fraction if bit is off. If whitePixel not zero than add the filter fraction if bit is on. */ nLevelsM1 = nLevels - 1; if (!blackIsZero) { for (i = 0; i < 512; i++) { fraction = 0.0; if (!(i & 1)) fraction += filter [0]; if (!(i & 2)) fraction += filter [1]; if (!(i & 4)) fraction += filter [2]; if (!(i & 8)) fraction += filter [3]; if (!(i & 16)) fraction += filter [4]; if (!(i & 32)) fraction += filter [5]; if (!(i & 64)) fraction += filter [6]; if (!(i & 128)) fraction += filter [7]; if (!(i & 256)) fraction += filter [8]; value = fraction * nLevelsM1 + 0.5; if (value > nLevelsM1) value = nLevelsM1; pTable [i] = value; } } else { /* a 1 is white */ for (i = 0; i < 512; i++) { fraction = 0.0; if (i & 1) fraction += filter [0]; if (i & 2) fraction += filter [1]; if (i & 4) fraction += filter [2]; if (i & 8) fraction += filter [3]; if (i & 16) fraction += filter [4]; if (i & 32) fraction += filter [5]; if (i & 64) fraction += filter [6]; if (i & 128) fraction += filter [7]; if (i & 256) fraction += filter [8]; value = fraction * nLevelsM1 + 0.5; if (value > nLevelsM1) value = nLevelsM1; pTable [i] = value; } } } /* --------------------- ilBiGrayInit -------------------------- */ /* Init() function: copy values from given images to private for fast reference. */ static ilError ilBiGrayInit ( ilBiGrayPrivPtr pPriv, ilImageInfo *pSrcImage, ilImageInfo *pDstImage ) { pPriv->lineAcc = pPriv->heightDiff; pPriv->srcRowBytes = pSrcImage->plane[0].nBytesPerRow; pPriv->pSrcPixels = pSrcImage->plane[0].pPixels; pPriv->dstRowBytes = pDstImage->plane[0].nBytesPerRow; pPriv->pDstPixels = pDstImage->plane[0].pPixels; /* Copy caller's threshold if scaling to bitonal. */ if (pPriv->bitonalOutput) pPriv->grayThreshold = *pPriv->pGrayThreshold; return IL_OK; } /* --------------------- ilBiGrayExecute -------------------------- */ /* Execute() pipe element function for scaling bitonal to gray. */ static ilError ilBiGrayExecute ( ilExecuteData *pData, long dstLine, long *pNLines ) { #define ULPTR CARD32 * #ifdef LSB_BIT_ORDER # define SPECIAL_MASK_BIT 0x00000001 /* for LSB bit order */ # define SHIFT_MASK(m) ((m) <<= 1) #else # define SPECIAL_MASK_BIT 0x80000000 /* for MSB bit order */ # define SHIFT_MASK(m) ((m) >>= 1) #endif long nLines, nLinesWritten; ilPtr pSrcLine, pDstLine; long colAcc, nDstBits, dstWidth, bottomLine, line; long srcBytesAbove, srcBytesBelow; ilBiGrayPrivPtr pPriv; CARD32 index, *pSrc; int shift, bitNumber; /* Point pSrcLine to srcLine: = middle line of 3x3 matrix. Set bottomLine to last available line in the buffer. */ pPriv = (ilBiGrayPrivPtr)pData->pPrivate; dstWidth = pPriv->dstWidth; pSrcLine = pPriv->pSrcPixels + pData->srcLine * pPriv->srcRowBytes; pDstLine = pPriv->pDstPixels + dstLine * pPriv->dstRowBytes; nLines = *pNLines; if (nLines <= 0) return 0; /* no lines, EXIT */ if (pPriv->srcBufferHeight) bottomLine = pPriv->srcBufferHeight - 1; else bottomLine = pData->srcLine + nLines - 1; line = pData->srcLine - 1; /* line of top of 3x3 matrix */ nLinesWritten = 0; while (TRUE) { while (pPriv->lineAcc > 0) { if (nLines-- <= 0) goto BGLinesDone; pPriv->lineAcc -= pPriv->dstHeight; pSrcLine += pPriv->srcRowBytes; line++; } if (nLines-- <= 0) goto BGLinesDone; pPriv->lineAcc += pPriv->heightDiff; /* Algorithm: skip src bits, using basic scale algorithm. To form dst bit, form 9 bit value from 3x3, with top bits in high order bits. Lookup the 9 bit value in table of correct gray scale. "bitNumber" is the # of the left bit of 3x3; 31 = left bit of long. If bitNumber is < 2, the bits cross a long boundary: get bits from bytes, low order of this long and high order byte of next long. pSrc will point to the middle line of 3x3; the line above is "-srcBytesAbove" away; the line below is "srcBytesBelow" away. If the 3x3 is outside of the buffer being read, set "srcBytesAbove/Below" to 0, causing the middle line to be re-read and replicated to replace the unaccessible line. */ srcBytesAbove = (line < 0) ? 0 : pPriv->srcRowBytes; srcBytesBelow = ((line + 2) > bottomLine) ? 0 : pPriv->srcRowBytes; nDstBits = pPriv->dstWidth; /* Set pSrc and bitNumber to point to the rightmost bit of the long before the first long. The "colAcc > 0" check below will be true, causing a bump to the next bit (first bit in row), _except_ when "widthDiff" is 0 (no hori scaling). In this case, set colAcc to 1, forcing the first colAcc>0 check to be true and bumping past first bit - the rest proceeds normally. */ pSrc = ((ULPTR)pSrcLine) - 1; bitNumber = 0; colAcc = pPriv->widthDiff; if (colAcc <= 0) colAcc = 1; /* If bitonal output, logically produce a gray pixel, then output a 1 if gray is < threshold. */ if (pPriv->bitonalOutput) { ULPTR pDst; CARD32 mask, outLong; pDst = (ULPTR)pDstLine; mask = SPECIAL_MASK_BIT; outLong = 0; /* fill with 1's as necessary */ while (TRUE) { while (colAcc > 0) { colAcc -= dstWidth; if (--bitNumber < 0) { pSrc++; bitNumber = 31; } } colAcc += pPriv->widthDiff; /* If done and mask != left bit on, output bits in "outLong". */ if (nDstBits <= 0) { if (mask != SPECIAL_MASK_BIT) { flip_bits((ilPtr)&outLong, sizeof(outLong)); *pDst++ = outLong; } break; /* done this line; exit while */ } if (--nDstBits <= 0) { INT32 rightBit = (((ilPtr)pSrc - pSrcLine) << 3) + 34 - bitNumber; if ((rightBit > pPriv->srcWidth) && (pPriv->srcWidth > 3)) if (++bitNumber > 31) {bitNumber = 0; pSrc--; } } if (bitNumber >= 2) { shift = bitNumber - 2; index = (*((ULPTR)((ilPtr)pSrc - srcBytesAbove)) >> shift) & 7; index <<= 3; index |= (*pSrc >> shift) & 7; index <<= 3; index |= (*((ULPTR)((ilPtr)pSrc + srcBytesBelow)) >> shift) & 7; } else { ilPtr pSrcTemp = (ilPtr)pSrc - srcBytesAbove + 3; shift = 6 + bitNumber; index = ((CARD32)((*pSrcTemp << 8) | *(pSrcTemp + 1)) >> shift) & 7; index <<= 3; pSrcTemp += srcBytesAbove; index |= ((CARD32)((*pSrcTemp << 8) | *(pSrcTemp + 1)) >> shift) & 7; index <<= 3; pSrcTemp += srcBytesBelow; index |= ((CARD32)((*pSrcTemp << 8) | *(pSrcTemp + 1)) >> shift) & 7; } if (pPriv->filterValues [index] < pPriv->grayThreshold) outLong |= mask; if (!(SHIFT_MASK(mask))) { flip_bits((ilPtr)&outLong, sizeof(outLong)); *pDst++ = outLong; mask = SPECIAL_MASK_BIT; outLong = 0; } if (--bitNumber < 0) { /* next src bit */ pSrc++; bitNumber = 31; } } /* END column loop */ } else { /* outputting gray */ ilPtr pDst; pDst = pDstLine; while (TRUE) { while (colAcc > 0) { colAcc -= dstWidth; if (--bitNumber < 0) { pSrc++; bitNumber = 31; } } colAcc += pPriv->widthDiff; if (nDstBits <= 0) break; /* done this line; exit while */ if (--nDstBits <= 0) { INT32 rightBit = (((ilPtr)pSrc - pSrcLine) << 3) + 34 - bitNumber; if ((rightBit > pPriv->srcWidth) && (pPriv->srcWidth > 3)) if (++bitNumber > 31) {bitNumber = 0; pSrc--; } } if (bitNumber >= 2) { shift = bitNumber - 2; index = (*((ULPTR)((ilPtr)pSrc - srcBytesAbove)) >> shift) & 7; index <<= 3; index |= (*pSrc >> shift) & 7; index <<= 3; index |= (*((ULPTR)((ilPtr)pSrc + srcBytesBelow)) >> shift) & 7; } else { ilPtr pSrcTemp = (ilPtr)pSrc - srcBytesAbove + 3; shift = 6 + bitNumber; index = ((CARD32)((*pSrcTemp << 8) | *(pSrcTemp + 1)) >> shift) & 7; index <<= 3; pSrcTemp += srcBytesAbove; index |= ((CARD32)((*pSrcTemp << 8) | *(pSrcTemp + 1)) >> shift) & 7; index <<= 3; pSrcTemp += srcBytesBelow; index |= ((CARD32)((*pSrcTemp << 8) | *(pSrcTemp + 1)) >> shift) & 7; } *pDst++ = pPriv->filterValues [index]; if (--bitNumber < 0) { /* next src bit */ pSrc++; bitNumber = 31; } } /* END column loop */ } /* Line done; next src, dst lines; inc line index and # lines written. */ pSrcLine += pPriv->srcRowBytes; line++; pDstLine += pPriv->dstRowBytes; nLinesWritten++; } /* END loop, one dst line */ BGLinesDone: *pNLines = nLinesWritten; return IL_OK; } /* ------------------------- ilScaleBitonalToGray ---------------------------- */ /* Called by ilScale(). Adds an element to "pipe" to scale the pipe image to "dstWidth" by "dstHeight" which both must be > 0. The input pipe image must be a bitonal, bit-per-pixel image, with the given "blackIsZero". "pInfo" points to the pipe info, "dstWidth/Height" is the (> 0) size to scale to, and nGrayLevels is the number of levels of gray. The output image is a gray image. Only a scale down in both directions is allowed; the following must be true: srcWidth must be >= dstWidth and srcHeight must be >= dstHeight! */ IL_PRIVATE void _ilScaleBitonalToGray ( ilPipe pipe, unsigned long dstWidth, unsigned long dstHeight, unsigned long nGrayLevels, ilBool blackIsZero, ilPipeInfo *pInfo ) { ilBiGrayPrivPtr pPriv; ilDstElementData dstData; ilImageDes des; /* Idiot check the number of gray levels */ if ((nGrayLevels < 2) || (nGrayLevels > 256)) { ilDeclarePipeInvalid (pipe, IL_ERROR_LEVELS_PER_SAMPLE); return; /* EXIT */ } /* Add a filter to do the scale, then init *pPriv. */ dstData.producerObject = (ilObject)NULL; des = *IL_DES_GRAY; des.nLevelsPerSample[0] = nGrayLevels; dstData.pDes = &des; dstData.pFormat = IL_FORMAT_BYTE; dstData.width = dstWidth; dstData.height = dstHeight; dstData.stripHeight = ((dstHeight * pInfo->recommendedStripHeight) / pInfo->height) + 1; dstData.constantStrip = FALSE; dstData.pPalette = (unsigned short *)NULL; pPriv = (ilBiGrayPrivPtr)ilAddPipeElement (pipe, IL_FILTER, sizeof (ilBiGrayPrivRec), IL_ADD_PIPE_HOLD_SRC, (ilSrcElementData *)NULL, &dstData, ilBiGrayInit, IL_NPF, IL_NPF, ilBiGrayExecute, NULL, 0); if (!pPriv) return; /* Pipe element added, init pPriv. If not a tempImage, reading directly from a permanent image: set srcHeight to image height; else set to 0. */ pPriv->bitonalOutput = FALSE; pPriv->srcWidth = pInfo->width; pPriv->srcHeight = pInfo->height; pPriv->dstWidth = dstWidth; pPriv->dstHeight = dstHeight; pPriv->widthDiff = pInfo->width - dstWidth; pPriv->heightDiff = pInfo->height - dstHeight; if (!pInfo->tempImage) pPriv->srcBufferHeight = pInfo->height; else pPriv->srcBufferHeight = 0; /* Init the filter table in private based on scale factors and # gray levels. */ ilInit9BitFilter (pPriv->srcWidth, pPriv->srcHeight, pPriv->dstWidth, pPriv->dstHeight, nGrayLevels, blackIsZero, pPriv->filterValues); pipe->context->error = IL_OK; } /* ------------------------- ilAreaScaleBitonal ---------------------------- */ /* Called by ilScale(). Adds an element to "pipe" to scale the pipe image to "dstWidth" by "dstHeight" which both must be > 0. The input pipe image must be a bitonal, bit-per-pixel image, with _any_ "blackIsZero". The resulting image is of the same type. "pInfo" points to the pipe info, "dstWidth/Height" is the (> 0) size to scale to. Only a scale down in both directions is allowed; the following must be true: srcWidth must be >= dstWidth and srcHeight must be >= dstHeight! Logically the same as ilScaleBitonalToGray(): a gray byte is produced for each destination pixel, using 256 levels of gray. If that byte is >= "*pGrayThreshold"; a white pixel is written; else a black pixel is written. NOTE: the value at "*pGrayThreshold" is read each time the pipe is executed (during Init()); the caller change its value and get different results without recreating the pipe. If "pGrayThreshold" is NULL, a default value is used. */ IL_PRIVATE void _ilAreaScaleBitonal ( ilPipe pipe, unsigned long dstWidth, unsigned long dstHeight, int *pGrayThreshold, ilPipeInfo *pInfo ) { ilBiGrayPrivPtr pPriv; ilDstElementData dstData; #define DEFAULT_GRAY_THRESHOLD 128 /* assumes 256 levels */ /* Add a filter to do the scale, then init *pPriv. */ dstData.producerObject = (ilObject)NULL; dstData.pDes = (ilImageDes *)NULL; dstData.pFormat = IL_FORMAT_BIT; dstData.width = dstWidth; dstData.height = dstHeight; dstData.stripHeight = ((dstHeight * pInfo->recommendedStripHeight) / pInfo->height) + 1; dstData.constantStrip = FALSE; dstData.pPalette = (unsigned short *)NULL; pPriv = (ilBiGrayPrivPtr)ilAddPipeElement (pipe, IL_FILTER, sizeof (ilBiGrayPrivRec), IL_ADD_PIPE_HOLD_SRC, (ilSrcElementData *)NULL, &dstData, ilBiGrayInit, IL_NPF, IL_NPF, ilBiGrayExecute, NULL, 0); if (!pPriv) return; /* Pipe element added, init pPriv. If not a tempImage, reading directly from a permanent image: set srcHeight to image height; else set to 0. */ pPriv->bitonalOutput = TRUE; if (pGrayThreshold) pPriv->pGrayThreshold = pGrayThreshold; else { pPriv->defaultGrayThreshold = DEFAULT_GRAY_THRESHOLD; pPriv->pGrayThreshold = &pPriv->defaultGrayThreshold; } pPriv->srcWidth = pInfo->width; pPriv->srcHeight = pInfo->height; pPriv->dstWidth = dstWidth; pPriv->dstHeight = dstHeight; pPriv->widthDiff = pInfo->width - dstWidth; pPriv->heightDiff = pInfo->height - dstHeight; if (!pInfo->tempImage) pPriv->srcBufferHeight = pInfo->height; else pPriv->srcBufferHeight = 0; /* Init the filter table in private based on scale factors and 256 gray levels. Note that "blackIsZero" is passed as FALSE below, because we want to output the same blackIsZero as input. We output a zero when the gray is >= threshold; this works for either sense of blackIsZero. */ ilInit9BitFilter (pPriv->srcWidth, pPriv->srcHeight, pPriv->dstWidth, pPriv->dstHeight, 256, FALSE, pPriv->filterValues); pipe->context->error = IL_OK; }