12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358 |
- /*
- * 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: ilycbcr.c /main/5 1996/06/19 12:20:22 ageorge $ */
- /**---------------------------------------------------------------------
- ***
- *** (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/ilycbcr.c : Code for handling IL_YCBCR images, including
- conversions to/from RGB, and subsampling - upsampling is in /ilc/ilupsample.c
- */
- #include <stdlib.h>
- #include "ilint.h"
- #include "ilpipelem.h"
- #include "ilconvert.h"
- #include "ilerrors.h"
- /* ========================== Sub/Upsample Code ================================== */
- #define NSUBSAMPLES 3 /* hardwired to 3 samples: Y, Cb, Cr */
- /* Sub/Upsubsample factors as shifts (1=0, 2=1, 4=2) - others not supported. */
- typedef struct {
- unsigned int horiz, vert;
- } ilSubsampleShiftRec, *ilSubsampleShiftPtr;
- /* Private for sub/upsampling filters. */
- typedef struct {
- int nSamples; /* # of samples (components) to process */
- ilSubsampleShiftRec shift[NSUBSAMPLES]; /* upsample mul as shift value */
- } ilSubsamplePrivRec, *ilSubsamplePrivPtr;
- /* ========================== Subsample Code =================================== */
- /* -------------------------- ilExecuteSubsample ------------------------------- */
- /* Execute(): subsample as necessary "pPriv->nSamples" planes of the source image.
- The source image must be planar, byte/pixel.
- */
- static ilError ilExecuteSubsample (
- ilExecuteData *pData,
- long dstLine,
- long *pNLines /* ignored on input */
- )
- {
- ilSubsamplePrivPtr pPriv;
- ilImagePlaneInfo *pSrcPlane, *pDstPlane;
- int nSamples;
- ilPtr pSrcLine, pDstLine;
- long height, width;
- long srcRowInc, dstRowBytes;
- long nDstLinesM1, nDstPixelsM1Init;
- ilSubsampleShiftPtr pShift;
- long nDstPixelsM1, srcRowBytes, i;
- ilPtr pSrc, pDst;
- unsigned int pixel;
- /* Get width and height of _upsampled_ image; exit if zero. */
- pPriv = (ilSubsamplePrivPtr)pData->pPrivate;
- height = *pNLines;
- if (height <= 0)
- return IL_OK;
- width = pData->pSrcImage->width;
- if (width <= 0)
- return IL_OK;
- /* Loop on samples (components), upsample/translate each plane separately.
- Note that "srcLine" is shifted - indexing into plane based on vert subsample.
- */
- for (pSrcPlane = pData->pSrcImage->plane, pDstPlane = pData->pDstImage->plane,
- pShift = pPriv->shift, nSamples = pPriv->nSamples;
- nSamples-- > 0;
- pSrcPlane++, pDstPlane++, pShift++) {
- /* Get # dst lines/pixels across - 1; skip plane if none */
- nDstLinesM1 = height >> pShift->vert;
- if (nDstLinesM1 <= 0)
- continue;
- nDstLinesM1--;
- nDstPixelsM1Init = width >> pShift->horiz;
- if (nDstPixelsM1Init <= 0)
- continue;
- nDstPixelsM1Init--;
- /* Point to src/dst; note dstLine scaled down by vert subsample shift.
- Set srcRowInc to srcRowBytes scaled up by vertical shift.
- */
- srcRowBytes = pSrcPlane->nBytesPerRow;
- pSrcLine = pSrcPlane->pPixels + pData->srcLine * srcRowBytes;
- srcRowInc = srcRowBytes << pShift->vert;
- dstRowBytes = pDstPlane->nBytesPerRow;
- pDstLine = pDstPlane->pPixels + (dstLine >> pShift->vert) * dstRowBytes;
- switch (pShift->horiz) { /* NOTE: vert shift must be the same! */
- /* 0 = no subsampling; just copy plane info */
- case 0:
- pSrc = pSrcLine;
- pDst = pDstLine;
- do {
- bcopy ((char *)pSrc, (char *)pDst, width);
- pSrc += srcRowBytes;
- pDst += dstRowBytes;
- } while (--nDstLinesM1 >= 0);
- break;
- /* 1 = 1/4 of pixels (half in each dimension). Take the average of 4
- pixels: (x,y), (x+1,y), (x,y+1), (x+1,y+1), for each even x,y.
- */
- case 1:
- do {
- pSrc = pSrcLine;
- pSrcLine += srcRowInc;
- pDst = pDstLine;
- pDstLine += dstRowBytes;
- nDstPixelsM1 = nDstPixelsM1Init;
- do {
- pixel = *pSrc;
- pixel += *(pSrc + 1);
- pixel += *(pSrc + srcRowBytes);
- pixel += *(pSrc + srcRowBytes + 1);
- pSrc += 2; /* two pixels to the right */
- *pDst++ = pixel >> 2; /* store 1/4 sum of 4 pixels */
- } while (--nDstPixelsM1 >= 0);
- } while (--nDstLinesM1 >= 0);
- break;
- /* 2 = 1/16 of pixels (1/4 in each dimension). Take the average of 16 pixels,
- a block below and to the right of (x,y), x,y = multiple of 4.
- */
- case 2:
- do {
- pSrc = pSrcLine;
- pSrcLine += srcRowInc;
- pDst = pDstLine;
- pDstLine += dstRowBytes;
- nDstPixelsM1 = nDstPixelsM1Init;
- do {
- pixel = 0;
- i = 3;
- do {
- pixel += *pSrc;
- pixel += *(pSrc + 1);
- pixel += *(pSrc + 2);
- pixel += *(pSrc + 3);
- pSrc += srcRowBytes;
- } while (--i >= 0);
- *pDst++ = pixel >> 4; /* store 1/16 sum of 16 pixels */
- pSrc += -srcRowInc + 4; /* back to top line, 4 to right */
- } while (--nDstPixelsM1 >= 0);
- } while (--nDstLinesM1 >= 0);
- break;
- } /* END switch subsample shift */
- } /* END each plane */
- return IL_OK;
- }
- /* ---------------------------- ilSubsampleYCbCr ----------------------------- */
- /* Subsample the pipe image which must be a fully upsampled YCbCr image.
- pFormat points to the source format; on return, *pFormat is updated
- to the dst format. *pDes to the _destination_ des, i.e. has subsample values.
- */
- IL_PRIVATE ilBool _ilSubsampleYCbCr (
- ilPipe pipe,
- ilPipeInfo *pInfo,
- ilImageDes *pDes,
- ilImageFormat *pFormat
- )
- {
- ilSubsamplePrivPtr pSubPriv;
- ilSubsamplePrivRec priv;
- ilSubsampleShiftRec *pShift;
- ilYCbCrSampleInfo *pSample;
- int sample;
- ilDstElementData dstData;
- ilSrcElementData srcData;
- /* Only 8 bit planar YCbCr current handled; cvt if not that format. */
- if ((pFormat->sampleOrder != IL_SAMPLE_PLANES)
- || (pFormat->nBitsPerSample[0] != 8)
- || (pFormat->nBitsPerSample[1] != 8)
- || (pFormat->nBitsPerSample[2] != 8)) {
- *pFormat = *IL_FORMAT_3BYTE_PLANE;
- if (!ilConvert (pipe, (ilImageDes *)NULL, pFormat, 0, NULL))
- return FALSE;
- }
- /* Determine subsample values: only 1 (no subsampling), 2 (1/4 = 1/2 in each
- dimension) or 4 (1/16) allowed; same both vertical/horizontal.
- */
- priv.nSamples = NSUBSAMPLES;
- pSample = pDes->typeInfo.YCbCr.sample;
- pShift = priv.shift;
- for (sample = 0; sample < NSUBSAMPLES; sample++, pShift++, pSample++) {
- if (pSample->subsampleHoriz != pSample->subsampleVert)
- return ilDeclarePipeInvalid (pipe, IL_ERROR_SUBSAMPLE);
- switch ((unsigned int)pSample->subsampleHoriz) {
- case 1: pShift->horiz = 0; break;
- case 2: pShift->horiz = 1; break;
- case 4: pShift->horiz = 2; break;
- default: return ilDeclarePipeInvalid (pipe, IL_ERROR_SUBSAMPLE);
- }
- pShift->vert = pShift->horiz; /* same subsampling both dimensions */
- }
- /* Init dstData, add subsample filter. */
- dstData.producerObject = (ilObject)NULL;
- dstData.pDes = pDes;
- dstData.pFormat = pFormat;
- dstData.width = pInfo->width;
- dstData.height = pInfo->height;
- dstData.stripHeight = 0;
- dstData.constantStrip = FALSE;
- dstData.pPalette = (unsigned short *)NULL;
-
- /* Demand constant strips, a multiple of maximum subsample (4). */
- srcData.consumerImage = (ilObject)NULL;
- srcData.stripHeight = 8; /* Arbitrary, but must be multiple of 4 */
- srcData.constantStrip = TRUE;
- srcData.minBufferHeight = 0;
- pSubPriv = (ilSubsamplePrivPtr)ilAddPipeElement (pipe, IL_FILTER,
- sizeof (ilSubsamplePrivRec), 0, &srcData,
- &dstData, IL_NPF, IL_NPF, IL_NPF, ilExecuteSubsample, NULL, 0);
- if (!pSubPriv)
- return FALSE;
- *pSubPriv = priv; /* copy priv to priv ptr */
- return TRUE;
- }
- /* ======================= YCbCr to RGB Code =================================== */
- /* Lookup tables for YCbCr to RGB conversion. Each table is indexed by Y, Cb or Cr,
- and contains two values; one the upper short (1st) the other in the lower (2nd);
- the upper short values are used to compute green.
- Y G cvt: Y factor = (1 - Lb - Lr)/Lg scale for refBlack/White
- Cb G cvt: Cb factor = -Lb*(2 - 2*Lb)/Lg Cb scale * (2 - 2 * Lr)
- Cr G cvt: Cr factor = -Lr*(2 - 2*Lr)/Lg Cr scale * (2 - 2 * Lb)
- */
- typedef struct {
- long YTrans[256]; /* values indexed by Y, as above */
- long CbTrans[256]; /* values indexed by Cb, as above */
- long CrTrans[256]; /* values indexed by Cr, as above */
- } ilRGBTransRec, *ilRGBTransPtr;
- /* Private for RGB to YCbCr filter. */
- typedef struct {
- ilRGBTransPtr pTrans; /* ptr to all the translate tables */
- long y; /* for dither only: current Y */
- } ilYCbCrToRGBPrivRec, *ilYCbCrToRGBPrivPtr;
- /* Pointer to "std" translation table: works for "std" YCbCr values -
- those in IL_DES_YCBCR.
- */
- static ilRGBTransPtr _ilStdYCbCrTransPtr = (ilRGBTransPtr)NULL;
- /* --------------------------- ilDestroyTransTable --------------------- */
- /* Destroy function: used only if pPriv->pTrans points to locally malloc'd
- translate table, i.e. not using std values and table.
- */
- static ilError ilDestroyTransTable (
- ilYCbCrToRGBPrivPtr pPriv
- )
- {
- IL_FREE (pPriv->pTrans);
- return IL_OK;
- }
- /* --------------------- ilExecutePlanarYCbCrToRGB ----------------------------- */
- /* Execute(): convert _planar_ src YCbCr to _pixel_ dst RGB.
- NOTE: the YCbCr image must have been upsampled, i.e. thru ilExecuteUpsample().
- */
- static ilError ilExecutePlanarYCbCrToRGB (
- ilExecuteData *pData,
- long dstLine,
- long *pNLines
- )
- {
- ilPtr pYLine, pCbLine, pCrLine, pDstLine;
- ilRGBTransPtr pTrans;
- ilPtr pY, pCb, pCr, pDst;
- long YTrans, CbTrans, CrTrans, pixel, nPixelsM1;
- int Y;
- long nPixelsM1Init, nLinesM1;
- long YRowBytes, CbRowBytes, CrRowBytes, dstRowBytes;
- ilImagePlaneInfo *pPlane;
- /* Set nPixels/LinesM1 to # pixels / lines - 1; exit if either 0. */
- nPixelsM1Init = pData->pSrcImage->width;
- if (nPixelsM1Init <= 0)
- return 0;
- nPixelsM1Init--;
- nLinesM1 = *pNLines;
- if (nLinesM1 <= 0)
- return 0;
- nLinesM1--;
- /* Point pY/Cb/CrLine to 1st line in src planes; pDstLine to 1st line in dst.
- Get bytes/row for each of them.
- */
- pPlane = pData->pSrcImage->plane;
- YRowBytes = pPlane->nBytesPerRow;
- pYLine = pPlane->pPixels + pData->srcLine * YRowBytes;
- pPlane++;
- CbRowBytes = pPlane->nBytesPerRow;
- pCbLine = pPlane->pPixels + pData->srcLine * CbRowBytes;
- pPlane++;
- CrRowBytes = pPlane->nBytesPerRow;
- pCrLine = pPlane->pPixels + pData->srcLine * CrRowBytes;
- pPlane = pData->pDstImage->plane;
- dstRowBytes = pPlane->nBytesPerRow;
- pDstLine = pPlane->pPixels + dstLine * dstRowBytes;
- /* Convert planar YCbCr to pixel RGB. */
- pTrans = ((ilYCbCrToRGBPrivPtr)pData->pPrivate)->pTrans;
- do {
- pY = pYLine;
- pYLine += YRowBytes;
- pCb = pCbLine;
- pCbLine += CbRowBytes;
- pCr = pCrLine;
- pCrLine += CrRowBytes;
- pDst = pDstLine;
- pDstLine += dstRowBytes;
- nPixelsM1 = nPixelsM1Init;
- do {
- YTrans = pTrans->YTrans[*pY++];
- CrTrans = pTrans->CrTrans[*pCr++];
- Y = ((short)YTrans);
- CbTrans = pTrans->CbTrans[*pCb++];
- pixel = Y + ((short)CrTrans);
- if (pixel >> 8) goto plClipR;
- plRClipped: *pDst++ = pixel; /* store red */
- pixel = YTrans >> 16;
- pixel += CbTrans >> 16;
- pixel += CrTrans >> 16;
- if (pixel >> 8) goto plClipG;
- plGClipped: *pDst++ = pixel; /* store green */
- pixel = Y + ((short)CbTrans);
- if (pixel >> 8) goto plClipB;
- plBClipped: *pDst++ = pixel; /* store blue */
- } while (--nPixelsM1 >= 0);
- } while (--nLinesM1 >= 0);
- return IL_OK;
- /* goto point for when R, G or B out of range: clamp pixel and go back and continue.
- This is done to avoid taking a branch in the most common case: not out of range.
- */
- plClipR: if (pixel > 255) pixel = 255; else pixel = 0;
- goto plRClipped;
- plClipG: if (pixel > 255) pixel = 255; else pixel = 0;
- goto plGClipped;
- plClipB: if (pixel > 255) pixel = 255; else pixel = 0;
- goto plBClipped;
- }
- /* --------------------- ilExecutePixelYCbCrToRGB ----------------------------- */
- /* Execute(): convert _pixel_ src YCbCr to _pixel_ dst RGB.
- NOTE: the YCbCr image must have been upsampled, i.e. thru ilExecuteUpsample().
- */
- static ilError ilExecutePixelYCbCrToRGB (
- ilExecuteData *pData,
- long dstLine,
- long *pNLines
- )
- {
- ilPtr pSrcLine, pDstLine;
- ilRGBTransPtr pTrans;
- ilPtr pSrc, pDst;
- long YTrans, CbTrans, CrTrans, pixel, nPixelsM1;
- int Y;
- long nPixelsM1Init, nLinesM1;
- long srcRowBytes, dstRowBytes;
- ilImagePlaneInfo *pPlane;
- /* Set nPixels/LinesM1 to # pixels / lines - 1; exit if either 0. */
- nPixelsM1Init = pData->pSrcImage->width;
- if (nPixelsM1Init <= 0)
- return 0;
- nPixelsM1Init--;
- nLinesM1 = *pNLines;
- if (nLinesM1 <= 0)
- return 0;
- nLinesM1--;
- /* Point pY/Cb/CrLine to 1st line in src planes; pDstLine to 1st line in dst.
- Get bytes/row for each of them.
- */
- pPlane = pData->pSrcImage->plane;
- srcRowBytes = pPlane->nBytesPerRow;
- pSrcLine = pPlane->pPixels + pData->srcLine * srcRowBytes;
- pPlane = pData->pDstImage->plane;
- dstRowBytes = pPlane->nBytesPerRow;
- pDstLine = pPlane->pPixels + dstLine * dstRowBytes;
- /* Convert pixel YCbCr to pixel RGB. */
- pTrans = ((ilYCbCrToRGBPrivPtr)pData->pPrivate)->pTrans;
- do {
- pSrc = pSrcLine;
- pSrcLine += srcRowBytes;
- pDst = pDstLine;
- pDstLine += dstRowBytes;
- nPixelsM1 = nPixelsM1Init;
- do {
- YTrans = pTrans->YTrans[*pSrc++];
- CbTrans = pTrans->CbTrans[*pSrc++];
- Y = ((short)YTrans);
- CrTrans = pTrans->CrTrans[*pSrc++];
- pixel = Y + ((short)CrTrans);
- if (pixel >> 8) goto piClipR;
- piRClipped: *pDst++ = pixel; /* store red */
- pixel = YTrans >> 16;
- pixel += CbTrans >> 16;
- pixel += CrTrans >> 16;
- if (pixel >> 8) goto piClipG;
- piGClipped: *pDst++ = pixel; /* store green */
- pixel = Y + ((short)CbTrans);
- if (pixel >> 8) goto piClipB;
- piBClipped: *pDst++ = pixel; /* store blue */
- } while (--nPixelsM1 >= 0);
- } while (--nLinesM1 >= 0);
- return IL_OK;
- /* goto point for when R, G or B out of range: clamp pixel and go back and continue.
- This is done to avoid taking a branch in the most common case: not out of range.
- */
- piClipR: if (pixel > 255) pixel = 255; else pixel = 0;
- goto piRClipped;
- piClipG: if (pixel > 255) pixel = 255; else pixel = 0;
- goto piGClipped;
- piClipB: if (pixel > 255) pixel = 255; else pixel = 0;
- goto piBClipped;
- }
- /* ------------------------- ilGetYCbCrConvertTable -------------------------- */
- /* Returns a pointer to the YCbCr conversion table (ilRGBTransRec); either
- "_ilStdYCbCrTransPtr" or a ptr to a table that caller must destroy, or
- null if a malloc error occurs.
- */
- static ilRGBTransPtr ilGetYCbCrConvertTable (
- ilImageDes *pDes
- )
- {
- ilRGBTransPtr pTrans;
- const ilYCbCrInfo *pYCbCr, *pStdYCbCr;
- double Lr, Lg, Lb, scaledY;
- double factor, gFactor;
- int i, refBlack, upper, lower;
- ilBool isStd;
- struct {
- int refBlack;
- double scale;
- } sample [3];
- /* If luma and refBlack/White info same as std IL_DES_YCBCR, then reuse
- the table at _ilStdYCbCrTransPtr; create it here if does not exist yet.
- */
- pYCbCr = &pDes->typeInfo.YCbCr;
- pStdYCbCr = &IL_DES_YCBCR->typeInfo.YCbCr;
- if ((pYCbCr->sample[0].refBlack == pStdYCbCr->sample[0].refBlack)
- && (pYCbCr->sample[0].refWhite == pStdYCbCr->sample[0].refWhite)
- && (pYCbCr->sample[1].refBlack == pStdYCbCr->sample[1].refBlack)
- && (pYCbCr->sample[1].refWhite == pStdYCbCr->sample[1].refWhite)
- && (pYCbCr->sample[2].refBlack == pStdYCbCr->sample[2].refBlack)
- && (pYCbCr->sample[2].refWhite == pStdYCbCr->sample[2].refWhite)
- && (pYCbCr->lumaRed == pStdYCbCr->lumaRed)
- && (pYCbCr->lumaGreen == pStdYCbCr->lumaGreen)
- && (pYCbCr->lumaBlue == pStdYCbCr->lumaBlue)) {
- isStd = TRUE;
- pTrans = _ilStdYCbCrTransPtr; /* alloc below if not there yet */
- }
- else {
- isStd = FALSE;
- pTrans = (ilRGBTransPtr)NULL; /* allocate it below */
- }
- /* Build translate tables. First, from pg 3 of Appendix O of TIFF spec:
- R = Cr * (2 - 2 * Lr) + Y
- B = Cb * (2 - 2 * Lb) + Y
- G = (Y - Lb * B - Lr * R) / Lg
- Get green in terms of Cb and Cr:
- G = (Y - Lb*B - Lr*R) / Lg
- G = (Y - Lb*(Cb * (2 - 2*Lb) + Y) - Lr*(Cr * (2 - 2*Lr) + Y)) / Lg
- G = (Y - Lb*Cb*(2 - 2*Lb) - Lb*Y - Lr*Cr*(2 - 2*Lr) - Lr*Y) / Lg
- G = (Y - Lb*Y - Lr*Y - Cb*Lb*(2 - 2*Lb) - Cr*Lr*(2 - 2*Lr)) / Lg
- G = (Y * (1 - Lb - Lr) - Cb*Lb*(2 - 2*Lb) - Cr*Lr*(2 - 2*Lr)) / Lg
- G = Y * (1 - Lb - Lr)/Lg + Cb * -Lb*(2 - 2*Lb)/Lg + Cr * -Lr*(2 - 2*Lr)/Lg
- */
- if (!pTrans) { /* non-std or std table not build yet */
- pTrans = (ilRGBTransPtr)IL_MALLOC (sizeof (ilRGBTransRec));
- if (!pTrans)
- return pTrans;
- /* The Y, Cb and Cr components must be scaled up based on refBlack/White.
- Factor that scaling in everywhere Y/Cb/Cr are looked up.
- First get the scale and refBlack for each sample.
- */
- for (i = 0; i < 3; i++) {
- sample[i].refBlack = pYCbCr->sample[i].refBlack;
- sample[i].scale = ((i == 0) ? 255 : 127) /
- (pYCbCr->sample[i].refWhite - pYCbCr->sample[i].refBlack);
- }
- /* Calc the Cb/Cr lookup tables, factoring in their scale factors. */
- Lr = ((double)pYCbCr->lumaRed / (double)10000);
- Lg = ((double)pYCbCr->lumaGreen / (double)10000);
- Lb = ((double)pYCbCr->lumaBlue / (double)10000);
- /* Y translation: lower is scaled Y, upper is value added to get green */
- gFactor = (1 - Lb - Lr) / Lg;
- for (i = 0; i < 256; i++) {
- scaledY = sample[0].scale * i - sample[0].refBlack;
- upper = scaledY * gFactor + 0.5;
- lower = scaledY;
- pTrans->YTrans[i] = (upper << 16) | (lower & 0xffff);
- }
- /* Cb: lower is added to Y to get blue, upper added to get green */
- factor = (2 - 2 * Lb) * sample[1].scale;
- gFactor = -Lb * (2 - 2 * Lb) / Lg;
- refBlack = sample[1].refBlack;
- for (i = 0; i < 256; i++) {
- upper = (i - refBlack) * gFactor + 0.5;
- lower = (i - refBlack) * factor + 0.5;
- pTrans->CbTrans[i] = (upper << 16) | (lower & 0xffff);
- }
- /* Cr: lower is added to Y to get red, upper added to get green */
- factor = (2 - 2 * Lr) * sample[2].scale;
- gFactor = -Lr * (2 - 2 * Lr) / Lg;
- refBlack = sample[2].refBlack;
- for (i = 0; i < 256; i++) {
- upper = (i - refBlack) * gFactor + 0.5;
- lower = (i - refBlack) * factor + 0.5;
- pTrans->CrTrans[i] = (upper << 16) | (lower & 0xffff);
- }
- /* If creating "std" conversion table for first time, set ptr to it */
- if (isStd)
- _ilStdYCbCrTransPtr = pTrans;
- } /* END build table */
- return pTrans;
- }
- /* ---------------------------- ilConvertYCbCrToRGB ----------------------------- */
- /* Convert from source type (pDes->type) == IL_YCBCR to RBB.
- pFormat points to the source format; on return, *pFormat is updated
- to the dst format, *pDes to the dst descriptor.
- */
- IL_PRIVATE ilBool _ilConvertYCbCrToRGB (
- ilPipe pipe,
- ilPipeInfo *pInfo,
- ilImageDes *pDes,
- ilImageFormat *pFormat
- )
- {
- ilDstElementData dstData;
- ilError (*executeFunction)(), (*destroyFunction)();
- ilYCbCrToRGBPrivPtr pPriv;
- ilRGBTransPtr pTrans;
- if ((pFormat->nBitsPerSample[0] != 8)
- || (pFormat->nBitsPerSample[1] != 8)
- || (pFormat->nBitsPerSample[2] != 8))
- return ilDeclarePipeInvalid (pipe, IL_ERROR_NOT_IMPLEMENTED);
- /* Handle planar/pixel src format with different executeFunction, but in
- either case output RGB in 3 byte pixel format.
- */
- if (pFormat->sampleOrder == IL_SAMPLE_PIXELS)
- executeFunction = ilExecutePixelYCbCrToRGB;
- else {
- executeFunction = ilExecutePlanarYCbCrToRGB;
- *pFormat = *IL_FORMAT_3BYTE_PIXEL;
- }
- /* Get translation table or error; destroy it if not the "std" one */
- pTrans = ilGetYCbCrConvertTable (pDes);
- if (!pTrans)
- return ilDeclarePipeInvalid (pipe, IL_ERROR_MALLOC);
- destroyFunction = (pTrans == _ilStdYCbCrTransPtr) ? IL_NPF : ilDestroyTransTable;
- /* Add element to the pipe, init private */
- dstData.producerObject = (ilObject)NULL;
- *pDes = *IL_DES_RGB;
- dstData.pDes = pDes;
- dstData.pFormat = pFormat;
- dstData.width = pInfo->width;
- dstData.height = pInfo->height;
- dstData.stripHeight = 0;
- dstData.constantStrip = FALSE;
- dstData.pPalette = (unsigned short *)NULL;
- pPriv = (ilYCbCrToRGBPrivPtr)ilAddPipeElement (pipe, IL_FILTER,
- sizeof(ilYCbCrToRGBPrivRec), 0, (ilSrcElementData *)NULL, &dstData,
- IL_NPF, IL_NPF, destroyFunction, executeFunction, NULL, 0);
- if (!pPriv) {
- if (destroyFunction) /* local copy of pTrans; free it */
- IL_FREE (pTrans);
- return FALSE;
- }
- pPriv->pTrans = pTrans;
- return TRUE;
- }
- /* ======================= Dither YCbCr Code =================================== */
- /* Defines for # bits of RGB to dither to. NOTE! however that these are not
- arbitrary; # bits of red must = # bits of green, because the pre-dither-mul'd
- value of "Y" is the same for both.
- */
- #define YCBCR2_NR_BITS 2 /* # of bits of R/G/B to dither to */
- #define YCBCR2_NG_BITS 3
- #define YCBCR2_NB_BITS 2
- #define YCBCR2_MAX_R ((1 << YCBCR2_NR_BITS) - 1) /* max value for R/G/B */
- #define YCBCR2_MAX_G ((1 << YCBCR2_NG_BITS) - 1)
- #define YCBCR2_MAX_B ((1 << YCBCR2_NB_BITS) - 1)
- /* Private for RGB to dithered pixel filter. */
- typedef struct {
- ilRGBTransRec trans; /* YCbCr->dither-mul'd values tables */
- ilPtr pTranslate; /* ptr to ilMap() lookup table pixels */
- unsigned short *pPalette; /* ptr to palette to destroy or null */
- long y; /* Init(): current Y within dst image */
- ilByte translate [256]; /* identity image if no mapImage given */
- } ilYCbCr2DitherPrivRec, *ilYCbCr2DitherPrivPtr;
- /* Init() function: init the counter of "y" within private */
- static ilError ilYCbCr2DitherInit (
- ilYCbCr2DitherPrivPtr pPriv,
- ilImageInfo *pSrcImage,
- ilImageInfo *pDstImage
- )
- {
- pPriv->y = 0;
- return IL_OK;
- }
- /* Destroy function: destroy pTrans if not std and pPalette if not null */
- static ilError ilYCbCr2DitherDestroy (
- ilYCbCr2DitherPrivPtr pPriv
- )
- {
- if (pPriv->pPalette)
- IL_FREE (pPriv->pPalette);
- return IL_OK;
- }
- /* ---------------------- ilYCbCr2DitherExecute ------------------------ */
- /* Execute() function: dither from subsampled-by-2 YCbCr to 8 bit pixels.
- Dithering is done to RGB levels of 484 (232 in bits).
- */
- static ilError ilYCbCr2DitherExecute (
- ilExecuteData *pData,
- long dstLine,
- long *pNLines
- )
- {
- ilYCbCr2DitherPrivPtr pPriv;
- ilImagePlaneInfo *pPlane;
- long nLinesDiv2, halfWidthM1, y;
- long CbRowBytes, CrRowBytes;
- ilPtr pYLine, pCbLine, pCrLine, pDstLine;
- int *pKernelRowEnd;
- long nPixelsDiv2M1;
- ilPtr pCb, pCr;
- long YRowBytes, dstRowBytes;
- long Y, Cb, Cr, YTrans, CbTrans, CrTrans, comp, pixel;
- ilPtr pY, pDst;
- ilRGBTransPtr pTrans;
- ilPtr pTranslate;
- int *pKernel, kernel;
- #define YCBCR2_KERNEL_SIZE 8 /* size of dither kernel used */
- /* 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 = (ilYCbCr2DitherPrivPtr)pData->pPrivate;
- halfWidthM1 = pData->pSrcImage->width >> 1; /* 1/2 # of pixels - 1 */
- nLinesDiv2 = *pNLines >> 1;
- if ((halfWidthM1 <= 0) || (nLinesDiv2 <= 0))
- return IL_OK;
- halfWidthM1 -= 1;
- y = pPriv->y;
- pPriv->y += *pNLines;
- pTrans = &pPriv->trans;
- pTranslate = pPriv->pTranslate;
- /* Do 4 pixels at a time, using 4 Ys (below L, L, below R, R) for each Cb,Cr pair.
- For each Y, do one pixel: get "comp" (RGB) as done in other YCbCr->RGB
- filters, except that all values are pre-multiplied by what the dither tables
- would yield if the lookup to _ilMul4/8[] was done. "pixel" is then used as
- an index into pTranslate table to get pixel to write to dst.
- See comments in ilGetYCbCrDitherTable() for details.
- */
- 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 */
- nPixelsDiv2M1 = halfWidthM1;
- /* Point to even row within kernel based on "y"; point to end of row */
- pKernel = (int *)&_ilDitherKernel[(y & 6) * YCBCR2_KERNEL_SIZE];
- y += 2;
- pKernelRowEnd = pKernel + 8;
- /* Dither 4 pixels, using one set of Cb,Cr and 4 Y's. */
- do {
- CrTrans = pTrans->CrTrans[*pCr++];
- CbTrans = pTrans->CbTrans[*pCb++];
- Cr = ((short)CrTrans);
- CrTrans >>= 16;
- Cb = ((short)CbTrans);
- CbTrans >>= 16;
- CbTrans += CrTrans; /* add Cb, Cr contributions to green */
- /* Do pixel below */
- YTrans = pTrans->YTrans[pY[YRowBytes]];
- kernel = pKernel[YCBCR2_KERNEL_SIZE];
- Y = ((short)YTrans);
- Y += kernel; /* pre-add kernel to Y */
- pixel = (Y + Cr) >> 8;
- if (pixel >> YCBCR2_NR_BITS) goto d0ClipR;
- d0RClipped: comp = ((YTrans >> 16) + CbTrans + kernel) >> 8;
- if (comp >> YCBCR2_NG_BITS) goto d0ClipG;
- d0GClipped: pixel = (pixel << YCBCR2_NG_BITS) + comp;
- comp = (Y + Cb) >> 8;
- if (comp >> YCBCR2_NB_BITS) goto d0ClipB;
- d0BClipped: pixel = (pixel << YCBCR2_NB_BITS) + comp;
- pDst[dstRowBytes] = pTranslate[pixel];
- /* Do pixel, move pKernel, pY, pDst one to the right */
- YTrans = pTrans->YTrans[*pY++];
- kernel = *pKernel++;
- Y = ((short)YTrans);
- Y += kernel;
- pixel = (Y + Cr) >> 8;
- if (pixel >> YCBCR2_NR_BITS) goto d1ClipR;
- d1RClipped: comp = ((YTrans >> 16) + CbTrans + kernel) >> 8;
- if (comp >> YCBCR2_NG_BITS) goto d1ClipG;
- d1GClipped: pixel = (pixel << YCBCR2_NG_BITS) + comp;
- comp = (Y + Cb) >> 8;
- if (comp >> YCBCR2_NB_BITS) goto d1ClipB;
- d1BClipped: pixel = (pixel << YCBCR2_NB_BITS) + comp;
- *pDst++ = pTranslate[pixel];
- /* Same as above two steps for one pixel to the right */
- YTrans = pTrans->YTrans[pY[YRowBytes]];
- kernel = pKernel[YCBCR2_KERNEL_SIZE];
- Y = ((short)YTrans);
- Y += kernel; /* pre-add kernel to Y */
- pixel = (Y + Cr) >> 8;
- if (pixel >> YCBCR2_NR_BITS) goto d2ClipR;
- d2RClipped: comp = ((YTrans >> 16) + CbTrans + kernel) >> 8;
- if (comp >> YCBCR2_NG_BITS) goto d2ClipG;
- d2GClipped: pixel = (pixel << YCBCR2_NG_BITS) + comp;
- comp = (Y + Cb) >> 8;
- if (comp >> YCBCR2_NB_BITS) goto d2ClipB;
- d2BClipped: pixel = (pixel << YCBCR2_NB_BITS) + comp;
- pDst[dstRowBytes] = pTranslate[pixel];
- /* Do pixel, move pKernel, pY, pDst one to the right */
- YTrans = pTrans->YTrans[*pY++];
- kernel = *pKernel++;
- Y = ((short)YTrans);
- Y += kernel;
- pixel = (Y + Cr) >> 8;
- if (pixel >> YCBCR2_NR_BITS) goto d3ClipR;
- d3RClipped: comp = ((YTrans >> 16) + CbTrans + kernel) >> 8;
- if (comp >> YCBCR2_NG_BITS) goto d3ClipG;
- d3GClipped: pixel = (pixel << YCBCR2_NG_BITS) + comp;
- comp = (Y + Cb) >> 8;
- if (comp >> YCBCR2_NB_BITS) goto d3ClipB;
- d3BClipped: pixel = (pixel << YCBCR2_NB_BITS) + comp;
- *pDst++ = pTranslate[pixel];
- /* If pKernel off end of row, reset back (-8) to beginning of row */
- if (pKernel >= pKernelRowEnd)
- pKernel -= YCBCR2_KERNEL_SIZE;
- } while (--nPixelsDiv2M1 >= 0);
- }
- return IL_OK;
- /* goto point for when R, G or B out of range: clamp pixel and go back and continue.
- This is done to avoid taking a branch in the most common case: not out of range.
- */
- d0ClipR: if (pixel > YCBCR2_MAX_R) pixel = YCBCR2_MAX_R; else pixel = 0;
- goto d0RClipped;
- d0ClipG: if (comp > YCBCR2_MAX_G) comp = YCBCR2_MAX_G; else comp = 0;
- goto d0GClipped;
- d0ClipB: if (comp > YCBCR2_MAX_B) comp = YCBCR2_MAX_B; else comp = 0;
- goto d0BClipped;
- d1ClipR: if (pixel > YCBCR2_MAX_R) pixel = YCBCR2_MAX_R; else pixel = 0;
- goto d1RClipped;
- d1ClipG: if (comp > YCBCR2_MAX_G) comp = YCBCR2_MAX_G; else comp = 0;
- goto d1GClipped;
- d1ClipB: if (comp > YCBCR2_MAX_B) comp = YCBCR2_MAX_B; else comp = 0;
- goto d1BClipped;
- d2ClipR: if (pixel > YCBCR2_MAX_R) pixel = YCBCR2_MAX_R; else pixel = 0;
- goto d2RClipped;
- d2ClipG: if (comp > YCBCR2_MAX_G) comp = YCBCR2_MAX_G; else comp = 0;
- goto d2GClipped;
- d2ClipB: if (comp > YCBCR2_MAX_B) comp = YCBCR2_MAX_B; else comp = 0;
- goto d2BClipped;
- d3ClipR: if (pixel > YCBCR2_MAX_R) pixel = YCBCR2_MAX_R; else pixel = 0;
- goto d3RClipped;
- d3ClipG: if (comp > YCBCR2_MAX_G) comp = YCBCR2_MAX_G; else comp = 0;
- goto d3GClipped;
- d3ClipB: if (comp > YCBCR2_MAX_B) comp = YCBCR2_MAX_B; else comp = 0;
- goto d3BClipped;
- }
- /* ------------------------- ilSetupYCbCrDitherTable -------------------------- */
- /* Setup the given YCbCr conversion table with values for converting YCbCr->RGB
- with the dither values pre-multiplied into them.
- */
- static void ilSetupYCbCrDitherTable (
- ilRGBTransPtr pTrans,
- ilImageDes *pDes
- )
- {
- ilYCbCrInfo *pYCbCr;
- double Lr, Lg, Lb, scaledY;
- double factor, gFactor, rbDither, gDither;
- int i, refBlack, upper, lower;
- struct {
- int refBlack;
- double scale;
- } sample [3];
- /* This function sets up the given translation table, similar to
- ilGetYCbCrConvertTable(); see that function for more details. The difference
- is that the multiplications done for dithering (i.e. by table lookup into
- _ilMul4/8[]) are done into the values stored in the table.
- For example, red is obtained from Y and Cr by:
- red = (short)YTrans[Y] + (short)CrTrans[Cr] (1)
- then red is dithered down to 4 levels by:
- red = (pMul4[red] + kernel) >> 8; (2)
- where "pMul4[i]" = i * (nLevels(4) - 1) * 256 / 255"; see /ilc/ildither.c
- But if the values in the low-order short of YTrans and CrTrans are
- premultiplied by "(nLevels - 1) * 256 / 255" then the lookup into pMul4[]
- can be skipped, and step (2) above can be skipped.
- */
- pYCbCr = &pDes->typeInfo.YCbCr;
- for (i = 0; i < 3; i++) {
- sample[i].refBlack = pYCbCr->sample[i].refBlack;
- sample[i].scale = ((i == 0) ? 255 : 127) /
- (pYCbCr->sample[i].refWhite - pYCbCr->sample[i].refBlack);
- }
- /* Calc the Cb/Cr lookup tables, factoring in their scale factors. */
- Lr = ((double)pYCbCr->lumaRed / (double)10000);
- Lg = ((double)pYCbCr->lumaGreen / (double)10000);
- Lb = ((double)pYCbCr->lumaBlue / (double)10000);
- /* Calc the RGB dither multiply factors; R and B must be the same! */
- rbDither = ((1 << YCBCR2_NR_BITS) - 1) * 256 / 255;
- gDither = ((1 << YCBCR2_NG_BITS) - 1) * 256 / 255;
- /* Y translation: lower is scaled Y, upper is value added to get green */
- gFactor = (1 - Lb - Lr) / Lg;
- for (i = 0; i < 256; i++) {
- scaledY = sample[0].scale * i - sample[0].refBlack;
- upper = scaledY * gFactor * gDither + 0.5;
- lower = scaledY * rbDither;
- pTrans->YTrans[i] = (upper << 16) | (lower & 0xffff);
- }
- /* Cb: lower is added to Y to get blue, upper added to get green */
- factor = (2 - 2 * Lb) * sample[1].scale;
- gFactor = -Lb * (2 - 2 * Lb) / Lg;
- refBlack = sample[1].refBlack;
- for (i = 0; i < 256; i++) {
- upper = (i - refBlack) * gFactor * gDither + 0.5;
- lower = (i - refBlack) * factor * rbDither + 0.5;
- pTrans->CbTrans[i] = (upper << 16) | (lower & 0xffff);
- }
- /* Cr: lower is added to Y to get red, upper added to get green */
- factor = (2 - 2 * Lr) * sample[2].scale;
- gFactor = -Lr * (2 - 2 * Lr) / Lg;
- refBlack = sample[2].refBlack;
- for (i = 0; i < 256; i++) {
- upper = (i - refBlack) * gFactor * gDither + 0.5;
- lower = (i - refBlack) * factor * rbDither + 0.5;
- pTrans->CrTrans[i] = (upper << 16) | (lower & 0xffff);
- }
- }
- /* ---------------------------- _ilDitherYCbCr ----------------------------- */
- /* Does conversions of some forms of YCbCr to dithered IL_PALETTE.
- Returns "true" if conversion handled, else "false" if not handled or if error.
- The pipe image must be an uncompressed YCbCr image.
- */
- IL_PRIVATE ilBool _ilDitherYCbCr (
- ilPipe pipe,
- ilPipeInfo *pInfo,
- ilImageDes *pDes,
- ilImageFormat *pFormat,
- int option,
- ilConvertToPaletteInfo *pData
- )
- {
- ilSrcElementData srcData;
- ilDstElementData dstData;
- ilPtr pTranslate;
- ilYCbCr2DitherPrivPtr pPriv;
- unsigned short *pPalette;
- ilImageDes des;
- /* Check for case handled here: 8x8 area dither, levels 484 specified; pipe
- image is 8 bit/comp YCbCr, subsampled by 2, even width and height.
- */
- if (((option != IL_CONVERT_TO_PALETTE) && (option != 0))
- || !pData
- || (pData->method != IL_AREA_DITHER)
- || (pData->levels[0] != 4)
- || (pData->levels[1] != 8)
- || (pData->levels[0] != 4)
- || (pData->kernelSize != 8)
- || (pFormat->sampleOrder == IL_SAMPLE_PIXELS)
- || (pFormat->nBitsPerSample[0] != 8)
- || (pFormat->nBitsPerSample[1] != 8)
- || (pFormat->nBitsPerSample[2] != 8)
- || (pDes->typeInfo.YCbCr.sample[0].subsampleHoriz != 1)
- || (pDes->typeInfo.YCbCr.sample[0].subsampleVert != 1)
- || (pDes->typeInfo.YCbCr.sample[1].subsampleHoriz != 2)
- || (pDes->typeInfo.YCbCr.sample[1].subsampleVert != 2)
- || (pDes->typeInfo.YCbCr.sample[2].subsampleHoriz != 2)
- || (pDes->typeInfo.YCbCr.sample[2].subsampleVert != 2)
- || ((pInfo->width | pInfo->height) & 1)) { /* width or height odd */
- pipe->context->error = 0; /* can't handle; not an error */
- return FALSE;
- }
- /* If mapImage given, validate it and point to its pixels. If not given,
- pTranslate becomes null and will point to "identity" mapImage below.
- */
- pTranslate = (ilPtr)NULL;
- if (pData->mapImage) {
- ilImageInfo *pInfo;
- if (!ilQueryClientImage (pData->mapImage, &pInfo, 0)
- || (pInfo->width != 256) || (pInfo->height != 1)
- || (pInfo->pDes->compression != IL_UNCOMPRESSED)
- || (pInfo->pDes->nSamplesPerPixel != 1)
- || (pInfo->pFormat->nBitsPerSample[0] != 8))
- return ilDeclarePipeInvalid (pipe, IL_ERROR_MAP_IMAGE);
- pTranslate = pInfo->plane[0].pPixels;
- }
- /* alloc pPalette unless not palette dst; init with ramp if not choosing */
- pPalette = (unsigned short *)NULL;
- if (pData->dstType == IL_PALETTE) {
- int red, green, blue;
- int redLevel, greenLevel, blueLevel;
- unsigned short *pPal;
- pPalette = (unsigned short *)IL_MALLOC_ZERO (3 * 256 * sizeof(unsigned short));
- if (!pPalette)
- return ilDeclarePipeInvalid (pipe, IL_ERROR_MALLOC);
- pPal = pPalette;
- for (red = 0; red < pData->levels[0]; red++) {
- redLevel = 65535 * red / (pData->levels[0] - 1);
- for (green = 0; green < pData->levels[1]; green++) {
- greenLevel = 65535 * green / (pData->levels[1] - 1);
- for (blue = 0; blue < pData->levels[2]; blue++) {
- blueLevel = 65535 * blue / (pData->levels[2] - 1);
- pPal [0] = redLevel;
- pPal [256] = greenLevel;
- pPal [512] = blueLevel;
- pPal++;
- }
- }
- }
- }
- /* Add element to the pipe, init private. Accept strips as they are;
- presumably they will be even # lines because image is subsampled.
- */
- srcData.consumerImage = (ilObject)NULL;
- srcData.stripHeight = pInfo->stripHeight;
- srcData.constantStrip = FALSE;
- srcData.minBufferHeight = 0;
- dstData.producerObject = (ilObject)NULL;
- des = *IL_DES_GRAY;
- des.type = pData->dstType;
- if (des.type == IL_PALETTE) {
- des.flags = IL_DITHERED_PALETTE;
- des.typeInfo.palette.levels[0] = pData->levels[0];
- des.typeInfo.palette.levels[1] = pData->levels[1];
- des.typeInfo.palette.levels[2] = pData->levels[2];
- }
- dstData.pDes = &des;
- dstData.pFormat = IL_FORMAT_BYTE;
- dstData.width = pInfo->width;
- dstData.height = pInfo->height;
- dstData.stripHeight = 0;
- dstData.constantStrip = FALSE;
- dstData.pPalette = pPalette;
- pPriv = (ilYCbCr2DitherPrivPtr)ilAddPipeElement (pipe, IL_FILTER,
- sizeof(ilYCbCr2DitherPrivRec), 0, &srcData, &dstData,
- ilYCbCr2DitherInit, IL_NPF, ilYCbCr2DitherDestroy, ilYCbCr2DitherExecute, NULL, 0);
- if (!pPriv) {
- return FALSE;
- }
- /* Init pPriv; point pTranslate to identity image if no mapImage given */
- if (pTranslate)
- pPriv->pTranslate = pTranslate;
- else {
- int i;
- pPriv->pTranslate = pPriv->translate;
- for (i = 0; i < 256; i++)
- pPriv->translate[i] = i;
- }
- pPriv->pPalette = pPalette;
- /* Init tables for YCbCr->RGB conversion plus dithering */
- ilSetupYCbCrDitherTable (&pPriv->trans, pDes);
- /* Update pipe info and return */
- ilGetPipeInfo (pipe, FALSE, pInfo, pDes, pFormat);
- return TRUE;
- }
- /* ======================= RGB To YCbCr Code =================================== */
- /* # of bits of fixed point precision for interim values during conversion */
- #define PR2Y 16
- /* Private for RGB to YCbCr filter. For each component to be calculated (Y/Cb/Cr),
- a table indexed by the src component (R/G/B) which gives the contribution
- for that src component, in fixed point: <32-PR2Y bits>.<PR2Y bits>
- */
- typedef struct {
- long YR[256], YG[256], YB[256]; /* component lookup tables */
- long CbR[256], CbG[256], CbB[256];
- long CrR[256], CrG[256], CrB[256];
- long YRefBlack, CbRefBlack, CrRefBlack; /* component reference black */
- } ilRGBToYCbCrPrivRec, *ilRGBToYCbCrPrivPtr;
- /* Macro to do floor/ceiling of given value: makes it 0..255.
- Shift right by 8; if not zero, is < 0 or > 255; handle those cases.
- */
- #define BYTE_RANGE(_pixel) { \
- if (_pixel & ~0xff) \
- if ((_pixel) < 0) _pixel = 0; \
- else _pixel = 255; \
- }
- /* ----------------------- ilExecuteRGBToYCbCr -------------------------------- */
- /* Execute(): convert _pixel_ src RGB to _planar_ dst YCbCr.
- */
- static ilError ilExecuteRGBToYCbCr (
- ilExecuteData *pData,
- long dstLine,
- long *pNLines
- )
- {
- ilRGBToYCbCrPrivPtr pPriv;
- ilPtr pSrcLine, pYLine, pCbLine, pCrLine;
- ilPtr pSrc, pY, pCb, pCr;
- long nPixelsM1;
- long YRefBlack, CbRefBlack, CrRefBlack;
- long nPixelsM1Init, nLinesM1;
- long YRowBytes, CbRowBytes, CrRowBytes, srcRowBytes;
- ilImagePlaneInfo *pPlane;
- long R, G, B, pixel;
- /* Set nPixels/LinesM1 to # pixels / lines - 1; exit if either 0. */
- pPriv = (ilRGBToYCbCrPrivPtr)pData->pPrivate;
- nPixelsM1Init = pData->pSrcImage->width;
- if (nPixelsM1Init <= 0)
- return 0;
- nPixelsM1Init--;
- nLinesM1 = *pNLines;
- if (nLinesM1 <= 0)
- return 0;
- nLinesM1--;
- /* Point pSrcLine to first line of src RGB (pixel-order) data; point
- pY/Cb/CrLine to 1st line in dst YCbCr (planar-order) data planes.
- Get bytes/row for each of them.
- */
- pPlane = pData->pSrcImage->plane;
- srcRowBytes = pPlane->nBytesPerRow;
- pSrcLine = pPlane->pPixels + pData->srcLine * srcRowBytes;
- pPlane = pData->pDstImage->plane;
- YRowBytes = pPlane->nBytesPerRow;
- pYLine = pPlane->pPixels + dstLine * YRowBytes;
- pPlane++;
- CbRowBytes = pPlane->nBytesPerRow;
- pCbLine = pPlane->pPixels + dstLine * CbRowBytes;
- pPlane++;
- CrRowBytes = pPlane->nBytesPerRow;
- pCrLine = pPlane->pPixels + dstLine * CrRowBytes;
- /* Load component refBlack values, plus 1/2 shift (precision) amt for rounding
- into values, shifted up so it can be added before right shift.
- */
- YRefBlack = (pPriv->YRefBlack << PR2Y) + (1 << (PR2Y - 1));
- CbRefBlack = (pPriv->CbRefBlack << PR2Y) + (1 << (PR2Y - 1));
- CrRefBlack = (pPriv->CrRefBlack << PR2Y) + (1 << (PR2Y - 1));
- /* Convert pixel RGB to planar YCbCr. */
- do {
- pSrc = pSrcLine;
- pSrcLine += srcRowBytes;
- pY = pYLine;
- pYLine += YRowBytes;
- pCb = pCbLine;
- pCbLine += CbRowBytes;
- pCr = pCrLine;
- pCrLine += CrRowBytes;
- nPixelsM1 = nPixelsM1Init;
- do {
- R = *pSrc++;
- G = *pSrc++;
- B = *pSrc++;
- pixel = (pPriv->YR[R] + pPriv->YG[G] + pPriv->YB[B] + YRefBlack) >> PR2Y;
- if (pixel >> 8) goto YClip;
- YClipped: *pY++ = pixel;
- pixel = (pPriv->CbR[R] + pPriv->CbG[G] + pPriv->CbB[B] + CbRefBlack) >> PR2Y;
- if (pixel >> 8) goto CbClip;
- CbClipped: *pCb++ = pixel;
- pixel = (pPriv->CrR[R] + pPriv->CrG[G] + pPriv->CrB[B] + CrRefBlack) >> PR2Y;
- if (pixel >> 8) goto CrClip;
- CrClipped: *pCr++ = pixel;
- } while (--nPixelsM1 >= 0);
- } while (--nLinesM1 >= 0);
- return IL_OK;
- /* goto point for when Y/Cb/Cr out of range: clamp pixel and go back and continue.
- This is done to avoid taking a branch in the most common case: not out of range.
- */
- YClip: if (pixel > 255) pixel = 255; else pixel = 0;
- goto YClipped;
- CbClip: if (pixel > 255) pixel = 255; else pixel = 0;
- goto CbClipped;
- CrClip: if (pixel > 255) pixel = 255; else pixel = 0;
- goto CrClipped;
- }
- /* ---------------------------- ilConvertRGBToYCbCr ----------------------------- */
- /* Convert from source type (pDes->type) == RGB to YCbCr.
- pFormat points to the source format; on return, *pFormat is updated
- to the dst format. pNewDes points to the dst dest; *pDes to the src (pipe) des;
- on return it is updated to the new descriptor.
- */
- IL_PRIVATE ilBool _ilConvertRGBToYCbCr (
- ilPipe pipe,
- ilPipeInfo *pInfo,
- ilImageDes *pDes,
- const ilImageDes *pNewDes,
- ilImageFormat *pFormat
- )
- {
- ilRGBToYCbCrPrivPtr pPriv;
- ilYCbCrInfo *pYCbCr;
- ilDstElementData dstData;
- double Lr, Lg, Lb, range;
- long YR, YG, YB;
- long CbR, CbG, CbB;
- long CrR, CrG, CrB;
- int i;
- /* Get format = planar order, 8 bits / pixel, or error */
- if ((pFormat->nBitsPerSample[0] != 8)
- || (pFormat->nBitsPerSample[1] != 8)
- || (pFormat->nBitsPerSample[2] != 8)
- || (pFormat->sampleOrder != IL_SAMPLE_PIXELS)) {
- if (!ilConvert (pipe, (ilImageDes *)NULL, IL_FORMAT_3BYTE_PIXEL, 0, NULL))
- return FALSE;
- }
- /* Add filter to write 3 planar YCbCr format image. Update pDes: std YCbCr but
- with caller's sample refBlack/White and lumaRed/Green/Blue.
- */
- *pDes = *IL_DES_YCBCR;
- pDes->typeInfo.YCbCr.sample[0].refBlack = pNewDes->typeInfo.YCbCr.sample[0].refBlack;
- pDes->typeInfo.YCbCr.sample[0].refWhite = pNewDes->typeInfo.YCbCr.sample[0].refWhite;
- pDes->typeInfo.YCbCr.sample[1].refBlack = pNewDes->typeInfo.YCbCr.sample[1].refBlack;
- pDes->typeInfo.YCbCr.sample[1].refWhite = pNewDes->typeInfo.YCbCr.sample[1].refWhite;
- pDes->typeInfo.YCbCr.sample[2].refBlack = pNewDes->typeInfo.YCbCr.sample[2].refBlack;
- pDes->typeInfo.YCbCr.sample[2].refWhite = pNewDes->typeInfo.YCbCr.sample[2].refWhite;
- pDes->typeInfo.YCbCr.lumaRed = pNewDes->typeInfo.YCbCr.lumaRed;
- pDes->typeInfo.YCbCr.lumaGreen = pNewDes->typeInfo.YCbCr.lumaGreen;
- pDes->typeInfo.YCbCr.lumaBlue = pNewDes->typeInfo.YCbCr.lumaBlue;
- *pFormat = *IL_FORMAT_3BYTE_PLANE;
- dstData.producerObject = (ilObject)NULL;
- dstData.pDes = pDes;
- dstData.pFormat = pFormat;
- dstData.width = pInfo->width;
- dstData.height = pInfo->height;
- dstData.stripHeight = 0;
- dstData.constantStrip = FALSE;
- dstData.pPalette = (unsigned short *)NULL;
- pPriv = (ilRGBToYCbCrPrivPtr)ilAddPipeElement (pipe, IL_FILTER,
- sizeof(ilRGBToYCbCrPrivRec), 0, (ilSrcElementData *)NULL, &dstData,
- IL_NPF, IL_NPF, IL_NPF, ilExecuteRGBToYCbCr, NULL, 0);
- if (!pPriv)
- return FALSE;
- /* See Appendix O pg 3 of TIFF spec. Use fixed point (16.16) for
- floating point values.
- Y = Lr*R + Lg*G + Lb*B
- Cb = (B - Y) / (2-2*Lb)
- = (B - Lr*R - Lg*G - Lb*B) / (2-2*Lb)
- = R * -Lr/(2-2*Lb) + G * -Lg/(2-2*Lb) + B * (1-Lb)/(2-2*Lb)
- Cr = (R - Y) / (2-2*Lr)
- = (R - Lr*R - Lg*G - Lb*B) / (2-2*Lr)
- = R * (1-Lr)/(2-2*Lr) + G * -Lg/(2-2*Lr) + B * -Lb/(2-2*Lr)
- In each case, the scale up to account for the scaling range is done
- by multiplying the scaling range into each RGB factor. The refBlack is
- then added (not shown above). Each of the values multiplied times R/G/B
- is called "Y/Cb/Cr|R/G/B".
- */
- pYCbCr = &pDes->typeInfo.YCbCr;
- Lr = (double)pYCbCr->lumaRed / 10000;
- Lg = (double)pYCbCr->lumaGreen / 10000;
- Lb = (double)pYCbCr->lumaBlue / 10000;
- /* Y R/G/B multiples, plus refBlack. */
- range = (1 << PR2Y) * (double)(pYCbCr->sample[0].refWhite - pYCbCr->sample[0].refBlack) / 255;
- YR = range * Lr + 0.5;
- YG = range * Lg + 0.5;
- YB = range * Lb + 0.5;
- pPriv->YRefBlack = pYCbCr->sample[0].refBlack;
- /* Cb and Cr multiples, refBlack. Note coding range for Cb/Cr == 127. */
- range = (1 << PR2Y) * (double)(pYCbCr->sample[1].refWhite - pYCbCr->sample[1].refBlack) / 127;
- CbR = range * -Lr/(2-2*Lb) + 0.5;
- CbG = range * -Lg/(2-2*Lb) + 0.5;
- CbB = range * (1-Lb)/(2-2*Lb) + 0.5;
- pPriv->CbRefBlack = pYCbCr->sample[1].refBlack;
- range = (1 << PR2Y) * (double)(pYCbCr->sample[2].refWhite - pYCbCr->sample[2].refBlack) / 127;
- CrR = range * (1-Lr)/(2-2*Lr) + 0.5;
- CrG = range * -Lg/(2-2*Lr) + 0.5;
- CrB = range * -Lb/(2-2*Lr) + 0.5;
- pPriv->CrRefBlack = pYCbCr->sample[2].refBlack;
- /* Build lookup table which is the result of multiplying the above by 0..255,
- i.e. the source RGB values. Using this lookup table is equivalent to multiply
- but a whole lot faster.
- */
- for (i = 0; i < 256; i++) {
- pPriv->YR[i] = YR * i;
- pPriv->YG[i] = YG * i;
- pPriv->YB[i] = YB * i;
- pPriv->CbR[i] = CbR * i;
- pPriv->CbG[i] = CbG * i;
- pPriv->CbB[i] = CbB * i;
- pPriv->CrR[i] = CrR * i;
- pPriv->CrG[i] = CrG * i;
- pPriv->CrB[i] = CrB * i;
- }
- return TRUE;
- }
|