cdesktopenv/cde/lib/DtHelp/il/ilXycbcr.c

457 lines
18 KiB
C

/*
* 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 <math.h>
#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<<DYCBCR_NBITS_Y/CBCR. The YCbCr->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:
<left><right><below><below right>.
*/
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: <left><right><below><below right>.
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 <Cb><Cr><Y>, where <Y> is "DYCBCR_NBITS_Y" bits
in size and <Cb><Cr> 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;
}