847 lines
34 KiB
C
847 lines
34 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: ilupsample.c /main/6 1996/06/19 12:20:32 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/ilupsample.c : Code for doing upsampling, or scaling
|
|
by 1, 2, 4x of planar images; each plane independent.
|
|
*/
|
|
#include "ilint.h"
|
|
#include "ilpipelem.h"
|
|
#include "ilconvert.h"
|
|
#include "ilerrors.h"
|
|
|
|
/* ========================== Fast Code ================================= */
|
|
/* This code handles the upsampling cases where the vertical and horizontal subsample
|
|
factors are equal (generally the case). It is faster than the general case code.
|
|
*/
|
|
|
|
/* Private for fast upsampling filters. "subsampleShift" is the shift value (0,1,2)
|
|
for how that sample was subsampled; "shift" is the shift for upsampling that
|
|
sample. They are the same unless "double"; shift[i]=subsampleShift[i]+1 then.
|
|
*/
|
|
typedef struct {
|
|
unsigned int nSamples;
|
|
unsigned int subsampleShift[IL_MAX_SAMPLES];
|
|
unsigned int shift[IL_MAX_SAMPLES];
|
|
int scaleFactor;
|
|
} ilUpFastPrivRec, *ilUpFastPrivPtr;
|
|
|
|
|
|
/* -------------------------- ilExecuteFastUpsample ------------------------------ */
|
|
/* Called by ilExecuteFastUpsample() to quadruple (scale up by 4) one plane.
|
|
*/
|
|
static void ilUpsampleQuadruple (
|
|
long nSrcLines,
|
|
long nSrcBytes,
|
|
long srcRowBytes,
|
|
ilPtr pSrcLine,
|
|
long dstRowBytes,
|
|
ilPtr pDstLine
|
|
)
|
|
{
|
|
int temp0, temp1, delta;
|
|
unsigned long aLong;
|
|
long nSrcBytesM1, dstOffset;
|
|
ilPtr pSrc, pSrcBelow, pDst;
|
|
int left, leftBelow, right, rightBelow;
|
|
|
|
#define FUPINTER(_start, _delta, _temp, _result) { \
|
|
_result = (_start) << 8; \
|
|
_temp += _delta; \
|
|
_result += _temp >> 2; \
|
|
_temp += _delta; \
|
|
_result <<= 8; \
|
|
_result += _temp >> 2; \
|
|
_temp += _delta; \
|
|
_result <<= 8; \
|
|
_result += _temp >> 2; \
|
|
}
|
|
|
|
/* Quadrupling: similar to doubling except * 4. Basically interpolate
|
|
and write 4 values from src pixels "left" and "right", by setting
|
|
"temp" = left * 4, then adding "right-left" each time to it and
|
|
writing that result div 4. Do down for four lines.
|
|
*/
|
|
dstOffset = 4 - 3 * dstRowBytes; /* from 3 lines down to next long */
|
|
while (nSrcLines-- > 0) { /* double and interpolate next line */
|
|
pSrc = pSrcLine;
|
|
if (nSrcLines > 0) /* else use last for below line */
|
|
pSrcLine += srcRowBytes;
|
|
pSrcBelow = pSrcLine;
|
|
pDst = pDstLine;
|
|
pDstLine += dstRowBytes << 2; /* skip down 4 dst lines */
|
|
|
|
nSrcBytesM1 = nSrcBytes - 1;
|
|
if (nSrcBytesM1 > 0) {
|
|
left = *pSrc++;
|
|
leftBelow = *pSrcBelow++;
|
|
}
|
|
else { /* one pixel; use left as right */
|
|
left = *pSrc;
|
|
leftBelow = *pSrcBelow;
|
|
}
|
|
while (TRUE) {
|
|
right = *pSrc++;
|
|
temp0 = left << 2;
|
|
delta = right - left;
|
|
FUPINTER (left, delta, temp0, aLong)
|
|
*((unsigned long *)pDst) = aLong;
|
|
pDst += dstRowBytes;
|
|
|
|
rightBelow = *pSrcBelow++;
|
|
temp0 = (left << 1) + left;
|
|
temp0 += leftBelow;
|
|
temp1 = (right << 1) + right;
|
|
temp1 += rightBelow;
|
|
delta = (temp1 >> 2) - (temp0 >> 2);
|
|
FUPINTER (temp0>>2, delta, temp0, aLong)
|
|
*((unsigned long *)pDst) = aLong;
|
|
pDst += dstRowBytes;
|
|
|
|
temp0 = (left + leftBelow) >> 1;
|
|
temp1 = (right + rightBelow) >> 1;
|
|
delta = temp1 - temp0;
|
|
temp0 <<= 2;
|
|
FUPINTER (temp0>>2, delta, temp0, aLong)
|
|
*((unsigned long *)pDst) = aLong;
|
|
pDst += dstRowBytes;
|
|
|
|
temp0 = (leftBelow << 1) + leftBelow;
|
|
temp0 += left;
|
|
temp1 = (rightBelow << 1) + rightBelow;
|
|
temp1 += right;
|
|
delta = (temp1 >> 2) - (temp0 >> 2);
|
|
FUPINTER (temp0>>2, delta, temp0, aLong)
|
|
*((unsigned long *)pDst) = aLong;
|
|
pDst += dstOffset; /* bump to next long of first line */
|
|
|
|
left = right;
|
|
leftBelow = rightBelow;
|
|
if (--nSrcBytesM1 < 0)
|
|
break; /* last pixel; done */
|
|
if (nSrcBytesM1 == 0) { /* next-to-last pixel; back up src */
|
|
pSrc--;
|
|
pSrcBelow--;
|
|
}
|
|
} /* END while bytes across */
|
|
} /* END while lines */
|
|
}
|
|
|
|
|
|
/* -------------------------- ilExecuteFastUpsample ------------------------------ */
|
|
/* Execute(): upsample as necessary "pPriv->nSamples" planes of the source image,
|
|
where the hori/vertical upsampling factors are equal for each plane.
|
|
*/
|
|
static ilError ilExecuteFastUpsample (
|
|
ilExecuteData *pData,
|
|
long dstLine,
|
|
long *pNLines
|
|
)
|
|
{
|
|
ilUpFastPrivPtr pPriv;
|
|
ilImagePlaneInfo *pSrcPlane, *pDstPlane;
|
|
int sample, subsampleShift;
|
|
ilPtr pSrcLine, pDstLine;
|
|
long width, nLines, nSrcLines, nSrcBytes, srcRowBytes, nSrcBytesM2;
|
|
long dstRowBytes;
|
|
ilPtr pSrc, pSrcBelow, pDst;
|
|
int left, leftBelow, right, rightBelow;
|
|
|
|
/* Get width and height of _upsampled_ image; exit if zero. */
|
|
pPriv = (ilUpFastPrivPtr)pData->pPrivate;
|
|
nLines = *pNLines; /* # of lines before subsampling */
|
|
*pNLines = nLines << pPriv->scaleFactor; /* # of dst lines after any scaling */
|
|
if (nLines <= 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.
|
|
*/
|
|
pSrcPlane = pData->pSrcImage->plane;
|
|
pDstPlane = pData->pDstImage->plane;
|
|
for (sample = 0; sample < pPriv->nSamples; sample++, pSrcPlane++, pDstPlane++) {
|
|
subsampleShift = pPriv->subsampleShift[sample];
|
|
|
|
srcRowBytes = pSrcPlane->nBytesPerRow;
|
|
pSrcLine = pSrcPlane->pPixels + (pData->srcLine >> subsampleShift) * srcRowBytes;
|
|
dstRowBytes = pDstPlane->nBytesPerRow;
|
|
pDstLine = pDstPlane->pPixels + dstLine * dstRowBytes;
|
|
|
|
/* Handle tiny image: if subsampling left nothing, fill with zeros. */
|
|
nSrcLines = nLines >> subsampleShift;
|
|
nSrcBytes = width >> subsampleShift;
|
|
if ((nSrcLines <= 0) || (nSrcBytes <= 0)) {
|
|
long i = nLines;
|
|
while (i-- > 0) {
|
|
bzero ((char *)pDstLine, width);
|
|
pDstLine += dstRowBytes;
|
|
}
|
|
}
|
|
else {
|
|
switch (pPriv->shift[sample]) {
|
|
|
|
/* No upsampling/doubling: just copy this plane */
|
|
case 0:
|
|
pSrc = pSrcLine;
|
|
pDst = pDstLine;
|
|
if (srcRowBytes == dstRowBytes)
|
|
bcopy ((char *)pSrc, (char *)pDst, nSrcBytes * nSrcLines);
|
|
else while (nSrcLines-- > 0) {
|
|
bcopy ((char *)pSrc, (char *)pDst, nSrcBytes);
|
|
pSrc += srcRowBytes;
|
|
pDst += dstRowBytes;
|
|
}
|
|
break;
|
|
|
|
/* Doubling: loop while there are a pair of src lines, replicating
|
|
and interpolating into dst. For each pixel across: do left with
|
|
interpolated pixel between. For each pixel pair, interpolate
|
|
between hori and vertically. Replicate last pixel. For last line,
|
|
point "pSrcBelow" to last src line: replication of last line occurs.
|
|
*/
|
|
case 1:
|
|
pSrc = pSrcLine;
|
|
pDst = pDstLine;
|
|
while (nSrcLines-- > 0) { /* double and interpolate next line */
|
|
pSrc = pSrcLine;
|
|
if (nSrcLines > 0) /* else use last for below line */
|
|
pSrcLine += srcRowBytes;
|
|
pSrcBelow = pSrcLine;
|
|
pDst = pDstLine;
|
|
pDstLine += dstRowBytes << 1; /* skip down 2 dst lines */
|
|
|
|
leftBelow = *pSrcBelow++; /* do first (left) byte */
|
|
left = *pSrc++;
|
|
pDst[dstRowBytes] = (left + leftBelow) >> 1;
|
|
*pDst++ = left;
|
|
nSrcBytesM2 = nSrcBytes - 2;
|
|
if (nSrcBytesM2 >= 0) { /* middle bytes with interpolation */
|
|
do {
|
|
rightBelow = *pSrcBelow++;
|
|
right = *pSrc++;
|
|
left = (left + right) >> 1;
|
|
pDst[dstRowBytes] = (left + ((leftBelow + rightBelow) >> 1)) >> 1;
|
|
*pDst++ = left;
|
|
pDst[dstRowBytes] = (right + rightBelow) >> 1;
|
|
*pDst++ = right;
|
|
left = right;
|
|
leftBelow = rightBelow;
|
|
} while (--nSrcBytesM2 >= 0);
|
|
}
|
|
*pDst = left; /* replicate last byte */
|
|
pDst[dstRowBytes] = (left + leftBelow) >> 1;
|
|
} /* END while lines */
|
|
break; /* END doubling */
|
|
|
|
case 2:
|
|
ilUpsampleQuadruple (nSrcLines, nSrcBytes, srcRowBytes, pSrcLine,
|
|
dstRowBytes, pDstLine);
|
|
break;
|
|
|
|
} /* END switch sample's shift */
|
|
} /* END not tiny image */
|
|
} /* END for each sample/plane */
|
|
|
|
return IL_OK;
|
|
}
|
|
|
|
/* ---------------------------- _ilFastUpsample ----------------------------- */
|
|
/* Attempt to upsample and scale based on "scaleFactor" (0 = no scaling; 1 = double;
|
|
2 = 4x), or return false if it cannot be done with the given pipe image (in which
|
|
case pipe->context->error == 0) or if error occurs (error != 0). Pipe image
|
|
must be decompressed before calling this function.
|
|
Note: when scaling up by 2x or 4x, always yields even/ *4 width/height.
|
|
Thus, *cannot* be used to upsample odd width/height YCbCr images.
|
|
*/
|
|
IL_PRIVATE ilBool _ilFastUpsample (
|
|
ilPipe pipe,
|
|
ilPipeInfo *pInfo,
|
|
ilImageDes *pDes,
|
|
ilImageFormat *pFormat,
|
|
int scaleFactor
|
|
)
|
|
{
|
|
ilUpFastPrivPtr pUpPriv;
|
|
ilUpFastPrivRec upPriv;
|
|
int i, j;
|
|
ilYCbCrSampleInfo *pSample;
|
|
ilDstElementData dstData;
|
|
ilSrcElementData srcData;
|
|
const ilImageDes *pNewDes;
|
|
|
|
/* Check image type; return if not handled, else init upPrivRec */
|
|
pipe->context->error = IL_OK; /* assume no error */
|
|
pNewDes = (ilImageDes *)NULL; /* assume no descriptor change */
|
|
upPriv.scaleFactor = scaleFactor;
|
|
|
|
switch (pDes->type) {
|
|
case IL_GRAY:
|
|
if (!scaleFactor) return TRUE; /* no scaling is noop; return */
|
|
if (!ilConvert (pipe, IL_DES_GRAY, IL_FORMAT_BYTE, 0, NULL))
|
|
return FALSE;
|
|
upPriv.nSamples = 1; /* double one plane */
|
|
upPriv.subsampleShift[0] = 0;
|
|
upPriv.shift[0] = scaleFactor;
|
|
ilGetPipeInfo (pipe, FALSE, pInfo, pDes, pFormat);
|
|
break;
|
|
|
|
case IL_RGB:
|
|
if (!scaleFactor) return TRUE; /* no scaling is noop; return */
|
|
if (!ilConvert (pipe, IL_DES_RGB, IL_FORMAT_3BYTE_PLANE, 0, NULL))
|
|
return FALSE;
|
|
upPriv.nSamples = 3; /* double three planes */
|
|
upPriv.subsampleShift[0] = upPriv.subsampleShift[1] = upPriv.subsampleShift[2] = 0;
|
|
upPriv.shift[0] = upPriv.shift[1] = upPriv.shift[2] = scaleFactor;
|
|
ilGetPipeInfo (pipe, FALSE, pInfo, pDes, pFormat);
|
|
break;
|
|
|
|
/* YCbCr: hori/vert subsample each plane must be equal and must be planar */
|
|
case IL_YCBCR:
|
|
if (pFormat->sampleOrder != IL_SAMPLE_PLANES)
|
|
return FALSE;
|
|
upPriv.nSamples = 3;
|
|
for (i = 0, pSample = pDes->typeInfo.YCbCr.sample; i < 3; i++, pSample++) {
|
|
if (pSample->subsampleHoriz != pSample->subsampleVert)
|
|
return FALSE;
|
|
j = _ilSubsampleShift [pSample->subsampleHoriz];
|
|
upPriv.subsampleShift[i] = j; /* shift due to subsampling */
|
|
j += scaleFactor; /* plus that due to scaling */
|
|
if (j > 2) /* more than 4x scale; can't do */
|
|
return FALSE;
|
|
upPriv.shift[i] = j; /* shift due to subsampling and doubling */
|
|
}
|
|
pNewDes = IL_DES_YCBCR; /* now YCbCr with no subsampling */
|
|
break;
|
|
|
|
default:
|
|
return FALSE; /* can't handle image type */
|
|
}
|
|
|
|
/* Add the pipe element: width / height scaled if double */
|
|
dstData.producerObject = (ilObject)NULL;
|
|
dstData.pDes = pNewDes;
|
|
dstData.pFormat = pFormat;
|
|
dstData.width = pInfo->width << scaleFactor;
|
|
dstData.height = pInfo->height << scaleFactor;
|
|
dstData.stripHeight = pInfo->stripHeight << scaleFactor;
|
|
dstData.constantStrip = pInfo->constantStrip;
|
|
dstData.pPalette = (unsigned short *)NULL;
|
|
|
|
/* Set format: 32 bit alignment required if any scales up by 4x.
|
|
Don't mark as such if width multiple of 4, as other filters may check it.
|
|
*/
|
|
if (dstData.width & 3) { /* not long-aligned */
|
|
for (i = 0; i < upPriv.nSamples; i++)
|
|
if (upPriv.shift[i] == 2) {
|
|
pFormat->rowBitAlign = 32;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Use current strip height rather than allowing image to be split into
|
|
smaller strips; there is a copied rather than smoothed line at the end
|
|
of each strip, so should do min number of strips. Could force no strips
|
|
for better quality, but performance penalty probably not worth it.
|
|
*/
|
|
srcData.consumerImage = (ilObject)NULL;
|
|
srcData.stripHeight = pInfo->stripHeight;
|
|
srcData.constantStrip = FALSE;
|
|
srcData.minBufferHeight = 0;
|
|
pUpPriv = (ilUpFastPrivPtr)ilAddPipeElement (pipe, IL_FILTER,
|
|
sizeof (ilUpFastPrivRec), 0, &srcData,
|
|
&dstData, IL_NPF, IL_NPF, IL_NPF, ilExecuteFastUpsample, NULL, 0);
|
|
if (!pUpPriv)
|
|
return FALSE;
|
|
*pUpPriv = upPriv;
|
|
|
|
ilGetPipeInfo (pipe, FALSE, pInfo, pDes, pFormat);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/* ========================== Slow General Code ================================= */
|
|
/* This code handles the upsampling cases where the vertical and horizontal subsample
|
|
factors are not the same.
|
|
*/
|
|
|
|
/* Upsample factors as shifts (1=0, 2=1, 4=2) - others not supported. */
|
|
typedef struct {
|
|
unsigned int horiz, vert;
|
|
} ilUpsampleShiftRec, *ilUpsampleShiftPtr;
|
|
|
|
/* Private for upsampling filters. */
|
|
typedef struct {
|
|
int nSamples; /* # of samples (components) to process */
|
|
ilUpsampleShiftRec shift[IL_MAX_SAMPLES]; /* upsample mul as shift value */
|
|
} ilUpsamplePrivRec, *ilUpsamplePrivPtr;
|
|
|
|
|
|
|
|
/* -------------------------- ilUpsampleHorizontal ------------------------------ */
|
|
/* Do horizontal upsampling as necessary first, spreading the dst lines
|
|
based on vertical upsampling.
|
|
# of lines to do is "height" divided by vertical upsample factor.
|
|
*/
|
|
static void ilUpsampleHorizontal (
|
|
unsigned int shift, /* 0 = no upsample; 1 = * 2 (doubling); 2 = * 4 */
|
|
long width, /* width of _whole_ (un-upsampled) image */
|
|
long nLines, /* # of src lines to upsample */
|
|
long srcRowBytes, /* bytes / row of src (downsampled) image */
|
|
ilPtr pSrcLine, /* ptr to first line of src image */
|
|
long dstRowInc, /* byte offset between dst lines written */
|
|
ilPtr pDstLine /* ptr to first line of dst image */
|
|
)
|
|
{
|
|
long nMidPixels, nEndPixels;
|
|
long nPixelsM1;
|
|
ilPtr pSrc, pDst;
|
|
ilByte pixel, prevPixel;
|
|
|
|
if (nLines <= 0)
|
|
return;
|
|
|
|
/* Upsample based on "shift", i.e. scale up by 2 ++ "shift". */
|
|
switch (shift) {
|
|
|
|
/* 0: no horizontal upsampling - just copy */
|
|
case 0:
|
|
pSrc = pSrcLine;
|
|
pDst = pDstLine;
|
|
while (nLines-- > 0) {
|
|
bcopy ((char *)pSrc, (char *)pDst, width);
|
|
pSrc += srcRowBytes;
|
|
pDst += dstRowInc;
|
|
}
|
|
break;
|
|
|
|
/* 1: doubling; The image for this plane is 1/2 "width". Copy the first pixel,
|
|
interpolate pixels between previous pixels and the last pixel, then replicate
|
|
last pixel to fill out width. Example: width == 9, source width = 4 (0..3):
|
|
A B C:
|
|
<0> | <0+1/2> <1> <1+2/2> <2> <2+3/2> <3> | <3> <3>
|
|
A: write first src pixel (pixel <0>); set into "prevPixel"
|
|
B: write (interpolated pixel, src pixel) pair "nMidPixels" times, leaving
|
|
srcPixel in prevPixel
|
|
C: replicate last pixel (prevPixel) "nEndPixels" times
|
|
*/
|
|
case 1:
|
|
nMidPixels = (width >> 1) - 1;
|
|
nEndPixels = width - (nMidPixels << 1) - 1;
|
|
while (nLines-- > 0) {
|
|
pSrc = pSrcLine;
|
|
pSrcLine += srcRowBytes;
|
|
pDst = pDstLine;
|
|
pDstLine += dstRowInc;
|
|
prevPixel = *pSrc++;
|
|
*pDst++ = prevPixel; /* prevPixel = first src pixel; copy to dst */
|
|
if (nMidPixels > 0) { /* <interpolated>, <src> pairs */
|
|
nPixelsM1 = nMidPixels - 1;
|
|
do {
|
|
pixel = *pSrc++;
|
|
*pDst++ = (pixel + prevPixel) >> 1;
|
|
*pDst++ = pixel;
|
|
prevPixel = pixel;
|
|
} while (--nPixelsM1 >= 0);
|
|
}
|
|
if (nEndPixels > 0) {
|
|
nPixelsM1 = nEndPixels - 1;
|
|
do {
|
|
*pDst++ = prevPixel;
|
|
} while (--nPixelsM1 >= 0);
|
|
}
|
|
} /* END while lines */
|
|
break;
|
|
|
|
/* 1: quadrupling; The image for this plane is 1/4 "width". Copy the first pixel,
|
|
interpolate pixels between previous pixels and the last pixel, then replicate
|
|
last pixel to fill out width. To interpolate: add "delta" = pixel-prevPixel
|
|
to "temp" (which starts at prevPixel*4) then divide temp by 4 before storing.
|
|
For example, if prevPixel = 3 and pixel = 6, delta = 3, pixels are:
|
|
3 (prevPixel) | (12+3)/4=3 (15+3)/4=4 (18+3)/4=5 6 (src pixel) |
|
|
*/
|
|
case 2: {
|
|
int delta, temp;
|
|
|
|
nMidPixels = (width >> 2) - 1;
|
|
nEndPixels = width - (nMidPixels << 2) - 1;
|
|
while (nLines-- > 0) {
|
|
pSrc = pSrcLine;
|
|
pSrcLine += srcRowBytes;
|
|
pDst = pDstLine;
|
|
pDstLine += dstRowInc;
|
|
prevPixel = *pSrc++;
|
|
*pDst++ = prevPixel; /* prevPixel = first src pixel; copy to dst */
|
|
if (nMidPixels > 0) { /* <3 interpolated>, <src> 4tuples */
|
|
nPixelsM1 = nMidPixels - 1;
|
|
do {
|
|
pixel = *pSrc++;
|
|
delta = pixel - prevPixel;
|
|
temp = (prevPixel << 2);
|
|
temp += delta;
|
|
*pDst++ = temp >> 2;
|
|
temp += delta;
|
|
*pDst++ = temp >> 2;
|
|
temp += delta;
|
|
*pDst++ = temp >> 2;
|
|
*pDst++ = pixel;
|
|
prevPixel = pixel;
|
|
} while (--nPixelsM1 >= 0);
|
|
}
|
|
if (nEndPixels > 0) {
|
|
nPixelsM1 = nEndPixels - 1;
|
|
do {
|
|
*pDst++ = prevPixel;
|
|
} while (--nPixelsM1 >= 0);
|
|
}
|
|
} /* END while lines */
|
|
break;
|
|
} /* END case 2 */
|
|
} /* END switch shift */
|
|
} /* END ilUpsampleHorizontal */
|
|
|
|
|
|
/* -------------------------- ilUpsampleVertical ------------------------------ */
|
|
/* Do vertical upsampling as necessary, after the horizontal upsampling is done.
|
|
Vertical upsampling is done in the dst buffer: pLine points to first line
|
|
(which contains an already upsampled line); rowBytes is bytes/row of buffer.
|
|
*/
|
|
static void ilUpsampleVertical (
|
|
unsigned int shift, /* 0 = no upsample; 1 = * 2 (doubling); 2 = * 4 */
|
|
long width, /* width of _whole_ (un-upsampled) image */
|
|
long nLines, /* # of dst lines (after upsampling) */
|
|
long rowBytes, /* bytes / row in src/dst image */
|
|
ilPtr pLine /* ptr to first line of src/dst image */
|
|
)
|
|
{
|
|
long nPixelsM1Init;
|
|
long nPixelsM1;
|
|
|
|
/* Return if no pixels or lines to do. */
|
|
if (nLines <= 0)
|
|
return;
|
|
nPixelsM1Init = width - 1;
|
|
if (nPixelsM1Init < 0)
|
|
return;
|
|
|
|
/* Upsample based on "shift", i.e. scale up by 2 ++ "shift". */
|
|
switch (shift) {
|
|
|
|
/* 0: no vertical upsampling; buffer is already complete */
|
|
case 0:
|
|
return; /* EXIT */
|
|
|
|
/* 1: doubling. Interpolate between pairs of lines, starting with the first
|
|
and third lines, filling in the second with the average of the two.
|
|
*/
|
|
case 1: {
|
|
ilPtr pDst, pSrc1, pSrc2;
|
|
|
|
nLines--; /* don't count first line already in buffer */
|
|
while (nLines >= 2) { /* two src lines, line between to interpolate */
|
|
nLines -= 2;
|
|
pSrc1 = pLine;
|
|
pLine += rowBytes;
|
|
pDst = pLine;
|
|
pLine += rowBytes;
|
|
pSrc2 = pLine;
|
|
nPixelsM1 = nPixelsM1Init;
|
|
do {
|
|
*pDst++ = (*pSrc1++ + *pSrc2++) >> 1;
|
|
} while (--nPixelsM1 >= 0);
|
|
}
|
|
|
|
/* Replicate last line to fill out to last line(s) */
|
|
pDst = pLine;
|
|
while (nLines-- > 0) {
|
|
pDst += rowBytes;
|
|
bcopy ((char *)pLine, (char *)(pDst), width);
|
|
}
|
|
break;
|
|
} /* END doubling */
|
|
|
|
/* 1: quadrupling. Interpolate between quadruples of lines, starting with lines
|
|
0 and 4, interpolating 1..3 - see horizontal upsampling.
|
|
*/
|
|
case 2: {
|
|
ilPtr pSrc, pDst;
|
|
int pixel, delta;
|
|
long rowBytesTimes4 = rowBytes << 2;
|
|
|
|
nLines--; /* don't count first line already in buffer */
|
|
while (nLines >= 4) { /* four src lines, line between to interpolate */
|
|
nLines -= 4;
|
|
pSrc = pLine;
|
|
pLine += rowBytesTimes4; /* point 4 lines down */
|
|
nPixelsM1 = nPixelsM1Init;
|
|
do {
|
|
pDst = pSrc;
|
|
pixel = *pSrc++; /* pixel from top src line; next pixel */
|
|
delta = *(pDst + rowBytesTimes4) - pixel; /* delta = bottom - top */
|
|
pixel <<= 2; /* work in pixels * 4, /4 before storing */
|
|
pixel += delta;
|
|
pDst += rowBytes;
|
|
*pDst = pixel >> 2; /* store one interpolated */
|
|
pixel += delta;
|
|
pDst += rowBytes;
|
|
*pDst = pixel >> 2; /* store one interpolated */
|
|
pixel += delta;
|
|
pDst += rowBytes;
|
|
*pDst = pixel >> 2; /* store one interpolated */
|
|
} while (--nPixelsM1 >= 0);
|
|
}
|
|
|
|
/* Replicate last line to fill out to last line(s) */
|
|
pDst = pLine;
|
|
while (nLines-- > 0) {
|
|
pDst += rowBytes;
|
|
bcopy ((char *)pLine, (char *)(pDst), width);
|
|
}
|
|
break;
|
|
} /* END quadrupling */
|
|
} /* END switch shift */
|
|
} /* END ilUpsampleVertical */
|
|
|
|
|
|
/* -------------------------- ilExecuteUpsample ------------------------------- */
|
|
/* Execute(): upsample as necessary "pPriv->nSamples" planes of the source image.
|
|
*/
|
|
static ilError ilExecuteUpsample (
|
|
ilExecuteData *pData,
|
|
long dstLine,
|
|
long *pNLines /* ignored on input */
|
|
)
|
|
{
|
|
ilUpsamplePrivPtr pPriv;
|
|
ilImagePlaneInfo *pSrcPlane, *pDstPlane;
|
|
int nSamples;
|
|
ilPtr pSrcLine, pDstLine;
|
|
long height, width, nLines;
|
|
long srcRowBytes, dstRowBytes;
|
|
ilUpsampleShiftPtr pShift;
|
|
|
|
/* Get width and height of _upsampled_ image; exit if zero. */
|
|
pPriv = (ilUpsamplePrivPtr)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++) {
|
|
|
|
srcRowBytes = pSrcPlane->nBytesPerRow;
|
|
pSrcLine = pSrcPlane->pPixels + (pData->srcLine >> pShift->vert) * srcRowBytes;
|
|
dstRowBytes = pDstPlane->nBytesPerRow;
|
|
pDstLine = pDstPlane->pPixels + dstLine * dstRowBytes;
|
|
|
|
/* Upsample horizontal "height >> pShift->vert" src lines (e.g. half
|
|
the lines for shift = 1). Write the results starting at pDstLine,
|
|
each line "dstRowBytes << pShift->vert" bytes after the other, e.g.
|
|
if pShift->vert is 1 (double), leave one line gap between each dst line.
|
|
But first, handle very small image: if subsampling left nothing,
|
|
fill with zeros. Note that small strips will not cause this to happen,
|
|
because stripHeight checked when element added.
|
|
*/
|
|
nLines = height >> pShift->vert;
|
|
if ((nLines <= 0) || ((width >> pShift->horiz) <= 0)) {
|
|
long i = height;
|
|
while (i-- > 0) {
|
|
bzero ((char *)pDstLine, width);
|
|
pDstLine += dstRowBytes;
|
|
}
|
|
}
|
|
else {
|
|
ilUpsampleHorizontal (pShift->horiz, width, nLines,
|
|
srcRowBytes, pSrcLine, dstRowBytes << pShift->vert, pDstLine);
|
|
|
|
/* Upsample vertically, interpolating between the lines just written. */
|
|
ilUpsampleVertical (pShift->vert, width, height, dstRowBytes, pDstLine);
|
|
}
|
|
}
|
|
|
|
return IL_OK;
|
|
}
|
|
|
|
/* ---------------------------- ilUpsampleYCbCr ----------------------------- */
|
|
/* Upsample and / or convert to gray the pipe image which must be a YCbCr image.
|
|
If "toGray" is true, the Y plane only will be upsampled (or copied) resulting
|
|
in a gray image; else a planar YCbCr image will result.
|
|
If "upSample" is true must upsample; "upSample" and/or "toGray" must be true.
|
|
pFormat points to the source format; on return, *pFormat is updated
|
|
to the dst format, *pDes to the dst descriptor.
|
|
*/
|
|
IL_PRIVATE ilBool _ilUpsampleYCbCr (
|
|
ilPipe pipe,
|
|
ilPipeInfo *pInfo,
|
|
ilImageDes *pDes,
|
|
ilImageFormat *pFormat,
|
|
ilBool toGray,
|
|
ilBool upSample
|
|
)
|
|
{
|
|
ilUpsamplePrivPtr pUpPriv;
|
|
ilImageDes pipeDes;
|
|
ilUpsampleShiftRec *pShift;
|
|
ilYCbCrSampleInfo *pSample;
|
|
int sample;
|
|
ilDstElementData dstData;
|
|
ilSrcElementData srcData;
|
|
long i, j;
|
|
|
|
/* Only 8 bit planar YCbCr can currently be upsampled; if not that and upsampling
|
|
needed, error; if no upsampling (only convert to gray) convert to planar.
|
|
NOTE: can't ilConvert() to planar if upsampling needed as ilConvert() will
|
|
call this function back to upsample, thereby recursing "forever".
|
|
*/
|
|
if ((pFormat->sampleOrder != IL_SAMPLE_PLANES)
|
|
|| (pFormat->nBitsPerSample[0] != 8)
|
|
|| (pFormat->nBitsPerSample[1] != 8)
|
|
|| (pFormat->nBitsPerSample[2] != 8)) {
|
|
if (upSample)
|
|
return ilDeclarePipeInvalid (pipe, IL_ERROR_NOT_IMPLEMENTED);
|
|
if (!ilConvert (pipe, (ilImageDes *)NULL, IL_FORMAT_3BYTE_PLANE, 0, NULL))
|
|
return FALSE;
|
|
*pFormat = *IL_FORMAT_3BYTE_PLANE;
|
|
}
|
|
|
|
#if 0
|
|
|
|
This code currently not included. It tries the "fast" upsampling code, which
|
|
is in fact faster than the "slow" upsampling code, but for some reason the resulting
|
|
YCbCr data causes the conversion to RGB to be much slower, probably because more
|
|
out-of-range (0..255) values are generated which cause the clip code to be hit.
|
|
For now, the fast upsample code is used only by ilScale(..., IL_SCALE_SAMPLE) for
|
|
2x and 4x scales up.
|
|
|
|
/* Try to use fast upsampling case, but not if "toGray" true, or if width/height
|
|
of resulting image is not multiple of subsample factor.
|
|
*/
|
|
if (!toGray) {
|
|
ilBool tryFast = TRUE;
|
|
int factor;
|
|
for (sample = 0; sample < 3; sample++) {
|
|
factor = pDes->typeInfo.YCbCr.sample[sample].subsampleHoriz;
|
|
if (((factor == 2) && ((pInfo->width | pInfo->height) & 1))
|
|
|| ((factor == 4) && ((pInfo->width | pInfo->height) & 3)))
|
|
tryFast = FALSE;
|
|
}
|
|
if (tryFast) {
|
|
if (_ilFastUpsample (pipe, pInfo, pDes, pFormat, FALSE))
|
|
return TRUE; /* handled; done */
|
|
if (pipe->context->error) /* not handled but error; exit */
|
|
return FALSE;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* Must do "slow" upsample: init dstData for filter(s) to be added. */
|
|
pipeDes = *pDes;
|
|
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). To avoid the
|
|
problem of having the last strip have 3 lines or less (and therefore for * 4
|
|
upsampling require that the previous line be copied - but no previous line
|
|
available), bump stripHeight by 4 until stripHeight mod 4 > 3, or until
|
|
requiring whole image as one strip.
|
|
*/
|
|
i = 8;
|
|
while (TRUE) {
|
|
j = dstData.height % i;
|
|
if ((j == 0) || (j > 3))
|
|
break;
|
|
if ((i += 4) >= dstData.height) {
|
|
i = dstData.height;
|
|
break;
|
|
}
|
|
}
|
|
srcData.consumerImage = (ilObject)NULL;
|
|
srcData.stripHeight = i;
|
|
srcData.constantStrip = TRUE;
|
|
srcData.minBufferHeight = 0;
|
|
|
|
/* Add a filter to upsample; if "toGray" upsample/copy one plane and done. */
|
|
if (toGray) {
|
|
*pDes = *IL_DES_GRAY;
|
|
*pFormat = *IL_FORMAT_BYTE;
|
|
}
|
|
else {
|
|
pDes->typeInfo.YCbCr.sample[0].subsampleHoriz = 1;
|
|
pDes->typeInfo.YCbCr.sample[0].subsampleVert = 1;
|
|
pDes->typeInfo.YCbCr.sample[1].subsampleHoriz = 1;
|
|
pDes->typeInfo.YCbCr.sample[1].subsampleVert = 1;
|
|
pDes->typeInfo.YCbCr.sample[2].subsampleHoriz = 1;
|
|
pDes->typeInfo.YCbCr.sample[2].subsampleVert = 1; /* des now upsampled */
|
|
}
|
|
pUpPriv = (ilUpsamplePrivPtr)ilAddPipeElement (pipe, IL_FILTER,
|
|
sizeof (ilUpsamplePrivRec), 0, &srcData,
|
|
&dstData, IL_NPF, IL_NPF, IL_NPF, ilExecuteUpsample, NULL, 0);
|
|
if (!pUpPriv)
|
|
return FALSE;
|
|
|
|
/* Init pUpPriv. */
|
|
pUpPriv->nSamples = (toGray) ? 1 : 3;
|
|
pSample = pipeDes.typeInfo.YCbCr.sample;
|
|
pShift = pUpPriv->shift;
|
|
|
|
for (sample = 0; sample < pUpPriv->nSamples; sample++, pShift++, pSample++) {
|
|
pShift->horiz = _ilSubsampleShift [pSample->subsampleHoriz];
|
|
pShift->vert = _ilSubsampleShift [pSample->subsampleVert];
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|