diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /bitmap/resample.cpp | |
| download | archived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.tar.xz archived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.zip | |
Diffstat (limited to 'bitmap/resample.cpp')
| -rw-r--r-- | bitmap/resample.cpp | 919 |
1 files changed, 919 insertions, 0 deletions
diff --git a/bitmap/resample.cpp b/bitmap/resample.cpp new file mode 100644 index 0000000..0ca61f0 --- /dev/null +++ b/bitmap/resample.cpp @@ -0,0 +1,919 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "nvtc.h" +#include "bitmap/imageformat.h" +#include "basetypes.h" +#include "tier0/dbg.h" +#include <malloc.h> +#include <memory.h> +#include "mathlib/mathlib.h" +#include "mathlib/vector.h" +#include "tier1/utlmemory.h" +#include "tier1/strtools.h" +#include "mathlib/compressed_vector.h" + +// Should be last include +#include "tier0/memdbgon.h" + + +namespace ImageLoader +{ + +//----------------------------------------------------------------------------- +// Gamma correction +//----------------------------------------------------------------------------- +static void ConstructFloatGammaTable( float* pTable, float srcGamma, float dstGamma ) +{ + for( int i = 0; i < 256; i++ ) + { + pTable[i] = 255.0 * pow( (float)i / 255.0f, srcGamma / dstGamma ); + } +} + +void ConstructGammaTable( unsigned char* pTable, float srcGamma, float dstGamma ) +{ + int v; + for( int i = 0; i < 256; i++ ) + { + double f; + f = 255.0 * pow( (float)i / 255.0f, srcGamma / dstGamma ); + v = ( int )(f + 0.5f); + if( v < 0 ) + { + v = 0; + } + else if( v > 255 ) + { + v = 255; + } + pTable[i] = ( unsigned char )v; + } +} + +void GammaCorrectRGBA8888( unsigned char *pSrc, unsigned char* pDst, int width, int height, int depth, + unsigned char* pGammaTable ) +{ + for (int h = 0; h < depth; ++h ) + { + for (int i = 0; i < height; ++i ) + { + for (int j = 0; j < width; ++j ) + { + int idx = (h * width * height + i * width + j) * 4; + + // don't gamma correct alpha + pDst[idx] = pGammaTable[pSrc[idx]]; + pDst[idx+1] = pGammaTable[pSrc[idx+1]]; + pDst[idx+2] = pGammaTable[pSrc[idx+2]]; + } + } + } +} + +void GammaCorrectRGBA8888( unsigned char *src, unsigned char* dst, int width, int height, int depth, + float srcGamma, float dstGamma ) +{ + if (srcGamma == dstGamma) + { + if (src != dst) + { + memcpy( dst, src, GetMemRequired( width, height, depth, IMAGE_FORMAT_RGBA8888, false ) ); + } + return; + } + + static unsigned char gamma[256]; + static float lastSrcGamma = -1; + static float lastDstGamma = -1; + + if (lastSrcGamma != srcGamma || lastDstGamma != dstGamma) + { + ConstructGammaTable( gamma, srcGamma, dstGamma ); + lastSrcGamma = srcGamma; + lastDstGamma = dstGamma; + } + + GammaCorrectRGBA8888( src, dst, width, height, depth, gamma ); +} + + +//----------------------------------------------------------------------------- +// Generate a NICE filter kernel +//----------------------------------------------------------------------------- +static void GenerateNiceFilter( float wratio, float hratio, float dratio, int kernelDiameter, float* pKernel, float *pInvKernel ) +{ + // Compute a kernel... + int h, i, j; + int kernelWidth = kernelDiameter * wratio; + int kernelHeight = kernelDiameter * hratio; + int kernelDepth = ( dratio != 0 ) ? kernelDiameter * dratio : 1; + + // This is a NICE filter + // sinc pi*x * a box from -3 to 3 * sinc ( pi * x/3) + // where x is the pixel # in the destination (shrunken) image. + // only problem here is that the NICE filter has a very large kernel + // (7x7 x wratio x hratio x dratio) + float dx = 1.0f / (float)wratio; + float dy = 1.0f / (float)hratio; + float z, dz; + + if (dratio != 0.0f) + { + dz = 1.0f / (float)dratio; + z = -((float)kernelDiameter - dz) * 0.5f; + } + else + { + dz = 0.0f; + z = 0.0f; + } + + float total = 0.0f; + for ( h = 0; h < kernelDepth; ++h ) + { + float y = -((float)kernelDiameter - dy) * 0.5f; + for ( i = 0; i < kernelHeight; ++i ) + { + float x = -((float)kernelDiameter - dx) * 0.5f; + for ( j = 0; j < kernelWidth; ++j ) + { + int nKernelIndex = kernelWidth * ( i + h * kernelHeight ) + j; + + float d = sqrt( x * x + y * y + z * z ); + if (d > kernelDiameter * 0.5f) + { + pKernel[nKernelIndex] = 0.0f; + } + else + { + float t = M_PI * d; + if ( t != 0 ) + { + float sinc = sin( t ) / t; + float sinc3 = 3.0f * sin( t / 3.0f ) / t; + pKernel[nKernelIndex] = sinc * sinc3; + } + else + { + pKernel[nKernelIndex] = 1.0f; + } + total += pKernel[nKernelIndex]; + } + x += dx; + } + y += dy; + } + z += dz; + } + + // normalize + float flInvFactor = ( dratio == 0 ) ? wratio * hratio : dratio * wratio * hratio; + float flInvTotal = (total != 0.0f) ? 1.0f / total : 1.0f; + + for ( h = 0; h < kernelDepth; ++h ) + { + for ( i = 0; i < kernelHeight; ++i ) + { + int nPixel = kernelWidth * ( h * kernelHeight + i ); + for ( j = 0; j < kernelWidth; ++j ) + { + pKernel[nPixel + j] *= flInvTotal; + pInvKernel[nPixel + j] = flInvFactor * pKernel[nPixel + j]; + } + } + } +} + +//----------------------------------------------------------------------------- +// Resample an image +//----------------------------------------------------------------------------- +static inline unsigned char Clamp( float x ) +{ + int idx = (int)(x + 0.5f); + if (idx < 0) idx = 0; + else if (idx > 255) idx = 255; + return idx; +} + +inline bool IsPowerOfTwo( int x ) +{ + return (x & ( x - 1 )) == 0; +} + + +struct KernelInfo_t +{ + float *m_pKernel; + float *m_pInvKernel; + int m_nWidth; + int m_nHeight; + int m_nDepth; + int m_nDiameter; +}; + +enum KernelType_t +{ + KERNEL_DEFAULT = 0, + KERNEL_NORMALMAP, + KERNEL_ALPHATEST, +}; + +typedef void (*ApplyKernelFunc_t)( const KernelInfo_t &kernel, const ResampleInfo_t &info, int wratio, int hratio, int dratio, float* gammaToLinear, float *pAlphaResult ); + +//----------------------------------------------------------------------------- +// Apply Kernel to an image +//----------------------------------------------------------------------------- +template< int type, bool bNiceFilter > +class CKernelWrapper +{ +public: + static inline int ActualX( int x, const ResampleInfo_t &info ) + { + if ( info.m_nFlags & RESAMPLE_CLAMPS ) + return clamp( x, 0, info.m_nSrcWidth - 1 ); + + // This works since info.m_nSrcWidth is a power of two. + // Even for negative #s! + return x & (info.m_nSrcWidth - 1); + } + + static inline int ActualY( int y, const ResampleInfo_t &info ) + { + if ( info.m_nFlags & RESAMPLE_CLAMPT ) + return clamp( y, 0, info.m_nSrcHeight - 1 ); + + // This works since info.m_nSrcHeight is a power of two. + // Even for negative #s! + return y & (info.m_nSrcHeight - 1); + } + + static inline int ActualZ( int z, const ResampleInfo_t &info ) + { + if ( info.m_nFlags & RESAMPLE_CLAMPU ) + return clamp( z, 0, info.m_nSrcDepth - 1 ); + + // This works since info.m_nSrcDepth is a power of two. + // Even for negative #s! + return z & (info.m_nSrcDepth - 1); + } + + static void ComputeAveragedColor( const KernelInfo_t &kernel, const ResampleInfo_t &info, + int startX, int startY, int startZ, float *gammaToLinear, float *total ) + { + total[0] = total[1] = total[2] = total[3] = 0.0f; + for ( int j = 0, srcZ = startZ; j < kernel.m_nDepth; ++j, ++srcZ ) + { + int sz = ActualZ( srcZ, info ); + sz *= info.m_nSrcWidth * info.m_nSrcHeight; + + for ( int k = 0, srcY = startY; k < kernel.m_nHeight; ++k, ++srcY ) + { + int sy = ActualY( srcY, info ); + sy *= info.m_nSrcWidth; + + int kernelIdx; + if ( bNiceFilter ) + { + kernelIdx = kernel.m_nWidth * ( k + j * kernel.m_nHeight ); + } + else + { + kernelIdx = 0; + } + + for ( int l = 0, srcX = startX; l < kernel.m_nWidth; ++l, ++srcX, ++kernelIdx ) + { + int sx = ActualX( srcX, info ); + int srcPixel = (sz + sy + sx) << 2; + + float flKernelFactor; + if ( bNiceFilter ) + { + flKernelFactor = kernel.m_pKernel[kernelIdx]; + if ( flKernelFactor == 0.0f ) + continue; + } + else + { + flKernelFactor = kernel.m_pKernel[0]; + } + + if ( type == KERNEL_NORMALMAP ) + { + total[0] += flKernelFactor * info.m_pSrc[srcPixel + 0]; + total[1] += flKernelFactor * info.m_pSrc[srcPixel + 1]; + total[2] += flKernelFactor * info.m_pSrc[srcPixel + 2]; + total[3] += flKernelFactor * info.m_pSrc[srcPixel + 3]; + } + else if ( type == KERNEL_ALPHATEST ) + { + total[0] += flKernelFactor * gammaToLinear[ info.m_pSrc[srcPixel + 0] ]; + total[1] += flKernelFactor * gammaToLinear[ info.m_pSrc[srcPixel + 1] ]; + total[2] += flKernelFactor * gammaToLinear[ info.m_pSrc[srcPixel + 2] ]; + if ( info.m_pSrc[srcPixel + 3] > 192 ) + { + total[3] += flKernelFactor * 255.0f; + } + } + else + { + total[0] += flKernelFactor * gammaToLinear[ info.m_pSrc[srcPixel + 0] ]; + total[1] += flKernelFactor * gammaToLinear[ info.m_pSrc[srcPixel + 1] ]; + total[2] += flKernelFactor * gammaToLinear[ info.m_pSrc[srcPixel + 2] ]; + total[3] += flKernelFactor * info.m_pSrc[srcPixel + 3]; + } + } + } + } + } + + static void AddAlphaToAlphaResult( const KernelInfo_t &kernel, const ResampleInfo_t &info, + int startX, int startY, int startZ, float flAlpha, float *pAlphaResult ) + { + for ( int j = 0, srcZ = startZ; j < kernel.m_nDepth; ++j, ++srcZ ) + { + int sz = ActualZ( srcZ, info ); + sz *= info.m_nSrcWidth * info.m_nSrcHeight; + + for ( int k = 0, srcY = startY; k < kernel.m_nHeight; ++k, ++srcY ) + { + int sy = ActualY( srcY, info ); + sy *= info.m_nSrcWidth; + + int kernelIdx; + if ( bNiceFilter ) + { + kernelIdx = k * kernel.m_nWidth + j * kernel.m_nWidth * kernel.m_nHeight; + } + else + { + kernelIdx = 0; + } + + for ( int l = 0, srcX = startX; l < kernel.m_nWidth; ++l, ++srcX, ++kernelIdx ) + { + int sx = ActualX( srcX, info ); + int srcPixel = sz + sy + sx; + + float flKernelFactor; + if ( bNiceFilter ) + { + flKernelFactor = kernel.m_pInvKernel[kernelIdx]; + if ( flKernelFactor == 0.0f ) + continue; + } + else + { + flKernelFactor = kernel.m_pInvKernel[0]; + } + + pAlphaResult[srcPixel] += flKernelFactor * flAlpha; + } + } + } + } + + static void AdjustAlphaChannel( const KernelInfo_t &kernel, const ResampleInfo_t &info, + int wratio, int hratio, int dratio, float *pAlphaResult ) + { + // Find the delta between the alpha + source image + for ( int k = 0; k < info.m_nSrcDepth; ++k ) + { + for ( int i = 0; i < info.m_nSrcHeight; ++i ) + { + int dstPixel = i * info.m_nSrcWidth + k * info.m_nSrcWidth * info.m_nSrcHeight; + for ( int j = 0; j < info.m_nSrcWidth; ++j, ++dstPixel ) + { + pAlphaResult[dstPixel] = fabs( pAlphaResult[dstPixel] - info.m_pSrc[dstPixel * 4 + 3] ); + } + } + } + + // Apply the kernel to the image + int nInitialZ = (dratio >> 1) - ((dratio * kernel.m_nDiameter) >> 1); + int nInitialY = (hratio >> 1) - ((hratio * kernel.m_nDiameter) >> 1); + int nInitialX = (wratio >> 1) - ((wratio * kernel.m_nDiameter) >> 1); + + float flAlphaThreshhold = (info.m_flAlphaHiFreqThreshhold >= 0 ) ? 255.0f * info.m_flAlphaHiFreqThreshhold : 255.0f * 0.4f; + + float flInvFactor = (dratio == 0) ? 1.0f / (hratio * wratio) : 1.0f / (hratio * wratio * dratio); + + for ( int h = 0; h < info.m_nDestDepth; ++h ) + { + int startZ = dratio * h + nInitialZ; + for ( int i = 0; i < info.m_nDestHeight; ++i ) + { + int startY = hratio * i + nInitialY; + int dstPixel = ( info.m_nDestWidth * (i + h * info.m_nDestHeight) ) << 2; + for ( int j = 0; j < info.m_nDestWidth; ++j, dstPixel += 4 ) + { + if ( info.m_pDest[ dstPixel + 3 ] == 255 ) + continue; + + int startX = wratio * j + nInitialX; + float flAlphaDelta = 0.0f; + + for ( int m = 0, srcZ = startZ; m < dratio; ++m, ++srcZ ) + { + int sz = ActualZ( srcZ, info ); + sz *= info.m_nSrcWidth * info.m_nSrcHeight; + + for ( int k = 0, srcY = startY; k < hratio; ++k, ++srcY ) + { + int sy = ActualY( srcY, info ); + sy *= info.m_nSrcWidth; + + for ( int l = 0, srcX = startX; l < wratio; ++l, ++srcX ) + { + // HACK: This temp variable fixes an internal compiler error in vs2005 + int temp = srcX; + int sx = ActualX( temp, info ); + + int srcPixel = sz + sy + sx; + flAlphaDelta += pAlphaResult[srcPixel]; + } + } + } + + flAlphaDelta *= flInvFactor; + if ( flAlphaDelta > flAlphaThreshhold ) + { + info.m_pDest[ dstPixel + 3 ] = 255.0f; + } + } + } + } + } + + static void ApplyKernel( const KernelInfo_t &kernel, const ResampleInfo_t &info, int wratio, int hratio, int dratio, float* gammaToLinear, float *pAlphaResult ) + { + float invDstGamma = 1.0f / info.m_flDestGamma; + + // Apply the kernel to the image + int nInitialZ = (dratio >> 1) - ((dratio * kernel.m_nDiameter) >> 1); + int nInitialY = (hratio >> 1) - ((hratio * kernel.m_nDiameter) >> 1); + int nInitialX = (wratio >> 1) - ((wratio * kernel.m_nDiameter) >> 1); + + float flAlphaThreshhold = (info.m_flAlphaThreshhold >= 0 ) ? 255.0f * info.m_flAlphaThreshhold : 255.0f * 0.4f; + for ( int k = 0; k < info.m_nDestDepth; ++k ) + { + int startZ = dratio * k + nInitialZ; + + for ( int i = 0; i < info.m_nDestHeight; ++i ) + { + int startY = hratio * i + nInitialY; + int dstPixel = (i * info.m_nDestWidth + k * info.m_nDestWidth * info.m_nDestHeight) << 2; + + for ( int j = 0; j < info.m_nDestWidth; ++j, dstPixel += 4 ) + { + int startX = wratio * j + nInitialX; + + float total[4]; + ComputeAveragedColor( kernel, info, startX, startY, startZ, gammaToLinear, total ); + + // NOTE: Can't use a table here, we lose too many bits + if( type == KERNEL_NORMALMAP ) + { + for ( int ch = 0; ch < 4; ++ ch ) + info.m_pDest[ dstPixel + ch ] = Clamp( info.m_flColorGoal[ch] + ( info.m_flColorScale[ch] * ( total[ch] - info.m_flColorGoal[ch] ) ) ); + } + else if ( type == KERNEL_ALPHATEST ) + { + // If there's more than 40% coverage, then keep the pixel (renormalize the color based on coverage) + float flAlpha = ( total[3] >= flAlphaThreshhold ) ? 255 : 0; + + for ( int ch = 0; ch < 3; ++ ch ) + info.m_pDest[ dstPixel + ch ] = Clamp( 255.0f * pow( ( info.m_flColorGoal[ch] + ( info.m_flColorScale[ch] * ( ( total[ch] > 0 ? total[ch] : 0 ) - info.m_flColorGoal[ch] ) ) ) / 255.0f, invDstGamma ) ); + info.m_pDest[ dstPixel + 3 ] = Clamp( flAlpha ); + + AddAlphaToAlphaResult( kernel, info, startX, startY, startZ, flAlpha, pAlphaResult ); + } + else + { + for ( int ch = 0; ch < 3; ++ ch ) + info.m_pDest[ dstPixel + ch ] = Clamp( 255.0f * pow( ( info.m_flColorGoal[ch] + ( info.m_flColorScale[ch] * ( ( total[ch] > 0 ? total[ch] : 0 ) - info.m_flColorGoal[ch] ) ) ) / 255.0f, invDstGamma ) ); + info.m_pDest[ dstPixel + 3 ] = Clamp( info.m_flColorGoal[3] + ( info.m_flColorScale[3] * ( total[3] - info.m_flColorGoal[3] ) ) ); + } + } + } + + if ( type == KERNEL_ALPHATEST ) + { + AdjustAlphaChannel( kernel, info, wratio, hratio, dratio, pAlphaResult ); + } + } + } +}; + +typedef CKernelWrapper< KERNEL_DEFAULT, false > ApplyKernelDefault_t; +typedef CKernelWrapper< KERNEL_NORMALMAP, false > ApplyKernelNormalmap_t; +typedef CKernelWrapper< KERNEL_ALPHATEST, false > ApplyKernelAlphatest_t; +typedef CKernelWrapper< KERNEL_DEFAULT, true > ApplyKernelDefaultNice_t; +typedef CKernelWrapper< KERNEL_NORMALMAP, true > ApplyKernelNormalmapNice_t; +typedef CKernelWrapper< KERNEL_ALPHATEST, true > ApplyKernelAlphatestNice_t; + +static ApplyKernelFunc_t g_KernelFunc[] = +{ + ApplyKernelDefault_t::ApplyKernel, + ApplyKernelNormalmap_t::ApplyKernel, + ApplyKernelAlphatest_t::ApplyKernel, +}; + +static ApplyKernelFunc_t g_KernelFuncNice[] = +{ + ApplyKernelDefaultNice_t::ApplyKernel, + ApplyKernelNormalmapNice_t::ApplyKernel, + ApplyKernelAlphatestNice_t::ApplyKernel, +}; + +bool ResampleRGBA8888( const ResampleInfo_t& info ) +{ + // No resampling needed, just gamma correction + if ( info.m_nSrcWidth == info.m_nDestWidth && info.m_nSrcHeight == info.m_nDestHeight && info.m_nSrcDepth == info.m_nDestDepth ) + { + // Here, we need to gamma convert the source image.. + GammaCorrectRGBA8888( info.m_pSrc, info.m_pDest, info.m_nSrcWidth, info.m_nSrcHeight, info.m_nSrcDepth, info.m_flSrcGamma, info.m_flDestGamma ); + return true; + } + + // fixme: has to be power of two for now. + if( !IsPowerOfTwo(info.m_nSrcWidth) || !IsPowerOfTwo(info.m_nSrcHeight) || !IsPowerOfTwo(info.m_nSrcDepth) || + !IsPowerOfTwo(info.m_nDestWidth) || !IsPowerOfTwo(info.m_nDestHeight) || !IsPowerOfTwo(info.m_nDestDepth) ) + { + return false; + } + + // fixme: can only downsample for now. + if( (info.m_nSrcWidth < info.m_nDestWidth) || (info.m_nSrcHeight < info.m_nDestHeight) || (info.m_nSrcDepth < info.m_nDestDepth) ) + { + return false; + } + + // Compute gamma tables... + static float gammaToLinear[256]; + static float lastSrcGamma = -1; + + if (lastSrcGamma != info.m_flSrcGamma) + { + ConstructFloatGammaTable( gammaToLinear, info.m_flSrcGamma, 1.0f ); + lastSrcGamma = info.m_flSrcGamma; + } + + int wratio = info.m_nSrcWidth / info.m_nDestWidth; + int hratio = info.m_nSrcHeight / info.m_nDestHeight; + int dratio = (info.m_nSrcDepth != info.m_nDestDepth) ? info.m_nSrcDepth / info.m_nDestDepth : 0; + + KernelInfo_t kernel; + + float* pTempMemory = 0; + float* pTempInvMemory = 0; + static float* kernelCache[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + static float* pInvKernelCache[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + float pKernelMem[1]; + float pInvKernelMem[1]; + if ( info.m_nFlags & RESAMPLE_NICE_FILTER ) + { + // Kernel size is measured in dst pixels + kernel.m_nDiameter = 6; + + // Compute a kernel... + kernel.m_nWidth = kernel.m_nDiameter * wratio; + kernel.m_nHeight = kernel.m_nDiameter * hratio; + kernel.m_nDepth = kernel.m_nDiameter * dratio; + if ( kernel.m_nDepth == 0 ) + { + kernel.m_nDepth = 1; + } + + // Cache the filter (2d kernels only).... + int power = -1; + + if ( (wratio == hratio) && (dratio == 0) ) + { + power = 0; + int tempWidth = wratio; + while (tempWidth > 1) + { + ++power; + tempWidth >>= 1; + } + + // Don't cache anything bigger than 512x512 + if (power >= 10) + { + power = -1; + } + } + + if (power >= 0) + { + if (!kernelCache[power]) + { + kernelCache[power] = new float[kernel.m_nWidth * kernel.m_nHeight]; + pInvKernelCache[power] = new float[kernel.m_nWidth * kernel.m_nHeight]; + GenerateNiceFilter( wratio, hratio, dratio, kernel.m_nDiameter, kernelCache[power], pInvKernelCache[power] ); + } + + kernel.m_pKernel = kernelCache[power]; + kernel.m_pInvKernel = pInvKernelCache[power]; + } + else + { + // Don't cache non-square kernels, or 3d kernels + pTempMemory = new float[kernel.m_nWidth * kernel.m_nHeight * kernel.m_nDepth]; + pTempInvMemory = new float[kernel.m_nWidth * kernel.m_nHeight * kernel.m_nDepth]; + GenerateNiceFilter( wratio, hratio, dratio, kernel.m_nDiameter, pTempMemory, pTempInvMemory ); + kernel.m_pKernel = pTempMemory; + kernel.m_pInvKernel = pTempInvMemory; + } + } + else + { + // Compute a kernel... + kernel.m_nWidth = wratio; + kernel.m_nHeight = hratio; + kernel.m_nDepth = dratio ? dratio : 1; + + kernel.m_nDiameter = 1; + + // Simple implementation of a box filter that doesn't block the stack! + pKernelMem[0] = 1.0f / (float)(kernel.m_nWidth * kernel.m_nHeight * kernel.m_nDepth); + pInvKernelMem[0] = 1.0f; + kernel.m_pKernel = pKernelMem; + kernel.m_pInvKernel = pInvKernelMem; + } + + float *pAlphaResult = NULL; + KernelType_t type; + if ( info.m_nFlags & RESAMPLE_NORMALMAP ) + { + type = KERNEL_NORMALMAP; + } + else if ( info.m_nFlags & RESAMPLE_ALPHATEST ) + { + int nSize = info.m_nSrcHeight * info.m_nSrcWidth * info.m_nSrcDepth * sizeof(float); + pAlphaResult = (float*)malloc( nSize ); + memset( pAlphaResult, 0, nSize ); + type = KERNEL_ALPHATEST; + } + else + { + type = KERNEL_DEFAULT; + } + + if ( info.m_nFlags & RESAMPLE_NICE_FILTER ) + { + g_KernelFuncNice[type]( kernel, info, wratio, hratio, dratio, gammaToLinear, pAlphaResult ); + if (pTempMemory) + { + delete[] pTempMemory; + } + } + else + { + g_KernelFunc[type]( kernel, info, wratio, hratio, dratio, gammaToLinear, pAlphaResult ); + } + + if ( pAlphaResult ) + { + free( pAlphaResult ); + } + + return true; +} + +bool ResampleRGBA16161616( const ResampleInfo_t& info ) +{ + // HDRFIXME: This is some lame shit right here. (We need to get NICE working, etc, etc.) + + // Make sure everything is power of two. + Assert( ( info.m_nSrcWidth & ( info.m_nSrcWidth - 1 ) ) == 0 ); + Assert( ( info.m_nSrcHeight & ( info.m_nSrcHeight - 1 ) ) == 0 ); + Assert( ( info.m_nDestWidth & ( info.m_nDestWidth - 1 ) ) == 0 ); + Assert( ( info.m_nDestHeight & ( info.m_nDestHeight - 1 ) ) == 0 ); + + // Make sure that we aren't upscsaling the image. . .we do`n't support that very well. + Assert( info.m_nSrcWidth >= info.m_nDestWidth ); + Assert( info.m_nSrcHeight >= info.m_nDestHeight ); + + int nSampleWidth = info.m_nSrcWidth / info.m_nDestWidth; + int nSampleHeight = info.m_nSrcHeight / info.m_nDestHeight; + + unsigned short *pSrc = ( unsigned short * )info.m_pSrc; + unsigned short *pDst = ( unsigned short * )info.m_pDest; + int x, y; + for( y = 0; y < info.m_nDestHeight; y++ ) + { + for( x = 0; x < info.m_nDestWidth; x++ ) + { + int accum[4]; + accum[0] = accum[1] = accum[2] = accum[3] = 0; + int nSampleY; + for( nSampleY = 0; nSampleY < nSampleHeight; nSampleY++ ) + { + int nSampleX; + for( nSampleX = 0; nSampleX < nSampleWidth; nSampleX++ ) + { + accum[0] += ( int )pSrc[((x*nSampleWidth+nSampleX)+(y*nSampleHeight+nSampleY)*info.m_nSrcWidth)*4+0]; + accum[1] += ( int )pSrc[((x*nSampleWidth+nSampleX)+(y*nSampleHeight+nSampleY)*info.m_nSrcWidth)*4+1]; + accum[2] += ( int )pSrc[((x*nSampleWidth+nSampleX)+(y*nSampleHeight+nSampleY)*info.m_nSrcWidth)*4+2]; + accum[3] += ( int )pSrc[((x*nSampleWidth+nSampleX)+(y*nSampleHeight+nSampleY)*info.m_nSrcWidth)*4+3]; + } + } + int i; + for( i = 0; i < 4; i++ ) + { + accum[i] /= ( nSampleWidth * nSampleHeight ); + accum[i] = max( accum[i], 0 ); + accum[i] = min( accum[i], 65535 ); + pDst[(x+y*info.m_nDestWidth)*4+i] = ( unsigned short )accum[i]; + } + } + } + return true; +} + +bool ResampleRGB323232F( const ResampleInfo_t& info ) +{ + // HDRFIXME: This is some lame shit right here. (We need to get NICE working, etc, etc.) + + // Make sure everything is power of two. + Assert( ( info.m_nSrcWidth & ( info.m_nSrcWidth - 1 ) ) == 0 ); + Assert( ( info.m_nSrcHeight & ( info.m_nSrcHeight - 1 ) ) == 0 ); + Assert( ( info.m_nDestWidth & ( info.m_nDestWidth - 1 ) ) == 0 ); + Assert( ( info.m_nDestHeight & ( info.m_nDestHeight - 1 ) ) == 0 ); + + // Make sure that we aren't upscaling the image. . .we do`n't support that very well. + Assert( info.m_nSrcWidth >= info.m_nDestWidth ); + Assert( info.m_nSrcHeight >= info.m_nDestHeight ); + + int nSampleWidth = info.m_nSrcWidth / info.m_nDestWidth; + int nSampleHeight = info.m_nSrcHeight / info.m_nDestHeight; + + float *pSrc = ( float * )info.m_pSrc; + float *pDst = ( float * )info.m_pDest; + int x, y; + for( y = 0; y < info.m_nDestHeight; y++ ) + { + for( x = 0; x < info.m_nDestWidth; x++ ) + { + float accum[4]; + accum[0] = accum[1] = accum[2] = accum[3] = 0; + int nSampleY; + for( nSampleY = 0; nSampleY < nSampleHeight; nSampleY++ ) + { + int nSampleX; + for( nSampleX = 0; nSampleX < nSampleWidth; nSampleX++ ) + { + accum[0] += pSrc[((x*nSampleWidth+nSampleX)+(y*nSampleHeight+nSampleY)*info.m_nSrcWidth)*3+0]; + accum[1] += pSrc[((x*nSampleWidth+nSampleX)+(y*nSampleHeight+nSampleY)*info.m_nSrcWidth)*3+1]; + accum[2] += pSrc[((x*nSampleWidth+nSampleX)+(y*nSampleHeight+nSampleY)*info.m_nSrcWidth)*3+2]; + } + } + int i; + for( i = 0; i < 3; i++ ) + { + accum[i] /= ( nSampleWidth * nSampleHeight ); + pDst[(x+y*info.m_nDestWidth)*3+i] = accum[i]; + } + } + } + return true; +} + +//----------------------------------------------------------------------------- +// Generates mipmap levels +//----------------------------------------------------------------------------- +void GenerateMipmapLevels( unsigned char* pSrc, unsigned char* pDst, int width, + int height, int depth, ImageFormat imageFormat, float srcGamma, float dstGamma, int numLevels ) +{ + int dstWidth = width; + int dstHeight = height; + int dstDepth = depth; + + // temporary storage for the mipmaps + int tempMem = GetMemRequired( dstWidth, dstHeight, dstDepth, IMAGE_FORMAT_RGBA8888, false ); + CUtlMemory<unsigned char> tmpImage; + tmpImage.EnsureCapacity( tempMem ); + + while( true ) + { + // This generates a mipmap in RGBA8888, linear space + ResampleInfo_t info; + info.m_pSrc = pSrc; + info.m_pDest = tmpImage.Base(); + info.m_nSrcWidth = width; + info.m_nSrcHeight = height; + info.m_nSrcDepth = depth; + info.m_nDestWidth = dstWidth; + info.m_nDestHeight = dstHeight; + info.m_nDestDepth = dstDepth; + info.m_flSrcGamma = srcGamma; + info.m_flDestGamma = dstGamma; + + ResampleRGBA8888( info ); + + // each mipmap level needs to be color converted separately + ConvertImageFormat( tmpImage.Base(), IMAGE_FORMAT_RGBA8888, + pDst, imageFormat, dstWidth, dstHeight, 0, 0 ); + + if (numLevels == 0) + { + // We're done after we've made the 1x1 mip level + if (dstWidth == 1 && dstHeight == 1 && dstDepth == 1) + return; + } + else + { + if (--numLevels <= 0) + return; + } + + // Figure out where the next level goes + int memRequired = ImageLoader::GetMemRequired( dstWidth, dstHeight, dstDepth, imageFormat, false); + pDst += memRequired; + + // shrink by a factor of 2, but clamp at 1 pixel (non-square textures) + dstWidth = dstWidth > 1 ? dstWidth >> 1 : 1; + dstHeight = dstHeight > 1 ? dstHeight >> 1 : 1; + dstDepth = dstDepth > 1 ? dstDepth >> 1 : 1; + } +} + +void GenerateMipmapLevelsLQ( unsigned char* pSrc, unsigned char* pDst, int width, int height, + ImageFormat imageFormat, int numLevels ) +{ + CUtlMemory<unsigned char> tmpImage; + + const unsigned char* pSrcLevel = pSrc; + + int mipmap0Size = GetMemRequired( width, height, 1, IMAGE_FORMAT_RGBA8888, false ); + + // TODO: Could work with any 8888 format without conversion. + if ( imageFormat != IMAGE_FORMAT_RGBA8888 ) + { + // Damn and blast, had to allocate memory. + tmpImage.EnsureCapacity( mipmap0Size ); + ConvertImageFormat( tmpImage.Base(), IMAGE_FORMAT_RGBA8888, pSrc, imageFormat, width, height, 0, 0 ); + pSrcLevel = tmpImage.Base(); + } + + // Copy the 0th level over. + memcpy( pDst, pSrcLevel, mipmap0Size ); + + int dstWidth = width; + int dstHeight = height; + unsigned char* pDstLevel = pDst + mipmap0Size; + + int srcWidth = width; + int srcHeight = height; + + // Distance from one pixel to the next + const int cStride = 4; + + do + { + dstWidth = Max( 1, dstWidth >> 1 ); + dstHeight = Max( 1, dstHeight >> 1 ); + + // Distance from one row to the next. + const int cSrcPitch = cStride * srcWidth * ( srcHeight > 1 ? 1 : 0); + const int cSrcStride = srcWidth > 1 ? cStride : 0; + + const unsigned char* pSrcPixel = pSrcLevel; + unsigned char* pDstPixel = pDstLevel; + + for ( int j = 0; j < dstHeight; ++j ) + { + for ( int i = 0; i < dstWidth; ++i ) + { + // This doesn't round. It's crappy. It's a simple bilerp. + pDstPixel[ 0 ] = ( ( unsigned int ) pSrcPixel[ 0 ] + ( unsigned int ) pSrcPixel[ 0 + cSrcStride ] + ( unsigned int ) pSrcPixel[ 0 + cSrcPitch ] + ( unsigned int ) pSrcPixel[ 0 + cSrcPitch + cSrcStride ] ) >> 2; + pDstPixel[ 1 ] = ( ( unsigned int ) pSrcPixel[ 1 ] + ( unsigned int ) pSrcPixel[ 1 + cSrcStride ] + ( unsigned int ) pSrcPixel[ 1 + cSrcPitch ] + ( unsigned int ) pSrcPixel[ 1 + cSrcPitch + cSrcStride ] ) >> 2; + pDstPixel[ 2 ] = ( ( unsigned int ) pSrcPixel[ 2 ] + ( unsigned int ) pSrcPixel[ 2 + cSrcStride ] + ( unsigned int ) pSrcPixel[ 2 + cSrcPitch ] + ( unsigned int ) pSrcPixel[ 2 + cSrcPitch + cSrcStride ] ) >> 2; + pDstPixel[ 3 ] = ( ( unsigned int ) pSrcPixel[ 3 ] + ( unsigned int ) pSrcPixel[ 3 + cSrcStride ] + ( unsigned int ) pSrcPixel[ 3 + cSrcPitch ] + ( unsigned int ) pSrcPixel[ 3 + cSrcPitch + cSrcStride ] ) >> 2; + + pDstPixel += cStride; + pSrcPixel += cStride * 2; // We advance 2 source pixels for each pixel. + } + + // Need to bump down a row. + pSrcPixel += cSrcPitch; + } + + // Update for the next go round! + pSrcLevel = pDstLevel; + pDstLevel += GetMemRequired( dstWidth, dstHeight, 1, IMAGE_FORMAT_RGBA8888, false ); + + srcWidth = Max( 1, srcWidth >> 1 ); + srcHeight = Max( 1, srcHeight >> 1 ); + } while ( srcWidth > 1 || srcHeight > 1 ); +} + + +} // ImageLoader namespace ends + |