cosmopolitan/third_party/avir/avir_dil.h

1014 lines
24 KiB
C++

/* clang-format off */
//$ nobt
//$ nocpp
/**
* @file avir_dil.h
*
* @brief Inclusion file for de-interleaved image resizing functions.
*
* This file includes the "CImageResizerFilterStepDIL" class which implements
* image resizing functions in de-interleaved mode.
*
* AVIR Copyright (c) 2015-2019 Aleksey Vaneev
*/
namespace avir {
/**
* @brief De-interleaved filtering steps implementation class.
*
* This class implements scanline filtering functions in de-interleaved mode.
* This means that pixels are processed in groups.
*
* @tparam fptype Floating point type to use for storing pixel elements.
* SIMD types cannot be used.
* @tparam fptypesimd The SIMD type used to store a pack of "fptype" values.
*/
template< class fptype, class fptypesimd >
class CImageResizerFilterStepDIL :
public CImageResizerFilterStep< fptype, fptype >
{
public:
using CImageResizerFilterStep< fptype, fptype > :: IsUpsample;
using CImageResizerFilterStep< fptype, fptype > :: ResampleFactor;
using CImageResizerFilterStep< fptype, fptype > :: Flt;
using CImageResizerFilterStep< fptype, fptype > :: FltOrig;
using CImageResizerFilterStep< fptype, fptype > :: FltLatency;
using CImageResizerFilterStep< fptype, fptype > :: Vars;
using CImageResizerFilterStep< fptype, fptype > :: InLen;
using CImageResizerFilterStep< fptype, fptype > :: InPrefix;
using CImageResizerFilterStep< fptype, fptype > :: InSuffix;
using CImageResizerFilterStep< fptype, fptype > :: InElIncr;
using CImageResizerFilterStep< fptype, fptype > :: OutLen;
using CImageResizerFilterStep< fptype, fptype > :: OutPrefix;
using CImageResizerFilterStep< fptype, fptype > :: OutSuffix;
using CImageResizerFilterStep< fptype, fptype > :: OutElIncr;
using CImageResizerFilterStep< fptype, fptype > :: PrefixDC;
using CImageResizerFilterStep< fptype, fptype > :: SuffixDC;
using CImageResizerFilterStep< fptype, fptype > :: RPosBuf;
using CImageResizerFilterStep< fptype, fptype > :: FltBank;
using CImageResizerFilterStep< fptype, fptype > :: EdgePixelCount;
/**
* Function performs "packing" (de-interleaving) of a scanline and type
* conversion. If required, the sRGB gamma correction is applied.
*
* @param ip0 Input scanline, pixel elements interleaved.
* @param op0 Output scanline, pixel elements are grouped, "l" elements
* apart.
* @param l The number of pixels to "pack".
*/
template< class Tin >
void packScanline( const Tin* const ip0, fptype* const op0,
const int l ) const
{
const int ElCount = Vars -> ElCount;
int j;
if( !Vars -> UseSRGBGamma )
{
for( j = 0; j < ElCount; j++ )
{
const Tin* ip = ip0 + j;
fptype* const op = op0 + j * InElIncr;
int i;
for( i = 0; i < l; i++ )
{
op[ i ] = (fptype) *ip;
ip += ElCount;
}
}
}
else
{
const fptype gm = (fptype) Vars -> InGammaMult;
for( j = 0; j < ElCount; j++ )
{
const Tin* ip = ip0 + j;
fptype* const op = op0 + j * InElIncr;
int i;
for( i = 0; i < l; i++ )
{
op[ i ] = convertSRGB2Lin( (fptype) *ip * gm );
ip += ElCount;
}
}
}
}
/**
* Function applies Linear to sRGB gamma correction to the specified
* scanline.
*
* @param p Scanline.
* @param l The number of pixels to de-linearize.
* @param Vars0 Image resizing-related variables.
*/
static void applySRGBGamma( fptype* const p0, const int l,
const CImageResizerVars& Vars0 )
{
const int ElCount = Vars0.ElCount;
const fptype gm = (fptype) Vars0.OutGammaMult;
int j;
for( j = 0; j < ElCount; j++ )
{
fptype* const p = p0 + j * l;
int i;
for( i = 0; i < l; i++ )
{
p[ i ] = convertLin2SRGB( p[ i ]) * gm;
}
}
}
/**
* Function converts vertical scanline to horizontal scanline. This
* function is called by the image resizer when image is resized
* vertically. This means that the vertical scanline is stored in the
* same format produced by the packScanline() and maintained by other
* filtering functions.
*
* @param ip Input vertical scanline, pixel elements are grouped, SrcLen
* elements apart.
* @param op Output buffer (temporary buffer used during resizing), pixel
* elements are grouped, "l" elements apart.
* @param SrcLen The number of pixels in the input scanline, also used to
* calculate input buffer increment.
* @param SrcIncr Input buffer increment to the next vertical pixel.
*/
void convertVtoH( const fptype* ip, fptype* op, const int SrcLen,
const int SrcIncr ) const
{
const int ElCount = Vars -> ElCount;
const int SrcElIncr = SrcIncr / ElCount;
const int ips1 = SrcElIncr;
const int ips2 = SrcElIncr * 2;
const int ips3 = SrcElIncr * 3;
const int ops1 = InElIncr;
const int ops2 = InElIncr * 2;
const int ops3 = InElIncr * 3;
int j;
if( ElCount == 1 )
{
for( j = 0; j < SrcLen; j++ )
{
op[ 0 ] = ip[ 0 ];
ip += SrcIncr;
op++;
}
}
else
if( ElCount == 4 )
{
for( j = 0; j < SrcLen; j++ )
{
op[ 0 ] = ip[ 0 ];
op[ ops1 ] = ip[ ips1 ];
op[ ops2 ] = ip[ ips2 ];
op[ ops3 ] = ip[ ips3 ];
ip += SrcIncr;
op++;
}
}
else
if( ElCount == 3 )
{
for( j = 0; j < SrcLen; j++ )
{
op[ 0 ] = ip[ 0 ];
op[ ops1 ] = ip[ ips1 ];
op[ ops2 ] = ip[ ips2 ];
ip += SrcIncr;
op++;
}
}
else
if( ElCount == 2 )
{
for( j = 0; j < SrcLen; j++ )
{
op[ 0 ] = ip[ 0 ];
op[ ops1 ] = ip[ ips1 ];
ip += SrcIncr;
op++;
}
}
}
/**
* Function performs "unpacking" of a scanline and type conversion
* (truncation is used when floating point is converted to integer).
* The unpacking function assumes that scanline is stored in the style
* produced by the packScanline() function.
*
* @param ip0 Input scanline, pixel elements are grouped, "l" elements
* apart.
* @param op0 Output scanline, pixel elements are interleaved.
* @param l The number of pixels to "unpack".
* @param Vars0 Image resizing-related variables. ElCount is assumed to be
* equal to ElCountIO.
*/
template< class Tout >
static void unpackScanline( const fptype* const ip0, Tout* const op0,
const int l, const CImageResizerVars& Vars0 )
{
const int ElCount = Vars0.ElCount;
int j;
for( j = 0; j < ElCount; j++ )
{
const fptype* const ip = ip0 + j * l;
Tout* op = op0 + j;
int i;
for( i = 0; i < l; i++ )
{
*op = (Tout) ip[ i ];
op += ElCount;
}
}
}
/**
* Function prepares input scanline buffer for *this filtering step.
* Left- and right-most pixels are replicated to make sure no buffer
* overrun happens. Such approach also allows to bypass any pointer
* range checks.
*
* @param Src Source buffer.
*/
void prepareInBuf( fptype* Src ) const
{
if( IsUpsample || InPrefix + InSuffix == 0 )
{
return;
}
int j;
for( j = 0; j < Vars -> ElCount; j++ )
{
replicateArray( Src, 1, Src - InPrefix, InPrefix, 1 );
fptype* const Src2 = Src + InLen - 1;
replicateArray( Src2, 1, Src2 + 1, InSuffix, 1 );
Src += InElIncr;
}
}
/**
* Function peforms scanline upsampling with filtering.
*
* @param Src Source scanline buffer (length = this -> InLen). Source
* scanline increment will be equal to ElCount.
* @param Dst Destination scanline buffer.
*/
void doUpsample( const fptype* Src, fptype* Dst ) const
{
const int elalign = Vars -> elalign;
const int opstep = ResampleFactor;
const fptype* const f = Flt;
const int flen = Flt.getCapacity();
int l;
int i;
int j;
for( j = 0; j < Vars -> ElCount; j++ )
{
const fptype* ip = Src;
fptype* op0 = &Dst[ -OutPrefix ];
memset( op0, 0, ( OutPrefix + OutLen + OutSuffix ) *
sizeof( fptype ));
if( FltOrig.getCapacity() > 0 )
{
// Do not perform filtering, only upsample.
op0 += OutPrefix % ResampleFactor;
l = OutPrefix / ResampleFactor;
while( l > 0 )
{
op0[ 0 ] = ip[ 0 ];
op0 += opstep;
l--;
}
l = InLen - 1;
while( l > 0 )
{
op0[ 0 ] = ip[ 0 ];
op0 += opstep;
ip++;
l--;
}
l = OutSuffix / ResampleFactor;
while( l >= 0 )
{
op0[ 0 ] = ip[ 0 ];
op0 += opstep;
l--;
}
Src += InElIncr;
Dst += OutElIncr;
continue;
}
l = InPrefix;
fptypesimd ipv = (fptypesimd) ip[ 0 ];
while( l > 0 )
{
for( i = 0; i < flen; i += elalign )
{
fptypesimd :: addu( op0 + i,
fptypesimd :: load( f + i ) * ipv );
}
op0 += opstep;
l--;
}
l = InLen - 1;
while( l > 0 )
{
ipv = (fptypesimd) ip[ 0 ];
for( i = 0; i < flen; i += elalign )
{
fptypesimd :: addu( op0 + i,
fptypesimd :: load( f + i ) * ipv );
}
ip++;
op0 += opstep;
l--;
}
l = InSuffix;
ipv = (fptypesimd) ip[ 0 ];
while( l >= 0 )
{
for( i = 0; i < flen; i += elalign )
{
fptypesimd :: addu( op0 + i,
fptypesimd :: load( f + i ) * ipv );
}
op0 += opstep;
l--;
}
const fptype* dc = SuffixDC;
l = SuffixDC.getCapacity();
for( i = 0; i < l; i += elalign )
{
fptypesimd :: addu( op0 + i,
fptypesimd :: load( dc + i ) * ipv );
}
ipv = (fptypesimd) Src[ 0 ];
op0 = Dst - InPrefix * opstep;
dc = PrefixDC;
l = PrefixDC.getCapacity();
for( i = 0; i < l; i += elalign )
{
fptypesimd :: addu( op0 + i,
fptypesimd :: load( dc + i ) * ipv );
}
Src += InElIncr;
Dst += OutElIncr;
}
}
/**
* Function peforms scanline filtering with optional downsampling.
* Function makes use of the symmetry of the filter.
*
* @param Src Source scanline buffer (length = this -> InLen). Source
* scanline increment will be equal to 1.
* @param Dst Destination scanline buffer.
* @param DstIncr Destination scanline buffer increment, used for
* horizontal or vertical scanline stepping.
*/
void doFilter( const fptype* const Src, fptype* Dst,
const int DstIncr ) const
{
const int ElCount = Vars -> ElCount;
const int elalign = Vars -> elalign;
const fptype* const f = &Flt[ 0 ];
const int flen = Flt.getCapacity();
const int ipstep = ResampleFactor;
int i;
int j;
if( ElCount == 1 )
{
const fptype* ip = Src - EdgePixelCount * ipstep - FltLatency;
fptype* op = Dst;
int l = OutLen;
while( l > 0 )
{
fptypesimd s = fptypesimd :: load( f ) *
fptypesimd :: loadu( ip );
for( i = elalign; i < flen; i += elalign )
{
s += fptypesimd :: load( f + i ) *
fptypesimd :: loadu( ip + i );
}
op[ 0 ] = s.hadd();
op += DstIncr;
ip += ipstep;
l--;
}
}
else
if( DstIncr == 1 )
{
for( j = 0; j < ElCount; j++ )
{
const fptype* ip = Src - EdgePixelCount * ipstep -
FltLatency + j * InElIncr;
fptype* op = Dst + j * OutElIncr;
int l = OutLen;
while( l > 0 )
{
fptypesimd s = fptypesimd :: load( f ) *
fptypesimd :: loadu( ip );
for( i = elalign; i < flen; i += elalign )
{
s += fptypesimd :: load( f + i ) *
fptypesimd :: loadu( ip + i );
}
op[ 0 ] = s.hadd();
op += DstIncr;
ip += ipstep;
l--;
}
}
}
else
{
const fptype* ip0 = Src - EdgePixelCount * ipstep - FltLatency;
fptype* op0 = Dst;
int l = OutLen;
while( l > 0 )
{
const fptype* ip = ip0;
fptype* op = op0;
for( j = 0; j < ElCount; j++ )
{
fptypesimd s = fptypesimd :: load( f ) *
fptypesimd :: loadu( ip );
for( i = elalign; i < flen; i += elalign )
{
s += fptypesimd :: load( f + i ) *
fptypesimd :: loadu( ip + i );
}
op[ 0 ] = s.hadd();
ip += InElIncr;
op += OutElIncr;
}
ip0 += ipstep;
op0 += DstIncr;
l--;
}
}
}
/**
* Function performs resizing of a single scanline. This function does
* not "know" about the length of the source scanline buffer. This buffer
* should be padded with enough pixels so that ( SrcPos - FilterLenD2 ) is
* always >= 0 and ( SrcPos + ( DstLineLen - 1 ) * k + FilterLenD2 + 1 )
* does not exceed source scanline's buffer length. SrcLine's increment is
* assumed to be equal to 1.
*
* @param SrcLine Source scanline buffer.
* @param DstLine Destination (resized) scanline buffer.
* @param DstLineIncr Destination scanline position increment, used for
* horizontal or vertical scanline stepping.
* @param xx Temporary buffer, of size FltBank -> getFilterLen(), must be
* aligned by fpclass :: fpalign.
*/
void doResize( const fptype* SrcLine, fptype* DstLine,
int DstLineIncr, fptype* const xx ) const
{
const int IntFltLen = FltBank -> getFilterLen();
const int ElCount = Vars -> ElCount;
const int elalign = Vars -> elalign;
const typename CImageResizerFilterStep< fptype, fptype > ::
CResizePos* rpos = &(*RPosBuf)[ 0 ];
int DstLineLen = OutLen;
int i;
int j;
#define AVIR_RESIZE_PART1 \
while( DstLineLen > 0 ) \
{ \
const fptypesimd x = (fptypesimd) rpos -> x; \
const fptype* ftp = rpos -> ftp; \
const fptype* ftp2 = rpos -> ftp + IntFltLen; \
const fptype* Src = SrcLine + rpos -> SrcOffs;
#define AVIR_RESIZE_PART1nx \
while( DstLineLen > 0 ) \
{ \
const fptype* ftp = rpos -> ftp; \
const fptype* Src = SrcLine + rpos -> SrcOffs;
#define AVIR_RESIZE_PART2 \
DstLine += DstLineIncr; \
rpos++; \
DstLineLen--; \
}
if( ElCount == 1 )
{
if( FltBank -> getOrder() == 1 )
{
AVIR_RESIZE_PART1
fptypesimd sum = ( fptypesimd :: load( ftp ) +
fptypesimd :: load( ftp2 ) * x ) *
fptypesimd :: loadu( Src );
for( i = elalign; i < IntFltLen; i += elalign )
{
sum += ( fptypesimd :: load( ftp + i ) +
fptypesimd :: load( ftp2 + i ) * x ) *
fptypesimd :: loadu( Src + i );
}
DstLine[ 0 ] = sum.hadd();
AVIR_RESIZE_PART2
}
else
{
AVIR_RESIZE_PART1nx
fptypesimd sum = fptypesimd :: load( ftp ) *
fptypesimd :: loadu( Src );
for( i = elalign; i < IntFltLen; i += elalign )
{
sum += fptypesimd :: load( ftp + i ) *
fptypesimd :: loadu( Src + i );
}
DstLine[ 0 ] = sum.hadd();
AVIR_RESIZE_PART2
}
}
else
if( DstLineIncr == 1 )
{
// Horizontal-oriented processing, element loop is outer.
const int SrcIncr = InElIncr;
const int DstLineElIncr = OutElIncr - DstLineIncr * DstLineLen;
if( FltBank -> getOrder() == 1 )
{
for( j = 0; j < ElCount; j++ )
{
AVIR_RESIZE_PART1
fptypesimd sum = 0.0;
for( i = 0; i < IntFltLen; i += elalign )
{
sum += ( fptypesimd :: load( ftp + i ) +
fptypesimd :: load( ftp2 + i ) * x ) *
fptypesimd :: loadu( Src + i );
}
DstLine[ 0 ] = sum.hadd();
AVIR_RESIZE_PART2
DstLine += DstLineElIncr;
SrcLine += SrcIncr;
DstLineLen = OutLen;
rpos = &(*RPosBuf)[ 0 ];
}
}
else
{
for( j = 0; j < ElCount; j++ )
{
AVIR_RESIZE_PART1nx
fptypesimd sum = fptypesimd :: load( ftp ) *
fptypesimd :: loadu( Src );
for( i = elalign; i < IntFltLen; i += elalign )
{
sum += fptypesimd :: load( ftp + i ) *
fptypesimd :: loadu( Src + i );
}
DstLine[ 0 ] = sum.hadd();
AVIR_RESIZE_PART2
DstLine += DstLineElIncr;
SrcLine += SrcIncr;
DstLineLen = OutLen;
rpos = &(*RPosBuf)[ 0 ];
}
}
}
else
{
const int SrcIncr = InElIncr;
const int DstLineElIncr = OutElIncr;
DstLineIncr -= DstLineElIncr * ElCount;
if( FltBank -> getOrder() == 1 )
{
AVIR_RESIZE_PART1
for( i = 0; i < IntFltLen; i += elalign )
{
( fptypesimd :: load( ftp + i ) +
fptypesimd :: load( ftp2 + i ) * x ).store( xx + i );
}
for( j = 0; j < ElCount; j++ )
{
fptypesimd sum = fptypesimd :: load( xx ) *
fptypesimd :: loadu( Src );
for( i = elalign; i < IntFltLen; i += elalign )
{
sum += fptypesimd :: load( xx + i ) *
fptypesimd :: loadu( Src + i );
}
DstLine[ 0 ] = sum.hadd();
DstLine += DstLineElIncr;
Src += SrcIncr;
}
AVIR_RESIZE_PART2
}
else
{
AVIR_RESIZE_PART1nx
for( j = 0; j < ElCount; j++ )
{
fptypesimd sum = fptypesimd :: load( ftp ) *
fptypesimd :: loadu( Src );
for( i = elalign; i < IntFltLen; i += elalign )
{
sum += fptypesimd :: load( ftp + i ) *
fptypesimd :: loadu( Src + i );
}
DstLine[ 0 ] = sum.hadd();
DstLine += DstLineElIncr;
Src += SrcIncr;
}
AVIR_RESIZE_PART2
}
}
#undef AVIR_RESIZE_PART2
#undef AVIR_RESIZE_PART1nx
#undef AVIR_RESIZE_PART1
}
};
/**
* @brief Image resizer's default de-interleaved dithering class.
*
* This class defines an object that performs rounding, clipping and dithering
* operations over horizontal scanline pixels before scanline is stored in the
* output buffer.
*
* This ditherer implementation uses de-interleaved SIMD algorithm.
*
* @tparam fptype Floating point type to use for storing pixel data. SIMD
* types cannot be used.
* @tparam fptypesimd The SIMD type used to store a pack of "fptype" values.
*/
template< class fptype, class fptypesimd >
class CImageResizerDithererDefDIL
{
public:
/**
* Function initializes the ditherer object.
*
* @param aLen Scanline length in pixels to process.
* @param aVars Image resizing-related variables.
* @param aTrMul Bit-depth truncation multiplier. 1 - no additional
* truncation.
* @param aPkOut Peak output value allowed.
*/
void init( const int aLen, const CImageResizerVars& aVars,
const double aTrMul, const double aPkOut )
{
Len = aLen;
Vars = &aVars;
LenE = aLen * Vars -> ElCount;
TrMul0 = aTrMul;
PkOut0 = aPkOut;
}
/**
* @return "True" if dithering is recursive relative to scanlines meaning
* multi-threaded execution is not supported by this dithering method.
*/
static bool isRecursive()
{
return( false );
}
/**
* Function performs rounding and clipping operations.
*
* @param ResScanline The buffer containing the final scanline.
*/
void dither( fptype* const ResScanline ) const
{
const int elalign = Vars -> elalign;
const fptypesimd c0 = 0.0;
const fptypesimd PkOut = (fptypesimd) PkOut0;
int j;
if( TrMul0 == 1.0 )
{
// Optimization - do not perform bit truncation.
for( j = 0; j < LenE - elalign; j += elalign )
{
const fptypesimd z0 = round(
fptypesimd :: loadu( ResScanline + j ));
clamp( z0, c0, PkOut ).storeu( ResScanline + j );
}
const int lim = LenE - j;
const fptypesimd z0 = round(
fptypesimd :: loadu( ResScanline + j, lim ));
clamp( z0, c0, PkOut ).storeu( ResScanline + j, lim );
}
else
{
const fptypesimd TrMul = (fptypesimd) TrMul0;
for( j = 0; j < LenE - elalign; j += elalign )
{
const fptypesimd z0 = round(
fptypesimd :: loadu( ResScanline + j ) / TrMul ) * TrMul;
clamp( z0, c0, PkOut ).storeu( ResScanline + j );
}
const int lim = LenE - j;
const fptypesimd z0 = round(
fptypesimd :: loadu( ResScanline + j, lim ) / TrMul ) * TrMul;
clamp( z0, c0, PkOut ).storeu( ResScanline + j, lim );
}
}
protected:
int Len; ///< Scanline's length in pixels.
///<
const CImageResizerVars* Vars; ///< Image resizing-related variables.
///<
int LenE; ///< = LenE * ElCount.
///<
double TrMul0; ///< Bit-depth truncation multiplier.
///<
double PkOut0; ///< Peak output value allowed.
///<
};
/**
* @brief Image resizer's error-diffusion dithering class, de-interleaved
* mode.
*
* This ditherer implements error-diffusion dithering which looks good, and
* whose results are compressed by PNG well.
*
* @tparam fptype Floating point type to use for storing pixel data. SIMD
* types cannot be used.
* @tparam fptypesimd Processing type, SIMD can be used.
*/
template< class fptype, class fptypesimd >
class CImageResizerDithererErrdDIL
{
public:
/**
* Function initializes the ditherer object.
*
* @param aLen Scanline length in pixels to process.
* @param aVars Image resizing-related variables.
* @param aTrMul Bit-depth truncation multiplier. 1 - no additional
* truncation.
* @param aPkOut Peak output value allowed.
*/
void init( const int aLen, const CImageResizerVars& aVars,
const double aTrMul, const double aPkOut )
{
Len = aLen;
Vars = &aVars;
LenE = aLen * Vars -> ElCount;
TrMul0 = aTrMul;
PkOut0 = aPkOut;
ResScanlineDith0.alloc( LenE + Vars -> ElCount, sizeof( fptype ));
ResScanlineDith = ResScanlineDith0 + Vars -> ElCount;
int i;
for( i = 0; i < LenE + Vars -> ElCount; i++ )
{
ResScanlineDith0[ i ] = 0.0;
}
}
static bool isRecursive()
{
return( true );
}
void dither( fptype* const ResScanline )
{
const int ea = Vars -> elalign;
const fptypesimd c0 = 0.0;
const fptypesimd TrMul = (fptypesimd) TrMul0;
const fptypesimd PkOut = (fptypesimd) PkOut0;
int j;
for( j = 0; j < LenE - ea; j += ea )
{
fptypesimd :: addu( ResScanline + j,
fptypesimd :: loadu( ResScanlineDith + j ));
c0.storeu( ResScanlineDith + j );
}
int lim = LenE - j;
fptypesimd :: addu( ResScanline + j,
fptypesimd :: loadu( ResScanlineDith + j, lim ), lim );
c0.storeu( ResScanlineDith + j, lim );
const int Len1 = Len - 1;
fptype* rs = ResScanline;
fptype* rsd = ResScanlineDith;
int i;
for( i = 0; i < Vars -> ElCount; i++ )
{
for( j = 0; j < Len1; j++ )
{
// Perform rounding, noise estimation and saturation.
fptype* const rsj = rs + j;
const fptype z0 = round( rsj[ 0 ] / TrMul ) * TrMul;
const fptype Noise = rsj[ 0 ] - z0;
rsj[ 0 ] = clamp( z0, (fptype) 0.0, PkOut );
fptype* const rsdj = rsd + j;
rsj[ 1 ] += Noise * (fptype) 0.364842;
rsdj[ -1 ] += Noise * (fptype) 0.207305;
rsdj[ 0 ] += Noise * (fptype) 0.364842;
rsdj[ 1 ] += Noise * (fptype) 0.063011;
}
// Process the last pixel element in scanline.
const fptype z1 = round( rs[ Len1 ] / TrMul ) * TrMul;
const fptype Noise2 = rs[ Len1 ] - z1;
rs[ Len1 ] = clamp( z1, c0, PkOut );
rsd[ Len1 - 1 ] += Noise2 * (fptype) 0.207305;
rsd[ Len1 ] += Noise2 * (fptype) 0.364842;
rs += Len;
rsd += Len;
}
}
protected:
int Len; ///< Scanline's length in pixels.
///<
const CImageResizerVars* Vars; ///< Image resizing-related variables.
///<
int LenE; ///< = LenE * ElCount.
///<
double TrMul0; ///< Bit-depth truncation multiplier.
///<
double PkOut0; ///< Peak output value allowed.
///<
CBuffer< fptype > ResScanlineDith0; ///< Error propagation buffer for
///< dithering, first pixel unused.
///<
fptype* ResScanlineDith; ///< Error propagation buffer pointer which skips
///< the first ElCount elements.
///<
};
/**
* @brief Floating-point processing definition and abstraction class for
* de-interleaved processing.
*
* This class defines several constants and typedefs that point to classes
* that should be used by the image resizing algorithm. This implementation
* points to de-interleaved processing classes.
*
* @tparam afptype Floating point type to use for storing intermediate data
* and variables. SIMD types should not be used.
* @tparam afptypesimd SIMD type used to perform processing.
* @tparam adith Ditherer class to use during processing.
*/
template< class afptype, class afptypesimd,
class adith = CImageResizerDithererDefDIL< afptype, afptypesimd > >
class fpclass_def_dil
{
public:
typedef afptype fptype; ///< Floating-point type to use during processing.
///<
typedef afptype fptypeatom; ///< Atomic type "fptype" consists of.
///<
static const int fppack = 1; ///< The number of atomic types stored in a
///< single "fptype" element.
///<
static const int fpalign = sizeof( afptypesimd ); ///< Suggested alignment
///< size in bytes. This is not a required alignment, because image
///< resizing algorithm cannot be made to have a strictly aligned data
///< access in all cases (e.g. de-interleaved interpolation cannot
///< perform aligned accesses).
///<
static const int elalign = sizeof( afptypesimd ) / sizeof( afptype ); ///<
///< Length alignment of arrays of elements. This applies to filters
///< and intermediate buffers: this constant forces filters and
///< scanlines to have a length which is a multiple of this value, for
///< more efficient SIMD implementation.
///<
static const int packmode = 1; ///< 0 if interleaved packing, 1 if
///< de-interleaved.
///<
typedef CImageResizerFilterStepDIL< fptype, afptypesimd > CFilterStep; ///<
///< Filtering step class to use during processing.
///<
typedef adith CDitherer; ///< Ditherer class to use during processing.
///<
};
} // namespace avir