You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1618 lines
46 KiB
1618 lines
46 KiB
//===== Copyright © 1996-2006, Valve Corporation, All rights reserved. ======//
|
|
//
|
|
// Purpose:
|
|
//
|
|
//===========================================================================//
|
|
|
|
#include <tier0/platform.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
#include <stdlib.h>
|
|
#include "bitmap/floatbitmap.h"
|
|
#include <tier2/tier2.h>
|
|
#include "bitmap/imageformat.h"
|
|
#include "bitmap/tgaloader.h"
|
|
#include "tier1/strtools.h"
|
|
#include "filesystem.h"
|
|
#include "vstdlib/jobthread.h"
|
|
|
|
// for PSD loading
|
|
#include "bitmap/bitmap.h"
|
|
#include "bitmap/psd.h"
|
|
#include "tier2/utlstreambuffer.h"
|
|
|
|
// NOTE: This has to be the last file included!
|
|
#include "tier0/memdbgon.h"
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Globals
|
|
//-----------------------------------------------------------------------------
|
|
IThreadPool *FloatBitMap_t::sm_pFBMThreadPool = NULL;
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Sets a thread pool for all float bitmap work to be done on
|
|
//-----------------------------------------------------------------------------
|
|
void FloatBitMap_t::SetThreadPool( IThreadPool* pPool )
|
|
{
|
|
sm_pFBMThreadPool = pPool;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Utility methods
|
|
//-----------------------------------------------------------------------------
|
|
#define SQ(x) ((x)*(x))
|
|
|
|
// linear interpolate between 2 control points (L,R)
|
|
inline float LinInterp( float frac, float L, float R )
|
|
{
|
|
return ( ( ( R - L ) * frac ) + L );
|
|
}
|
|
|
|
// bilinear interpolate between 4 control points (UL,UR,LL,LR)
|
|
inline float BiLinInterp( float Xfrac, float Yfrac, float UL, float UR, float LL, float LR )
|
|
{
|
|
float iu = LinInterp( Xfrac, UL, UR );
|
|
float il = LinInterp( Xfrac, LL, LR );
|
|
return( LinInterp( Yfrac, iu, il ) );
|
|
}
|
|
|
|
// trilinear interpolate between 8 control points (ULN,URN,LLN,LRN,ULF,URF,LLF,LRF)
|
|
inline float TriLinInterp( float Xfrac, float Yfrac, float Zfrac, float ULN,
|
|
float URN, float LLN, float LRN, float ULF, float URF, float LLF, float LRF )
|
|
{
|
|
float iu = LinInterp( Xfrac, ULN, URN );
|
|
float il = LinInterp( Xfrac, LLN, LRN );
|
|
float jn =( LinInterp( Yfrac, iu, il ) );
|
|
|
|
iu = LinInterp( Xfrac, ULF, URF );
|
|
il = LinInterp( Xfrac, LLF, LRF );
|
|
float jf =( LinInterp( Yfrac, iu, il ) );
|
|
|
|
return( LinInterp( Zfrac, jn, jf ) );
|
|
}
|
|
|
|
static char GetChar( FileHandle_t & f )
|
|
{
|
|
char a;
|
|
g_pFullFileSystem->Read( &a, 1, f );
|
|
return a;
|
|
}
|
|
|
|
static int GetInt( FileHandle_t & f )
|
|
{
|
|
char buf[100];
|
|
char * bout = buf;
|
|
for( ; ; )
|
|
{
|
|
char c = GetChar( f );
|
|
if ( ( c <'0' ) || ( c >'9' ) )
|
|
break;
|
|
*( bout++ ) = c;
|
|
}
|
|
*( bout++ ) = 0;
|
|
return atoi( buf );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Constructors
|
|
//-----------------------------------------------------------------------------
|
|
FloatBitMap_t::FloatBitMap_t( int nWidth, int nHeight, int nDepth, int nAttributeMask )
|
|
{
|
|
Init( nWidth, nHeight, nDepth, nAttributeMask );
|
|
}
|
|
|
|
FloatBitMap_t::FloatBitMap_t( const FloatBitMap_t *pOrig )
|
|
{
|
|
Init( pOrig->NumCols(), pOrig->NumRows(), pOrig->NumSlices() );
|
|
LoadFromFloatBitmap( pOrig );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Initialize, shutdown
|
|
//-----------------------------------------------------------------------------
|
|
void FloatBitMap_t::Shutdown()
|
|
{
|
|
Purge();
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Construct a floating point gamma table
|
|
//-----------------------------------------------------------------------------
|
|
static void ConstructFloatGammaTable( float* pTable, float flSrcGamma, float flDstGamma )
|
|
{
|
|
float flOO1023 = 1.0f / 1023.0f;
|
|
for( int i = 0; i < 1024; i++ )
|
|
{
|
|
pTable[i] = pow( (float)i * flOO1023, flSrcGamma / flDstGamma );
|
|
}
|
|
}
|
|
|
|
static void ConstructShortGammaTable( uint16* pTable, float flSrcGamma, float flDstGamma )
|
|
{
|
|
float flOO1023 = 1.0f / 1023.0f;
|
|
for( int i = 0; i < 1024; i++ )
|
|
{
|
|
pTable[i] = ( uint16 )( 1023.0f * pow( (float)i * flOO1023, flSrcGamma / flDstGamma ) + 0.5f );
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Gets the gamma table
|
|
//-----------------------------------------------------------------------------
|
|
static const float *GetFloatGammaTable( float flSrcGamma )
|
|
{
|
|
// Compute gamma tables...
|
|
static float s_pGammaToLinear[1024];
|
|
static float s_flLastSrcGamma = -1;
|
|
|
|
if ( s_flLastSrcGamma != flSrcGamma )
|
|
{
|
|
ConstructFloatGammaTable( s_pGammaToLinear, flSrcGamma, 1.0f );
|
|
s_flLastSrcGamma = flSrcGamma;
|
|
}
|
|
|
|
return s_pGammaToLinear;
|
|
}
|
|
|
|
static const uint16 *GetShortGammaTable( float flDestGamma )
|
|
{
|
|
// Compute gamma tables...
|
|
static uint16 s_pLinearToGamma[1024];
|
|
static float s_flLastDestGamma = -1;
|
|
|
|
if ( s_flLastDestGamma != flDestGamma )
|
|
{
|
|
ConstructShortGammaTable( s_pLinearToGamma, 1.0f, flDestGamma );
|
|
s_flLastDestGamma = flDestGamma;
|
|
}
|
|
|
|
return s_pLinearToGamma;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Loads from a buffer, assumes dimensions match the bitmap size
|
|
//-----------------------------------------------------------------------------
|
|
void FloatBitMap_t::LoadFromBuffer( const void *pBuffer, size_t nBufSize, ImageFormat fmt, float flGamma )
|
|
{
|
|
if ( !pBuffer || !nBufSize )
|
|
return;
|
|
|
|
Assert( ImageLoader::GetMemRequired( NumCols(), NumRows(), NumSlices(), 1, fmt ) == (int)nBufSize );
|
|
if( ImageLoader::GetMemRequired( NumCols(), NumRows(), NumSlices(), 1, fmt ) != (int)nBufSize )
|
|
{
|
|
Warning( "FloatBitMap_t::LoadFromBuffer: Received improper buffer size, skipping!\n" );
|
|
return;
|
|
}
|
|
|
|
// Cache off constant values
|
|
float c[FBM_ATTR_COUNT] = { 0.0 };
|
|
int nMask = GetAttributeMask();
|
|
for ( int i = 0; i < FBM_ATTR_COUNT; ++i )
|
|
{
|
|
if ( ( nMask & ( 1 << i ) ) == 0 )
|
|
{
|
|
c[i] = ConstantValue( i );
|
|
}
|
|
}
|
|
|
|
switch( fmt )
|
|
{
|
|
case IMAGE_FORMAT_ABGR8888:
|
|
LoadFromBufferRGBA( ( const ABGR8888_t* )pBuffer, nBufSize / sizeof(ABGR8888_t), GetFloatGammaTable( flGamma ) );
|
|
break;
|
|
|
|
case IMAGE_FORMAT_RGBA8888:
|
|
LoadFromBufferRGBA( ( const RGBA8888_t* )pBuffer, nBufSize / sizeof(RGBA8888_t), GetFloatGammaTable( flGamma ) );
|
|
break;
|
|
|
|
case IMAGE_FORMAT_BGRA8888:
|
|
LoadFromBufferRGBA( ( const BGRA8888_t* )pBuffer, nBufSize / sizeof(BGRA8888_t), GetFloatGammaTable( flGamma ) );
|
|
break;
|
|
|
|
case IMAGE_FORMAT_RGB888:
|
|
LoadFromBufferRGB( ( const RGB888_t* )pBuffer, nBufSize / sizeof(RGB888_t), GetFloatGammaTable( flGamma ) );
|
|
break;
|
|
|
|
case IMAGE_FORMAT_BGR888:
|
|
LoadFromBufferRGB( ( const BGR888_t* )pBuffer, nBufSize / sizeof(BGR888_t), GetFloatGammaTable( flGamma ) );
|
|
break;
|
|
|
|
case IMAGE_FORMAT_BGRX8888:
|
|
LoadFromBufferRGB( ( BGRX8888_t* )pBuffer, nBufSize / sizeof(BGRX8888_t), GetFloatGammaTable( flGamma ) );
|
|
break;
|
|
|
|
case IMAGE_FORMAT_UV88:
|
|
LoadFromBufferUV( ( const UV88_t* )pBuffer, nBufSize / sizeof(UV88_t) );
|
|
break;
|
|
|
|
case IMAGE_FORMAT_UVWQ8888:
|
|
LoadFromBufferUVWQ( ( const UVWQ8888_t* )pBuffer, nBufSize / sizeof(UVWQ8888_t) );
|
|
break;
|
|
|
|
case IMAGE_FORMAT_UVLX8888:
|
|
LoadFromBufferUVLX( ( const UVLX8888_t* )pBuffer, nBufSize / sizeof(UVLX8888_t) );
|
|
break;
|
|
|
|
default:
|
|
Warning( "FloatBitMap_t::LoadFromBuffer: Unsupported color format, skipping!\n" );
|
|
Assert( 0 );
|
|
break;
|
|
}
|
|
|
|
// Restore constant values
|
|
for ( int i = 0; i < FBM_ATTR_COUNT; ++i )
|
|
{
|
|
if ( ( nMask & ( 1 << i ) ) == 0 )
|
|
{
|
|
FillAttr( i, c[i] );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void FloatBitMap_t::LoadFromFloatBitmap( const FloatBitMap_t *pOrig )
|
|
{
|
|
Assert( ( NumCols() == pOrig->NumCols() ) && ( NumRows() == pOrig->NumRows() ) && ( NumSlices() == pOrig->NumSlices() ) );
|
|
if ( ( NumCols() != pOrig->NumCols() ) || ( NumRows() != pOrig->NumRows() ) || ( NumSlices() != pOrig->NumSlices() ) )
|
|
{
|
|
Warning( "FloatBitMap_t::LoadFromFloatBitmap: Received improper bitmap size, skipping!\n" );
|
|
return;
|
|
}
|
|
|
|
float flDefaultVal[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
|
|
for ( int i = 0; i < 4; ++i )
|
|
{
|
|
if ( !HasAttributeData( (FBMAttribute_t)i ) )
|
|
{
|
|
FillAttr( i, flDefaultVal[i] );
|
|
}
|
|
else
|
|
{
|
|
CopyAttrFrom( *pOrig, i );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Writes into a buffer, assumes dimensions match the bitmap size
|
|
//-----------------------------------------------------------------------------
|
|
void FloatBitMap_t::WriteToBuffer( void *pBuffer, size_t nBufSize, ImageFormat fmt, float flGamma ) const
|
|
{
|
|
if ( !pBuffer || !nBufSize )
|
|
return;
|
|
|
|
Assert( ImageLoader::GetMemRequired( NumCols(), NumRows(), NumSlices(), 1, fmt ) == (int)nBufSize );
|
|
if( ImageLoader::GetMemRequired( NumCols(), NumRows(), NumSlices(), 1, fmt ) != (int)nBufSize )
|
|
{
|
|
Warning( "FloatBitMap_t::WriteToBuffer: Received improper buffer size, skipping!\n" );
|
|
return;
|
|
}
|
|
|
|
switch( fmt )
|
|
{
|
|
case IMAGE_FORMAT_ABGR8888:
|
|
WriteToBufferRGBA( ( ABGR8888_t* )pBuffer, nBufSize / sizeof(ABGR8888_t), GetShortGammaTable( flGamma ) );
|
|
break;
|
|
|
|
case IMAGE_FORMAT_RGBA8888:
|
|
WriteToBufferRGBA( ( RGBA8888_t* )pBuffer, nBufSize / sizeof(RGBA8888_t), GetShortGammaTable( flGamma ) );
|
|
break;
|
|
|
|
case IMAGE_FORMAT_BGRA8888:
|
|
WriteToBufferRGBA( ( BGRA8888_t* )pBuffer, nBufSize / sizeof(BGRA8888_t), GetShortGammaTable( flGamma ) );
|
|
break;
|
|
|
|
case IMAGE_FORMAT_RGB888:
|
|
WriteToBufferRGB( ( RGB888_t* )pBuffer, nBufSize / sizeof(RGB888_t), GetShortGammaTable( flGamma ) );
|
|
break;
|
|
|
|
case IMAGE_FORMAT_BGR888:
|
|
WriteToBufferRGB( ( BGR888_t* )pBuffer, nBufSize / sizeof(BGR888_t), GetShortGammaTable( flGamma ) );
|
|
break;
|
|
|
|
case IMAGE_FORMAT_BGRX8888:
|
|
WriteToBufferRGB( ( BGRX8888_t* )pBuffer, nBufSize / sizeof(BGRX8888_t), GetShortGammaTable( flGamma ) );
|
|
break;
|
|
|
|
case IMAGE_FORMAT_UV88:
|
|
WriteToBufferUV( ( UV88_t* )pBuffer, nBufSize / sizeof(UV88_t) );
|
|
break;
|
|
|
|
case IMAGE_FORMAT_UVWQ8888:
|
|
WriteToBufferUVWQ( ( UVWQ8888_t* )pBuffer, nBufSize / sizeof(UVWQ8888_t) );
|
|
break;
|
|
|
|
case IMAGE_FORMAT_UVLX8888:
|
|
WriteToBufferUVLX( ( UVLX8888_t* )pBuffer, nBufSize / sizeof(UVLX8888_t) );
|
|
break;
|
|
|
|
default:
|
|
Warning( "FloatBitMap_t::WriteToBuffer: Unsupported color format, skipping!\n" );
|
|
Assert( 0 );
|
|
break;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Loads from a PSD file
|
|
//-----------------------------------------------------------------------------
|
|
bool FloatBitMap_t::LoadFromPSD( const char *pFilename )
|
|
{
|
|
Bitmap_t bitmap;
|
|
CUtlStreamBuffer buf( pFilename, "GAME", CUtlBuffer::READ_ONLY );
|
|
if ( IsPSDFile( buf ) )
|
|
{
|
|
if ( !PSDReadFileRGBA8888( buf, bitmap ) )
|
|
return false;
|
|
|
|
if ( bitmap.Format() != IMAGE_FORMAT_RGBA8888 )
|
|
return false;
|
|
}
|
|
Init( bitmap.Width(), bitmap.Height() );
|
|
for ( int x = 0; x < bitmap.Width(); x++ )
|
|
{
|
|
for ( int y = 0; y < bitmap.Height(); y++ )
|
|
{
|
|
RGBA8888_t* pPixel = (RGBA8888_t*)bitmap.GetPixel( x, y );
|
|
Pixel( x, y, 0, 0 ) = pPixel->r / 255.0f;
|
|
Pixel( x, y, 0, 1 ) = pPixel->g / 255.0f;
|
|
Pixel( x, y, 0, 2 ) = pPixel->b / 255.0f;
|
|
Pixel( x, y, 0, 3 ) = pPixel->a / 255.0f;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Loads from a PFM file
|
|
//-----------------------------------------------------------------------------
|
|
#define PFM_MAX_XSIZE 2048
|
|
bool FloatBitMap_t::LoadFromPFM( const char* fname )
|
|
{
|
|
bool bSuccess = false;
|
|
FileHandle_t f = g_pFullFileSystem->Open(fname, "rb");
|
|
if ( !f )
|
|
return false;
|
|
|
|
if( ( GetChar( f ) == 'P' ) && ( GetChar( f ) == 'F' ) && ( GetChar( f ) == '\n' ) )
|
|
{
|
|
int nWidth = GetInt( f );
|
|
int nHeight = GetInt( f );
|
|
|
|
// eat crap until the next newline
|
|
while( GetChar( f ) != '\n' )
|
|
{
|
|
}
|
|
|
|
// printf("file %s w=%d h=%d\n",fname,Width,Height);
|
|
Init( nWidth, nHeight );
|
|
|
|
for( int y = nHeight - 1; y >= 0; y-- )
|
|
{
|
|
float linebuffer[PFM_MAX_XSIZE * 3];
|
|
g_pFullFileSystem->Read( linebuffer, 3 * nWidth * sizeof( float ), f );
|
|
for( int x = 0; x < nWidth; x++ )
|
|
{
|
|
for( int c = 0; c < 3; c++ )
|
|
{
|
|
Pixel( x, y, 0, c ) = linebuffer[x * 3 + c];
|
|
}
|
|
}
|
|
}
|
|
bSuccess = true;
|
|
}
|
|
g_pFullFileSystem->Close( f ); // close file after reading
|
|
return bSuccess;
|
|
}
|
|
|
|
bool FloatBitMap_t::WritePFM( const char* fname )
|
|
{
|
|
FileHandle_t f = g_pFullFileSystem->Open(fname, "wb");
|
|
|
|
if ( f )
|
|
{
|
|
g_pFullFileSystem->FPrintf( f, "PF\n%d %d\n-1.000000\n", NumCols(), NumRows());
|
|
for( int y = NumRows() - 1; y >= 0; y-- )
|
|
{
|
|
float linebuffer[PFM_MAX_XSIZE * 3];
|
|
for( int x = 0; x < NumCols(); x++ )
|
|
{
|
|
for( int c = 0; c < 3; c++ )
|
|
{
|
|
linebuffer[x * 3 + c]= Pixel( x, y, 0, c );
|
|
}
|
|
}
|
|
g_pFullFileSystem->Write( linebuffer, 3 * NumCols() * sizeof( float ), f );
|
|
}
|
|
g_pFullFileSystem->Close( f );
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
float FloatBitMap_t::InterpolatedPixel( float x, float y, int comp ) const
|
|
{
|
|
int Top = ( int )floor( y );
|
|
float Yfrac = y - Top;
|
|
int Bot = MIN( NumRows() - 1, Top + 1 );
|
|
int Left = ( int )floor( x );
|
|
float Xfrac = x - Left;
|
|
int Right = MIN( NumCols() - 1, Left + 1 );
|
|
return
|
|
BiLinInterp( Xfrac, Yfrac,
|
|
Pixel( Left, Top, 0, comp ),
|
|
Pixel( Right, Top, 0, comp ),
|
|
Pixel( Left, Bot, 0, comp ),
|
|
Pixel( Right, Bot, 0, comp ) );
|
|
|
|
}
|
|
|
|
float FloatBitMap_t::InterpolatedPixel( float x, float y, float z, int comp ) const
|
|
{
|
|
int Top = ( int )floor( y );
|
|
float Yfrac = y - Top;
|
|
int Bot = MIN( NumRows() - 1, Top + 1 );
|
|
int Left = ( int )floor( x );
|
|
float Xfrac = x - Left;
|
|
int Right = MIN( NumCols() - 1, Left + 1 );
|
|
int Near = ( int )floor( z );
|
|
float Zfrac = z - Near;
|
|
int Far = MIN( NumSlices() - 1, Near + 1 );
|
|
return
|
|
TriLinInterp( Xfrac, Yfrac, Zfrac,
|
|
Pixel( Left, Top, Near, comp ),
|
|
Pixel( Right, Top, Near, comp ),
|
|
Pixel( Left, Bot, Near, comp ),
|
|
Pixel( Right, Bot, Near, comp ),
|
|
Pixel( Left, Top, Far, comp ),
|
|
Pixel( Right, Top, Far, comp ),
|
|
Pixel( Left, Bot, Far, comp ),
|
|
Pixel( Right, Bot, Far, comp ) );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Method to slam a particular channel to always be the same value
|
|
//-----------------------------------------------------------------------------
|
|
void FloatBitMap_t::SetChannel( int comp, float flValue )
|
|
{
|
|
for ( int z = 0; z < NumSlices(); z++ )
|
|
{
|
|
for ( int y = 0; y < NumRows(); y++ )
|
|
{
|
|
for ( int x = 0; x < NumCols(); x++ )
|
|
{
|
|
Pixel( x, y, z, comp ) = flValue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------
|
|
// resize (with bilinear filter) truecolor bitmap in place
|
|
|
|
void FloatBitMap_t::ReSize( int NewWidth, int NewHeight )
|
|
{
|
|
float XRatio = ( float )NumCols() / ( float )NewWidth;
|
|
float YRatio = ( float )NumRows() / ( float )NewHeight;
|
|
float SourceX, SourceY, Xfrac, Yfrac;
|
|
int Top, Bot, Left, Right;
|
|
|
|
FloatBitMap_t newrgba( NewWidth, NewHeight );
|
|
|
|
SourceY = 0;
|
|
for( int y = 0; y < NewHeight; y++ )
|
|
{
|
|
Yfrac = SourceY - floor( SourceY );
|
|
Top = ( int )SourceY;
|
|
Bot = ( int )(SourceY + 1 );
|
|
if ( Bot >= NumRows() )
|
|
Bot = NumRows() - 1;
|
|
SourceX = 0;
|
|
for( int x = 0; x < NewWidth; x++ )
|
|
{
|
|
Xfrac = SourceX - floor( SourceX );
|
|
Left = ( int )SourceX;
|
|
Right = ( int )( SourceX + 1 );
|
|
if ( Right >= NumCols() )
|
|
Right = NumCols() - 1;
|
|
for( int c = 0; c < 4; c++ )
|
|
{
|
|
newrgba.Pixel( x, y, 0, c ) = BiLinInterp( Xfrac, Yfrac,
|
|
Pixel( Left, Top, 0, c ),
|
|
Pixel( Right, Top, 0, c ),
|
|
Pixel( Left, Bot, 0, c ),
|
|
Pixel( Right, Bot, 0, c ) );
|
|
}
|
|
SourceX += XRatio;
|
|
}
|
|
SourceY += YRatio;
|
|
}
|
|
MoveDataFrom( newrgba );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Makes the image be a sub-range of the current image
|
|
//-----------------------------------------------------------------------------
|
|
void FloatBitMap_t::Crop( int x1, int y1, int z1, int nWidth, int nHeight, int nDepth )
|
|
{
|
|
int x2 = x1 + nWidth;
|
|
int y2 = y1 + nHeight;
|
|
int z2 = z1 + nDepth;
|
|
|
|
if ( ( x1 >= NumCols() || y1 >= NumRows() || z1 >= NumSlices() ) || ( x2 <= 0 || y2 <= 0 || z2 <= 0 ) )
|
|
{
|
|
Init( nWidth, nHeight, nDepth );
|
|
return;
|
|
}
|
|
|
|
x1 = clamp( x1, 0, NumCols() );
|
|
y1 = clamp( y1, 0, NumRows() );
|
|
z1 = clamp( z1, 0, NumSlices() );
|
|
x2 = clamp( x2, 0, NumCols() );
|
|
y2 = clamp( y2, 0, NumRows() );
|
|
z2 = clamp( z2, 0, NumSlices() );
|
|
nWidth = x2 - x1; nHeight = y2 - y1; nDepth = z2 - z1;
|
|
|
|
// Check for crops that don't require data movement
|
|
if ( NumSlices() <= 1 )
|
|
{
|
|
if ( nWidth == NumCols() )
|
|
{
|
|
m_nRows = nHeight;
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( nWidth == NumCols() && nHeight == NumRows() )
|
|
{
|
|
m_nSlices = nDepth;
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Generic data movement; works because data is stored in ascending
|
|
// order in memory in the same way as we're looping over the data
|
|
int ci[4];
|
|
int nAttr = ComputeValidAttributeList( ci );
|
|
for ( int z = 0; z < nDepth; ++z )
|
|
{
|
|
for ( int y = 0; y < nHeight; ++y )
|
|
{
|
|
for ( int x = 0; x < nWidth; ++x )
|
|
{
|
|
for ( int c = 0; c < nAttr; ++c )
|
|
{
|
|
Pixel( x, y, z, ci[c] ) = Pixel( x + x1, y + y1, z + z1, ci[c] );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
struct TGAHeader_t
|
|
{
|
|
unsigned char id_length, colormap_type, image_type;
|
|
unsigned char colormap_index0, colormap_index1, colormap_length0, colormap_length1;
|
|
unsigned char colormap_size;
|
|
unsigned char x_origin0, x_origin1, y_origin0, y_origin1, width0, width1, height0, height1;
|
|
unsigned char pixel_size, attributes;
|
|
};
|
|
|
|
bool FloatBitMap_t::WriteTGAFile( const char* filename ) const
|
|
{
|
|
FileHandle_t f = g_pFullFileSystem->Open(filename, "wb");
|
|
if ( f )
|
|
{
|
|
TGAHeader_t myheader;
|
|
memset( & myheader, 0, sizeof( myheader ) );
|
|
myheader.image_type = 2;
|
|
myheader.pixel_size = 32;
|
|
myheader.width0 = NumCols() & 0xff;
|
|
myheader.width1 = ( NumCols() >> 8 );
|
|
myheader.height0 = NumRows() & 0xff;
|
|
myheader.height1 = ( NumRows() >> 8 );
|
|
myheader.attributes = 0x20;
|
|
g_pFullFileSystem->Write( & myheader, sizeof( myheader ), f );
|
|
// now, write the pixels
|
|
for( int y = 0; y < NumRows(); y++ )
|
|
{
|
|
for( int x = 0; x < NumCols(); x++ )
|
|
{
|
|
PixRGBAF fpix = PixelRGBAF( x, y, 0 );
|
|
PixRGBA8 pix8 = PixRGBAF_to_8( fpix );
|
|
|
|
g_pFullFileSystem->Write( & pix8.Blue, 1, f );
|
|
g_pFullFileSystem->Write( & pix8.Green, 1, f );
|
|
g_pFullFileSystem->Write( & pix8.Red, 1, f );
|
|
g_pFullFileSystem->Write( & pix8.Alpha, 1, f );
|
|
}
|
|
}
|
|
g_pFullFileSystem->Close( f ); // close file after reading
|
|
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
FloatBitMap_t::FloatBitMap_t( const char* tgafilename )
|
|
{
|
|
// load from a tga or pfm or psd
|
|
if ( Q_stristr( tgafilename, ".psd") )
|
|
{
|
|
LoadFromPSD( tgafilename );
|
|
return;
|
|
}
|
|
if ( Q_stristr( tgafilename, ".pfm") )
|
|
{
|
|
LoadFromPFM( tgafilename );
|
|
return;
|
|
}
|
|
|
|
int width1, height1;
|
|
ImageFormat imageFormat1;
|
|
float gamma1;
|
|
|
|
if( !TGALoader::GetInfo( tgafilename, &width1, &height1, &imageFormat1, &gamma1 ) )
|
|
{
|
|
Warning( "FloatBitMap_t: Error opening %s\n", tgafilename );
|
|
Init( 1, 1 );
|
|
Pixel( 0, 0, 0, 0 ) = Pixel( 0, 0, 0, 3 ) = 1.0f;
|
|
Pixel( 0, 0, 0, 1 ) = Pixel( 0, 0, 0, 2 ) = 0.0f;
|
|
return;
|
|
}
|
|
|
|
Init( width1, height1 );
|
|
|
|
uint8 * pImage1Tmp =
|
|
new uint8 [ImageLoader::GetMemRequired( width1, height1, 1, imageFormat1, false )];
|
|
|
|
if( !TGALoader::Load( pImage1Tmp, tgafilename, width1, height1, imageFormat1, 2.2f, false ) )
|
|
{
|
|
Warning( "FloatBitMap_t: Error loading %s\n", tgafilename );
|
|
Init( 1, 1 );
|
|
Pixel( 0, 0, 0, 0 ) = Pixel( 0, 0, 0, 3 ) = 1.0f;
|
|
Pixel( 0, 0, 0, 1 ) = Pixel( 0, 0, 0, 2 ) = 0.0f;
|
|
delete[] pImage1Tmp;
|
|
return;
|
|
}
|
|
uint8 * pImage1 =
|
|
new uint8 [ImageLoader::GetMemRequired( width1, height1, 1, IMAGE_FORMAT_ABGR8888, false )];
|
|
|
|
ImageLoader::ConvertImageFormat( pImage1Tmp, imageFormat1, pImage1, IMAGE_FORMAT_ABGR8888, width1, height1, 0, 0 );
|
|
|
|
for( int y = 0; y < height1; y++ )
|
|
{
|
|
for( int x = 0; x < width1; x++ )
|
|
{
|
|
for( int c = 0; c < 4; c++ )
|
|
{
|
|
Pixel( x, y, 0, 3 - c ) = pImage1[c + 4 * ( x + ( y * width1 ) )]/ 255.0;
|
|
}
|
|
}
|
|
}
|
|
|
|
delete[] pImage1;
|
|
delete[] pImage1Tmp;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Downsample using bilinear filtering
|
|
//-----------------------------------------------------------------------------
|
|
void FloatBitMap_t::QuarterSize2D( FloatBitMap_t *pDest, int nStart, int nCount )
|
|
{
|
|
for( int y = nStart; y < nStart + nCount; y++ )
|
|
{
|
|
for( int x = 0; x < pDest->NumCols(); x++ )
|
|
{
|
|
for( int c = 0; c < 4; c++ )
|
|
{
|
|
pDest->Pixel( x, y, 0, c ) =
|
|
( ( Pixel( x * 2, y * 2, 0, c ) + Pixel( x * 2 + 1, y * 2, 0, c ) +
|
|
Pixel( x * 2, y * 2 + 1, 0, c ) + Pixel( x * 2 + 1, y * 2 + 1, 0, c ) ) / 4.0f );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Downsample using trilinear filtering
|
|
//-----------------------------------------------------------------------------
|
|
void FloatBitMap_t::QuarterSize3D( FloatBitMap_t *pDest, int nStart, int nCount )
|
|
{
|
|
for( int z = nStart; z < nStart + nCount; z++ )
|
|
{
|
|
for( int y = 0; y < pDest->NumRows(); y++ )
|
|
{
|
|
for( int x = 0; x < pDest->NumCols(); x++ )
|
|
{
|
|
for( int c = 0; c < 4; c++ )
|
|
{
|
|
pDest->Pixel( x, y, z, c ) =
|
|
( ( Pixel( x * 2, y * 2, z * 2, c ) + Pixel( x * 2 + 1, y * 2, z * 2, c ) +
|
|
Pixel( x * 2, y * 2 + 1,z * 2, c ) + Pixel( x * 2 + 1, y * 2 + 1,z * 2, c ) +
|
|
Pixel( x * 2, y * 2, z * 2 + 1, c ) + Pixel( x * 2 + 1, y * 2, z * 2 + 1, c ) +
|
|
Pixel( x * 2, y * 2 + 1,z * 2 + 1, c ) + Pixel( x * 2 + 1, y * 2 + 1,z * 2 + 1, c ) ) / 8.0f );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FloatBitMap_t::QuarterSize( FloatBitMap_t *pBitmap )
|
|
{
|
|
// generate a new bitmap half on each axis
|
|
bool bIs2D = ( NumSlices() == 1 );
|
|
|
|
int sx = NumCols() / 2;
|
|
int sy = NumRows() / 2;
|
|
int sz = NumSlices() / 2;
|
|
sx = MAX( sx, 1 );
|
|
sy = MAX( sy, 1 );
|
|
sz = MAX( sz, 1 );
|
|
|
|
pBitmap->Init( sx, sy, sz );
|
|
|
|
if ( bIs2D )
|
|
{
|
|
ParallelLoopProcessChunks( sm_pFBMThreadPool, pBitmap, 0, sy, 16, this, &FloatBitMap_t::QuarterSize2D );
|
|
}
|
|
else
|
|
{
|
|
ParallelLoopProcessChunks( sm_pFBMThreadPool, pBitmap, 0, sz, 8, this, &FloatBitMap_t::QuarterSize3D );
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Downsample using bilinear filtering
|
|
//-----------------------------------------------------------------------------
|
|
void FloatBitMap_t::QuarterSizeBlocky2D( FloatBitMap_t *pDest, int nStart, int nCount )
|
|
{
|
|
for( int y = nStart; y < nStart + nCount; y++ )
|
|
{
|
|
for( int x = 0; x < pDest->NumCols(); x++ )
|
|
{
|
|
for( int c = 0; c < 4; c++ )
|
|
{
|
|
pDest->Pixel( x, y, 0, c ) = Pixel( x * 2, y * 2, 0, c );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Downsample using trilinear filtering
|
|
//-----------------------------------------------------------------------------
|
|
void FloatBitMap_t::QuarterSizeBlocky3D( FloatBitMap_t *pDest, int nStart, int nCount )
|
|
{
|
|
for( int z = nStart; z < nStart + nCount; z++ )
|
|
{
|
|
for( int y = 0; y < pDest->NumRows(); y++ )
|
|
{
|
|
for( int x = 0; x < pDest->NumCols(); x++ )
|
|
{
|
|
for( int c = 0; c < 4; c++ )
|
|
{
|
|
pDest->Pixel( x, y, z, c ) = Pixel( x * 2, y * 2, z * 2, c );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Downsample using point sampling
|
|
//-----------------------------------------------------------------------------
|
|
void FloatBitMap_t::QuarterSizeBlocky( FloatBitMap_t *pBitmap )
|
|
{
|
|
// generate a new bitmap half on each axis
|
|
int sx = NumCols() / 2;
|
|
int sy = NumRows() / 2;
|
|
int sz = NumSlices() / 2;
|
|
sx = MAX( sx, 1 );
|
|
sy = MAX( sy, 1 );
|
|
sz = MAX( sz, 1 );
|
|
|
|
pBitmap->Init( sx, sy, sz );
|
|
|
|
if ( NumSlices() == 1 )
|
|
{
|
|
ParallelLoopProcessChunks( sm_pFBMThreadPool, pBitmap, 0, sy, 128, this, &FloatBitMap_t::QuarterSizeBlocky2D );
|
|
}
|
|
else
|
|
{
|
|
ParallelLoopProcessChunks( sm_pFBMThreadPool, pBitmap, 0, sz, 32, this, &FloatBitMap_t::QuarterSizeBlocky3D );
|
|
}
|
|
}
|
|
|
|
|
|
enum KernelType_t
|
|
{
|
|
KERNEL_DEFAULT = 0,
|
|
KERNEL_ALPHATEST,
|
|
};
|
|
|
|
struct FloatBitmapResampleInfo_t
|
|
{
|
|
FloatBitMap_t *m_pSrcBitmap;
|
|
FloatBitMap_t *m_pDestBitmap;
|
|
const KernelInfo_t *m_pKernel;
|
|
float *m_pAlphaResult;
|
|
int m_nFlags;
|
|
int m_nSrcWidth;
|
|
int m_nSrcHeight;
|
|
int m_nSrcDepth;
|
|
float m_flAlphaThreshhold;
|
|
float m_flAlphaHiFreqThreshhold;
|
|
int m_nWRatio;
|
|
int m_nHRatio;
|
|
int m_nDRatio;
|
|
};
|
|
|
|
typedef void (*ApplyKernelFunc_t)( FloatBitmapResampleInfo_t *pInfo, int nStart, int nCount );
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Apply Kernel to an image
|
|
//-----------------------------------------------------------------------------
|
|
template< int type, bool bIsPowerOfTwo >
|
|
class CKernelWrapper
|
|
{
|
|
public:
|
|
static inline int ActualX( int x, FloatBitmapResampleInfo_t *pInfo )
|
|
{
|
|
if ( pInfo->m_nFlags & DOWNSAMPLE_CLAMPS )
|
|
return clamp( x, 0, pInfo->m_nSrcWidth - 1 );
|
|
|
|
// This works since pInfo->m_nSrcWidth is a power of two.
|
|
// Even for negative #s!
|
|
if ( bIsPowerOfTwo )
|
|
return x & (pInfo->m_nSrcWidth - 1);
|
|
return x % pInfo->m_nSrcWidth;
|
|
}
|
|
|
|
static inline int ActualY( int y, FloatBitmapResampleInfo_t *pInfo )
|
|
{
|
|
if ( pInfo->m_nFlags & DOWNSAMPLE_CLAMPT )
|
|
return clamp( y, 0, pInfo->m_nSrcHeight - 1 );
|
|
|
|
// This works since pInfo->m_nSrcHeight is a power of two.
|
|
// Even for negative #s!
|
|
if ( bIsPowerOfTwo )
|
|
return y & (pInfo->m_nSrcHeight - 1);
|
|
return y % pInfo->m_nSrcHeight;
|
|
}
|
|
|
|
static inline int ActualZ( int z, FloatBitmapResampleInfo_t *pInfo )
|
|
{
|
|
if ( pInfo->m_nFlags & DOWNSAMPLE_CLAMPU )
|
|
return clamp( z, 0, pInfo->m_nSrcDepth - 1 );
|
|
|
|
// This works since pInfo->m_nSrcDepth is a power of two.
|
|
// Even for negative #s!
|
|
if ( bIsPowerOfTwo )
|
|
return z & (pInfo->m_nSrcDepth - 1);
|
|
return z % pInfo->m_nSrcDepth;
|
|
}
|
|
|
|
static void ComputeWeightedAverageColor( FloatBitmapResampleInfo_t *pInfo,
|
|
int startX, int startY, int startZ, float *total )
|
|
{
|
|
total[0] = total[1] = total[2] = total[3] = 0.0f;
|
|
for ( int j = 0, srcZ = startZ; j < pInfo->m_pKernel->m_nDepth; ++j, ++srcZ )
|
|
{
|
|
int sz = ActualZ( srcZ, pInfo );
|
|
|
|
for ( int k = 0, srcY = startY; k < pInfo->m_pKernel->m_nHeight; ++k, ++srcY )
|
|
{
|
|
int sy = ActualY( srcY, pInfo );
|
|
|
|
int kernelIdx = pInfo->m_pKernel->m_nWidth * ( k + j * pInfo->m_pKernel->m_nHeight );
|
|
for ( int l = 0, srcX = startX; l < pInfo->m_pKernel->m_nWidth; ++l, ++srcX, ++kernelIdx )
|
|
{
|
|
int sx = ActualX( srcX, pInfo );
|
|
|
|
float flKernelFactor = pInfo->m_pKernel->m_pKernel[kernelIdx];
|
|
if ( flKernelFactor == 0.0f )
|
|
continue;
|
|
|
|
total[FBM_ATTR_RED] += flKernelFactor * pInfo->m_pSrcBitmap->Pixel( sx, sy, sz, FBM_ATTR_RED );
|
|
total[FBM_ATTR_GREEN] += flKernelFactor * pInfo->m_pSrcBitmap->Pixel( sx, sy, sz, FBM_ATTR_GREEN );
|
|
total[FBM_ATTR_BLUE] += flKernelFactor * pInfo->m_pSrcBitmap->Pixel( sx, sy, sz, FBM_ATTR_BLUE );
|
|
if ( type != KERNEL_ALPHATEST )
|
|
{
|
|
total[FBM_ATTR_ALPHA] += flKernelFactor * pInfo->m_pSrcBitmap->Pixel( sx, sy, sz, FBM_ATTR_ALPHA );
|
|
}
|
|
else
|
|
{
|
|
if ( pInfo->m_pSrcBitmap->Pixel( sx, sy, sz, FBM_ATTR_ALPHA ) > ( 192.0f / 255.0f ) )
|
|
{
|
|
total[FBM_ATTR_ALPHA] += flKernelFactor;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void AddAlphaToAlphaResult( FloatBitmapResampleInfo_t *pInfo,
|
|
int startX, int startY, int startZ, float flAlpha, float *pAlphaResult )
|
|
{
|
|
for ( int j = 0, srcZ = startZ; j < pInfo->m_pKernel->m_nDepth; ++j, ++srcZ )
|
|
{
|
|
int sz = ActualZ( srcZ, pInfo );
|
|
sz *= pInfo->m_nSrcWidth * pInfo->m_nSrcHeight;
|
|
|
|
for ( int k = 0, srcY = startY; k < pInfo->m_pKernel->m_nHeight; ++k, ++srcY )
|
|
{
|
|
int sy = ActualY( srcY, pInfo );
|
|
sy *= pInfo->m_nSrcWidth;
|
|
|
|
int kernelIdx = k * pInfo->m_pKernel->m_nWidth + j * pInfo->m_pKernel->m_nWidth * pInfo->m_pKernel->m_nHeight;
|
|
for ( int l = 0, srcX = startX; l < pInfo->m_pKernel->m_nWidth; ++l, ++srcX, ++kernelIdx )
|
|
{
|
|
int sx = ActualX( srcX, pInfo );
|
|
int srcPixel = sz + sy + sx;
|
|
|
|
float flKernelFactor = pInfo->m_pKernel->m_pInvKernel[kernelIdx];
|
|
if ( flKernelFactor == 0.0f )
|
|
continue;
|
|
pAlphaResult[srcPixel] += flKernelFactor * flAlpha;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void AdjustAlphaChannel( FloatBitmapResampleInfo_t *pInfo, float *pAlphaResult )
|
|
{
|
|
// Find the delta between the alpha + source image
|
|
int i, k;
|
|
int nDstPixel = 0;
|
|
for ( k = 0; k < pInfo->m_nSrcDepth; ++k )
|
|
{
|
|
for ( i = 0; i < pInfo->m_nSrcHeight; ++i )
|
|
{
|
|
for ( int j = 0; j < pInfo->m_nSrcWidth; ++j, ++nDstPixel )
|
|
{
|
|
pAlphaResult[nDstPixel] = fabs( pAlphaResult[nDstPixel] - pInfo->m_pSrcBitmap->Pixel( j, i, k, FBM_ATTR_ALPHA ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
// Apply the kernel to the image
|
|
int nInitialZ = ( pInfo->m_nDRatio > 1 ) ? (pInfo->m_nDRatio >> 1) - ((pInfo->m_nDRatio * pInfo->m_pKernel->m_nDiameter) >> 1) : 0;
|
|
int nInitialY = ( pInfo->m_nHRatio > 1 ) ? (pInfo->m_nHRatio >> 1) - ((pInfo->m_nHRatio * pInfo->m_pKernel->m_nDiameter) >> 1) : 0;
|
|
int nInitialX = ( pInfo->m_nWRatio > 1 ) ? (pInfo->m_nWRatio >> 1) - ((pInfo->m_nWRatio * pInfo->m_pKernel->m_nDiameter) >> 1) : 0;
|
|
|
|
float flInvFactor = 1.0f;
|
|
if ( pInfo->m_nDRatio != 0 )
|
|
flInvFactor *= pInfo->m_nDRatio;
|
|
if ( pInfo->m_nHRatio != 0 )
|
|
flInvFactor *= pInfo->m_nHRatio;
|
|
if ( pInfo->m_nWRatio != 0 )
|
|
flInvFactor *= pInfo->m_nWRatio;
|
|
flInvFactor = 1.0f / flInvFactor;
|
|
|
|
for ( int h = 0; h < pInfo->m_pDestBitmap->NumSlices(); ++h )
|
|
{
|
|
int startZ = pInfo->m_nDRatio * h + nInitialZ;
|
|
for ( i = 0; i < pInfo->m_pDestBitmap->NumRows(); ++i )
|
|
{
|
|
int startY = pInfo->m_nHRatio * i + nInitialY;
|
|
for ( int j = 0; j < pInfo->m_pDestBitmap->NumCols(); ++j )
|
|
{
|
|
if ( pInfo->m_pDestBitmap->Pixel( j, i, h, FBM_ATTR_ALPHA ) == 1.0f )
|
|
continue;
|
|
|
|
int startX = pInfo->m_nWRatio * j + nInitialX;
|
|
float flAlphaDelta = 0.0f;
|
|
|
|
for ( int m = 0, srcZ = startZ; m < pInfo->m_nDRatio; ++m, ++srcZ )
|
|
{
|
|
int sz = ActualZ( srcZ, pInfo );
|
|
sz *= pInfo->m_nSrcWidth * pInfo->m_nSrcHeight;
|
|
|
|
for ( int k = 0, srcY = startY; k < pInfo->m_nHRatio; ++k, ++srcY )
|
|
{
|
|
int sy = ActualY( srcY, pInfo );
|
|
sy *= pInfo->m_nSrcWidth;
|
|
|
|
for ( int l = 0, srcX = startX; l < pInfo->m_nWRatio; ++l, ++srcX )
|
|
{
|
|
// HACK: This temp variable fixes an internal compiler error in vs2005
|
|
int temp = srcX;
|
|
int sx = ActualX( temp, pInfo );
|
|
|
|
int srcPixel = sz + sy + sx;
|
|
flAlphaDelta += pAlphaResult[srcPixel];
|
|
}
|
|
}
|
|
}
|
|
|
|
flAlphaDelta *= flInvFactor;
|
|
if ( flAlphaDelta > pInfo->m_flAlphaHiFreqThreshhold )
|
|
{
|
|
pInfo->m_pDestBitmap->Pixel( j, i, h, FBM_ATTR_ALPHA ) = 1.0f;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ApplyKernel( FloatBitmapResampleInfo_t *pInfo, int nStart, int nCount )
|
|
{
|
|
// Apply the kernel to the image
|
|
int nInitialZ = ( pInfo->m_nDRatio > 1 ) ? (pInfo->m_nDRatio >> 1) - ((pInfo->m_nDRatio * pInfo->m_pKernel->m_nDiameter) >> 1) : 0;
|
|
int nInitialY = ( pInfo->m_nHRatio > 1 ) ? (pInfo->m_nHRatio >> 1) - ((pInfo->m_nHRatio * pInfo->m_pKernel->m_nDiameter) >> 1) : 0;
|
|
int nInitialX = ( pInfo->m_nWRatio > 1 ) ? (pInfo->m_nWRatio >> 1) - ((pInfo->m_nWRatio * pInfo->m_pKernel->m_nDiameter) >> 1) : 0;
|
|
|
|
int dw = pInfo->m_pDestBitmap->NumCols();
|
|
int dh = pInfo->m_pDestBitmap->NumRows();
|
|
int dd = pInfo->m_pDestBitmap->NumSlices();
|
|
|
|
int sk, ek, si, ei;
|
|
if ( dd == 1 )
|
|
{
|
|
sk = 0; ek = dd; si = nStart; ei = nStart + nCount;
|
|
}
|
|
else
|
|
{
|
|
sk = nStart; ek = nStart + nCount; si = 0; ei = dh;
|
|
}
|
|
|
|
for ( int k = sk; k < ek; ++k )
|
|
{
|
|
int startZ = pInfo->m_nDRatio * k + nInitialZ;
|
|
for ( int i = si; i < ei; ++i )
|
|
{
|
|
int startY = pInfo->m_nHRatio * i + nInitialY;
|
|
for ( int j = 0; j < dw; ++j )
|
|
{
|
|
int startX = pInfo->m_nWRatio * j + nInitialX;
|
|
|
|
float total[4];
|
|
ComputeWeightedAverageColor( pInfo, startX, startY, startZ, total );
|
|
|
|
// NOTE: Can't use a table here, we lose too many bits
|
|
if( type != KERNEL_ALPHATEST )
|
|
{
|
|
for ( int ch = 0; ch < 4; ++ ch )
|
|
{
|
|
pInfo->m_pDestBitmap->Pixel( j, i, k, ch ) = clamp( total[ch], 0.0f, 1.0f );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// If there's more than 40% coverage, then keep the pixel (renormalize the color based on coverage)
|
|
float flAlpha = ( total[3] >= pInfo->m_flAlphaThreshhold ) ? 1.0f : 0.0f;
|
|
|
|
for ( int ch = 0; ch < 3; ++ ch )
|
|
{
|
|
pInfo->m_pDestBitmap->Pixel( j, i, k, ch ) = clamp( total[ch], 0.0f, 1.0f );
|
|
}
|
|
pInfo->m_pDestBitmap->Pixel( j, i, k, FBM_ATTR_ALPHA ) = clamp( flAlpha, 0.0f, 1.0f );
|
|
|
|
AddAlphaToAlphaResult( pInfo, startX, startY, startZ, flAlpha, pInfo->m_pAlphaResult );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( type == KERNEL_ALPHATEST )
|
|
{
|
|
AdjustAlphaChannel( pInfo, pInfo->m_pAlphaResult );
|
|
}
|
|
}
|
|
};
|
|
|
|
typedef CKernelWrapper< KERNEL_DEFAULT, false > ApplyKernelDefault_t;
|
|
typedef CKernelWrapper< KERNEL_ALPHATEST, false > ApplyKernelAlphatest_t;
|
|
typedef CKernelWrapper< KERNEL_DEFAULT, true > ApplyKernelDefaultPow2_t;
|
|
typedef CKernelWrapper< KERNEL_ALPHATEST, true > ApplyKernelAlphatestPow2_t;
|
|
|
|
static ApplyKernelFunc_t g_KernelFunc[] =
|
|
{
|
|
ApplyKernelDefault_t::ApplyKernel,
|
|
ApplyKernelAlphatest_t::ApplyKernel,
|
|
};
|
|
|
|
static ApplyKernelFunc_t g_KernelFuncPow2[] =
|
|
{
|
|
ApplyKernelDefaultPow2_t::ApplyKernel,
|
|
ApplyKernelAlphatestPow2_t::ApplyKernel,
|
|
};
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Downsample using specified kernel (NOTE: Dest bitmap needs to have been initialized w/ final size)
|
|
//-----------------------------------------------------------------------------
|
|
void FloatBitMap_t::DownsampleNiceFiltered( const DownsampleInfo_t& downsampleInfo, FloatBitMap_t *pDestBitmap )
|
|
{
|
|
FloatBitmapResampleInfo_t info;
|
|
|
|
info.m_nFlags = downsampleInfo.m_nFlags;
|
|
info.m_pSrcBitmap = this;
|
|
info.m_pDestBitmap = pDestBitmap;
|
|
info.m_nWRatio = NumCols() / pDestBitmap->NumCols();
|
|
info.m_nHRatio = NumRows() / pDestBitmap->NumRows();
|
|
info.m_nDRatio = NumSlices() / pDestBitmap->NumSlices();
|
|
info.m_nSrcWidth = NumCols();
|
|
info.m_nSrcHeight = NumRows();
|
|
info.m_nSrcDepth = NumSlices();
|
|
info.m_flAlphaThreshhold = ( downsampleInfo.m_flAlphaThreshhold >= 0.0f ) ? downsampleInfo.m_flAlphaThreshhold : 0.4f;
|
|
info.m_flAlphaHiFreqThreshhold = ( downsampleInfo.m_flAlphaHiFreqThreshhold >= 0.0f ) ? downsampleInfo.m_flAlphaHiFreqThreshhold : 0.4f;
|
|
info.m_pAlphaResult = NULL;
|
|
|
|
KernelInfo_t kernel;
|
|
ImageLoader::ComputeNiceFilterKernel( info.m_nWRatio, info.m_nHRatio, info.m_nDRatio, &kernel );
|
|
info.m_pKernel = &kernel;
|
|
|
|
bool bIsPowerOfTwo = IsPowerOfTwo( info.m_nSrcWidth ) && IsPowerOfTwo( info.m_nSrcHeight ) && IsPowerOfTwo( info.m_nSrcDepth );
|
|
|
|
KernelType_t type;
|
|
if ( downsampleInfo.m_nFlags & DOWNSAMPLE_ALPHATEST )
|
|
{
|
|
int nSize = info.m_nSrcHeight * info.m_nSrcWidth * info.m_nSrcDepth * sizeof(float);
|
|
info.m_pAlphaResult = (float*)malloc( nSize );
|
|
memset( info.m_pAlphaResult, 0, nSize );
|
|
type = KERNEL_ALPHATEST;
|
|
}
|
|
else
|
|
{
|
|
type = KERNEL_DEFAULT;
|
|
}
|
|
|
|
int nCount, nChunkSize;
|
|
if ( info.m_nDRatio == 1 )
|
|
{
|
|
nCount = pDestBitmap->NumRows();
|
|
nChunkSize = 16;
|
|
}
|
|
else
|
|
{
|
|
nCount = pDestBitmap->NumSlices();
|
|
nChunkSize = 8;
|
|
}
|
|
|
|
if ( bIsPowerOfTwo )
|
|
{
|
|
ParallelLoopProcessChunks( sm_pFBMThreadPool, &info, 0, nCount, nChunkSize, g_KernelFuncPow2[type] );
|
|
}
|
|
else
|
|
{
|
|
ParallelLoopProcessChunks( sm_pFBMThreadPool, &info, 0, nCount, nChunkSize, g_KernelFunc[type] );
|
|
}
|
|
|
|
if ( info.m_pAlphaResult )
|
|
{
|
|
free( info.m_pAlphaResult );
|
|
}
|
|
ImageLoader::CleanupNiceFilterKernel( &kernel );
|
|
}
|
|
|
|
|
|
Vector FloatBitMap_t::AverageColor( void )
|
|
{
|
|
Vector ret( 0, 0, 0 );
|
|
for( int y = 0; y < NumRows(); y++ )
|
|
for( int x = 0; x < NumCols(); x++ )
|
|
for( int c = 0; c < 3; c++ )
|
|
ret[c]+= Pixel( x, y, 0, c );
|
|
ret *= 1.0 / ( NumCols() * NumRows() );
|
|
return ret;
|
|
}
|
|
|
|
float FloatBitMap_t::BrightestColor( void )
|
|
{
|
|
float ret = 0.0;
|
|
for( int y = 0; y < NumRows(); y++ )
|
|
for( int x = 0; x < NumCols(); x++ )
|
|
{
|
|
Vector v( Pixel( x, y, 0, 0 ), Pixel( x, y, 0, 1 ), Pixel( x, y, 0, 2 ) );
|
|
ret = MAX( ret, v.Length() );
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
template < class T > static inline void SWAP( T & a, T & b )
|
|
{
|
|
T temp = a;
|
|
a = b;
|
|
b = temp;
|
|
}
|
|
|
|
void FloatBitMap_t::RaiseToPower( float power )
|
|
{
|
|
for( int y = 0; y < NumRows(); y++ )
|
|
for( int x = 0; x < NumCols(); x++ )
|
|
for( int c = 0; c < 3; c++ )
|
|
Pixel( x, y, 0, c ) = pow( ( float )MAX( 0.0, Pixel( x, y, 0, c ) ), ( float )power );
|
|
|
|
}
|
|
|
|
void FloatBitMap_t::Logize( void )
|
|
{
|
|
for( int y = 0; y < NumRows(); y++ )
|
|
for( int x = 0; x < NumCols(); x++ )
|
|
for( int c = 0; c < 3; c++ )
|
|
Pixel( x, y, 0, c ) = log( 1.0 + Pixel( x, y, 0, c ) );
|
|
|
|
}
|
|
|
|
void FloatBitMap_t::UnLogize( void )
|
|
{
|
|
for( int y = 0; y < NumRows(); y++ )
|
|
for( int x = 0; x < NumCols(); x++ )
|
|
for( int c = 0; c < 3; c++ )
|
|
Pixel( x, y, 0, c ) = exp( Pixel( x, y, 0, c ) ) - 1;
|
|
}
|
|
|
|
|
|
void FloatBitMap_t::Clear( float r, float g, float b, float a )
|
|
{
|
|
for ( int z = 0; z < NumSlices(); ++z )
|
|
{
|
|
for( int y = 0; y < NumRows(); y++ )
|
|
{
|
|
for( int x = 0; x < NumCols(); x++ )
|
|
{
|
|
Pixel( x, y, z, 0 ) = r;
|
|
Pixel( x, y, z, 1 ) = g;
|
|
Pixel( x, y, z, 2 ) = b;
|
|
Pixel( x, y, z, 3 ) = a;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FloatBitMap_t::ScaleRGB( float scale_factor )
|
|
{
|
|
for( int y = 0; y < NumRows(); y++ )
|
|
for( int x = 0; x < NumCols(); x++ )
|
|
for( int c = 0; c < 3; c++ )
|
|
Pixel( x, y, 0, c ) *= scale_factor;
|
|
}
|
|
|
|
static int dx[4]={0, - 1, 1, 0};
|
|
static int dy[4]={- 1, 0, 0, 1};
|
|
|
|
#define NDELTAS 4
|
|
|
|
void FloatBitMap_t::SmartPaste( const FloatBitMap_t & b, int xofs, int yofs, uint32 Flags )
|
|
{
|
|
// now, need to make Difference map
|
|
FloatBitMap_t DiffMap0( this );
|
|
FloatBitMap_t DiffMap1( this );
|
|
FloatBitMap_t DiffMap2( this );
|
|
FloatBitMap_t DiffMap3( this );
|
|
FloatBitMap_t * deltas[4]={& DiffMap0, & DiffMap1, & DiffMap2, & DiffMap3};
|
|
for( int x = 0; x < NumCols(); x++ )
|
|
for( int y = 0; y < NumRows(); y++ )
|
|
for( int c = 0; c < 3; c++ )
|
|
{
|
|
for( int i = 0; i < NDELTAS; i++ )
|
|
{
|
|
int x1 = x + dx[i];
|
|
int y1 = y + dy[i];
|
|
x1 = MAX( 0, x1 );
|
|
x1 = MIN( NumCols() - 1, x1 );
|
|
y1 = MAX( 0, y1 );
|
|
y1 = MIN( NumRows() - 1, y1 );
|
|
float dx1 = Pixel( x, y, 0, c ) - Pixel( x1, y1, 0, c );
|
|
deltas[i]-> Pixel( x, y, 0, c ) = dx1;
|
|
}
|
|
}
|
|
for( int x = 1; x < b.NumCols() - 1; x++ )
|
|
for( int y = 1; y < b.NumRows() - 1; y++ )
|
|
for( int c = 0; c < 3; c++ )
|
|
{
|
|
for( int i = 0; i < NDELTAS; i++ )
|
|
{
|
|
float diff = b.Pixel( x, y, 0, c ) - b.Pixel( x + dx[i], y + dy[i], 0, c );
|
|
deltas[i]-> Pixel( x + xofs, y + yofs, 0, c ) = diff;
|
|
if ( Flags & SPFLAGS_MAXGRADIENT )
|
|
{
|
|
float dx1 = Pixel( x + xofs, y + yofs, 0, c ) - Pixel( x + dx[i]+ xofs, y + dy[i]+ yofs, 0, c );
|
|
if ( fabs( dx1 ) > fabs( diff ) )
|
|
deltas[i]-> Pixel( x + xofs, y + yofs, 0, c ) = dx1;
|
|
}
|
|
}
|
|
}
|
|
|
|
// now, calculate modifiability
|
|
for( int x = 0; x < NumCols(); x++ )
|
|
for( int y = 0; y < NumRows(); y++ )
|
|
{
|
|
float modify = 0;
|
|
if (
|
|
( x > xofs + 1 ) && ( x <= xofs + b.NumCols() - 2 ) &&
|
|
( y > yofs + 1 ) && ( y <= yofs + b.NumRows() - 2 ) )
|
|
modify = 1;
|
|
Alpha( x, y, 0 ) = modify;
|
|
}
|
|
|
|
// // now, force a fex pixels in center to be constant
|
|
// int midx=xofs+b.Width/2;
|
|
// int midy=yofs+b.Height/2;
|
|
// for(x=midx-10;x<midx+10;x++)
|
|
// for(int y=midy-10;y<midy+10;y++)
|
|
// {
|
|
// Alpha(x,y)=0;
|
|
// for(int c=0;c<3;c++)
|
|
// Pixel(x,y,c)=b.Pixel(x-xofs,y-yofs,c);
|
|
// }
|
|
Poisson( deltas, 6000, Flags );
|
|
}
|
|
|
|
void FloatBitMap_t::ScaleGradients( void )
|
|
{
|
|
// now, need to make Difference map
|
|
FloatBitMap_t DiffMap0( this );
|
|
FloatBitMap_t DiffMap1( this );
|
|
FloatBitMap_t DiffMap2( this );
|
|
FloatBitMap_t DiffMap3( this );
|
|
FloatBitMap_t * deltas[4]={& DiffMap0, & DiffMap1, & DiffMap2, & DiffMap3};
|
|
double gsum = 0.0;
|
|
for( int x = 0; x < NumCols(); x++ )
|
|
for( int y = 0; y < NumRows(); y++ )
|
|
for( int c = 0; c < 3; c++ )
|
|
{
|
|
for( int i = 0; i < NDELTAS; i++ )
|
|
{
|
|
int x1 = x + dx[i];
|
|
int y1 = y + dy[i];
|
|
x1 = MAX( 0, x1 );
|
|
x1 = MIN( NumCols() - 1, x1 );
|
|
y1 = MAX( 0, y1 );
|
|
y1 = MIN( NumRows() - 1, y1 );
|
|
float dx1 = Pixel( x, y, 0, c ) - Pixel( x1, y1, 0, c );
|
|
deltas[i]-> Pixel( x, y, 0, c ) = dx1;
|
|
gsum += fabs( dx1 );
|
|
}
|
|
}
|
|
// now, reduce gradient changes
|
|
// float gavg=gsum/(NumCols()*NumRows());
|
|
for( int x = 0; x < NumCols(); x++ )
|
|
for( int y = 0; y < NumRows(); y++ )
|
|
for( int c = 0; c < 3; c++ )
|
|
{
|
|
for( int i = 0; i < NDELTAS; i++ )
|
|
{
|
|
float norml = 1.1 * deltas[i]-> Pixel( x, y, 0, c );
|
|
// if (norml<0.0)
|
|
// norml=-pow(-norml,1.2);
|
|
// else
|
|
// norml=pow(norml,1.2);
|
|
deltas[i]-> Pixel( x, y, 0, c ) = norml;
|
|
}
|
|
}
|
|
|
|
// now, calculate modifiability
|
|
for( int x = 0; x < NumCols(); x++ )
|
|
for( int y = 0; y < NumRows(); y++ )
|
|
{
|
|
float modify = 0;
|
|
if (
|
|
( x > 0 ) && ( x < NumCols() - 1 ) &&
|
|
( y ) && ( y < NumRows() - 1 ) )
|
|
{
|
|
modify = 1;
|
|
Alpha( x, y, 0 ) = modify;
|
|
}
|
|
}
|
|
|
|
Poisson( deltas, 2200, 0 );
|
|
}
|
|
|
|
|
|
|
|
static inline float FLerp( float f1, float f2, float t )
|
|
{
|
|
return f1 + ( f2 - f1 ) * t;
|
|
}
|
|
|
|
void FloatBitMap_t::MakeTileable( void )
|
|
{
|
|
FloatBitMap_t rslta( this );
|
|
// now, need to make Difference map
|
|
FloatBitMap_t DiffMapX( this );
|
|
FloatBitMap_t DiffMapY( this );
|
|
// set each pixel=avg-pixel
|
|
FloatBitMap_t * cursrc =& rslta;
|
|
for( int x = 1; x < NumCols() - 1; x++ )
|
|
for( int y = 1; y < NumRows() - 1; y++ )
|
|
for( int c = 0; c < 3; c++ )
|
|
{
|
|
DiffMapX.Pixel( x, y, 0, c ) = Pixel( x, y, 0, c ) - Pixel( x + 1, y, 0, c );
|
|
DiffMapY.Pixel( x, y, 0, c ) = Pixel( x, y, 0, c ) - Pixel( x, y + 1, 0, c );
|
|
}
|
|
// initialize edge conditions
|
|
for( int x = 0; x < NumCols(); x++ )
|
|
{
|
|
for( int c = 0; c < 3; c++ )
|
|
{
|
|
float a = 0.5 * ( Pixel( x, NumRows() - 1, 0, c ) += Pixel( x, 0, 0, c ) );
|
|
rslta.Pixel( x, NumRows() - 1, 0, c ) = a;
|
|
rslta.Pixel( x, 0, 0, c ) = a;
|
|
}
|
|
}
|
|
for( int y = 0; y < NumRows(); y++ )
|
|
{
|
|
for( int c = 0; c < 3; c++ )
|
|
{
|
|
float a = 0.5 * ( Pixel( NumCols() - 1, y, 0, c ) + Pixel( 0, y, 0, c ) );
|
|
rslta.Pixel( NumCols() - 1, y, 0, c ) = a;
|
|
rslta.Pixel( 0, y, 0, c ) = a;
|
|
}
|
|
}
|
|
FloatBitMap_t rsltb( & rslta );
|
|
FloatBitMap_t * curdst =& rsltb;
|
|
|
|
// now, ready to iterate
|
|
for( int pass = 0; pass < 10; pass++ )
|
|
{
|
|
float error = 0.0;
|
|
for( int x = 1; x < NumCols() - 1; x++ )
|
|
for( int y = 1; y < NumRows() - 1; y++ )
|
|
for( int c = 0; c < 3; c++ )
|
|
{
|
|
float desiredx = DiffMapX.Pixel( x, y, 0, c ) + cursrc->Pixel( x + 1, y, 0, c );
|
|
float desiredy = DiffMapY.Pixel( x, y, 0, c ) + cursrc->Pixel( x, y + 1, 0, c );
|
|
float desired = 0.5 * ( desiredy + desiredx );
|
|
curdst->Pixel( x, y, 0, c ) = FLerp( cursrc->Pixel( x, y, 0, c ), desired, 0.5 );
|
|
error += SQ( desired - cursrc->Pixel( x, y, 0, c ) );
|
|
}
|
|
SWAP( cursrc, curdst );
|
|
}
|
|
// paste result
|
|
for( int x = 0; x < NumCols(); x++ )
|
|
for( int y = 0; y < NumRows(); y++ )
|
|
for( int c = 0; c < 3; c++ )
|
|
Pixel( x, y, 0, c ) = curdst->Pixel( x, y, 0, c );
|
|
}
|
|
|
|
|
|
void FloatBitMap_t::GetAlphaBounds( int & minx, int & miny, int & maxx, int & maxy )
|
|
{
|
|
for( minx = 0; minx < NumCols(); minx++ )
|
|
{
|
|
int y;
|
|
for( y = 0; y < NumRows(); y++ )
|
|
if ( Alpha( minx, y, 0 ) )
|
|
break;
|
|
if ( y != NumRows() )
|
|
break;
|
|
}
|
|
for( maxx = NumCols() - 1; maxx >= 0; maxx-- )
|
|
{
|
|
int y;
|
|
for( y = 0; y < NumRows(); y++ )
|
|
if ( Alpha( maxx, y, 0 ) )
|
|
break;
|
|
if ( y != NumRows() )
|
|
break;
|
|
}
|
|
for( miny = 0; minx < NumRows(); miny++ )
|
|
{
|
|
int x;
|
|
for( x = minx; x <= maxx; x++ )
|
|
if ( Alpha( x, miny, 0 ) )
|
|
break;
|
|
if ( x < maxx )
|
|
break;
|
|
}
|
|
for( maxy = NumRows() - 1; maxy >= 0; maxy-- )
|
|
{
|
|
int x;
|
|
for( x = minx; x <= maxx; x++ )
|
|
if ( Alpha( x, maxy, 0 ) )
|
|
break;
|
|
if ( x < maxx )
|
|
break;
|
|
}
|
|
}
|
|
|
|
void FloatBitMap_t::Poisson( FloatBitMap_t * deltas[4],
|
|
int n_iters,
|
|
uint32 flags // SPF_xxx
|
|
)
|
|
{
|
|
int minx, miny, maxx, maxy;
|
|
GetAlphaBounds( minx, miny, maxx, maxy );
|
|
minx = MAX( 1, minx );
|
|
miny = MAX( 1, miny );
|
|
maxx = MIN( NumCols() - 2, maxx );
|
|
maxy = MIN( NumRows() - 2, maxy );
|
|
if ( ( ( maxx - minx ) > 25 ) && ( maxy - miny ) > 25 )
|
|
{
|
|
// perform at low resolution
|
|
FloatBitMap_t * lowdeltas[NDELTAS];
|
|
for( int i = 0; i < NDELTAS; i++ )
|
|
{
|
|
lowdeltas[i] = new FloatBitMap_t;
|
|
deltas[i]->QuarterSize( lowdeltas[i] );
|
|
}
|
|
FloatBitMap_t * tmp = new FloatBitMap_t;
|
|
QuarterSize( tmp );
|
|
tmp->Poisson( lowdeltas, n_iters * 4, flags );
|
|
// now, propagate results from tmp to us
|
|
for( int x = 0; x < tmp->NumCols(); x++ )
|
|
for( int y = 0; y < tmp->NumRows(); y++ )
|
|
for( int xi = 0; xi < 2; xi++ )
|
|
for( int yi = 0; yi < 2; yi++ )
|
|
if ( Alpha( x * 2 + xi, y * 2 + yi, 0 ) )
|
|
{
|
|
for( int c = 0; c < 3; c++ )
|
|
Pixel( x * 2 + xi, y * 2 + yi, 0, c ) =
|
|
FLerp( Pixel( x * 2 + xi, y * 2 + yi, 0, c ), tmp->Pixel( x, y, 0, c ), Alpha( x * 2 + xi, y * 2 + yi, 0 ) );
|
|
}
|
|
char fname[80];
|
|
sprintf(fname,"sub%dx%d.tga",tmp->NumCols(),tmp->NumRows());
|
|
tmp->WriteTGAFile( fname );
|
|
sprintf(fname,"submrg%dx%d.tga",tmp->NumCols(),tmp->NumRows());
|
|
WriteTGAFile( fname );
|
|
delete tmp;
|
|
for( int i = 0; i < NDELTAS; i++ )
|
|
delete lowdeltas[i];
|
|
}
|
|
FloatBitMap_t work1( this );
|
|
FloatBitMap_t work2( this );
|
|
FloatBitMap_t * curdst =& work1;
|
|
FloatBitMap_t * cursrc =& work2;
|
|
// now, ready to iterate
|
|
while( n_iters-- )
|
|
{
|
|
float error = 0.0;
|
|
for( int x = minx; x <= maxx; x++ )
|
|
{
|
|
for( int y = miny; y <= maxy; y++ )
|
|
{
|
|
if ( Alpha( x, y, 0 ) )
|
|
{
|
|
for( int c = 0; c < 3; c++ )
|
|
{
|
|
float desired = 0.0;
|
|
for( int i = 0; i < NDELTAS; i++ )
|
|
desired += deltas[i]-> Pixel( x, y, 0, c ) + cursrc->Pixel( x + dx[i], y + dy[i], 0, c );
|
|
desired *= ( 1.0 / NDELTAS );
|
|
// desired=FLerp(Pixel(x,y,c),desired,Alpha(x,y));
|
|
curdst->Pixel( x, y, 0, c ) = FLerp( cursrc->Pixel( x, y, 0, c ), desired, 0.5 );
|
|
error += SQ( desired - cursrc->Pixel( x, y, 0, c ) );
|
|
}
|
|
}
|
|
SWAP( cursrc, curdst );
|
|
}
|
|
}
|
|
}
|
|
// paste result
|
|
for( int x = 0; x < NumCols(); x++ )
|
|
{
|
|
for( int y = 0; y < NumRows(); y++ )
|
|
{
|
|
for( int c = 0; c < 3; c++ )
|
|
{
|
|
Pixel( x, y, 0, c ) = curdst->Pixel( x, y, 0, c );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|