/* * 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: ilXycbcr.c /main/3 1995/10/23 15:41:23 rswiston $ */ /**--------------------------------------------------------------------- *** *** (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). *** ***-------------------------------------------------------------------*/ #include "ilint.h" #include "ilXint.h" #include #include "ilpipelem.h" #include "ilerrors.h" /* Number of bits of Y and Cb/Cr used to dither before a table lookup to convert to a dithered pixel. The number of levels dithered to (nLY/CbCr) is 1<RGB lookup table is "nLY * nLCbCr * nLCbCr" longs in size. */ #define DYCBCR_NBITS_Y 6 #define DYCBCR_NBITS_CBCR 4 /* Tables generated by /il/util/gendithertables.c */ static unsigned int _il2x2DitherKernel[4] = {42, 234, 170, 106}; /* Private data for YCbCr to dithered pixel filter */ typedef struct { unsigned long pixels[1 << (DYCBCR_NBITS_Y + DYCBCR_NBITS_CBCR + DYCBCR_NBITS_CBCR)]; } ilDitherYCbCrPrivRec, *ilDitherYCbCrPrivPtr; /* ---------------------- ilFastYCbCrDitherExecute ------------------------ */ /* Execute() function: dither from subsampled-by-2 YCbCr to 8 bit pixels. */ static ilError ilFastYCbCrDitherExecute ( ilExecuteData *pData, long dstLine, long *pNLines ) { ilDitherYCbCrPrivPtr pPriv; ilImagePlaneInfo *pPlane; long nLinesDiv2, halfWidth; long CbRowBytes, CrRowBytes; ilPtr pYLine, pCbLine, pCrLine, pDstLine; unsigned long *pTable; long YRowBytes, dstRowBytes, nPixelsDiv2, CbCr, temp0, temp1; ilPtr pY, pCb, pCr, pDst; /* This filter handles a pipe image of YCbCr subsampled by 2 in Cb/Cr only. The # of lines of Cb/Cr is therefore 1/2 the # of lines of Y. Note: can only handle images with even width/height; checked elsewhere. */ pPlane = pData->pSrcImage->plane; YRowBytes = pPlane->nBytesPerRow; pYLine = pPlane->pPixels; pPlane++; CbRowBytes = pPlane->nBytesPerRow; pCbLine = pPlane->pPixels; pPlane++; CrRowBytes = pPlane->nBytesPerRow; pCrLine = pPlane->pPixels; if (pData->srcLine) { pYLine += pData->srcLine * YRowBytes; pCbLine += (pData->srcLine >> 1) * CbRowBytes; pCrLine += (pData->srcLine >> 1) * CrRowBytes; } pPlane = pData->pDstImage->plane; dstRowBytes = pPlane->nBytesPerRow; pDstLine = pPlane->pPixels + dstLine * dstRowBytes; pPriv = (ilDitherYCbCrPrivPtr)pData->pPrivate; halfWidth = pData->pSrcImage->width >> 1; /* 1/2 # of lines, pixels per line */ nLinesDiv2 = *pNLines >> 1; pTable = pPriv->pixels; /* Do 4 pixels at a time, using 4 Ys for each Cb,Cr pair: get Cb,Cr into upper bits of "CbCr", then add in each Y and use result to index into table that yields 4 dithered pixels: . */ while (nLinesDiv2-- > 0) { pY = pYLine; pYLine += YRowBytes << 1; /* skip 2 lines; read 2 lines each loop */ pCb = pCbLine; pCbLine += CbRowBytes; pCr = pCrLine; pCrLine += CrRowBytes; pDst = pDstLine; pDstLine += dstRowBytes << 1; /* skip 2 lines; write 2 lines each loop */ nPixelsDiv2 = halfWidth; while (nPixelsDiv2-- > 0) { temp0 = *pCb++; CbCr = *pCr++; temp0 >>= (8 - DYCBCR_NBITS_CBCR); CbCr >>= (8 - DYCBCR_NBITS_CBCR); CbCr += temp0 << DYCBCR_NBITS_CBCR; CbCr <<= DYCBCR_NBITS_Y; /* CbCr now in upper bits w/ room for Y */ temp0 = pY[YRowBytes]; /* do pixel and pixel below */ temp1 = *pY++; temp0 = CbCr + (temp0 >> (8-DYCBCR_NBITS_Y)); pDst[dstRowBytes] = pTable[temp0] >> 8; temp1 = CbCr + (temp1 >> (8-DYCBCR_NBITS_Y)); *pDst++ = pTable[temp1] >> 24; temp0 = pY[YRowBytes]; /* do pixel and pixel below */ temp1 = *pY++; temp0 = CbCr + (temp0 >> (8-DYCBCR_NBITS_Y)); pDst[dstRowBytes] = pTable[temp0]; temp1 = CbCr + (temp1 >> (8-DYCBCR_NBITS_Y)); *pDst++ = pTable[temp1] >> 16; } } return IL_OK; } /* -------------------- ilFastYCbCrDitherDoubleExecute ---------------------- */ /* Execute() function: dither and double the given # of src lines. */ static ilError ilFastYCbCrDitherDoubleExecute ( ilExecuteData *pData, long dstLine, long *pNLines ) { ilDitherYCbCrPrivPtr pPriv; ilImagePlaneInfo *pPlane; long nLinesDiv2, halfWidth; long CbRowBytes, CrRowBytes, dstRowBytes; ilPtr pYLine, pCbLine, pCrLine; unsigned long *pTable; long YRowBytes, nPixelsDiv2, CbCr, temp0, temp1; long dstRowShorts, dstRowShortsTimes2, dstRowShortsTimes3; ilPtr pY, pCb, pCr; unsigned short *pDst; unsigned short *pDstLine; /* This filter handles a pipe image of YCbCr subsampled by 2 in Cb/Cr only. The # of lines of Cb/Cr is therefore 1/2 the # of lines of Y. Note: can only handle images with even width/height; checked elsewhere. */ pPlane = pData->pSrcImage->plane; YRowBytes = pPlane->nBytesPerRow; pYLine = pPlane->pPixels; pPlane++; CbRowBytes = pPlane->nBytesPerRow; pCbLine = pPlane->pPixels; pPlane++; CrRowBytes = pPlane->nBytesPerRow; pCrLine = pPlane->pPixels; if (pData->srcLine) { pYLine += pData->srcLine * YRowBytes; pCbLine += (pData->srcLine >> 1) * CbRowBytes; pCrLine += (pData->srcLine >> 1) * CrRowBytes; } /* Access dst buffer as shorts */ pPlane = pData->pDstImage->plane; dstRowBytes = pPlane->nBytesPerRow; pDstLine = (unsigned short *)(pPlane->pPixels + dstLine * dstRowBytes); dstRowShorts = dstRowBytes >> 1; dstRowShortsTimes2 = dstRowShorts * 2; dstRowShortsTimes3 = dstRowShorts * 3; pPriv = (ilDitherYCbCrPrivPtr)pData->pPrivate; halfWidth = pData->pSrcImage->width >> 1; /* 1/2 # of lines, pixels per line */ nLinesDiv2 = *pNLines >> 1; *pNLines <<= 1; /* write 2x # of src lines */ pTable = pPriv->pixels; /* Do 4 src pixels at a time, using 4 Ys for each Cb,Cr pair: get Cb,Cr into upper bits of "CbCr", then add in each Y and use result to index into table that yields 4 dithered pixels: . To double, output those 4 pixels as shown above; 1 src pixel becomes 4 dst. */ while (nLinesDiv2-- > 0) { pY = pYLine; pYLine += YRowBytes << 1; /* skip 2 lines; read 2 lines each loop */ pCb = pCbLine; pCbLine += CbRowBytes; pCr = pCrLine; pCrLine += CrRowBytes; pDst = pDstLine; pDstLine += dstRowShorts << 2; /* skip 4 lines; write 4 lines each loop */ nPixelsDiv2 = halfWidth; while (nPixelsDiv2-- > 0) { temp0 = *pCb++; CbCr = *pCr++; temp0 >>= (8 - DYCBCR_NBITS_CBCR); CbCr >>= (8 - DYCBCR_NBITS_CBCR); CbCr += temp0 << DYCBCR_NBITS_CBCR; CbCr <<= DYCBCR_NBITS_Y; /* CbCr now in upper bits w/ room for Y */ temp0 = pY[YRowBytes]; /* do pixel and pixel below */ temp1 = *pY++; temp0 = CbCr + (temp0 >> (8-DYCBCR_NBITS_Y)); temp0 = pTable[temp0]; pDst[dstRowShortsTimes3] = (unsigned short)temp0; pDst[dstRowShortsTimes2] = (unsigned short)(temp0 >> 16); temp1 = CbCr + (temp1 >> (8-DYCBCR_NBITS_Y)); temp1 = pTable[temp1]; pDst[dstRowShorts] = (unsigned short)temp1; *pDst++ = (unsigned short)(temp1 >> 16); temp0 = pY[YRowBytes]; /* do pixel and pixel below */ temp1 = *pY++; temp0 = CbCr + (temp0 >> (8-DYCBCR_NBITS_Y)); temp0 = pTable[temp0]; pDst[dstRowShortsTimes3] = (unsigned short)temp0; pDst[dstRowShortsTimes2] = (unsigned short)(temp0 >> 16); temp1 = CbCr + (temp1 >> (8-DYCBCR_NBITS_Y)); temp1 = pTable[temp1]; pDst[dstRowShorts] = (unsigned short)temp1; *pDst++ = (unsigned short)(temp1 >> 16); } } return IL_OK; } /* ---------------------------- ilSetupYCbCrDitherTables ----------------------- */ /* Setup *pTable as necessary for the YCbCr to RGB (dither) conversion. */ static void ilSetupYCbCrDitherTables ( ilYCbCrInfo *pYCbCr, int nBitsR, int nBitsG, int nBitsB, ilPtr pMapPixels, unsigned long *pTable ) { int nTableEntries, nLevelsY, nLevelsCbCr; int Y, Cb, Cr, refY, refCb, refCr; int R, G, B, pixel, i, temp, kernel; double Lr, Lg, Lb; double ditherR, ditherG, ditherB, ditherY, ditherCbCr; #define CVT_TO_RGB { \ int tY = Y - pYCbCr->sample[0].refBlack;\ int tCb = Cb - pYCbCr->sample[1].refBlack;\ int tCr = Cr - pYCbCr->sample[2].refBlack;\ R = tCr * (2 - 2 * Lr) + tY;\ B = tCb * (2 - 2 * Lb) + tY;\ G = (tY - Lb * B - Lr * R) / Lg;\ if (R < 0) R = 0; else if (R > 255) R = 255;\ if (G < 0) G = 0; else if (G > 255) G = 255;\ if (B < 0) B = 0; else if (B > 255) B = 255;\ } #define YCBCR_DEBUG_TABLE 1 #ifdef YCBCR_DEBUG_TABLE /* Set pMapPixels to identity, for debugging */ ilByte debugMapPixels[256]; int tmp; int doDebug = FALSE; if (doDebug) { for (tmp = 0; tmp < 256; tmp++) debugMapPixels[tmp] = tmp; pMapPixels = debugMapPixels; } #endif /* Build the YCbCr to dither-RGB lookup table. The table is indexed by a YCbCr value , where is "DYCBCR_NBITS_Y" bits in size and are each "DYCBCR_NBITS_CBCR" in size. Indexed that way the table yields either four dithered pixels or a long 24 X pixel. Each table entry is calculated by converting the YCbCr value to its RGB equivalent and either dithering to form 4 pixels or a single long X pixel. */ nLevelsY = 1 << DYCBCR_NBITS_Y; nLevelsCbCr = 1 << DYCBCR_NBITS_CBCR; Lr = ((double)pYCbCr->lumaRed / (double)10000); Lg = ((double)pYCbCr->lumaGreen / (double)10000); Lb = ((double)pYCbCr->lumaBlue / (double)10000); ditherR = ((1 << nBitsR) - 1) * (double)256 / (double)255; ditherG = ((1 << nBitsG) - 1) * (double)256 / (double)255; ditherB = ((1 << nBitsB) - 1) * (double)256 / (double)255; for (refCb = 0; refCb < nLevelsCbCr; refCb++) { Cb = (255 * refCb) / (nLevelsCbCr - 1); for (refCr = 0; refCr < nLevelsCbCr; refCr++) { Cr = (255 * refCr) / (nLevelsCbCr - 1); for (refY = 0; refY < nLevelsY; refY++) { Y = (255 * refY) / (nLevelsY - 1); /* Store 4 dithered pixels, the result of dithering RGB with a 2x2 kernel. See /ilc/ildither.c for equivalent code. */ #define DITHER #ifdef COLORSLAM CVT_TO_RGB pixel = R >> (8 - nBitsR); pixel <<= nBitsG; pixel |= G >> (8 - nBitsG); pixel <<= nBitsB; pixel |= B >> (8 - nBitsB); pixel = pMapPixels[pixel]; *pTable++ = (pixel << 24) | (pixel << 16) | (pixel << 8) | pixel; #else #ifdef DIFFUSION { int temp1, temp2; int halfR = (1 << (7 - nBitsR)) - 1; int halfG = (1 << (7 - nBitsG)) - 1; int halfB = (1 << (7 - nBitsB)) - 1; int maskR = ~((1 << (8 - nBitsR)) - 1); int maskG = ~((1 << (8 - nBitsG)) - 1); int maskB = ~((1 << (8 - nBitsB)) - 1); CVT_TO_RGB for (i = 0, pixel = 0; i < 4; i++) { temp1 = R + halfR; if (temp1 > 255) temp1 = 255; else if (temp1 < 0) temp1 = 0; R += R - (temp1 & maskR); temp = temp1 >> (8 - nBitsR); temp1 = G + halfG; if (temp1 > 255) temp1 = 255; else if (temp1 < 0) temp1 = 0; G += G - (temp1 & maskG); temp <<= nBitsG; temp |= temp1 >> (8 - nBitsG); temp1 = B + halfB; if (temp1 > 255) temp1 = 255; else if (temp1 < 0) temp1 = 0; B += B - (temp1 & maskB); temp <<= nBitsB; temp |= temp1 >> (8 - nBitsB); pixel <<= 8; pixel |= pMapPixels[temp]; } *pTable++ = pixel; } #else #ifdef DITHER CVT_TO_RGB R = (double)R * ditherR; G = (double)G * ditherG; B = (double)B * ditherB; for (i = 0, pixel = 0; i < 4; i++) { kernel = _il2x2DitherKernel[i]; temp = (R + kernel) >> 8; temp <<= nBitsG; temp |= (G + kernel) >> 8; temp <<= nBitsB; temp |= (B + kernel) >> 8; pixel <<= 8; pixel |= pMapPixels[temp]; } *pTable++ = pixel; #endif #endif #endif } } } } /* ------------------------ _ilFastYCbCrDither ---------------------------- */ /* Called by ilConvertForXWrite() to do fast dithered display of YCbCr. The pipe image must be planar, subsampled by 2 in Cb/Cr, 1 in Y. "pMapPixels" points to 256 bytes from the XWC map image, which are folded into the lookup table used for this function. */ IL_PRIVATE ilBool _ilFastYCbCrDither ( ilPipe pipe, ilImageDes *pDes, ilPipeInfo *pInfo, int nBitsRed, int nBitsGreen, int nBitsBlue, ilBool doDouble, ilPtr pMapPixels ) { ilImageDes des; ilDitherYCbCrPrivPtr pPriv; ilDstElementData dstData; ilSrcElementData srcData; /* Add a filter to convert subsampled YCbCr to a dithered palette image. Force a single strip; image is at least 2 by 2 */ srcData.consumerImage = (ilObject)NULL; srcData.stripHeight = pInfo->height; srcData.constantStrip = TRUE; srcData.minBufferHeight = 0; dstData.producerObject = (ilObject)NULL; dstData.pDes = IL_DES_GRAY; /* arbitrary; not really used */ dstData.pFormat = IL_FORMAT_BYTE; dstData.width = pInfo->width; dstData.height = pInfo->height; dstData.stripHeight = srcData.stripHeight; if (doDouble) { dstData.width *= 2; dstData.height *= 2; dstData.stripHeight *= 2; } dstData.constantStrip = pInfo->constantStrip; dstData.pPalette = (unsigned short *)NULL; pPriv = (ilDitherYCbCrPrivPtr)ilAddPipeElement (pipe, IL_FILTER, sizeof(ilDitherYCbCrPrivRec), 0, &srcData, &dstData, IL_NPF, IL_NPF, IL_NPF, (doDouble) ? ilFastYCbCrDitherDoubleExecute : ilFastYCbCrDitherExecute, 0); if (!pPriv) return FALSE; /* Init the table in private */ ilSetupYCbCrDitherTables (&pDes->typeInfo.YCbCr, nBitsRed, nBitsGreen, nBitsBlue, pMapPixels, pPriv->pixels); return TRUE; }