summaryrefslogtreecommitdiff
path: root/bitmap
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /bitmap
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'bitmap')
-rw-r--r--bitmap/ImageByteSwap.cpp260
-rw-r--r--bitmap/bitmap.cpp461
-rw-r--r--bitmap/bitmap.vpc57
-rw-r--r--bitmap/colorconversion.cpp2373
-rw-r--r--bitmap/float_bm.cpp729
-rw-r--r--bitmap/float_bm2.cpp144
-rw-r--r--bitmap/float_bm3.cpp108
-rw-r--r--bitmap/float_bm4.cpp350
-rw-r--r--bitmap/float_bm_bilateral_filter.cpp95
-rw-r--r--bitmap/float_cube.cpp119
-rw-r--r--bitmap/imageformat.cpp526
-rw-r--r--bitmap/psd.cpp571
-rw-r--r--bitmap/resample.cpp919
-rw-r--r--bitmap/tgaloader.cpp1001
-rw-r--r--bitmap/tgawriter.cpp353
15 files changed, 8066 insertions, 0 deletions
diff --git a/bitmap/ImageByteSwap.cpp b/bitmap/ImageByteSwap.cpp
new file mode 100644
index 0000000..57217f1
--- /dev/null
+++ b/bitmap/ImageByteSwap.cpp
@@ -0,0 +1,260 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Image Byte Swapping. Isolate routines to own module to allow librarian
+// to ignore xbox 360 dependenices in non-applicable win32 projects.
+//
+//=============================================================================//
+
+#if defined( _WIN32 ) && !defined( _X360 ) && !defined( DX_TO_GL_ABSTRACTION )
+#include <windows.h>
+#endif
+#include "tier0/platform.h"
+#include "tier0/dbg.h"
+#include "bitmap/imageformat.h"
+
+// Should be last include
+#include "tier0/memdbgon.h"
+
+#if defined( _WIN32 ) && !defined( _X360 ) && !defined( NO_X360_XDK ) && !defined( DX_TO_GL_ABSTRACTION )
+// the x86 version of the 360 (used by win32 tools)
+// It would have been nice to use the 360 D3DFORMAT bit encodings, but the codes
+// are different for WIN32, and this routine is used by a WIN32 library to
+// manipulate 360 data, so there can be no reliance on WIN32 D3DFORMAT bits
+#pragma warning(push)
+#pragma warning(disable : 4458) // warning C4530: C++ exception handler used, but unwind semantics are not enabled. Specify /EHsc (disabled due to std headers having exception syntax)
+#include "..\x360xdk\include\win32\vs2005\d3d9.h"
+#include "..\x360xdk\include\win32\vs2005\XGraphics.h"
+#pragma warning(pop)
+#endif
+
+namespace ImageLoader
+{
+
+//-----------------------------------------------------------------------------
+// Known formats that can be converted. Used as a trap for 360 formats
+// that may occur but have not been validated yet.
+//-----------------------------------------------------------------------------
+ bool IsFormatValidForConversion( ImageFormat fmt )
+ {
+ switch ( fmt )
+ {
+ case IMAGE_FORMAT_RGBA8888:
+ case IMAGE_FORMAT_ABGR8888:
+ case IMAGE_FORMAT_RGB888:
+ case IMAGE_FORMAT_BGR888:
+ case IMAGE_FORMAT_ARGB8888:
+ case IMAGE_FORMAT_BGRA8888:
+ case IMAGE_FORMAT_BGRX8888:
+ case IMAGE_FORMAT_UVWQ8888:
+ case IMAGE_FORMAT_RGBA16161616F:
+ case IMAGE_FORMAT_RGBA16161616:
+ case IMAGE_FORMAT_UVLX8888:
+ case IMAGE_FORMAT_DXT1:
+ case IMAGE_FORMAT_DXT1_ONEBITALPHA:
+ case IMAGE_FORMAT_DXT3:
+ case IMAGE_FORMAT_DXT5:
+ case IMAGE_FORMAT_UV88:
+ return true;
+
+ // untested formats
+ default:
+ case IMAGE_FORMAT_RGB565:
+ case IMAGE_FORMAT_I8:
+ case IMAGE_FORMAT_IA88:
+ case IMAGE_FORMAT_A8:
+ case IMAGE_FORMAT_RGB888_BLUESCREEN:
+ case IMAGE_FORMAT_BGR888_BLUESCREEN:
+ case IMAGE_FORMAT_BGR565:
+ case IMAGE_FORMAT_BGRX5551:
+ case IMAGE_FORMAT_BGRA4444:
+ case IMAGE_FORMAT_BGRA5551:
+ case IMAGE_FORMAT_ATI1N:
+ case IMAGE_FORMAT_ATI2N:
+ break;
+ }
+
+ return false;
+ }
+
+
+//-----------------------------------------------------------------------------
+// Swaps the image element type within the format.
+// This is to ensure that >8 bit channels are in the correct endian format
+// as expected by the conversion process, which varies according to format,
+// input, and output.
+//-----------------------------------------------------------------------------
+ void PreConvertSwapImageData( unsigned char *pImageData, int nImageSize, ImageFormat imageFormat, int width, int stride )
+ {
+
+ Assert( IsFormatValidForConversion( imageFormat ) );
+
+#if !defined( DX_TO_GL_ABSTRACTION ) && !defined( NO_X360_XDK )
+ if ( IsPC() )
+ {
+ // running as a win32 tool, data is in expected order
+ // for conversion code
+ return;
+ }
+
+ // running on 360 and converting, input data must be x86 order
+ // swap to ensure conversion code gets valid data
+ XGENDIANTYPE xEndian;
+ switch ( imageFormat )
+ {
+ default:
+ return;
+
+ case IMAGE_FORMAT_RGBA16161616F:
+ case IMAGE_FORMAT_RGBA16161616:
+ xEndian = XGENDIAN_8IN16;
+ break;
+ }
+
+ int count;
+ if ( !stride )
+ {
+ stride = XGENDIANTYPE_GET_DATA_SIZE( xEndian );
+ count = nImageSize / stride;
+ XGEndianSwapMemory( pImageData, pImageData, xEndian, stride, count );
+ }
+ else
+ {
+ int nRows = nImageSize/stride;
+ for ( int i=0; i<nRows; i++ )
+ {
+ XGEndianSwapMemory( pImageData, pImageData, xEndian, XGENDIANTYPE_GET_DATA_SIZE( xEndian ), width );
+ pImageData += stride;
+ }
+ }
+#endif
+ }
+
+//-----------------------------------------------------------------------------
+// Swaps image bytes for use on a big endian platform. This is used after the conversion
+// process to match the 360 d3dformats.
+//-----------------------------------------------------------------------------
+ void PostConvertSwapImageData( unsigned char *pImageData, int nImageSize, ImageFormat imageFormat, int width, int stride )
+ {
+ Assert( IsFormatValidForConversion( imageFormat ) );
+
+#if !defined( DX_TO_GL_ABSTRACTION ) && !defined( NO_X360_XDK )
+ // It would have been nice to use the 360 D3DFORMAT bit encodings, but the codes
+ // are different for win32, and this routine is used by a win32 library to
+ // manipulate 360 data, so there can be no reliance on D3DFORMAT bits
+ XGENDIANTYPE xEndian;
+ switch ( imageFormat )
+ {
+ default:
+ return;
+
+ case IMAGE_FORMAT_RGBA16161616:
+ if ( IsX360() )
+ {
+ // running on 360 the conversion output is correct
+ return;
+ }
+ // running on the pc, the output needs to be in 360 order
+ xEndian = XGENDIAN_8IN16;
+ break;
+
+ case IMAGE_FORMAT_DXT1:
+ case IMAGE_FORMAT_DXT1_ONEBITALPHA:
+ case IMAGE_FORMAT_DXT3:
+ case IMAGE_FORMAT_DXT5:
+ case IMAGE_FORMAT_UV88:
+ case IMAGE_FORMAT_ATI1N:
+ case IMAGE_FORMAT_ATI2N:
+ xEndian = XGENDIAN_8IN16;
+ break;
+
+ case IMAGE_FORMAT_BGRA8888:
+ case IMAGE_FORMAT_BGRX8888:
+ case IMAGE_FORMAT_UVWQ8888:
+ case IMAGE_FORMAT_UVLX8888:
+ xEndian = XGENDIAN_8IN32;
+ break;
+ }
+
+ int count;
+ if ( !stride )
+ {
+ stride = XGENDIANTYPE_GET_DATA_SIZE( xEndian );
+ count = nImageSize / stride;
+ XGEndianSwapMemory( pImageData, pImageData, xEndian, stride, count );
+ }
+ else
+ {
+ int nRows = nImageSize/stride;
+ for ( int i=0; i<nRows; i++ )
+ {
+ XGEndianSwapMemory( pImageData, pImageData, xEndian, XGENDIANTYPE_GET_DATA_SIZE( xEndian ), width );
+ pImageData += stride;
+ }
+ }
+#endif
+ }
+
+//-----------------------------------------------------------------------------
+// Swaps image bytes.
+//-----------------------------------------------------------------------------
+ void ByteSwapImageData( unsigned char *pImageData, int nImageSize, ImageFormat imageFormat, int width, int stride )
+ {
+ Assert( IsFormatValidForConversion( imageFormat ) );
+
+#if !defined( DX_TO_GL_ABSTRACTION ) && !defined( NO_X360_XDK )
+ XGENDIANTYPE xEndian;
+ switch ( imageFormat )
+ {
+ case IMAGE_FORMAT_BGR888:
+ case IMAGE_FORMAT_I8:
+ case IMAGE_FORMAT_A8:
+ default:
+ return;
+
+ case IMAGE_FORMAT_BGRA8888:
+ case IMAGE_FORMAT_BGRX8888:
+ case IMAGE_FORMAT_UVWQ8888:
+ case IMAGE_FORMAT_UVLX8888:
+ case IMAGE_FORMAT_R32F:
+ case IMAGE_FORMAT_RGBA32323232F:
+ xEndian = XGENDIAN_8IN32;
+ break;
+
+ case IMAGE_FORMAT_BGR565:
+ case IMAGE_FORMAT_BGRX5551:
+ case IMAGE_FORMAT_BGRA5551:
+ case IMAGE_FORMAT_BGRA4444:
+ case IMAGE_FORMAT_IA88:
+ case IMAGE_FORMAT_DXT1:
+ case IMAGE_FORMAT_DXT1_ONEBITALPHA:
+ case IMAGE_FORMAT_DXT3:
+ case IMAGE_FORMAT_DXT5:
+ case IMAGE_FORMAT_ATI1N:
+ case IMAGE_FORMAT_ATI2N:
+ case IMAGE_FORMAT_UV88:
+ case IMAGE_FORMAT_RGBA16161616F:
+ case IMAGE_FORMAT_RGBA16161616:
+ xEndian = XGENDIAN_8IN16;
+ break;
+ }
+
+ int count;
+ if ( !stride )
+ {
+ stride = XGENDIANTYPE_GET_DATA_SIZE( xEndian );
+ count = nImageSize / stride;
+ XGEndianSwapMemory( pImageData, pImageData, xEndian, stride, count );
+ }
+ else
+ {
+ int nRows = nImageSize/stride;
+ for ( int i=0; i<nRows; i++ )
+ {
+ XGEndianSwapMemory( pImageData, pImageData, xEndian, XGENDIANTYPE_GET_DATA_SIZE( xEndian ), width );
+ pImageData += stride;
+ }
+ }
+#endif
+ }
+
+}
diff --git a/bitmap/bitmap.cpp b/bitmap/bitmap.cpp
new file mode 100644
index 0000000..e9f30af
--- /dev/null
+++ b/bitmap/bitmap.cpp
@@ -0,0 +1,461 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "bitmap/bitmap.h"
+#include "dbg.h"
+
+// Should be last include
+#include "tier0/memdbgon.h"
+
+bool Bitmap_t::IsValid() const
+{
+ if ( m_nWidth <= 0 || m_nHeight <= 0 || m_pBits == NULL )
+ {
+ Assert( m_nWidth == 0 );
+ Assert( m_nHeight == 0 );
+ Assert( m_pBits == NULL );
+ return false;
+ }
+ return true;
+}
+
+void Bitmap_t::Clear()
+{
+ if ( m_pBits && m_bOwnsBuffer )
+ {
+ free( m_pBits );
+ }
+ Reset();
+}
+
+void Bitmap_t::Init( int xs, int ys, ImageFormat imageFormat, int nStride )
+{
+
+ // Check for bogus allocation sizes
+ if (xs <= 0 || ys <= 0 )
+ {
+ Assert( xs == 0 );
+ Assert( ys == 0 );
+ Clear();
+ return;
+ }
+
+ int nPixSize = ImageLoader::SizeInBytes( imageFormat );
+
+ // Auto detect stride
+ if ( nStride == 0 )
+ {
+ nStride = nPixSize * xs;
+ }
+
+ // Check for NOP
+ if (
+ m_pBits
+ && m_bOwnsBuffer
+ && m_nWidth == xs
+ && m_nHeight == ys
+ && nStride == m_nStride
+ && nPixSize == m_nPixelSize )
+ {
+ // We're already got a buffer of the right size.
+ // The only thing that might be wrong is the pixel format.
+ m_ImageFormat = imageFormat;
+ return;
+ }
+
+ // Free up anything already allocated
+ Clear();
+
+ // Remember dimensions and pixel format
+ m_nWidth = xs;
+ m_nHeight = ys;
+ m_ImageFormat = imageFormat;
+ m_nPixelSize = nPixSize;
+ m_nStride = nStride;
+
+ // Allocate buffer. Because this is a PC game,
+ // failure is impossible....right?
+ m_pBits = (byte *)malloc( ys * m_nStride );
+
+ // Assume ownership
+ m_bOwnsBuffer = true;
+}
+
+void Bitmap_t::SetBuffer( int nWidth, int nHeight, ImageFormat imageFormat, unsigned char *pBits, bool bAssumeOwnership, int nStride )
+{
+ Assert( pBits );
+ Assert( nWidth > 0 );
+ Assert( nHeight > 0 );
+
+ // Free up anything already allocated
+ Clear();
+
+ // Remember dimensions and pixel format
+ m_nWidth = nWidth;
+ m_nHeight = nHeight;
+ m_ImageFormat = imageFormat;
+ m_nPixelSize = ImageLoader::SizeInBytes( imageFormat );
+ if ( nStride == 0 )
+ {
+ m_nStride = m_nPixelSize * nWidth;
+ }
+ else
+ {
+ m_nStride = nStride;
+ }
+
+ // Set our buffer pointer
+ m_pBits = pBits;
+
+ // Assume ownership of the buffer, if requested
+ m_bOwnsBuffer = bAssumeOwnership;
+
+ // We should be good to go
+ Assert( IsValid() );
+}
+
+Color Bitmap_t::GetColor( int x, int y ) const
+{
+ Assert( x >= 0 && x < m_nWidth );
+ Assert( y >= 0 && y < m_nHeight );
+ Assert( m_pBits );
+
+ // Get pointer to pixel data
+ byte *ptr = m_pBits + (y*m_nStride) + x* m_nPixelSize;
+
+ // Check supported image formats
+ switch ( m_ImageFormat )
+ {
+ case IMAGE_FORMAT_RGBA8888:
+ return Color( ptr[0], ptr[1], ptr[2], ptr[3] );
+
+ case IMAGE_FORMAT_ABGR8888:
+ return Color( ptr[3], ptr[2], ptr[1], ptr[0] );
+
+ default:
+ Assert( !"Unsupport image format!");
+ return Color( 255,0,255,255 );
+ }
+}
+
+void Bitmap_t::SetColor( int x, int y, Color c )
+{
+ Assert( x >= 0 && x < m_nWidth );
+ Assert( y >= 0 && y < m_nHeight );
+ Assert( m_pBits );
+
+ // Get pointer to pixel data
+ byte *ptr = m_pBits + (y*m_nStride) + x* m_nPixelSize;
+
+ // Check supported image formats
+ switch ( m_ImageFormat )
+ {
+ case IMAGE_FORMAT_RGBA8888:
+ ptr[0] = c.r();
+ ptr[1] = c.g();
+ ptr[2] = c.b();
+ ptr[3] = c.a();
+ break;
+
+ case IMAGE_FORMAT_ABGR8888:
+ ptr[0] = c.a();
+ ptr[1] = c.b();
+ ptr[2] = c.g();
+ ptr[3] = c.r();
+ break;
+
+ default:
+ Assert( !"Unsupport image format!");
+ break;
+ }
+}
+
+//bool LoadVTF( const char *pszFilename )
+//{
+//
+// // Load the raw file data
+// CUtlBuffer fileData;
+// if ( !filesystem->ReadFile( pszFilename, "game", fileData ) )
+// {
+// Warning( "Failed to load %s\n", pszFilename);
+// return false;
+// }
+//
+// return LoadVTFFromBuffer( fileData, pszFilename );
+//}
+//
+//bool LoadVTFFromBuffer( CUtlBuffer fileData, const char *pszDebugName = "buffer" )
+//{
+//
+// // Parse it into VTF object
+// IVTFTexture *pVTFTexture( CreateVTFTexture() );
+// if ( !pVTFTexture->Unserialize( fileData ) )
+// {
+// DestroyVTFTexture( pVTFTexture );
+// Warning( "Failed to deserialize VTF %s\n", pszDebugName);
+// return false;
+// }
+//
+// // We are re-reading our own files, so they should be 8888's
+// if ( pVTFTexture->Format() != IMAGE_FORMAT_RGBA8888 )
+// {
+// DestroyVTFTexture( pVTFTexture );
+// Warning( "%s isn't RGBA8888\n", pszDebugName);
+// return false;
+// }
+//
+// // Copy the image data
+// Allocate( pVTFTexture->Width(), pVTFTexture->Height() );
+// for ( int y = 0 ; y < m_nHeight ; ++y )
+// {
+// memcpy( PixPtr(0, y), pVTFTexture->ImageData(0, 0, 0, 0, y), m_nWidth*4 );
+// }
+//
+// // Clean up
+// DestroyVTFTexture( pVTFTexture );
+// return true;
+//}
+//
+//bool SaveVTF( CUtlBuffer &outBuffer )
+//{
+// // Create the VTF to write into
+// IVTFTexture *pVTFTexture( CreateVTFTexture() );
+// const int nFlags = TEXTUREFLAGS_NOMIP | TEXTUREFLAGS_NOLOD | TEXTUREFLAGS_SRGB;
+// if ( !pVTFTexture->Init( m_nWidth, m_nHeight, 1, IMAGE_FORMAT_RGBA8888, nFlags, 1, 1 ) )
+// {
+// DestroyVTFTexture( pVTFTexture );
+// return false;
+// }
+//
+// // write the rgba image to the vtf texture using the pixel writer
+// CPixelWriter pixelWriter;
+// pixelWriter.SetPixelMemory( pVTFTexture->Format(), pVTFTexture->ImageData(), pVTFTexture->RowSizeInBytes( 0 ) );
+//
+// for (int y = 0; y < m_nHeight; ++y)
+// {
+// pixelWriter.Seek( 0, y );
+// for (int x = 0; x < m_nWidth; ++x)
+// {
+// Color c = GetPix( x, y );
+// pixelWriter.WritePixel( c.r(), c.g(), c.b(), c.a() );
+// }
+// }
+//
+// // Serialize to the buffer
+// if ( !pVTFTexture->Serialize( outBuffer ) )
+// {
+// DestroyVTFTexture( pVTFTexture );
+// return false;
+// }
+// DestroyVTFTexture( pVTFTexture );
+// return true;
+//}
+
+//void Resize( int nNewSizeX, int nNewSizeY, const Image *pImgSrc = NULL )
+//{
+// if ( pImgSrc == NULL )
+// {
+// pImgSrc = this;
+// }
+//
+// if ( nNewSizeX == m_nWidth && nNewSizeY == m_nHeight && pImgSrc == this )
+// {
+// return;
+// }
+//
+// byte *pNewData = (byte *)malloc( nNewSizeX * nNewSizeY * 4 );
+// ImgUtl_StretchRGBAImage( pImgSrc->m_pBits, pImgSrc->m_nWidth, pImgSrc->m_nHeight, pNewData, nNewSizeX, nNewSizeY );
+// Clear();
+// m_pBits = pNewData;
+// m_nWidth = nNewSizeX;
+// m_nHeight = nNewSizeY;
+//}
+//
+//void Crop( int x0, int y0, int nNewSizeX, int nNewSizeY, const Image *pImgSrc )
+//{
+// if ( pImgSrc == NULL )
+// {
+// pImgSrc = this;
+// }
+//
+// if ( nNewSizeX == m_nWidth && nNewSizeY == m_nHeight && pImgSrc == this )
+// {
+// return;
+// }
+//
+//
+// Assert( x0 >= 0 );
+// Assert( y0 >= 0 );
+// Assert( x0 + nNewSizeX <= pImgSrc->m_nWidth );
+// Assert( y0 + nNewSizeY <= pImgSrc->m_nHeight );
+//
+// // Allocate new buffer
+// int nRowSize = nNewSizeX * 4;
+// byte *pNewData = (byte *)malloc( nNewSizeY * nRowSize );
+//
+// // Copy data, one row at a time
+// for ( int y = 0 ; y < nNewSizeY ; ++y )
+// {
+// memcpy( pNewData + y*nRowSize, pImgSrc->PixPtr(x0, y0+y), nRowSize );
+// }
+//
+// // Replace current buffer with the new one
+// Clear();
+// m_pBits = pNewData;
+// m_nWidth = nNewSizeX;
+// m_nHeight = nNewSizeY;
+//}
+
+void Bitmap_t::MakeLogicalCopyOf( Bitmap_t &src, bool bTransferBufferOwnership )
+{
+ // What does it mean to make a logical copy of an
+ // invalid bitmap? I'll tell you what it means: you have a bug.
+ Assert( src.IsValid() );
+
+ // Free up anything we already own
+ Clear();
+
+ // Copy all of the member variables so we are
+ // a logical copy of the source bitmap
+ m_nWidth = src.m_nWidth;
+ m_nHeight = src.m_nHeight;
+ m_nPixelSize = src.m_nPixelSize;
+ m_nStride = src.m_nStride;
+ m_ImageFormat = src.m_ImageFormat;
+ m_pBits = src.m_pBits;
+ Assert( !m_bOwnsBuffer );
+
+ // Check for assuming ownership of the buffer
+ if ( bTransferBufferOwnership )
+ {
+ if ( src.m_bOwnsBuffer )
+ {
+ m_bOwnsBuffer = true;
+ src.m_bOwnsBuffer = false;
+ }
+ else
+ {
+ // They don't own the buffer? Then who does?
+ // Maybe nobody, and it would safe to assume
+ // ownership. But more than likely, this is a
+ // bug.
+ Assert( src.m_bOwnsBuffer );
+
+ // And a leak is better than a double-free.
+ // Don't assume ownership of the buffer.
+ }
+ }
+}
+
+void Bitmap_t::Crop( int x0, int y0, int nWidth, int nHeight, const Bitmap_t *pImgSource )
+{
+ // Check for cropping in place, then save off our data to a temp
+ Bitmap_t temp;
+ if ( pImgSource == this || !pImgSource )
+ {
+ temp.MakeLogicalCopyOf( *this, m_bOwnsBuffer );
+ pImgSource = &temp;
+ }
+
+ // No source image?
+ if ( !pImgSource->IsValid() )
+ {
+ Assert( pImgSource->IsValid() );
+ return;
+ }
+
+ // Sanity check crop rectangle
+ Assert( x0 >= 0 );
+ Assert( y0 >= 0 );
+ Assert( x0 + nWidth <= pImgSource->Width() );
+ Assert( y0 + nHeight <= pImgSource->Height() );
+
+ // Allocate buffer
+ Init( nWidth, nHeight, pImgSource->Format() );
+
+ // Something wrong?
+ if ( !IsValid() )
+ {
+ Assert( IsValid() );
+ return;
+ }
+
+ // Copy the data a row at a time
+ int nRowSize = m_nWidth * m_nPixelSize;
+ for ( int y = 0 ; y < m_nHeight ; ++y )
+ {
+ memcpy( GetPixel(0,y), pImgSource->GetPixel( x0, y + y0 ), nRowSize );
+ }
+}
+
+void Bitmap_t::SetPixelData( const Bitmap_t &src, int nSrcX1, int nSrcY1, int nCopySizeX, int nCopySizeY, int nDestX1, int nDestY1 )
+{
+ // Safety
+ if ( !src.IsValid() )
+ {
+ Assert( src.IsValid() );
+ return;
+ }
+ if ( !IsValid() )
+ {
+ Assert( IsValid() );
+ return;
+ }
+
+ // You need to specify a valid source rectangle, we cannot clip that for you
+ if ( nSrcX1 < 0 || nSrcY1 < 0 || nSrcX1 + nCopySizeX > src.Width() || nSrcY1 + nCopySizeY > src.Height() )
+ {
+ Assert( nSrcX1 >= 0 );
+ Assert( nSrcY1 >= 0 );
+ Assert( nSrcX1 + nCopySizeX <= src.Width() );
+ Assert( nSrcY1 + nCopySizeY <= src.Height() );
+ return;
+ }
+
+ // But we can clip the rectangle if it extends outside the destination image in a perfectly
+ // reasonable way
+ if ( nDestX1 < 0 )
+ {
+ nCopySizeX += nDestX1;
+ nDestX1 = 0;
+ }
+ if ( nDestX1 + nCopySizeX > Width() )
+ {
+ nCopySizeX = Width() - nDestX1;
+ }
+ if ( nDestY1 < 0 )
+ {
+ nCopySizeY += nDestY1;
+ nDestY1 = 0;
+ }
+ if ( nDestY1 + nCopySizeY > Height() )
+ {
+ nCopySizeY = Height() - nDestY1;
+ }
+ if ( nCopySizeX <= 0 || nCopySizeY <= 0 )
+ {
+ return;
+ }
+
+ // Copy the pixel data
+ for ( int y = 0 ; y < nCopySizeY ; ++y )
+ {
+ // Wow, this could be a lot faster in the common case
+ // that the pixe formats are the same. But...this code
+ // is simple and works, and is NOT the root of all evil.
+ for ( int x = 0 ; x < nCopySizeX ; ++x )
+ {
+ Color c = src.GetColor( nSrcX1 + x, nSrcY1 + y );
+ SetColor( nDestX1 + x, nDestY1 + y, c );
+ }
+ }
+}
+
+void Bitmap_t::SetPixelData( const Bitmap_t &src, int nDestX1, int nDestY1 )
+{
+ SetPixelData( src, 0, 0, src.Width(), src.Height(), nDestX1, nDestY1 );
+}
+
diff --git a/bitmap/bitmap.vpc b/bitmap/bitmap.vpc
new file mode 100644
index 0000000..9471c15
--- /dev/null
+++ b/bitmap/bitmap.vpc
@@ -0,0 +1,57 @@
+//-----------------------------------------------------------------------------
+// BITMAP.VPC
+//
+// Project Script
+//-----------------------------------------------------------------------------
+
+$Macro SRCDIR ".."
+
+$include "$SRCDIR\vpc_scripts\source_lib_base.vpc"
+
+$Configuration
+{
+ $Compiler
+ {
+ $AdditionalIncludeDirectories "$BASE;$SRCDIR\dx9sdk\include" [$WINDOWS]
+ $AdditionalIncludeDirectories "$BASE;$SRCDIR\x360xdk\include\win32\vs2005" [$WINDOWS]
+ $AdditionalIncludeDirectories "$BASE;$SRCDIR\thirdparty\stb"
+ }
+}
+
+$Project "bitmap"
+{
+ $Folder "Source Files"
+ {
+ $File "ImageByteSwap.cpp"
+ $File "colorconversion.cpp"
+ $File "float_bm.cpp"
+ $File "float_bm2.cpp"
+ $File "float_bm3.cpp"
+ $File "float_bm4.cpp" [$WINDOWS]
+ $File "float_bm_bilateral_filter.cpp"
+ $File "float_cube.cpp"
+ $File "imageformat.cpp"
+ $File "psd.cpp"
+ $File "resample.cpp"
+ $File "tgaloader.cpp"
+ $File "tgawriter.cpp"
+ $File "bitmap.cpp"
+ }
+
+ $Folder "Header Files"
+ {
+ $File "$SRCDIR\public\bitmap\bitmap.h"
+ $File "$SRCDIR\public\bitmap\float_bm.h"
+ $File "$SRCDIR\public\bitmap\imageformat.h"
+ $File "$SRCDIR\public\bitmap\psd.h"
+ $File "$SRCDIR\public\bitmap\tgaloader.h"
+ $File "$SRCDIR\public\bitmap\tgawriter.h"
+ $File "$SRCDIR\thirdparty\stb\stb_dxt.h"
+ }
+
+ $Folder "Link Libraries" [$WIN32]
+ {
+ $Lib nvtc
+ $Lib ATI_Compress_MT_VC10
+ }
+}
diff --git a/bitmap/colorconversion.cpp b/bitmap/colorconversion.cpp
new file mode 100644
index 0000000..ac033fd
--- /dev/null
+++ b/bitmap/colorconversion.cpp
@@ -0,0 +1,2373 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifdef IS_WINDOWS_PC
+#include <windows.h>
+#endif
+#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"
+#include "nvtc.h"
+
+#ifdef POSIX
+typedef int32 *DWORD_PTR;
+#endif
+
+#include "ATI_Compress.h"
+#include "bitmap/float_bm.h"
+
+#define STB_DXT_IMPLEMENTATION
+#include "stb_dxt.h"
+
+// Should be last include
+#include "tier0/memdbgon.h"
+
+//-----------------------------------------------------------------------------
+// Various important function types for each color format
+//-----------------------------------------------------------------------------
+
+typedef void (*UserFormatToRGBA8888Func_t )( const uint8 *src, uint8 *dst, int numPixels );
+typedef void (*RGBA8888ToUserFormatFunc_t )( const uint8 *src, uint8 *dst, int numPixels );
+
+
+namespace ImageLoader
+{
+
+// Color Conversion functions
+static void RGBA8888ToRGBA8888( const uint8 *src, uint8 *dst, int numPixels );
+static void RGBA8888ToABGR8888( const uint8 *src, uint8 *dst, int numPixels );
+static void RGBA8888ToRGB888( const uint8 *src, uint8 *dst, int numPixels );
+static void RGBA8888ToBGR888( const uint8 *src, uint8 *dst, int numPixels );
+static void RGBA8888ToRGB565( const uint8 *src, uint8 *dst, int numPixels );
+static void RGBA8888ToI8( const uint8 *src, uint8 *dst, int numPixels );
+static void RGBA8888ToIA88( const uint8 *src, uint8 *dst, int numPixels );
+static void RGBA8888ToP8( const uint8 *src, uint8 *dst, int numPixels );
+static void RGBA8888ToA8( const uint8 *src, uint8 *dst, int numPixels );
+static void RGBA8888ToRGB888_BLUESCREEN( const uint8 *src, uint8 *dst, int numPixels );
+static void RGBA8888ToBGR888_BLUESCREEN( const uint8 *src, uint8 *dst, int numPixels );
+static void RGBA8888ToARGB8888( const uint8 *src, uint8 *dst, int numPixels );
+static void RGBA8888ToBGRA8888( const uint8 *src, uint8 *dst, int numPixels );
+static void RGBA8888ToBGRX8888( const uint8 *src, uint8 *dst, int numPixels );
+static void RGBA8888ToBGR565( const uint8 *src, uint8 *dst, int numPixels );
+static void RGBA8888ToBGRX5551( const uint8 *src, uint8 *dst, int numPixels );
+static void RGBA8888ToBGRA5551( const uint8 *src, uint8 *dst, int numPixels );
+static void RGBA8888ToBGRA4444( const uint8 *src, uint8 *dst, int numPixels );
+static void RGBA8888ToUV88( const uint8 *src, uint8 *dst, int numPixels );
+static void RGBA8888ToUVWQ8888( const uint8 *src, uint8 *dst, int numPixels );
+static void RGBA8888ToUVLX8888( const uint8 *src, uint8 *dst, int numPixels );
+//static void RGBA8888ToRGBA16161616F( const uint8 *src, uint8 *dst, int numPixels );
+
+static void ABGR8888ToRGBA8888( const uint8 *src, uint8 *dst, int numPixels );
+static void RGB888ToRGBA8888( const uint8 *src, uint8 *dst, int numPixels );
+static void BGR888ToRGBA8888( const uint8 *src, uint8 *dst, int numPixels );
+static void RGB565ToRGBA8888( const uint8 *src, uint8 *dst, int numPixels );
+static void I8ToRGBA8888( const uint8 *src, uint8 *dst, int numPixels );
+static void IA88ToRGBA8888( const uint8 *src, uint8 *dst, int numPixels );
+static void P8ToRGBA8888( const uint8 *src, uint8 *dst, int numPixels );
+static void A8ToRGBA8888( const uint8 *src, uint8 *dst, int numPixels );
+static void RGB888_BLUESCREENToRGBA8888( const uint8 *src, uint8 *dst, int numPixels );
+static void BGR888_BLUESCREENToRGBA8888( const uint8 *src, uint8 *dst, int numPixels );
+static void ARGB8888ToRGBA8888( const uint8 *src, uint8 *dst, int numPixels );
+static void BGRA8888ToRGBA8888( const uint8 *src, uint8 *dst, int numPixels );
+static void BGRX8888ToRGBA8888( const uint8 *src, uint8 *dst, int numPixels );
+static void BGR565ToRGBA8888( const uint8 *src, uint8 *dst, int numPixels );
+static void BGRX5551ToRGBA8888( const uint8 *src, uint8 *dst, int numPixels );
+static void BGRA5551ToRGBA8888( const uint8 *src, uint8 *dst, int numPixels );
+static void BGRA4444ToRGBA8888( const uint8 *src, uint8 *dst, int numPixels );
+static void UV88ToRGBA8888( const uint8 *src, uint8 *dst, int numPixels );
+static void UVWQ8888ToRGBA8888( const uint8 *src, uint8 *dst, int numPixels );
+static void UVLX8888ToRGBA8888( const uint8 *src, uint8 *dst, int numPixels );
+static void RGBA16161616ToRGBA8888( const uint8 *src, uint8 *dst, int numPixels );
+//static void RGBA16161616FToRGBA8888( const uint8 *src, uint8 *dst, int numPixels );
+
+
+static UserFormatToRGBA8888Func_t GetUserFormatToRGBA8888Func_t( ImageFormat srcImageFormat )
+{
+ switch( srcImageFormat )
+ {
+ case IMAGE_FORMAT_RGBA8888:
+ return RGBA8888ToRGBA8888;
+ case IMAGE_FORMAT_ABGR8888:
+ return ABGR8888ToRGBA8888;
+ case IMAGE_FORMAT_RGB888:
+ return RGB888ToRGBA8888;
+ case IMAGE_FORMAT_BGR888:
+ return BGR888ToRGBA8888;
+ case IMAGE_FORMAT_RGB565:
+ return NULL;
+ case IMAGE_FORMAT_I8:
+ return I8ToRGBA8888;
+ case IMAGE_FORMAT_IA88:
+ return IA88ToRGBA8888;
+ case IMAGE_FORMAT_A8:
+ return A8ToRGBA8888;
+ case IMAGE_FORMAT_RGB888_BLUESCREEN:
+ return RGB888_BLUESCREENToRGBA8888;
+ case IMAGE_FORMAT_BGR888_BLUESCREEN:
+ return BGR888_BLUESCREENToRGBA8888;
+ case IMAGE_FORMAT_ARGB8888:
+ return ARGB8888ToRGBA8888;
+ case IMAGE_FORMAT_BGRA8888:
+ return BGRA8888ToRGBA8888;
+ case IMAGE_FORMAT_BGRX8888:
+ return BGRX8888ToRGBA8888;
+ case IMAGE_FORMAT_BGR565:
+ return BGR565ToRGBA8888;
+ case IMAGE_FORMAT_BGRX5551:
+ return BGRX5551ToRGBA8888;
+ case IMAGE_FORMAT_BGRA5551:
+ return BGRA5551ToRGBA8888;
+ case IMAGE_FORMAT_BGRA4444:
+ return BGRA4444ToRGBA8888;
+ case IMAGE_FORMAT_UV88:
+ return UV88ToRGBA8888;
+ case IMAGE_FORMAT_UVWQ8888:
+ return UVWQ8888ToRGBA8888;
+ case IMAGE_FORMAT_UVLX8888:
+ return UVLX8888ToRGBA8888;
+ case IMAGE_FORMAT_RGBA16161616:
+ return RGBA16161616ToRGBA8888;
+ case IMAGE_FORMAT_RGBA16161616F:
+ return NULL;
+
+#if defined( _X360 )
+ case IMAGE_FORMAT_LINEAR_RGBA8888:
+ return RGBA8888ToRGBA8888;
+ case IMAGE_FORMAT_LINEAR_ABGR8888:
+ return ABGR8888ToRGBA8888;
+ case IMAGE_FORMAT_LINEAR_RGB888:
+ return RGB888ToRGBA8888;
+ case IMAGE_FORMAT_LINEAR_BGR888:
+ return BGR888ToRGBA8888;
+ case IMAGE_FORMAT_LINEAR_I8:
+ return I8ToRGBA8888;
+ case IMAGE_FORMAT_LINEAR_ARGB8888:
+ return ARGB8888ToRGBA8888;
+ case IMAGE_FORMAT_LINEAR_BGRA8888:
+ return BGRA8888ToRGBA8888;
+ case IMAGE_FORMAT_LINEAR_BGRX8888:
+ return BGRX8888ToRGBA8888;
+ case IMAGE_FORMAT_LINEAR_BGRX5551:
+ return BGRX5551ToRGBA8888;
+ case IMAGE_FORMAT_LINEAR_RGBA16161616:
+ return RGBA16161616ToRGBA8888;
+#endif
+
+ default:
+ return NULL;
+ }
+}
+
+static RGBA8888ToUserFormatFunc_t GetRGBA8888ToUserFormatFunc_t( ImageFormat dstImageFormat )
+{
+ switch( dstImageFormat )
+ {
+ case IMAGE_FORMAT_RGBA8888:
+ return RGBA8888ToRGBA8888;
+ case IMAGE_FORMAT_ABGR8888:
+ return RGBA8888ToABGR8888;
+ case IMAGE_FORMAT_RGB888:
+ return RGBA8888ToRGB888;
+ case IMAGE_FORMAT_BGR888:
+ return RGBA8888ToBGR888;
+ case IMAGE_FORMAT_RGB565:
+ return NULL;
+ case IMAGE_FORMAT_I8:
+ return RGBA8888ToI8;
+ case IMAGE_FORMAT_IA88:
+ return RGBA8888ToIA88;
+ case IMAGE_FORMAT_A8:
+ return RGBA8888ToA8;
+ case IMAGE_FORMAT_RGB888_BLUESCREEN:
+ return RGBA8888ToRGB888_BLUESCREEN;
+ case IMAGE_FORMAT_BGR888_BLUESCREEN:
+ return RGBA8888ToBGR888_BLUESCREEN;
+ case IMAGE_FORMAT_ARGB8888:
+ return RGBA8888ToARGB8888;
+ case IMAGE_FORMAT_BGRA8888:
+ return RGBA8888ToBGRA8888;
+ case IMAGE_FORMAT_BGRX8888:
+ return RGBA8888ToBGRX8888;
+ case IMAGE_FORMAT_BGR565:
+ return RGBA8888ToBGR565;
+ case IMAGE_FORMAT_BGRX5551:
+ return RGBA8888ToBGRX5551;
+ case IMAGE_FORMAT_BGRA5551:
+ return RGBA8888ToBGRA5551;
+ case IMAGE_FORMAT_BGRA4444:
+ return RGBA8888ToBGRA4444;
+ case IMAGE_FORMAT_UV88:
+ return RGBA8888ToUV88;
+ case IMAGE_FORMAT_UVWQ8888:
+ return RGBA8888ToUVWQ8888;
+ case IMAGE_FORMAT_UVLX8888:
+ return RGBA8888ToUVLX8888;
+ case IMAGE_FORMAT_RGBA16161616F:
+ return NULL;
+
+#if defined( _X360 )
+ case IMAGE_FORMAT_LINEAR_RGBA8888:
+ return RGBA8888ToRGBA8888;
+ case IMAGE_FORMAT_LINEAR_ABGR8888:
+ return RGBA8888ToABGR8888;
+ case IMAGE_FORMAT_LINEAR_RGB888:
+ return RGBA8888ToRGB888;
+ case IMAGE_FORMAT_LINEAR_BGR888:
+ return RGBA8888ToBGR888;
+ case IMAGE_FORMAT_LINEAR_I8:
+ return RGBA8888ToI8;
+ case IMAGE_FORMAT_LINEAR_ARGB8888:
+ return RGBA8888ToARGB8888;
+ case IMAGE_FORMAT_LINEAR_BGRA8888:
+ return RGBA8888ToBGRA8888;
+ case IMAGE_FORMAT_LINEAR_BGRX8888:
+ return RGBA8888ToBGRX8888;
+ case IMAGE_FORMAT_LINEAR_BGRX5551:
+ return RGBA8888ToBGRX5551;
+#endif
+
+ default:
+ return NULL;
+ }
+}
+
+
+
+#pragma pack(1)
+
+struct DXTColBlock
+{
+ WORD col0;
+ WORD col1;
+
+ // no bit fields - use bytes
+ BYTE row[4];
+};
+
+struct DXTAlphaBlock3BitLinear
+{
+ BYTE alpha0;
+ BYTE alpha1;
+
+ BYTE stuff[6];
+};
+
+#pragma pack()
+
+static inline void GetColorBlockColorsBGRA8888( DXTColBlock *pBlock, BGRA8888_t *col_0,
+ BGRA8888_t *col_1, BGRA8888_t *col_2,
+ BGRA8888_t *col_3, WORD & wrd )
+{
+ // input data is assumed to be x86 order
+ // swap to target platform for proper dxt decoding
+ WORD color0 = LittleShort( pBlock->col0 );
+ WORD color1 = LittleShort( pBlock->col1 );
+
+ // convert to full precision correctly.
+ // If this was a perf problem, we could optimize it. But this isn't used in any hotpaths
+ // (now) so let's just do the correct but slow fp math.
+ col_0->a = 0xff;
+ col_0->r = ( uint8 ) round( ( ( BGR565_t* ) &color0 )->r * 255.0f / 31.0f );
+ col_0->g = ( uint8 ) round( ( ( BGR565_t* ) &color0 )->g * 255.0f / 63.0f );
+ col_0->b = ( uint8 ) round( ( ( BGR565_t* ) &color0 )->b * 255.0f / 31.0f );
+
+ col_1->a = 0xff;
+ col_1->r = ( uint8 ) round( ( ( BGR565_t* ) &color1 )->r * 255.0f / 31.0f );
+ col_1->g = ( uint8 ) round( ( ( BGR565_t* ) &color1 )->g * 255.0f / 63.0f );
+ col_1->b = ( uint8 ) round( ( ( BGR565_t* ) &color1 )->b * 255.0f / 31.0f );
+
+ if ( color0 > color1 )
+ {
+ // Four-color block: derive the other two colors.
+ // 00 = color_0, 01 = color_1, 10 = color_2, 11 = color_3
+ // These two bit codes correspond to the 2-bit fields
+ // stored in the 64-bit block.
+
+ wrd = ((WORD)col_0->r * 2 + (WORD)col_1->r )/3;
+ // no +1 for rounding
+ // as bits have been shifted to 888
+ col_2->r = (BYTE)wrd;
+
+ wrd = ((WORD)col_0->g * 2 + (WORD)col_1->g )/3;
+ col_2->g = (BYTE)wrd;
+
+ wrd = ((WORD)col_0->b * 2 + (WORD)col_1->b )/3;
+ col_2->b = (BYTE)wrd;
+ col_2->a = 0xff;
+
+ wrd = ((WORD)col_0->r + (WORD)col_1->r *2 )/3;
+ col_3->r = (BYTE)wrd;
+
+ wrd = ((WORD)col_0->g + (WORD)col_1->g *2 )/3;
+ col_3->g = (BYTE)wrd;
+
+ wrd = ((WORD)col_0->b + (WORD)col_1->b *2 )/3;
+ col_3->b = (BYTE)wrd;
+ col_3->a = 0xff;
+ }
+ else
+ {
+ // Three-color block: derive the other color.
+ // 00 = color_0, 01 = color_1, 10 = color_2,
+ // 11 = transparent.
+ // These two bit codes correspond to the 2-bit fields
+ // stored in the 64-bit block.
+
+ // explicit for each component, unlike some refrasts...????
+
+ wrd = ((WORD)col_0->r + (WORD)col_1->r )/2;
+ col_2->r = (BYTE)wrd;
+ wrd = ((WORD)col_0->g + (WORD)col_1->g )/2;
+ col_2->g = (BYTE)wrd;
+ wrd = ((WORD)col_0->b + (WORD)col_1->b )/2;
+ col_2->b = (BYTE)wrd;
+ col_2->a = 0xff;
+
+ col_3->r = 0x00; // random color to indicate alpha
+ col_3->g = 0xff;
+ col_3->b = 0xff;
+ col_3->a = 0x00;
+ }
+}
+
+template <class CDestPixel>
+static inline void DecodeColorBlock( CDestPixel *pOutputImage, DXTColBlock *pColorBlock, int width,
+ BGRA8888_t *col_0, BGRA8888_t *col_1,
+ BGRA8888_t *col_2, BGRA8888_t *col_3 )
+{
+ // width is width of image in pixels
+ DWORD bits;
+ int r,n;
+
+ // bit masks = 00000011, 00001100, 00110000, 11000000
+ const DWORD masks[] = { 3 << 0, 3 << 2, 3 << 4, 3 << 6 };
+ const int shift[] = { 0, 2, 4, 6 };
+
+ // r steps through lines in y
+ for ( r=0; r < 4; r++, pOutputImage += width-4 ) // no width*4 as DWORD ptr inc will *4
+ {
+ // width * 4 bytes per pixel per line
+ // each j dxtc row is 4 lines of pixels
+
+ // n steps through pixels
+ for ( n=0; n < 4; n++ )
+ {
+ bits = pColorBlock->row[r] & masks[n];
+ bits >>= shift[n];
+
+ switch( bits )
+ {
+ case 0:
+ *pOutputImage = *col_0;
+ pOutputImage++; // increment to next output pixel
+ break;
+ case 1:
+ *pOutputImage = *col_1;
+ pOutputImage++;
+ break;
+ case 2:
+ *pOutputImage = *col_2;
+ pOutputImage++;
+ break;
+ case 3:
+ *pOutputImage = *col_3;
+ pOutputImage++;
+ break;
+ default:
+ Assert( 0 );
+ pOutputImage++;
+ break;
+ }
+ }
+ }
+}
+
+template <class CDestPixel>
+static inline void DecodeAlpha3BitLinear( CDestPixel *pImPos, DXTAlphaBlock3BitLinear *pAlphaBlock, int width, int nChannelSelect = 3 )
+{
+ static BYTE gBits[4][4];
+ static WORD gAlphas[8];
+ static BGRA8888_t gACol[4][4];
+
+ gAlphas[0] = pAlphaBlock->alpha0;
+ gAlphas[1] = pAlphaBlock->alpha1;
+
+ // 8-alpha or 6-alpha block?
+
+ if( gAlphas[0] > gAlphas[1] )
+ {
+ // 8-alpha block: derive the other 6 alphas.
+ // 000 = alpha_0, 001 = alpha_1, others are interpolated
+
+ gAlphas[2] = ( 6 * gAlphas[0] + gAlphas[1]) / 7; // bit code 010
+ gAlphas[3] = ( 5 * gAlphas[0] + 2 * gAlphas[1]) / 7; // Bit code 011
+ gAlphas[4] = ( 4 * gAlphas[0] + 3 * gAlphas[1]) / 7; // Bit code 100
+ gAlphas[5] = ( 3 * gAlphas[0] + 4 * gAlphas[1]) / 7; // Bit code 101
+ gAlphas[6] = ( 2 * gAlphas[0] + 5 * gAlphas[1]) / 7; // Bit code 110
+ gAlphas[7] = ( gAlphas[0] + 6 * gAlphas[1]) / 7; // Bit code 111
+ }
+ else
+ {
+ // 6-alpha block: derive the other alphas.
+ // 000 = alpha_0, 001 = alpha_1, others are interpolated
+
+ gAlphas[2] = (4 * gAlphas[0] + gAlphas[1]) / 5; // Bit code 010
+ gAlphas[3] = (3 * gAlphas[0] + 2 * gAlphas[1]) / 5; // Bit code 011
+ gAlphas[4] = (2 * gAlphas[0] + 3 * gAlphas[1]) / 5; // Bit code 100
+ gAlphas[5] = ( gAlphas[0] + 4 * gAlphas[1]) / 5; // Bit code 101
+ gAlphas[6] = 0; // Bit code 110
+ gAlphas[7] = 255; // Bit code 111
+ }
+
+ // Decode 3-bit fields into array of 16 BYTES with same value
+
+ // first two rows of 4 pixels each:
+ // pRows = (Alpha3BitRows*) & ( pAlphaBlock->stuff[0] );
+ const DWORD mask = 0x00000007; // bits = 00 00 01 11
+
+ DWORD bits = *( (DWORD*) & ( pAlphaBlock->stuff[0] ));
+
+ gBits[0][0] = (BYTE)( bits & mask );
+ bits >>= 3;
+ gBits[0][1] = (BYTE)( bits & mask );
+ bits >>= 3;
+ gBits[0][2] = (BYTE)( bits & mask );
+ bits >>= 3;
+ gBits[0][3] = (BYTE)( bits & mask );
+ bits >>= 3;
+ gBits[1][0] = (BYTE)( bits & mask );
+ bits >>= 3;
+ gBits[1][1] = (BYTE)( bits & mask );
+ bits >>= 3;
+ gBits[1][2] = (BYTE)( bits & mask );
+ bits >>= 3;
+ gBits[1][3] = (BYTE)( bits & mask );
+
+ // now for last two rows:
+ bits = *( (DWORD*) & ( pAlphaBlock->stuff[3] )); // last 3 bytes
+
+ gBits[2][0] = (BYTE)( bits & mask );
+ bits >>= 3;
+ gBits[2][1] = (BYTE)( bits & mask );
+ bits >>= 3;
+ gBits[2][2] = (BYTE)( bits & mask );
+ bits >>= 3;
+ gBits[2][3] = (BYTE)( bits & mask );
+ bits >>= 3;
+ gBits[3][0] = (BYTE)( bits & mask );
+ bits >>= 3;
+ gBits[3][1] = (BYTE)( bits & mask );
+ bits >>= 3;
+ gBits[3][2] = (BYTE)( bits & mask );
+ bits >>= 3;
+ gBits[3][3] = (BYTE)( bits & mask );
+
+ // decode the codes into alpha values
+ int row, pix;
+ for ( row = 0; row < 4; row++ )
+ {
+ for ( pix=0; pix < 4; pix++ )
+ {
+ gACol[row][pix].a = (BYTE) gAlphas[ gBits[row][pix] ];
+
+ Assert( gACol[row][pix].r == 0 );
+ Assert( gACol[row][pix].g == 0 );
+ Assert( gACol[row][pix].b == 0 );
+ }
+ }
+
+ // Write out alpha values to the image bits
+ for ( row=0; row < 4; row++, pImPos += width-4 )
+ {
+ for ( pix = 0; pix < 4; pix++ )
+ {
+ // zero the alpha bits of image pixel
+ switch ( nChannelSelect )
+ {
+ case 0:
+ pImPos->r = ( *(( BGRA8888_t *) &(gACol[row][pix])) ).a;
+ pImPos->g = 0; // Danger...stepping on the other color channels
+ pImPos->b = 0;
+ pImPos->a = 0;
+ break;
+ case 1:
+ pImPos->g = ( *(( BGRA8888_t *) &(gACol[row][pix])) ).a;
+ break;
+ case 2:
+ pImPos->b = ( *(( BGRA8888_t *) &(gACol[row][pix])) ).a;
+ break;
+ default:
+ case 3:
+ pImPos->a = ( *(( BGRA8888_t *) &(gACol[row][pix])) ).a;
+ break;
+ }
+
+ pImPos++;
+ }
+ }
+}
+
+template <class CDestPixel>
+static void ConvertFromDXT1( const uint8 *src, CDestPixel *dst, int width, int height )
+{
+ Assert( sizeof( BGRA8888_t ) == 4 );
+ Assert( sizeof( RGBA8888_t ) == 4 );
+ Assert( sizeof( RGB888_t ) == 3 );
+ Assert( sizeof( BGR888_t ) == 3 );
+ Assert( sizeof( BGR565_t ) == 2 );
+ Assert( sizeof( BGRA5551_t ) == 2 );
+ Assert( sizeof( BGRA4444_t ) == 2 );
+
+ int realWidth = 0;
+ int realHeight = 0;
+ CDestPixel *realDst = NULL;
+
+ // Deal with the case where we have a dimension smaller than 4.
+ if ( width < 4 || height < 4 )
+ {
+ realWidth = width;
+ realHeight = height;
+ // round up to the nearest four
+ width = ( width + 3 ) & ~3;
+ height = ( height + 3 ) & ~3;
+ realDst = dst;
+ dst = ( CDestPixel * )_alloca( width * height * sizeof( CDestPixel ) );
+ Assert( dst );
+ }
+ Assert( !( width % 4 ) );
+ Assert( !( height % 4 ) );
+
+ int xblocks, yblocks;
+ xblocks = width >> 2;
+ yblocks = height >> 2;
+ CDestPixel *pDstScan = dst;
+ DWORD *pSrcScan = ( DWORD * )src;
+
+ DXTColBlock *pBlock;
+ BGRA8888_t col_0, col_1, col_2, col_3;
+ WORD wrdDummy;
+
+ int i, j;
+ for ( j = 0; j < yblocks; j++ )
+ {
+ // 8 bytes per block
+ pBlock = ( DXTColBlock * )( ( uint8 * )pSrcScan + j * xblocks * 8 );
+ for ( i=0; i < xblocks; i++, pBlock++ )
+ {
+ GetColorBlockColorsBGRA8888( pBlock, &col_0, &col_1, &col_2, &col_3, wrdDummy );
+
+ // now decode the color block into the bitmap bits
+ // inline func:
+ pDstScan = dst + i*4 + j*4*width;
+ DecodeColorBlock<CDestPixel>( pDstScan, pBlock, width, &col_0, &col_1, &col_2, &col_3 );
+ }
+ }
+
+ // Deal with the case where we have a dimension smaller than 4.
+ if ( realDst )
+ {
+ int x, y;
+ for ( y = 0; y < realHeight; y++ )
+ {
+ for ( x = 0; x < realWidth; x++ )
+ {
+ realDst[x+(y*realWidth)] = dst[x+(y*width)];
+ }
+ }
+ }
+}
+
+template <class CDestPixel>
+static void ConvertFromDXT5( const uint8 *src, CDestPixel *dst, int width, int height )
+{
+ int realWidth = 0;
+ int realHeight = 0;
+ CDestPixel *realDst = NULL;
+
+ // Deal with the case where we have a dimension smaller than 4.
+ if ( width < 4 || height < 4 )
+ {
+ realWidth = width;
+ realHeight = height;
+ // round up to the nearest four
+ width = ( width + 3 ) & ~3;
+ height = ( height + 3 ) & ~3;
+ realDst = dst;
+ dst = ( CDestPixel * )_alloca( width * height * sizeof( CDestPixel ) );
+ Assert( dst );
+ }
+ Assert( !( width % 4 ) );
+ Assert( !( height % 4 ) );
+
+ int xblocks, yblocks;
+ xblocks = width >> 2;
+ yblocks = height >> 2;
+
+ CDestPixel *pDstScan = dst;
+ DWORD *pSrcScan = ( DWORD * )src;
+
+ DXTColBlock *pBlock;
+ DXTAlphaBlock3BitLinear *pAlphaBlock;
+
+ BGRA8888_t col_0, col_1, col_2, col_3;
+ WORD wrd;
+
+ int i,j;
+ for ( j=0; j < yblocks; j++ )
+ {
+ // 8 bytes per block
+ // 1 block for alpha, 1 block for color
+ pBlock = (DXTColBlock*) ( (uint8 *)pSrcScan + j * xblocks * 16 );
+
+ for ( i=0; i < xblocks; i++, pBlock ++ )
+ {
+ // inline
+ // Get alpha block
+ pAlphaBlock = (DXTAlphaBlock3BitLinear*) pBlock;
+
+ // inline func:
+ // Get color block & colors
+ pBlock++;
+
+ GetColorBlockColorsBGRA8888( pBlock, &col_0, &col_1, &col_2, &col_3, wrd );
+
+ pDstScan = dst + i*4 + j*4*width;
+
+ // Decode the color block into the bitmap bits
+ // inline func:
+ DecodeColorBlock<CDestPixel>( pDstScan, pBlock, width, &col_0, &col_1, &col_2, &col_3 );
+
+ // Overwrite the previous alpha bits with the alpha block
+ // info
+ DecodeAlpha3BitLinear( pDstScan, pAlphaBlock, width );
+ }
+ }
+
+ // Deal with the case where we have a dimension smaller than 4.
+ if ( realDst )
+ {
+ int x, y;
+ for( y = 0; y < realHeight; y++ )
+ {
+ for( x = 0; x < realWidth; x++ )
+ {
+ realDst[x+(y*realWidth)] = dst[x+(y*width)];
+ }
+ }
+ }
+}
+
+template <class CDestPixel>
+static void ConvertFromDXT5IgnoreAlpha( const uint8 *src, CDestPixel *dst, int width, int height )
+{
+ int realWidth = 0;
+ int realHeight = 0;
+ CDestPixel *realDst = NULL;
+
+ // Deal with the case where we have a dimension smaller than 4.
+ if ( width < 4 || height < 4 )
+ {
+ realWidth = width;
+ realHeight = height;
+ // round up to the nearest four
+ width = ( width + 3 ) & ~3;
+ height = ( height + 3 ) & ~3;
+ realDst = dst;
+ dst = ( CDestPixel * )_alloca( width * height * sizeof( CDestPixel ) );
+ Assert( dst );
+ }
+ Assert( !( width % 4 ) );
+ Assert( !( height % 4 ) );
+
+ int xblocks, yblocks;
+ xblocks = width >> 2;
+ yblocks = height >> 2;
+
+ CDestPixel *pDstScan = dst;
+ DWORD *pSrcScan = ( DWORD * )src;
+
+ DXTColBlock *pBlock;
+
+ BGRA8888_t col_0, col_1, col_2, col_3;
+ WORD wrd;
+
+ int i,j;
+ for ( j=0; j < yblocks; j++ )
+ {
+ // 8 bytes per block
+ // 1 block for alpha, 1 block for color
+ pBlock = (DXTColBlock*) ( (uint8 *)pSrcScan + j * xblocks * 16 );
+
+ for( i=0; i < xblocks; i++, pBlock ++ )
+ {
+ // inline func:
+ // Get color block & colors
+ pBlock++;
+
+ GetColorBlockColorsBGRA8888( pBlock, &col_0, &col_1, &col_2, &col_3, wrd );
+
+ pDstScan = dst + i*4 + j*4*width;
+
+ // Decode the color block into the bitmap bits
+ // inline func:
+ DecodeColorBlock<CDestPixel>( pDstScan, pBlock, width, &col_0, &col_1, &col_2, &col_3 );
+ }
+ }
+
+ // Deal with the case where we have a dimension smaller than 4.
+ if( realDst )
+ {
+ int x, y;
+ for( y = 0; y < realHeight; y++ )
+ {
+ for( x = 0; x < realWidth; x++ )
+ {
+ realDst[x+(y*realWidth)] = dst[x+(y*width)];
+ }
+ }
+ }
+}
+
+
+template <class CDestPixel>
+static void ConvertFromATIxN( const uint8 *src, CDestPixel *dst, int width, int height, bool bATI2N )
+{
+ int realWidth = 0;
+ int realHeight = 0;
+ CDestPixel *realDst = NULL;
+
+ // Deal with the case where we have a dimension smaller than 4.
+ if ( width < 4 || height < 4 )
+ {
+ realWidth = width;
+ realHeight = height;
+ // round up to the nearest four
+ width = ( width + 3 ) & ~3;
+ height = ( height + 3 ) & ~3;
+ realDst = dst;
+ dst = ( CDestPixel * )_alloca( width * height * sizeof( CDestPixel ) );
+ Assert( dst );
+ }
+ Assert( !( width % 4 ) );
+ Assert( !( height % 4 ) );
+
+ int xblocks, yblocks;
+ xblocks = width >> 2;
+ yblocks = height >> 2;
+
+ CDestPixel *pDstScan = dst;
+ DWORD *pSrcScan = ( DWORD * )src;
+
+ DXTAlphaBlock3BitLinear *pBlock;
+
+ int nBytesPerBlock = bATI2N ? 16 : 8;
+
+ int i,j;
+ for ( j=0; j < yblocks; j++ )
+ {
+ // 8 bytes per block
+ // 1 block for x, 1 block for y
+ pBlock = (DXTAlphaBlock3BitLinear*) ( (uint8 *)pSrcScan + j * xblocks * nBytesPerBlock );
+
+ for ( i=0; i < xblocks; i++, pBlock++ )
+ {
+ pDstScan = dst + i*4 + j*4*width;
+ DecodeAlpha3BitLinear( pDstScan, pBlock, width, 0 );
+
+ if ( bATI2N )
+ {
+ pBlock++;
+ DecodeAlpha3BitLinear( pDstScan, pBlock, width, 1 );
+ }
+ }
+ }
+
+ // Deal with the case where we have a dimension smaller than 4.
+ if ( realDst )
+ {
+ int x, y;
+ for( y = 0; y < realHeight; y++ )
+ {
+ for( x = 0; x < realWidth; x++ )
+ {
+ realDst[x+(y*realWidth)] = dst[x+(y*width)];
+ }
+ }
+ }
+}
+
+static DWORD GetDXTCEncodeType( ImageFormat imageFormat )
+{
+ switch ( imageFormat )
+ {
+ case IMAGE_FORMAT_DXT1:
+ return S3TC_ENCODE_RGB_FULL;
+ case IMAGE_FORMAT_DXT1_ONEBITALPHA:
+ return S3TC_ENCODE_RGB_FULL | S3TC_ENCODE_RGB_ALPHA_COMPARE;
+ case IMAGE_FORMAT_DXT3:
+ return S3TC_ENCODE_RGB_FULL | S3TC_ENCODE_ALPHA_EXPLICIT;
+ case IMAGE_FORMAT_DXT5:
+ return S3TC_ENCODE_RGB_FULL | S3TC_ENCODE_ALPHA_INTERPOLATED;
+ default:
+ return 0;
+ }
+}
+
+// Convert RGBA input to ATI1N or ATI2N format
+bool ConvertToATIxN( const uint8 *src, ImageFormat srcImageFormat,
+ uint8 *dst, ImageFormat dstImageFormat,
+ int width, int height, int srcStride, int dstStride )
+{
+#if !defined( _X360 ) && !defined( POSIX )
+
+ // from rgb(a) to ATIxN
+ if( srcStride != 0 || dstStride != 0 )
+ return false;
+
+ // If we're not the right format for the ATI compressor, we bail
+ if ( srcImageFormat != IMAGE_FORMAT_ARGB8888 )
+ return false;
+
+ // Define source image parameters and copy the bits into buffer
+ ATI_TC_Texture srcTexture;
+ srcTexture.dwSize = sizeof( srcTexture );
+ srcTexture.dwWidth = width;
+ srcTexture.dwHeight = height;
+ srcTexture.dwPitch = srcStride;
+ srcTexture.format = ATI_TC_FORMAT_ARGB_8888;
+ srcTexture.dwDataSize = ATI_TC_CalculateBufferSize( &srcTexture );
+ srcTexture.pData = (ATI_TC_BYTE*) malloc( srcTexture.dwDataSize );
+ memcpy( srcTexture.pData, src, srcTexture.dwDataSize );
+
+ ATI_TC_Texture destTexture;
+ destTexture.dwSize = sizeof( destTexture );
+ destTexture.dwWidth = width;
+ destTexture.dwHeight = height;
+ destTexture.dwPitch = 0;
+ destTexture.format = dstImageFormat == IMAGE_FORMAT_ATI2N ? ATI_TC_FORMAT_ATI2N : ATI_TC_FORMAT_ATI1N; // Assume it can only be one of these two...
+ destTexture.dwDataSize = ATI_TC_CalculateBufferSize( &destTexture );
+ destTexture.pData = (ATI_TC_BYTE*) dst;
+
+ ATI_TC_ERROR errATI = ATI_TC_ConvertTexture( &srcTexture, &destTexture, NULL, NULL, NULL, NULL ); // Convert it!
+
+ free( srcTexture.pData ); // Free temporary buffers
+
+ if ( errATI != ATI_TC_OK )
+ return false;
+
+ return true;
+#else
+ Assert( 0 );
+ return false;
+#endif
+}
+
+
+bool ConvertToDXTLegacy( const uint8 *src, ImageFormat srcImageFormat,
+ uint8 *dst, ImageFormat dstImageFormat,
+ int width, int height, int srcStride, int dstStride )
+{
+#if !defined( _X360 ) && !defined( POSIX )
+ // from rgb(a) to dxtN
+ if( srcStride != 0 || dstStride != 0 )
+ return false;
+
+ DDSURFACEDESC descIn;
+ DDSURFACEDESC descOut;
+ memset( &descIn, 0, sizeof(descIn) );
+ memset( &descOut, 0, sizeof(descOut) );
+ float weight[3] = {0.3086f, 0.6094f, 0.0820f};
+ DWORD dwEncodeType = GetDXTCEncodeType( dstImageFormat );
+
+ // Setup descIn
+ descIn.dwSize = sizeof(descIn);
+ descIn.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_LPSURFACE |
+ /*DDSD_PITCH | */ DDSD_PIXELFORMAT;
+ descIn.dwWidth = width;
+ descIn.dwHeight = height;
+ descIn.lPitch = width * ImageLoader::SizeInBytes( srcImageFormat );
+ descIn.lpSurface = ( LPVOID *) src;
+ descIn.ddpfPixelFormat.dwSize = sizeof( DDPIXELFORMAT );
+ switch ( srcImageFormat )
+ {
+ case IMAGE_FORMAT_RGBA8888:
+ descIn.ddpfPixelFormat.dwFlags = DDPF_RGB | DDPF_ALPHAPIXELS;
+ descIn.ddpfPixelFormat.dwRGBBitCount = 32;
+ descIn.ddpfPixelFormat.dwRBitMask = 0x0000ff;
+ descIn.ddpfPixelFormat.dwGBitMask = 0x00ff00;
+ descIn.ddpfPixelFormat.dwBBitMask = 0xff0000;
+ // must set this anyway or S3TC will lock up!!!
+ descIn.ddpfPixelFormat.dwRGBAlphaBitMask = 0xff000000;
+ break;
+ case IMAGE_FORMAT_BGRA8888:
+ descIn.ddpfPixelFormat.dwFlags = DDPF_RGB | DDPF_ALPHAPIXELS;
+ descIn.ddpfPixelFormat.dwRGBBitCount = 32;
+ descIn.ddpfPixelFormat.dwRBitMask = 0xFF0000;
+ descIn.ddpfPixelFormat.dwGBitMask = 0x00ff00;
+ descIn.ddpfPixelFormat.dwBBitMask = 0x0000FF;
+ // must set this anyway or S3TC will lock up!!!
+ descIn.ddpfPixelFormat.dwRGBAlphaBitMask = 0xff000000;
+ break;
+ case IMAGE_FORMAT_BGRX8888:
+ descIn.ddpfPixelFormat.dwFlags = DDPF_RGB;
+ descIn.ddpfPixelFormat.dwRGBBitCount = 32;
+ descIn.ddpfPixelFormat.dwRBitMask = 0xFF0000;
+ descIn.ddpfPixelFormat.dwGBitMask = 0x00ff00;
+ descIn.ddpfPixelFormat.dwBBitMask = 0x0000FF;
+ // must set this anyway or S3TC will lock up!!!
+ descIn.ddpfPixelFormat.dwRGBAlphaBitMask = 0xff000000;
+ break;
+ case IMAGE_FORMAT_RGB888:
+ descIn.ddpfPixelFormat.dwFlags = DDPF_RGB;
+ descIn.ddpfPixelFormat.dwRGBBitCount = 24;
+ descIn.ddpfPixelFormat.dwRBitMask = 0x0000ff;
+ descIn.ddpfPixelFormat.dwGBitMask = 0x00ff00;
+ descIn.ddpfPixelFormat.dwBBitMask = 0xff0000;
+ descIn.ddpfPixelFormat.dwRGBAlphaBitMask = 0xff000000;
+ break;
+ default:
+ return false;
+ }
+
+ // Setup descOut
+ descOut.dwSize = sizeof( descOut );
+
+ // Encode the texture
+ S3TCencode( &descIn, NULL, &descOut, dst, dwEncodeType, weight );
+ return true;
+#else
+ Assert( 0 );
+ return false;
+#endif
+}
+
+template < typename SrcPixel_t >
+void CompressSTB( uint8 *pDstBytes, ImageFormat dstFmt, const uint8 *pSrcBytes, int nWidth, int nHeight )
+{
+ const bool cbWriteAlpha = ( dstFmt == IMAGE_FORMAT_DXT5 );
+ const uint32 cDstStride = ( dstFmt == IMAGE_FORMAT_DXT1 ) ? 8 : 16;
+
+ const uint32 cPixX = (uint32) nWidth;
+ const uint32 cPixY = (uint32) nHeight;
+ const uint32 cSrcPitch = cPixX * sizeof( SrcPixel_t );
+ const uint32 cLastX = cPixX - 1;
+ const uint32 cLastY = cPixY - 1;
+
+ // STB always takes blocks as 4x4 of RGBA8888_t
+ RGBA8888_t srcBlock[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ SrcPixel_t* pSrcs[4] = { 0, 0, 0, 0 };
+
+ for ( uint32 y = 0; y < cPixY; y += 4 )
+ {
+ // This handles clamping for cPixY % 4 != 0
+ pSrcs[ 0 ] = ( SrcPixel_t* ) ( pSrcBytes + cSrcPitch * Min( y + 0, cLastY ) );
+ pSrcs[ 1 ] = ( SrcPixel_t* ) ( pSrcBytes + cSrcPitch * Min( y + 1, cLastY ) );
+ pSrcs[ 2 ] = ( SrcPixel_t* ) ( pSrcBytes + cSrcPitch * Min( y + 2, cLastY ) );
+ pSrcs[ 3 ] = ( SrcPixel_t* ) ( pSrcBytes + cSrcPitch * Min( y + 3, cLastY ) );
+
+ for ( uint x = 0; x < cPixX; x += 4 )
+ {
+ for ( uint i = 0; i < 4; ++i )
+ {
+ uint32 offsetX = Min( x + i, cLastX );
+ srcBlock[ 0 + i ] = pSrcs[ 0 ][ offsetX ];
+ srcBlock[ 4 + i ] = pSrcs[ 1 ][ offsetX ];
+ srcBlock[ 8 + i ] = pSrcs[ 2 ][ offsetX ];
+ srcBlock[ 12 + i ] = pSrcs[ 3 ][ offsetX ];
+ }
+
+ stb_compress_dxt_block( pDstBytes, ( const uint8* ) srcBlock, cbWriteAlpha, STB_DXT_NORMAL );
+ pDstBytes += cDstStride;
+ }
+ }
+}
+
+inline ImageFormat GetTrueImageFormat( ImageFormat fmt )
+{
+ switch ( fmt )
+ {
+ case IMAGE_FORMAT_DXT1_RUNTIME:
+ return IMAGE_FORMAT_DXT1;
+ case IMAGE_FORMAT_DXT5_RUNTIME:
+ return IMAGE_FORMAT_DXT5;
+ default: /* expected */
+ break;
+ }
+
+ return fmt;
+}
+
+bool ConvertToDXTRuntime( const uint8 *src, ImageFormat srcImageFormat,
+ uint8 *dst, ImageFormat dstImageFormat,
+ int width, int height, int srcStride, int dstStride )
+{
+ if ( srcStride != 0 || dstStride != 0 )
+ return false;
+
+ dstImageFormat = GetTrueImageFormat( dstImageFormat );
+
+ switch ( srcImageFormat )
+ {
+ case IMAGE_FORMAT_RGBA8888: CompressSTB<RGBA8888_t>( dst, dstImageFormat, src, width, height ); return true;
+ case IMAGE_FORMAT_RGB888: CompressSTB<RGB888_t> ( dst, dstImageFormat, src, width, height ); return true;
+ case IMAGE_FORMAT_BGRA8888: CompressSTB<BGRA8888_t>( dst, dstImageFormat, src, width, height ); return true;
+ case IMAGE_FORMAT_BGRX8888: CompressSTB<BGRX8888_t>( dst, dstImageFormat, src, width, height ); return true;
+ default:
+ Assert( !"Unexpected format here, wtf." );
+ break;
+ };
+
+ return false;
+}
+
+bool ConvertToDXT( const uint8 *src, ImageFormat srcImageFormat,
+ uint8 *dst, ImageFormat dstImageFormat,
+ int width, int height, int srcStride, int dstStride )
+{
+ // The STB compressor (the new compressor) is faster and higher quality in most cases, and has less error overall
+ // than the S3TC compressor. So use it by default, unless we're working with a format that STB doesn't support.
+ bool bUseNewCompressor = dstImageFormat != IMAGE_FORMAT_DXT1_ONEBITALPHA
+ && dstImageFormat != IMAGE_FORMAT_DXT3;
+
+// bool bUseNewCompressor = dstImageFormat == IMAGE_FORMAT_DXT1_RUNTIME
+// || dstImageFormat == IMAGE_FORMAT_DXT5_RUNTIME;
+
+ if ( bUseNewCompressor )
+ return ConvertToDXTRuntime( src, srcImageFormat, dst, dstImageFormat, width, height, srcStride, dstStride );
+
+ return ConvertToDXTLegacy( src, srcImageFormat, dst, dstImageFormat, width, height, srcStride, dstStride );
+}
+
+// HDRFIXME: This assumes that the 16-bit integer values are 4.12 fixed-point.
+void ConvertImageFormat_RGBA16161616_To_RGB323232F( unsigned short *pSrcImage, float *pDstImage, int width, int height )
+{
+ int srcSize = width * height * 4;
+ unsigned short *pSrcEnd = pSrcImage + srcSize;
+ unsigned short *pSrcScan = pSrcImage;
+ float *pDstScan = pDstImage;
+ for ( ; pSrcScan < pSrcEnd; pSrcScan += 4, pDstScan += 3 )
+ {
+ pDstScan[0] = ( ( float )pSrcScan[0] ) * ( 1.0f / ( ( float )( 1 << 12 ) ) );
+ pDstScan[1] = ( ( float )pSrcScan[1] ) * ( 1.0f / ( ( float )( 1 << 12 ) ) );
+ pDstScan[2] = ( ( float )pSrcScan[2] ) * ( 1.0f / ( ( float )( 1 << 12 ) ) );
+ }
+}
+
+// HDRFIXME: This assumes that the 16-bit integer values are 4.12 fixed-point.
+void ConvertImageFormat_RGB323232F_To_RGBA16161616( float *pSrcImage, unsigned short *pDstImage, int width, int height )
+{
+ int srcSize = width * height * 3;
+ float *pSrcEnd = pSrcImage + srcSize;
+ float *pSrcScan = pSrcImage;
+ unsigned short *pDstScan = pDstImage;
+ for ( ; pSrcScan < pSrcEnd; pSrcScan += 3, pDstScan += 4 )
+ {
+
+ pDstScan[0] = ( unsigned short )min( 65535.0f, ( pSrcScan[0] * ( ( ( float )( 1 << 12 ) ) ) ) );
+ pDstScan[1] = ( unsigned short )min( 65535.0f, ( pSrcScan[1] * ( ( ( float )( 1 << 12 ) ) ) ) );
+ pDstScan[2] = ( unsigned short )min( 65535.0f, ( pSrcScan[2] * ( ( ( float )( 1 << 12 ) ) ) ) );
+ pDstScan[3] = 65535;
+ }
+}
+
+void ConvertImageFormat_RGBA16161616F_To_RGB323232F( float16 *pSrcImage, float *pDstImage, int width, int height )
+{
+ int srcSize = width * height * 4;
+ float16 *pSrcEnd = pSrcImage + srcSize;
+ float16 *pSrcScan = pSrcImage;
+ float *pDstScan = pDstImage;
+ for( ; pSrcScan < pSrcEnd; pSrcScan += 4, pDstScan += 3 )
+ {
+ pDstScan[0] = pSrcScan[0].GetFloat();
+ pDstScan[1] = pSrcScan[1].GetFloat();
+ pDstScan[2] = pSrcScan[2].GetFloat();
+ }
+}
+
+void ConvertImageFormat_RGBA16161616F_To_RGBA323232F( float16 *pSrcImage, float *pDstImage, int width, int height , size_t src_stride)
+{
+ size_t s_stride=src_stride/2;
+ for(int y=0; y<height; y++)
+ {
+ float16 const *pSrcScan=pSrcImage;
+ float *pDstScan = pDstImage;
+ for(int x=0; x<width; x++)
+ {
+ pDstScan[0] = pSrcScan[0].GetFloat();
+ pDstScan[1] = pSrcScan[1].GetFloat();
+ pDstScan[2] = pSrcScan[2].GetFloat();
+ pDstScan[3] = pSrcScan[3].GetFloat();
+ pDstScan+=4;
+ pSrcScan+=4;
+ }
+ pSrcImage += s_stride;
+ pDstImage+=4*width;
+ }
+}
+
+
+void ConvertImageFormat_RGB323232F_To_RGBA16161616F( float *pSrcImage, float16 *pDstImage, int width, int height )
+{
+ int srcSize = width * height * 3;
+ float *pSrcEnd = pSrcImage + srcSize;
+ float *pSrcScan = pSrcImage;
+ float16 *pDstScan = pDstImage;
+ for( ; pSrcScan < pSrcEnd; pSrcScan += 3, pDstScan += 4 )
+ {
+ pDstScan[0].SetFloat( pSrcScan[0] );
+ pDstScan[1].SetFloat( pSrcScan[1] );
+ pDstScan[2].SetFloat( pSrcScan[2] );
+ }
+}
+
+void ConvertImageFormat_RGB323232F_To_RGBA8888( float *pSrcImage, uint8 *dst, int width, int height )
+{
+ FloatBitMap_t flbm;
+ flbm.AllocateRGB( width, height );
+
+ // Set the pixels
+ for ( int y = 0; y < height; ++ y )
+ {
+ for ( int x = 0; x < width; ++ x )
+ {
+ float *pf = &pSrcImage[ 3 * ( x + width * y ) ];
+ PixRGBAF fpix;
+ fpix.Red = pf[0];
+ fpix.Green = pf[1];
+ fpix.Blue = pf[2];
+ fpix.Alpha = 0.f;
+
+ flbm.WritePixelRGBAF( x, y, fpix );
+ }
+ }
+ // memcpy( flbm.RGBAData, pSrcImage, width * height * 4 );
+
+ flbm.CompressTo8Bits( 8.0 );
+
+ // Now, get the pixels
+ for ( int y = 0; y < height; ++ y )
+ {
+ for ( int x = 0; x < width; ++ x )
+ {
+ PixRGBAF fpix = flbm.PixelRGBAF( x, y );
+ PixRGBA8 pix8 = PixRGBAF_to_8( fpix );
+
+ uint8 *pch = &dst[ 4 * ( x + width * y ) ];
+
+ pch[0] = pix8.Red;
+ pch[1] = pix8.Green;
+ pch[2] = pix8.Blue;
+ pch[3] = pix8.Alpha;
+ }
+ }
+}
+
+void ConvertImageFormat_RGB323232F_To_BGRA8888( float *pSrcImage, uint8 *dst, int width, int height )
+{
+ FloatBitMap_t flbm;
+ flbm.AllocateRGB( width, height );
+
+ // Set the pixels
+ for ( int y = 0; y < height; ++ y )
+ {
+ for ( int x = 0; x < width; ++ x )
+ {
+ float *pf = &pSrcImage[ 3 * ( x + width * y ) ];
+ PixRGBAF fpix;
+ fpix.Red = pf[0];
+ fpix.Green = pf[1];
+ fpix.Blue = pf[2];
+ fpix.Alpha = 0.f;
+
+ flbm.WritePixelRGBAF( x, y, fpix );
+ }
+ }
+ // memcpy( flbm.RGBAData, pSrcImage, width * height * 4 );
+
+ flbm.CompressTo8Bits( 8.0 );
+
+ // Now, get the pixels
+ for ( int y = 0; y < height; ++ y )
+ {
+ for ( int x = 0; x < width; ++ x )
+ {
+ PixRGBAF fpix = flbm.PixelRGBAF( x, y );
+ PixRGBA8 pix8 = PixRGBAF_to_8( fpix );
+
+ uint8 *pch = &dst[ 4 * ( x + width * y ) ];
+
+ pch[0] = pix8.Blue;
+ pch[1] = pix8.Green;
+ pch[2] = pix8.Red;
+ pch[3] = pix8.Alpha;
+ }
+ }
+}
+
+// HDRFIXME: This assumes that the 16-bit integer values are 4.12 fixed-point.
+void ConvertImageFormat_RGBA16161616_To_RGBA16161616F( unsigned short *pSrcImage, float *pDstImage, int width, int height )
+{
+ int srcSize = width * height * 4;
+ unsigned short *pSrcEnd = pSrcImage + srcSize;
+ unsigned short *pSrcScan = pSrcImage;
+ float16 *pDstScan = ( float16 * )pDstImage;
+ for( ; pSrcScan < pSrcEnd; pSrcScan += 4, pDstScan += 4 )
+ {
+ pDstScan[0].SetFloat( pSrcScan[0] * ( 1.0f / ( float )( 1 << 16 ) ) );
+ pDstScan[1].SetFloat( pSrcScan[1] * ( 1.0f / ( float )( 1 << 16 ) ) );
+ pDstScan[2].SetFloat( pSrcScan[2] * ( 1.0f / ( float )( 1 << 16 ) ) );
+ pDstScan[3].SetFloat( pSrcScan[3] * ( 1.0f / ( float )( 1 << 16 ) ) );
+ }
+}
+
+void ConvertImageFormat_RGBA16161616F_To_RGBA16161616( float16 *pSrcImage, unsigned short *pDstImage, int width, int height )
+{
+ int srcSize = width * height * 4;
+ float16 *pSrcEnd = pSrcImage + srcSize;
+ float16 *pSrcScan = pSrcImage;
+ unsigned short *pDstScan = pDstImage;
+ for( ; pSrcScan < pSrcEnd; pSrcScan += 4, pDstScan += 4 )
+ {
+ int i;
+ for( i = 0; i < 4; i++ )
+ {
+ float val;
+ val = pSrcScan[i].GetFloat();
+ val *= ( float )( 1 << 12 );
+ val = max( val, 0.f );
+ val = min( val, 65535.0f );
+ pDstScan[i] = ( unsigned short )val;
+ }
+ }
+}
+
+bool ConvertImageFormat( const uint8 *src, ImageFormat srcImageFormat,
+ uint8 *dst, ImageFormat dstImageFormat,
+ int width, int height, int srcStride, int dstStride )
+{
+ // HDRFIXME: WE NEED A BIGGER INTERMEDIATE FORMAT!!!!!
+ if ( srcImageFormat == IMAGE_FORMAT_RGBA16161616 )
+ {
+ if ( dstImageFormat == IMAGE_FORMAT_RGB323232F )
+ {
+ Assert( srcStride == 0 && dstStride == 0 );
+ ConvertImageFormat_RGBA16161616_To_RGB323232F( ( unsigned short * )src, ( float * )dst, width, height );
+ return true;
+ }
+ if ( dstImageFormat == IMAGE_FORMAT_RGBA16161616F )
+ {
+ Assert( srcStride == 0 && dstStride == 0 );
+ ConvertImageFormat_RGBA16161616_To_RGBA16161616F( ( unsigned short * )src, ( float * )dst, width, height );
+ return true;
+ }
+ }
+ else if ( srcImageFormat == IMAGE_FORMAT_RGBA16161616F )
+ {
+ if ( dstImageFormat == IMAGE_FORMAT_RGB323232F )
+ {
+ Assert( srcStride == 0 && dstStride == 0 );
+ ConvertImageFormat_RGBA16161616F_To_RGB323232F( ( float16 * )src, ( float * )dst, width, height );
+ return true;
+ }
+ if ( dstImageFormat == IMAGE_FORMAT_RGBA32323232F )
+ {
+ Assert( dstStride == 0 );
+ ConvertImageFormat_RGBA16161616F_To_RGBA323232F( ( float16 * )src, ( float * )dst, width, height, srcStride );
+ return true;
+ }
+ if ( dstImageFormat == IMAGE_FORMAT_RGBA16161616 )
+ {
+ Assert( srcStride == 0 && dstStride == 0 );
+ ConvertImageFormat_RGBA16161616F_To_RGBA16161616( ( float16 * )src, ( unsigned short * )dst, width, height );
+ return true;
+ }
+ }
+ else if ( srcImageFormat == IMAGE_FORMAT_RGB323232F )
+ {
+ if ( dstImageFormat == IMAGE_FORMAT_RGBA16161616 )
+ {
+ Assert( srcStride == 0 && dstStride == 0 );
+ ConvertImageFormat_RGB323232F_To_RGBA16161616( ( float * )src, ( unsigned short * )dst, width, height );
+ return true;
+ }
+ if ( dstImageFormat == IMAGE_FORMAT_RGBA16161616F )
+ {
+ Assert( srcStride == 0 && dstStride == 0 );
+ ConvertImageFormat_RGB323232F_To_RGBA16161616F( ( float * )src, ( float16 * )dst, width, height );
+ return true;
+ }
+ if ( dstImageFormat == IMAGE_FORMAT_RGBA8888 )
+ {
+ Assert( srcStride == 0 );
+ ConvertImageFormat_RGB323232F_To_RGBA8888( ( float * )src, dst, width, height );
+ return true;
+ }
+ if ( dstImageFormat == IMAGE_FORMAT_BGRA8888 )
+ {
+ Assert( srcStride == 0 );
+ ConvertImageFormat_RGB323232F_To_BGRA8888( ( float * )src, dst, width, height );
+ return true;
+ }
+ }
+
+ // Fast path for just copying a compressed texture
+ if ( ( ( dstImageFormat == IMAGE_FORMAT_DXT1 || dstImageFormat == IMAGE_FORMAT_DXT1_RUNTIME ||
+ dstImageFormat == IMAGE_FORMAT_DXT3 ||
+ dstImageFormat == IMAGE_FORMAT_DXT5 || dstImageFormat == IMAGE_FORMAT_DXT5_RUNTIME ||
+ dstImageFormat == IMAGE_FORMAT_ATI1N ||
+ dstImageFormat == IMAGE_FORMAT_ATI2N ) && ( srcImageFormat == dstImageFormat ) ) ||
+ ( dstImageFormat == IMAGE_FORMAT_DXT5 && srcImageFormat == IMAGE_FORMAT_DXT5_RUNTIME ) ||
+ ( dstImageFormat == IMAGE_FORMAT_DXT1 && srcImageFormat == IMAGE_FORMAT_DXT1_RUNTIME ) ||
+ ( dstImageFormat == IMAGE_FORMAT_DXT5_RUNTIME && srcImageFormat == IMAGE_FORMAT_DXT5 ) ||
+ ( dstImageFormat == IMAGE_FORMAT_DXT1_RUNTIME && srcImageFormat == IMAGE_FORMAT_DXT1 ) )
+ {
+ // Fast path for compressed textures . . stride doesn't make as much sense.
+// Assert( srcStride == 0 && dstStride == 0 );
+
+ int memRequired;
+ memRequired = GetMemRequired( width, height, 1, srcImageFormat, false );
+ memcpy( dst, src, memRequired );
+ return true;
+ }
+ else if ( ( srcImageFormat == IMAGE_FORMAT_RGBA8888 ||
+ srcImageFormat == IMAGE_FORMAT_RGB888 || // RGBA source
+ srcImageFormat == IMAGE_FORMAT_BGRA8888 || //
+ srcImageFormat == IMAGE_FORMAT_BGRX8888 ) && // and
+ ( dstImageFormat == IMAGE_FORMAT_DXT1 || //
+ dstImageFormat == IMAGE_FORMAT_DXT3 || // DXT compressed dest
+ dstImageFormat == IMAGE_FORMAT_DXT5 ||
+ dstImageFormat == IMAGE_FORMAT_DXT1_RUNTIME ||
+ dstImageFormat == IMAGE_FORMAT_DXT5_RUNTIME ) )
+ {
+ return ConvertToDXT( src, srcImageFormat, dst, dstImageFormat, width, height, srcStride, dstStride );
+ }
+ else if ( ( srcImageFormat == IMAGE_FORMAT_ARGB8888 ) && // RGBA source and
+ ( dstImageFormat == IMAGE_FORMAT_ATI1N || dstImageFormat == IMAGE_FORMAT_ATI2N ) ) // ATI compressed dest
+ {
+ return ConvertToATIxN( src, srcImageFormat, dst, dstImageFormat, width, height, srcStride, dstStride );
+ }
+ else if ( ( dstImageFormat == IMAGE_FORMAT_RGBA8888 ||
+ dstImageFormat == IMAGE_FORMAT_BGRX8888 ||
+ dstImageFormat == IMAGE_FORMAT_BGRA8888 ||
+ dstImageFormat == IMAGE_FORMAT_BGRA4444 ||
+ dstImageFormat == IMAGE_FORMAT_BGRA5551 ||
+ dstImageFormat == IMAGE_FORMAT_BGRX5551 ||
+ dstImageFormat == IMAGE_FORMAT_BGR565 ||
+ dstImageFormat == IMAGE_FORMAT_BGR888 ||
+ dstImageFormat == IMAGE_FORMAT_RGB888 ) &&
+ ( srcImageFormat == IMAGE_FORMAT_DXT1 ||
+ srcImageFormat == IMAGE_FORMAT_DXT3 ||
+ srcImageFormat == IMAGE_FORMAT_DXT5 ||
+ srcImageFormat == IMAGE_FORMAT_ATI1N ||
+ srcImageFormat == IMAGE_FORMAT_ATI2N ) )
+ {
+ // from dxtN to rgb(a)
+ if ( srcStride != 0 || dstStride != 0 )
+ {
+ return false;
+ }
+ if ( srcImageFormat == IMAGE_FORMAT_DXT1 )
+ {
+ if ( dstImageFormat == IMAGE_FORMAT_RGBA8888 )
+ {
+ ConvertFromDXT1( src, ( RGBA8888_t * )dst, width, height );
+ return true;
+ }
+ if ( dstImageFormat == IMAGE_FORMAT_BGRA8888 ||
+ dstImageFormat == IMAGE_FORMAT_BGRX8888 )
+ {
+ ConvertFromDXT1( src, ( BGRA8888_t * )dst, width, height );
+ return true;
+ }
+ if ( dstImageFormat == IMAGE_FORMAT_RGB888 )
+ {
+ ConvertFromDXT1( src, ( RGB888_t * )dst, width, height );
+ return true;
+ }
+ if ( dstImageFormat == IMAGE_FORMAT_BGR888 )
+ {
+ ConvertFromDXT1( src, ( BGR888_t * )dst, width, height );
+ return true;
+ }
+ if ( dstImageFormat == IMAGE_FORMAT_BGR565 )
+ {
+ ConvertFromDXT1( src, ( BGR565_t * )dst, width, height );
+ return true;
+ }
+ if ( dstImageFormat == IMAGE_FORMAT_BGRA5551 ||
+ dstImageFormat == IMAGE_FORMAT_BGRX5551 )
+ {
+ ConvertFromDXT1( src, ( BGRA5551_t * )dst, width, height );
+ return true;
+ }
+ if ( dstImageFormat == IMAGE_FORMAT_BGRA4444 )
+ {
+ ConvertFromDXT1( src, ( BGRA4444_t * )dst, width, height );
+ return true;
+ }
+ }
+ else if ( srcImageFormat == IMAGE_FORMAT_ATI2N )
+ {
+ if ( dstImageFormat == IMAGE_FORMAT_BGRA8888 )
+ {
+ ConvertFromATIxN( src, ( BGRA8888_t * )dst, width, height, true );
+ return true;
+ }
+ }
+ else if ( srcImageFormat == IMAGE_FORMAT_ATI1N )
+ {
+ if ( dstImageFormat == IMAGE_FORMAT_BGRA8888 )
+ {
+ ConvertFromATIxN( src, ( BGRA8888_t * )dst, width, height, false );
+ return true;
+ }
+ }
+ else if ( srcImageFormat == IMAGE_FORMAT_DXT5 )
+ {
+ if ( dstImageFormat == IMAGE_FORMAT_RGBA8888 )
+ {
+ ConvertFromDXT5( src, ( RGBA8888_t * )dst, width, height );
+ return true;
+ }
+ if ( dstImageFormat == IMAGE_FORMAT_BGRA8888 ||
+ dstImageFormat == IMAGE_FORMAT_BGRX8888 )
+ {
+ ConvertFromDXT5( src, ( BGRA8888_t * )dst, width, height );
+ return true;
+ }
+ if ( dstImageFormat == IMAGE_FORMAT_RGB888 )
+ {
+ ConvertFromDXT5IgnoreAlpha( src, ( RGB888_t * )dst, width, height );
+ return true;
+ }
+ if ( dstImageFormat == IMAGE_FORMAT_BGR888 )
+ {
+ ConvertFromDXT5IgnoreAlpha( src, ( BGR888_t * )dst, width, height );
+ return true;
+ }
+ if ( dstImageFormat == IMAGE_FORMAT_BGR565 )
+ {
+ ConvertFromDXT5IgnoreAlpha( src, ( BGR565_t * )dst, width, height );
+ return true;
+ }
+ if ( dstImageFormat == IMAGE_FORMAT_BGRA5551 ||
+ dstImageFormat == IMAGE_FORMAT_BGRX5551 )
+ {
+ ConvertFromDXT5( src, ( BGRA5551_t * )dst, width, height );
+ return true;
+ }
+ if ( dstImageFormat == IMAGE_FORMAT_BGRA4444 )
+ {
+ ConvertFromDXT5( src, ( BGRA4444_t * )dst, width, height );
+ return true;
+ }
+ }
+ return false;
+ }
+ else if ( dstImageFormat == IMAGE_FORMAT_DXT1 ||
+ dstImageFormat == IMAGE_FORMAT_DXT3 ||
+ dstImageFormat == IMAGE_FORMAT_DXT5 ||
+ dstImageFormat == IMAGE_FORMAT_ATI1N ||
+ dstImageFormat == IMAGE_FORMAT_ATI2N ||
+ srcImageFormat == IMAGE_FORMAT_DXT1 ||
+ srcImageFormat == IMAGE_FORMAT_DXT3 ||
+ srcImageFormat == IMAGE_FORMAT_DXT5 ||
+ srcImageFormat == IMAGE_FORMAT_ATI1N ||
+ srcImageFormat == IMAGE_FORMAT_ATI2N )
+ {
+ // DxtN to DxtN
+ Assert( IsPC() );
+ return false;
+ }
+ else
+ {
+ // uncompressed textures
+ int line;
+ int srcPixelSize = SizeInBytes(srcImageFormat);
+ int dstPixelSize = SizeInBytes(dstImageFormat);
+
+ if ( srcStride == 0 )
+ {
+ srcStride = srcPixelSize * width;
+ }
+ if ( dstStride == 0 )
+ {
+ dstStride = dstPixelSize * width;
+ }
+
+ // Fast path...
+ if( ( srcImageFormat == dstImageFormat ) ||
+ ((srcImageFormat == IMAGE_FORMAT_BGRA8888) && (dstImageFormat == IMAGE_FORMAT_BGRX8888)) )
+ {
+ if ( IsX360() && ( srcStride == dstStride ) && ( width*srcPixelSize == srcStride ) )
+ {
+ // fastest path
+ memcpy( dst, src, height*srcStride );
+ return true;
+ }
+
+ for ( line = 0; line < height; ++line )
+ {
+ memcpy( dst, src, width*srcPixelSize );
+ dst += dstStride;
+ src += srcStride;
+ }
+ return true;
+ }
+
+ // format conversion
+ uint8 *lineBufRGBA8888 = (uint8 *)_alloca(width*4);
+
+ UserFormatToRGBA8888Func_t userFormatToRGBA8888Func;
+ RGBA8888ToUserFormatFunc_t RGBA8888ToUserFormatFunc;
+
+ userFormatToRGBA8888Func = GetUserFormatToRGBA8888Func_t( srcImageFormat );
+ RGBA8888ToUserFormatFunc = GetRGBA8888ToUserFormatFunc_t( dstImageFormat );
+
+ if ( !userFormatToRGBA8888Func || !RGBA8888ToUserFormatFunc )
+ {
+ return false;
+ }
+
+ for ( line = 0; line < height; line++ )
+ {
+ userFormatToRGBA8888Func( src + line * srcStride, lineBufRGBA8888, width );
+ RGBA8888ToUserFormatFunc( lineBufRGBA8888, dst + line * dstStride, width );
+ }
+ return true;
+ }
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Color conversion routines
+//-----------------------------------------------------------------------------
+void ConvertIA88ImageToNormalMapRGBA8888( const uint8 *src, int width,
+ int height, uint8 *dst,
+ float bumpScale )
+{
+ float heightScale = ( 1.0f / 255.0f ) * bumpScale;
+ float c, cx, cy;
+ float maxDim = ( width > height ) ? width : height;
+ float ooMaxDim = 1.0f / maxDim;
+
+ int s, t;
+ for( t = 0; t < height; t++ )
+ {
+ uint8 *dstPixel = &dst[t * width * 4];
+ for( s = 0; s < width; s++ )
+ {
+ c = src[( t * width + s ) * 2];
+ cx = src[( t * width + ((s+1)%width) ) * 2];
+ cy = src[( ((t+1)%height) * width + s ) * 2];
+
+ /*
+ // \Z (out of screen)
+ // \
+ // \
+ // \
+ // \----------- X
+ // |
+ // |
+ // |
+ // |
+ // |
+ // Y
+ */
+
+ Vector xVect, yVect, normal;
+ xVect[0] = ooMaxDim;
+ xVect[1] = 0.0f;
+ xVect[2] = (cx - c) * heightScale;
+
+ yVect[0] = 0.0f;
+ yVect[1] = ooMaxDim;
+ yVect[2] = (cy - c) * heightScale;
+
+ CrossProduct( xVect, yVect, normal );
+ VectorNormalize( normal );
+
+ /* Repack the normalized vector into an RGB unsigned byte
+ vector in the normal map image. */
+ dstPixel[0] = ( uint8 )( 128 + 127*normal[0] );
+ dstPixel[1] = ( uint8 )( 128 + 127*normal[1] );
+ dstPixel[2] = ( uint8 )( 128 + 127*normal[2] );
+ dstPixel[3] = src[( ( t * width + s ) * 2 ) + 1];
+ dstPixel += 4;
+ }
+ }
+}
+
+void ConvertNormalMapRGBA8888ToDUDVMapUVWQ8888( const uint8 *src, int width, int height,
+ uint8 *dst_ )
+{
+ unsigned const char *lastPixel = src + width * height * 4;
+ char *dst = ( char * )dst_; // NOTE: this is signed!!!!
+
+ for( ; src < lastPixel; src += 4, dst += 4 )
+ {
+ dst[0] = ( char )( ( ( int )src[0] ) - 127 );
+ dst[1] = ( char )( ( ( int )src[1] ) - 127 );
+ dst[2] = ( char )( ( ( int )src[2] ) - 127 );
+ dst[3] = ( char )( ( ( int )src[3] ) - 127 );
+ }
+}
+
+void ConvertNormalMapRGBA8888ToDUDVMapUVLX8888( const uint8 *src, int width, int height,
+ uint8 *dst_ )
+{
+ unsigned const char *lastPixel = src + width * height * 4;
+ char *dst = ( char * )dst_; // NOTE: this is signed!!!!
+
+ for( ; src < lastPixel; src += 4, dst += 4 )
+ {
+ dst[0] = ( char )( ( ( int )src[0] ) - 127 );
+ dst[1] = ( char )( ( ( int )src[1] ) - 127 );
+
+ uint8 *pUDst = (uint8 *)dst;
+ pUDst[2] = src[3];
+ pUDst[3] = 0xFF;
+ }
+}
+
+void ConvertNormalMapRGBA8888ToDUDVMapUV88( const uint8 *src, int width, int height,
+ uint8 *dst_ )
+{
+ unsigned const char *lastPixel = src + width * height * 4;
+ char *dst = ( char * )dst_; // NOTE: this is signed!!!!
+
+ for( ; src < lastPixel; src += 4, dst += 2 )
+ {
+ dst[0] = ( char )( ( ( int )src[0] ) - 127 );
+ dst[1] = ( char )( ( ( int )src[1] ) - 127 );
+ }
+}
+
+void NormalizeNormalMapRGBA8888( uint8 *src, int numTexels )
+{
+ uint8 *lastPixel = src + numTexels * 4;
+
+ for( uint8 *pixel = src; pixel < lastPixel; pixel += 4 )
+ {
+ Vector tmpVect;
+ tmpVect[0] = ( ( float )pixel[0] - 128.0f ) * ( 1.0f / 127.0f );
+ tmpVect[1] = ( ( float )pixel[1] - 128.0f ) * ( 1.0f / 127.0f );
+ tmpVect[2] = ( ( float )pixel[2] - 128.0f ) * ( 1.0f / 127.0f );
+
+ VectorNormalize( tmpVect );
+
+ pixel[0] = ( uint8 )( 128 + 127 * tmpVect[0] );
+ pixel[1] = ( uint8 )( 128 + 127 * tmpVect[1] );
+ pixel[2] = ( uint8 )( 128 + 127 * tmpVect[2] );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Image rotation
+//-----------------------------------------------------------------------------
+bool RotateImageLeft( const uint8 *src, uint8 *dst,
+ int widthHeight, ImageFormat imageFormat )
+{
+#define SRC(x,y) src[((x)+(y)*widthHeight)*sizeInBytes]
+#define DST(x,y) dst[((x)+(y)*widthHeight)*sizeInBytes]
+ if( IsCompressed( imageFormat ) )
+ {
+ return false;
+ }
+
+ int x, y;
+
+ uint8 tmp[4][16];
+ int halfWidthHeight = widthHeight >> 1;
+ int sizeInBytes = SizeInBytes( imageFormat );
+ Assert( sizeInBytes <= 16 && sizeInBytes > 0 );
+
+ for( y = 0; y < halfWidthHeight; y++ )
+ {
+ for( x = 0; x < halfWidthHeight; x++ )
+ {
+ memcpy( tmp[0], &SRC( x, y ), sizeInBytes );
+ memcpy( tmp[1], &SRC( y, widthHeight-x-1 ), sizeInBytes );
+ memcpy( tmp[2], &SRC( widthHeight-x-1, widthHeight-y-1 ), sizeInBytes );
+ memcpy( tmp[3], &SRC( widthHeight-y-1, x ), sizeInBytes );
+ memcpy( &DST( x, y ), tmp[3], sizeInBytes );
+ memcpy( &DST( y, widthHeight-x-1 ), tmp[0], sizeInBytes );
+ memcpy( &DST( widthHeight-x-1, widthHeight-y-1 ), tmp[1], sizeInBytes );
+ memcpy( &DST( widthHeight-y-1, x ), tmp[2], sizeInBytes );
+ }
+ }
+#undef SRC
+#undef DST
+ return true;
+}
+
+bool RotateImage180( const uint8 *src, uint8 *dst,
+ int widthHeight, ImageFormat imageFormat )
+{
+ // OPTIMIZE: do this transformation directly.
+ if( RotateImageLeft( src, dst, widthHeight, imageFormat ) )
+ {
+ return RotateImageLeft( dst, dst, widthHeight, imageFormat );
+ }
+ return false;
+}
+
+bool FlipImageVertically( void *pSrc, void *pDst, int nWidth, int nHeight, ImageFormat imageFormat, int nDstStride )
+{
+ if( IsCompressed( imageFormat ) )
+ return false;
+
+ int nSizeInBytes = SizeInBytes( imageFormat );
+ int nRowBytes = nSizeInBytes * nWidth;
+ int nSrcStride = nRowBytes;
+ if ( nDstStride == 0 )
+ {
+ nDstStride = nRowBytes;
+ }
+
+ uint8 *pSrcRow = (uint8*)pSrc;
+ uint8 *pDstRow = (uint8*)pDst + ((nHeight-1) * nDstStride);
+ if ( pSrc == pDst )
+ {
+ uint8* pTemp = (uint8*)_alloca( nRowBytes );
+ int nHalfHeight = nHeight >> 1;
+ for ( int i = 0; i < nHalfHeight; i++ )
+ {
+ memcpy( pTemp, pSrcRow, nRowBytes );
+ memcpy( pSrcRow, pDstRow, nRowBytes );
+ memcpy( pDstRow, pTemp, nRowBytes );
+
+ pSrcRow += nSrcStride;
+ pDstRow -= nDstStride;
+ }
+ }
+ else
+ {
+ for ( int i = 0; i < nHeight; i++ )
+ {
+ memcpy( pDstRow, pSrcRow, nRowBytes );
+ pSrcRow += nSrcStride;
+ pDstRow -= nDstStride;
+ }
+ }
+
+ return true;
+}
+
+bool FlipImageHorizontally( void *pSrc, void *pDst, int nWidth, int nHeight, ImageFormat imageFormat, int nDstStride )
+{
+ if( IsCompressed( imageFormat ) )
+ return false;
+
+ uint8 tmp[16];
+ int nSizeInBytes = SizeInBytes( imageFormat );
+ int nRowBytes = nSizeInBytes * nWidth;
+ Assert( nSizeInBytes <= 16 && nSizeInBytes > 0 );
+ int nSrcStride = nRowBytes;
+ if ( nDstStride == 0 )
+ {
+ nDstStride = nRowBytes;
+ }
+
+ int x, y;
+ uint8 *pSrcRow = (uint8*)pSrc;
+ uint8 *pDstRow = (uint8*)pDst;
+ if ( pSrc == pDst )
+ {
+ int nHalfWidth = nWidth >> 1;
+ for( y = 0; y < nHeight; y++ )
+ {
+ uint8 *pSrcPixel = pSrcRow;
+ uint8 *pDstPixel = pDstRow + nRowBytes - nSizeInBytes;
+ for( x = 0; x < nHalfWidth; x++ )
+ {
+ memcpy( tmp, pSrcPixel, nSizeInBytes );
+ memcpy( pSrcPixel, pDstPixel, nSizeInBytes );
+ memcpy( pDstPixel, tmp, nSizeInBytes );
+
+ pSrcPixel += nSizeInBytes;
+ pDstPixel -= nSizeInBytes;
+ }
+
+ pSrcRow += nSrcStride;
+ pDstRow += nDstStride;
+ }
+ }
+ else
+ {
+ for( y = 0; y < nHeight; y++ )
+ {
+ uint8 *pSrcPixel = pSrcRow;
+ uint8 *pDstPixel = pDstRow + nRowBytes - nSizeInBytes;
+ for( x = 0; x < nWidth; x++ )
+ {
+ memcpy( pDstPixel, pSrcPixel, nSizeInBytes );
+ pSrcPixel += nSizeInBytes;
+ pDstPixel -= nSizeInBytes;
+ }
+
+ pSrcRow += nSrcStride;
+ pDstRow += nDstStride;
+ }
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Image rotation
+//-----------------------------------------------------------------------------
+bool SwapAxes( uint8 *src, int widthHeight, ImageFormat imageFormat )
+{
+#define SRC(x,y) src[((x)+(y)*widthHeight)*sizeInBytes]
+ if( IsCompressed( imageFormat ) )
+ {
+ return false;
+ }
+
+ int x, y;
+
+ uint8 tmp[4];
+ int sizeInBytes = SizeInBytes( imageFormat );
+ Assert( sizeInBytes <= 4 && sizeInBytes > 0 );
+
+ for( y = 0; y < widthHeight; y++ )
+ {
+ for( x = 0; x < y; x++ )
+ {
+ memcpy( tmp, &SRC( x, y ), sizeInBytes );
+ memcpy( &SRC( x, y ), &SRC( y, x ), sizeInBytes );
+ memcpy( &SRC( y, x ), tmp, sizeInBytes );
+ }
+ }
+#undef SRC
+ return true;
+}
+
+void RGBA8888ToRGBA8888( const uint8 *src, uint8 *dst, int numPixels )
+{
+ memcpy( dst, src, 4 * numPixels );
+}
+
+void RGBA8888ToABGR8888( const uint8 *src, uint8 *dst, int numPixels )
+{
+ const uint8 *endSrc = src + numPixels * 4;
+ for ( ; src < endSrc; src += 4, dst += 4 )
+ {
+ dst[0] = src[3];
+ dst[1] = src[2];
+ dst[2] = src[1];
+ dst[3] = src[0];
+ }
+}
+
+void RGBA8888ToRGB888( const uint8 *src, uint8 *dst, int numPixels )
+{
+ const uint8 *endSrc = src + numPixels * 4;
+ for ( ; src < endSrc; src += 4, dst += 3 )
+ {
+ dst[0] = src[0];
+ dst[1] = src[1];
+ dst[2] = src[2];
+ }
+}
+
+void RGBA8888ToBGR888( const uint8 *src, uint8 *dst, int numPixels )
+{
+ const uint8 *endSrc = src + numPixels * 4;
+ for ( ; src < endSrc; src += 4, dst += 3 )
+ {
+ dst[0] = src[2];
+ dst[1] = src[1];
+ dst[2] = src[0];
+ }
+}
+
+void RGBA8888ToRGB565( const uint8 *src, uint8 *dst, int numPixels )
+{
+ Assert( 0 );
+ const uint8 *endSrc = src + numPixels * 4;
+ for ( ; src < endSrc; src += 4, dst += 2 )
+ {
+ }
+}
+
+void RGBA8888ToI8( const uint8 *src, uint8 *dst, int numPixels )
+{
+ const uint8 *endSrc = src + numPixels * 4;
+ for ( ; src < endSrc; src += 4, dst += 1 )
+ {
+ dst[0] = ( uint8 )( 0.299f * src[0] + 0.587f * src[1] + 0.114f * src[2] );
+ }
+}
+
+void RGBA8888ToIA88( const uint8 *src, uint8 *dst, int numPixels )
+{
+ // fixme: need to find the proper rgb weighting
+ const uint8 *endSrc = src + numPixels * 4;
+ for ( ; src < endSrc; src += 4, dst += 2 )
+ {
+ dst[0] = ( uint8 )( 0.299f * src[0] + 0.587f * src[1] + 0.114f * src[2] );
+ dst[1] = src[3];
+ }
+}
+
+void RGBA8888ToP8( const uint8 *src, uint8 *dst, int numPixels )
+{
+ Assert( 0 );
+}
+
+void RGBA8888ToA8( const uint8 *src, uint8 *dst, int numPixels )
+{
+ const uint8 *endSrc = src + numPixels * 4;
+ for ( ; src < endSrc; src += 4, dst += 1 )
+ {
+ dst[0] = src[3];
+ }
+}
+
+void RGBA8888ToRGB888_BLUESCREEN( const uint8 *src, uint8 *dst, int numPixels )
+{
+ const uint8 *endSrc = src + numPixels * 4;
+ for ( ; src < endSrc; src += 4, dst += 3 )
+ {
+ if( src[3] == 0 )
+ {
+ dst[0] = 0;
+ dst[1] = 0;
+ dst[2] = 255;
+ }
+ else
+ {
+ dst[0] = src[0];
+ dst[1] = src[1];
+ dst[2] = src[2];
+ }
+ }
+}
+
+void RGBA8888ToBGR888_BLUESCREEN( const uint8 *src, uint8 *dst, int numPixels )
+{
+ const uint8 *endSrc = src + numPixels * 4;
+ for ( ; src < endSrc; src += 4, dst += 3 )
+ {
+ if( src[3] == 0 )
+ {
+ dst[2] = 0;
+ dst[1] = 0;
+ dst[0] = 255;
+ }
+ else
+ {
+ dst[2] = src[0];
+ dst[1] = src[1];
+ dst[0] = src[2];
+ }
+ }
+}
+
+void RGBA8888ToARGB8888( const uint8 *src, uint8 *dst, int numPixels )
+{
+ const uint8 *endSrc = src + numPixels * 4;
+ for ( ; src < endSrc; src += 4, dst += 4 )
+ {
+ dst[0] = src[3];
+ dst[1] = src[0];
+ dst[2] = src[1];
+ dst[3] = src[2];
+ }
+}
+
+void RGBA8888ToBGRA8888( const uint8 *src, uint8 *dst, int numPixels )
+{
+ const uint8 *endSrc = src + numPixels * 4;
+ for ( ; src < endSrc; src += 4, dst += 4 )
+ {
+ dst[0] = src[2];
+ dst[1] = src[1];
+ dst[2] = src[0];
+ dst[3] = src[3];
+ }
+}
+
+void RGBA8888ToBGRX8888( const uint8 *src, uint8 *dst, int numPixels )
+{
+ const uint8 *endSrc = src + numPixels * 4;
+ for ( ; src < endSrc; src += 4, dst += 4 )
+ {
+ dst[0] = src[2];
+ dst[1] = src[1];
+ dst[2] = src[0];
+ }
+}
+
+void RGBA8888ToBGR565( const uint8 *src, uint8 *dst, int numPixels )
+{
+ unsigned short* pDstShort = (unsigned short*)dst;
+ const uint8 *endSrc = src + numPixels * 4;
+ for ( ; src < endSrc; src += 4, pDstShort ++ )
+ {
+ *pDstShort = ((src[0] >> 3) << 11) |
+ ((src[1] >> 2) << 5) |
+ (src[2] >> 3);
+ }
+}
+
+void RGBA8888ToBGRX5551( const uint8 *src, uint8 *dst, int numPixels )
+{
+ unsigned short* pDstShort = (unsigned short*)dst;
+ const uint8 *endSrc = src + numPixels * 4;
+ for ( ; src < endSrc; src += 4, pDstShort ++ )
+ {
+ *pDstShort = ((src[0] >> 3) << 10) |
+ ((src[1] >> 3) << 5) |
+ (src[2] >> 3);
+ }
+}
+
+void RGBA8888ToBGRA5551( const uint8 *src, uint8 *dst, int numPixels )
+{
+ unsigned short* pDstShort = (unsigned short*)dst;
+ const uint8 *endSrc = src + numPixels * 4;
+ for ( ; src < endSrc; src += 4, pDstShort ++ )
+ {
+ *pDstShort = ((src[0] >> 3) << 10) |
+ ((src[1] >> 3) << 5) |
+ (src[2] >> 3) |
+ (src[3] >> 7) << 15;
+ }
+}
+
+void RGBA8888ToBGRA4444( const uint8 *src, uint8 *dst, int numPixels )
+{
+ unsigned short* pDstShort = (unsigned short*)dst;
+ const uint8 *endSrc = src + numPixels * 4;
+ for ( ; src < endSrc; src += 4, pDstShort ++ )
+ {
+ *pDstShort = ((src[0] >> 4) << 8) |
+ ((src[1] >> 4) << 4) |
+ (src[2] >> 4) |
+ ((src[3] >> 4) << 12);
+ }
+}
+
+void RGBA8888ToUV88( const uint8 *src, uint8 *dst, int numPixels )
+{
+ const uint8 *endSrc = src + numPixels * 4;
+ for ( ; src < endSrc; src += 4, dst += 2 )
+ {
+ dst[0] = src[0];
+ dst[1] = src[1];
+ }
+}
+
+void RGBA8888ToUVWQ8888( const uint8 *src, uint8 *dst, int numPixels )
+{
+ RGBA8888ToRGBA8888( src, dst, numPixels );
+}
+
+void RGBA8888ToUVLX8888( const uint8 *src, uint8 *dst, int numPixels )
+{
+ RGBA8888ToRGBA8888( src, dst, numPixels );
+}
+
+void ABGR8888ToRGBA8888( const uint8 *src, uint8 *dst, int numPixels )
+{
+ const uint8 *endSrc = src + numPixels * 4;
+ for ( ; src < endSrc; src += 4, dst += 4 )
+ {
+ dst[0] = src[3];
+ dst[1] = src[2];
+ dst[2] = src[1];
+ dst[3] = src[0];
+ }
+}
+
+void RGB888ToRGBA8888( const uint8 *src, uint8 *dst, int numPixels )
+{
+ const uint8 *endSrc = src + numPixels * 3;
+ for ( ; src < endSrc; src += 3, dst += 4 )
+ {
+ dst[0] = src[0];
+ dst[1] = src[1];
+ dst[2] = src[2];
+ dst[3] = 255;
+ }
+}
+
+void BGR888ToRGBA8888( const uint8 *src, uint8 *dst, int numPixels )
+{
+ const uint8 *endSrc = src + numPixels * 3;
+ for ( ; src < endSrc; src += 3, dst += 4 )
+ {
+ dst[0] = src[2];
+ dst[1] = src[1];
+ dst[2] = src[0];
+ dst[3] = 255;
+ }
+}
+
+void RGB565ToRGBA8888( const uint8 *src, uint8 *dst, int numPixels )
+{
+ Assert( 0 );
+ const uint8 *endSrc = src + numPixels * 2;
+ for ( ; src < endSrc; src += 2, dst += 4 )
+ {
+ }
+}
+
+void I8ToRGBA8888( const uint8 *src, uint8 *dst, int numPixels )
+{
+ const uint8 *endSrc = src + numPixels;
+ for ( ; src < endSrc; src += 1, dst += 4 )
+ {
+ dst[0] = src[0];
+ dst[1] = src[0];
+ dst[2] = src[0];
+ dst[3] = 255;
+ }
+}
+
+void IA88ToRGBA8888( const uint8 *src, uint8 *dst, int numPixels )
+{
+ const uint8 *endSrc = src + numPixels * 2;
+ for ( ; src < endSrc; src += 2, dst += 4 )
+ {
+ dst[0] = src[0];
+ dst[1] = src[0];
+ dst[2] = src[0];
+ dst[3] = src[1];
+ }
+}
+
+void P8ToRGBA8888( const uint8 *src, uint8 *dst, int numPixels )
+{
+ Assert( 0 );
+}
+
+void A8ToRGBA8888( const uint8 *src, uint8 *dst, int numPixels )
+{
+ const uint8 *endSrc = src + numPixels;
+ for ( ; src < endSrc; src += 1, dst += 4 )
+ {
+ dst[0] = src[0];
+ dst[1] = src[0];
+ dst[2] = src[0];
+ dst[3] = src[0];
+ }
+}
+
+void RGB888_BLUESCREENToRGBA8888( const uint8 *src, uint8 *dst, int numPixels )
+{
+ const uint8 *endSrc = src + numPixels * 3;
+ for ( ; src < endSrc; src += 3, dst += 4 )
+ {
+ if( src[0] == 0 && src[1] == 0 && src[2] == 255 )
+ {
+ dst[0] = 0;
+ dst[1] = 0;
+ dst[2] = 0;
+ dst[3] = 0;
+ }
+ else
+ {
+ dst[0] = src[0];
+ dst[1] = src[1];
+ dst[2] = src[2];
+ dst[3] = 255;
+ }
+ }
+}
+
+void BGR888_BLUESCREENToRGBA8888( const uint8 *src, uint8 *dst, int numPixels )
+{
+ const uint8 *endSrc = src + numPixels * 3;
+ for ( ; src < endSrc; src += 3, dst += 4 )
+ {
+ if( src[2] == 0 && src[1] == 0 && src[0] == 255 )
+ {
+ dst[0] = 0;
+ dst[1] = 0;
+ dst[2] = 0;
+ dst[3] = 0;
+ }
+ else
+ {
+ dst[2] = src[0];
+ dst[1] = src[1];
+ dst[0] = src[2];
+ dst[3] = 255;
+ }
+ }
+}
+
+void ARGB8888ToRGBA8888( const uint8 *src, uint8 *dst, int numPixels )
+{
+ const uint8 *endSrc = src + numPixels * 4;
+ for ( ; src < endSrc; src += 4, dst += 4 )
+ {
+ dst[0] = src[1];
+ dst[1] = src[2];
+ dst[2] = src[3];
+ dst[3] = src[0];
+ }
+}
+
+void BGRA8888ToRGBA8888( const uint8 *src, uint8 *dst, int numPixels )
+{
+ const uint8 *endSrc = src + numPixels * 4;
+ for ( ; src < endSrc; src += 4, dst += 4 )
+ {
+ dst[0] = src[2];
+ dst[1] = src[1];
+ dst[2] = src[0];
+ dst[3] = src[3];
+ }
+}
+
+void BGRX8888ToRGBA8888( const uint8 *src, uint8 *dst, int numPixels )
+{
+ const uint8 *endSrc = src + numPixels * 4;
+ for ( ; src < endSrc; src += 4, dst += 4 )
+ {
+ dst[0] = src[2];
+ dst[1] = src[1];
+ dst[2] = src[0];
+ dst[3] = 255;
+ }
+}
+
+void BGR565ToRGBA8888( const uint8 *src, uint8 *dst, int numPixels )
+{
+ unsigned short* pSrcShort = (unsigned short*)src;
+ unsigned short* pEndSrc = pSrcShort + numPixels;
+ for ( ; pSrcShort < pEndSrc; pSrcShort++, dst += 4 )
+ {
+ int blue = (*pSrcShort & 0x1F);
+ int green = (*pSrcShort >> 5) & 0x3F;
+ int red = (*pSrcShort >> 11) & 0x1F;
+
+ // Expand to 8 bits
+ dst[0] = (red << 3) | (red >> 2);
+ dst[1] = (green << 2) | (green >> 4);
+ dst[2] = (blue << 3) | (blue >> 2);
+ dst[3] = 255;
+ }
+}
+
+void BGRX5551ToRGBA8888( const uint8 *src, uint8 *dst, int numPixels )
+{
+ unsigned short* pSrcShort = (unsigned short*)src;
+ unsigned short* pEndSrc = pSrcShort + numPixels;
+ for ( ; pSrcShort < pEndSrc; pSrcShort++, dst += 4 )
+ {
+ int blue = (*pSrcShort & 0x1F);
+ int green = (*pSrcShort >> 5) & 0x1F;
+ int red = (*pSrcShort >> 10) & 0x1F;
+
+ // Expand to 8 bits
+ dst[0] = (red << 3) | (red >> 2);
+ dst[1] = (green << 3) | (green >> 2);
+ dst[2] = (blue << 3) | (blue >> 2);
+ dst[3] = 255;
+ }
+}
+
+void BGRA5551ToRGBA8888( const uint8 *src, uint8 *dst, int numPixels )
+{
+ unsigned short* pSrcShort = (unsigned short*)src;
+ unsigned short* pEndSrc = pSrcShort + numPixels;
+ for ( ; pSrcShort < pEndSrc; pSrcShort++, dst += 4 )
+ {
+ int blue = (*pSrcShort & 0x1F);
+ int green = (*pSrcShort >> 5) & 0x1F;
+ int red = (*pSrcShort >> 10) & 0x1F;
+ int alpha = *pSrcShort & ( 1 << 15 );
+
+ // Expand to 8 bits
+ dst[0] = (red << 3) | (red >> 2);
+ dst[1] = (green << 3) | (green >> 2);
+ dst[2] = (blue << 3) | (blue >> 2);
+ // garymcthack
+ if( alpha )
+ {
+ dst[3] = 255;
+ }
+ else
+ {
+ dst[3] = 0;
+ }
+ }
+}
+
+void BGRA4444ToRGBA8888( const uint8 *src, uint8 *dst, int numPixels )
+{
+ unsigned short* pSrcShort = (unsigned short*)src;
+ unsigned short* pEndSrc = pSrcShort + numPixels;
+ for ( ; pSrcShort < pEndSrc; pSrcShort++, dst += 4 )
+ {
+ int blue = (*pSrcShort & 0xF);
+ int green = (*pSrcShort >> 4) & 0xF;
+ int red = (*pSrcShort >> 8) & 0xF;
+ int alpha = (*pSrcShort >> 12) & 0xF;
+
+ // Expand to 8 bits
+ // FIXME: shouldn't this be (red << 4) | red?
+ dst[0] = (red << 4) | (red >> 4);
+ dst[1] = (green << 4) | (green >> 4);
+ dst[2] = (blue << 4) | (blue >> 4);
+ dst[3] = (alpha << 4) | (alpha >> 4);
+ }
+}
+
+void UV88ToRGBA8888( const uint8 *src, uint8 *dst, int numPixels )
+{
+ const uint8* pEndSrc = src + numPixels * 2;
+ for ( ; src < pEndSrc; src += 2, dst += 4 )
+ {
+ dst[0] = src[0];
+ dst[1] = src[1];
+ dst[2] = 0;
+ dst[3] = 0;
+ }
+}
+
+void UVWQ8888ToRGBA8888( const uint8 *src, uint8 *dst, int numPixels )
+{
+ RGBA8888ToRGBA8888( src, dst, numPixels );
+}
+
+void UVLX8888ToRGBA8888( const uint8 *src, uint8 *dst, int numPixels )
+{
+ RGBA8888ToRGBA8888( src, dst, numPixels );
+}
+
+// HDRFIXME: This assumes that the 16-bit integer values are 4.12 fixed-point.
+void RGBA16161616ToRGBA8888( const uint8 *src_, uint8 *dst, int numPixels )
+{
+ unsigned short *src = ( unsigned short * )src_;
+ unsigned short *pEndSrc = src + numPixels * 4;
+ for ( ; src < pEndSrc; src += 4, dst += 4 )
+ {
+ dst[0] = min( 255, src[0] >> 4 );
+ dst[1] = min( 255, src[1] >> 4 );
+ dst[2] = min( 255, src[2] >> 4 );
+ dst[3] = min( 255, src[3] >> 8 );
+ }
+}
+
+} // ImageLoader namespace ends
+
+
+
diff --git a/bitmap/float_bm.cpp b/bitmap/float_bm.cpp
new file mode 100644
index 0000000..33ab8c1
--- /dev/null
+++ b/bitmap/float_bm.cpp
@@ -0,0 +1,729 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//===========================================================================//
+
+#include <tier0/platform.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include <stdlib.h>
+#include "bitmap/float_bm.h"
+#include <tier2/tier2.h>
+#include "bitmap/imageformat.h"
+#include "bitmap/tgaloader.h"
+#include "tier1/strtools.h"
+#include "filesystem.h"
+
+
+#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) );
+}
+
+FloatBitMap_t::FloatBitMap_t(int width, int height)
+{
+ RGBAData=0;
+ AllocateRGB(width,height);
+}
+
+FloatBitMap_t::FloatBitMap_t(FloatBitMap_t const *orig)
+{
+ RGBAData=0;
+ AllocateRGB(orig->Width,orig->Height);
+ memcpy(RGBAData,orig->RGBAData,Width*Height*sizeof(float)*4);
+}
+
+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);
+
+}
+
+#define PFM_MAX_XSIZE 2048
+
+bool FloatBitMap_t::LoadFromPFM(char const *fname)
+{
+ FileHandle_t f = g_pFullFileSystem->Open(fname, "rb");
+ if (f)
+ {
+ if( ( GetChar(f) == 'P' ) && (GetChar(f) == 'F' ) && ( GetChar(f) == '\n' ))
+ {
+ Width=GetInt(f);
+ Height=GetInt(f);
+
+ // eat crap until the next newline
+ while( GetChar(f) != '\n')
+ {
+ }
+
+ // printf("file %s w=%d h=%d\n",fname,Width,Height);
+ AllocateRGB(Width,Height);
+
+ for( int y = Height-1; y >= 0; y-- )
+ {
+ float linebuffer[PFM_MAX_XSIZE*3];
+ g_pFullFileSystem->Read(linebuffer,3*Width*sizeof(float),f);
+ for(int x=0;x<Width;x++)
+ {
+ for(int c=0;c<3;c++)
+ {
+ Pixel(x,y,c)=linebuffer[x*3+c];
+ }
+ }
+ }
+ }
+ g_pFullFileSystem->Close( f ); // close file after reading
+ }
+ return (RGBAData!=0);
+}
+
+bool FloatBitMap_t::WritePFM(char const *fname)
+{
+ FileHandle_t f = g_pFullFileSystem->Open(fname, "wb");
+
+ if ( f )
+ {
+ g_pFullFileSystem->FPrintf(f,"PF\n%d %d\n-1.000000\n",Width,Height);
+ for( int y = Height-1; y >= 0; y-- )
+ {
+ float linebuffer[PFM_MAX_XSIZE*3];
+ for(int x=0;x<Width;x++)
+ {
+ for(int c=0;c<3;c++)
+ {
+ linebuffer[x*3+c]=Pixel(x,y,c);
+ }
+ }
+ g_pFullFileSystem->Write(linebuffer,3*Width*sizeof(float),f);
+ }
+ g_pFullFileSystem->Close(f);
+
+ return true;
+ }
+
+ return false;
+}
+
+
+float FloatBitMap_t::InterpolatedPixel(float x, float y, int comp) const
+{
+ int Top= floor(y);
+ float Yfrac= y - Top;
+ int Bot= min(Height-1,Top+1);
+ int Left= floor(x);
+ float Xfrac= x - Left;
+ int Right= min(Width-1,Left+1);
+ return
+ BiLinInterp(Xfrac, Yfrac,
+ Pixel(Left, Top, comp),
+ Pixel(Right, Top, comp),
+ Pixel(Left, Bot, comp),
+ Pixel(Right, Bot, comp));
+
+}
+
+//-----------------------------------------------------------------
+// resize (with bilinear filter) truecolor bitmap in place
+
+void FloatBitMap_t::ReSize(int NewWidth, int NewHeight)
+{
+ float XRatio= (float)Width / (float)NewWidth;
+ float YRatio= (float)Height / (float)NewHeight;
+ float SourceX, SourceY, Xfrac, Yfrac;
+ int Top, Bot, Left, Right;
+
+ float *newrgba=new float[NewWidth * NewHeight * 4];
+
+ SourceY= 0;
+ for(int y=0;y<NewHeight;y++)
+ {
+ Yfrac= SourceY - floor(SourceY);
+ Top= SourceY;
+ Bot= SourceY+1;
+ if (Bot>=Height) Bot= Height-1;
+ SourceX= 0;
+ for(int x=0;x<NewWidth;x++)
+ {
+ Xfrac= SourceX - floor(SourceX);
+ Left= SourceX;
+ Right= SourceX+1;
+ if (Right>=Width) Right= Width-1;
+ for(int c=0;c<4;c++)
+ {
+ newrgba[4*(y*NewWidth+x)+c] = BiLinInterp(Xfrac, Yfrac,
+ Pixel(Left, Top, c),
+ Pixel(Right, Top, c),
+ Pixel(Left, Bot, c),
+ Pixel(Right, Bot, c));
+ }
+ SourceX+= XRatio;
+ }
+ SourceY+= YRatio;
+ }
+
+ delete[] RGBAData;
+ RGBAData=newrgba;
+
+ Width=NewWidth;
+ Height=NewHeight;
+}
+
+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(char const *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= Width & 0xff;
+ myheader.width1= (Width>>8);
+ myheader.height0= Height & 0xff;
+ myheader.height1= (Height>>8);
+ myheader.attributes=0x20;
+ g_pFullFileSystem->Write(&myheader,sizeof(myheader),f);
+ // now, write the pixels
+ for(int y=0;y<Height;y++)
+ {
+ for(int x=0;x<Width;x++)
+ {
+ PixRGBAF fpix = PixelRGBAF( x, y );
+ 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(char const *tgafilename)
+{
+ RGBAData=0;
+
+ // load from a tga or pfm
+ if (Q_stristr(tgafilename, ".pfm"))
+ {
+ LoadFromPFM(tgafilename);
+ return;
+ }
+
+ int width1, height1;
+ ImageFormat imageFormat1;
+ float gamma1;
+
+ if( !TGALoader::GetInfo( tgafilename, &width1, &height1, &imageFormat1, &gamma1 ) )
+ {
+ printf( "error loading %s\n", tgafilename);
+ exit( -1 );
+ }
+ AllocateRGB(width1,height1);
+
+ uint8 *pImage1Tmp =
+ new uint8 [ImageLoader::GetMemRequired( width1, height1, 1, imageFormat1, false )];
+
+ if( !TGALoader::Load( pImage1Tmp, tgafilename, width1, height1, imageFormat1, 2.2f, false ) )
+ {
+ printf( "error loading %s\n", tgafilename);
+ exit( -1 );
+ }
+ 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,3-c)=pImage1[c+4*(x+(y*width1))]/255.0;
+ }
+ }
+ }
+
+ delete[] pImage1;
+ delete[] pImage1Tmp;
+}
+
+FloatBitMap_t::~FloatBitMap_t(void)
+{
+ if (RGBAData)
+ delete[] RGBAData;
+}
+
+
+FloatBitMap_t *FloatBitMap_t::QuarterSize(void) const
+{
+ // generate a new bitmap half on each axis
+
+ FloatBitMap_t *newbm=new FloatBitMap_t(Width/2,Height/2);
+ for(int y=0;y<Height/2;y++)
+ for(int x=0;x<Width/2;x++)
+ {
+ for(int c=0;c<4;c++)
+ newbm->Pixel(x,y,c)=((Pixel(x*2,y*2,c)+Pixel(x*2+1,y*2,c)+
+ Pixel(x*2,y*2+1,c)+Pixel(x*2+1,y*2+1,c))/4);
+ }
+ return newbm;
+}
+
+FloatBitMap_t *FloatBitMap_t::QuarterSizeBlocky(void) const
+{
+ // generate a new bitmap half on each axis
+
+ FloatBitMap_t *newbm=new FloatBitMap_t(Width/2,Height/2);
+ for(int y=0;y<Height/2;y++)
+ for(int x=0;x<Width/2;x++)
+ {
+ for(int c=0;c<4;c++)
+ newbm->Pixel(x,y,c)=Pixel(x*2,y*2,c);
+ }
+ return newbm;
+}
+
+Vector FloatBitMap_t::AverageColor(void)
+{
+ Vector ret(0,0,0);
+ for(int y=0;y<Height;y++)
+ for(int x=0;x<Width;x++)
+ for(int c=0;c<3;c++)
+ ret[c]+=Pixel(x,y,c);
+ ret*=1.0/(Width*Height);
+ return ret;
+}
+
+float FloatBitMap_t::BrightestColor(void)
+{
+ float ret=0.0;
+ for(int y=0;y<Height;y++)
+ for(int x=0;x<Width;x++)
+ {
+ Vector v(Pixel(x,y,0),Pixel(x,y,1),Pixel(x,y,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<Height;y++)
+ for(int x=0;x<Width;x++)
+ for(int c=0;c<3;c++)
+ Pixel(x,y,c)=pow((float)MAX(0.0,Pixel(x,y,c)),(float)power);
+
+}
+
+void FloatBitMap_t::Logize(void)
+{
+ for(int y=0;y<Height;y++)
+ for(int x=0;x<Width;x++)
+ for(int c=0;c<3;c++)
+ Pixel(x,y,c)=log(1.0+Pixel(x,y,c));
+
+}
+
+void FloatBitMap_t::UnLogize(void)
+{
+ for(int y=0;y<Height;y++)
+ for(int x=0;x<Width;x++)
+ for(int c=0;c<3;c++)
+ Pixel(x,y,c)=exp(Pixel(x,y,c))-1;
+}
+
+
+void FloatBitMap_t::Clear(float r, float g, float b, float alpha)
+{
+ for(int y=0;y<Height;y++)
+ for(int x=0;x<Width;x++)
+ {
+ Pixel(x,y,0)=r;
+ Pixel(x,y,1)=g;
+ Pixel(x,y,2)=b;
+ Pixel(x,y,3)=alpha;
+ }
+}
+
+void FloatBitMap_t::ScaleRGB(float scale_factor)
+{
+ for(int y=0;y<Height;y++)
+ for(int x=0;x<Width;x++)
+ for(int c=0;c<3;c++)
+ Pixel(x,y,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(FloatBitMap_t const &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<Width;x++)
+ for(int y=0;y<Height;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(Width-1,x1);
+ y1=MAX(0,y1);
+ y1=MIN(Height-1,y1);
+ float dx1=Pixel(x,y,c)-Pixel(x1,y1,c);
+ deltas[i]->Pixel(x,y,c)=dx1;
+ }
+ }
+ for(int x=1;x<b.Width-1;x++)
+ for(int y=1;y<b.Height-1;y++)
+ for(int c=0;c<3;c++)
+ {
+ for(int i=0;i<NDELTAS;i++)
+ {
+ float diff=b.Pixel(x,y,c)-b.Pixel(x+dx[i],y+dy[i],c);
+ deltas[i]->Pixel(x+xofs,y+yofs,c)=diff;
+ if (Flags & SPFLAGS_MAXGRADIENT)
+ {
+ float dx1=Pixel(x+xofs,y+yofs,c)-Pixel(x+dx[i]+xofs,y+dy[i]+yofs,c);
+ if (fabs(dx1)>fabs(diff))
+ deltas[i]->Pixel(x+xofs,y+yofs,c)=dx1;
+ }
+ }
+ }
+
+ // now, calculate modifiability
+ for(int x=0;x<Width;x++)
+ for(int y=0;y<Height;y++)
+ {
+ float modify=0;
+ if (
+ (x>xofs+1) && (x<=xofs+b.Width-2) &&
+ (y>yofs+1) && (y<=yofs+b.Height-2))
+ modify=1;
+ Alpha(x,y)=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<Width;x++)
+ for(int y=0;y<Height;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(Width-1,x1);
+ y1=MAX(0,y1);
+ y1=MIN(Height-1,y1);
+ float dx1=Pixel(x,y,c)-Pixel(x1,y1,c);
+ deltas[i]->Pixel(x,y,c)=dx1;
+ gsum+=fabs(dx1);
+ }
+ }
+ // now, reduce gradient changes
+ // float gavg=gsum/(Width*Height);
+ for(int x=0;x<Width;x++)
+ for(int y=0;y<Height;y++)
+ for(int c=0;c<3;c++)
+ {
+ for(int i=0;i<NDELTAS;i++)
+ {
+ float norml=1.1*deltas[i]->Pixel(x,y,c);
+ // if (norml<0.0)
+ // norml=-pow(-norml,1.2);
+ // else
+ // norml=pow(norml,1.2);
+ deltas[i]->Pixel(x,y,c)=norml;
+ }
+ }
+
+ // now, calculate modifiability
+ for(int x=0;x<Width;x++)
+ for(int y=0;y<Height;y++)
+ {
+ float modify=0;
+ if (
+ (x>0) && (x<Width-1) &&
+ (y) && (y<Height-1))
+ {
+ modify=1;
+ Alpha(x,y)=modify;
+ }
+ }
+
+ Poisson(deltas,2200,0);
+}
+
+
+
+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<Width-1;x++)
+ for(int y=1;y<Height-1;y++)
+ for(int c=0;c<3;c++)
+ {
+ DiffMapX.Pixel(x,y,c)=Pixel(x,y,c)-Pixel(x+1,y,c);
+ DiffMapY.Pixel(x,y,c)=Pixel(x,y,c)-Pixel(x,y+1,c);
+ }
+ // initialize edge conditions
+ for(int x=0;x<Width;x++)
+ {
+ for(int c=0;c<3;c++)
+ {
+ float a=0.5*(Pixel(x,Height-1,c)+=Pixel(x,0,c));
+ rslta.Pixel(x,Height-1,c)=a;
+ rslta.Pixel(x,0,c)=a;
+ }
+ }
+ for(int y=0;y<Height;y++)
+ {
+ for(int c=0;c<3;c++)
+ {
+ float a=0.5*(Pixel(Width-1,y,c)+Pixel(0,y,c));
+ rslta.Pixel(Width-1,y,c)=a;
+ rslta.Pixel(0,y,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<Width-1;x++)
+ for(int y=1;y<Height-1;y++)
+ for(int c=0;c<3;c++)
+ {
+ float desiredx=DiffMapX.Pixel(x,y,c)+cursrc->Pixel(x+1,y,c);
+ float desiredy=DiffMapY.Pixel(x,y,c)+cursrc->Pixel(x,y+1,c);
+ float desired=0.5*(desiredy+desiredx);
+ curdst->Pixel(x,y,c)=FLerp(cursrc->Pixel(x,y,c),desired,0.5);
+ error+=SQ(desired-cursrc->Pixel(x,y,c));
+ }
+ SWAP(cursrc,curdst);
+ }
+ // paste result
+ for(int x=0;x<Width;x++)
+ for(int y=0;y<Height;y++)
+ for(int c=0;c<3;c++)
+ Pixel(x,y,c)=curdst->Pixel(x,y,c);
+}
+
+
+void FloatBitMap_t::GetAlphaBounds(int &minx, int &miny, int &maxx,int &maxy)
+{
+ for(minx=0;minx<Width;minx++)
+ {
+ int y;
+ for(y=0;y<Height;y++)
+ if (Alpha(minx,y))
+ break;
+ if (y!=Height)
+ break;
+ }
+ for(maxx=Width-1;maxx>=0;maxx--)
+ {
+ int y;
+ for(y=0;y<Height;y++)
+ if (Alpha(maxx,y))
+ break;
+ if (y!=Height)
+ break;
+ }
+ for(miny=0;minx<Height;miny++)
+ {
+ int x;
+ for(x=minx;x<=maxx;x++)
+ if (Alpha(x,miny))
+ break;
+ if (x<maxx)
+ break;
+ }
+ for(maxy=Height-1;maxy>=0;maxy--)
+ {
+ int x;
+ for(x=minx;x<=maxx;x++)
+ if (Alpha(x,maxy))
+ 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(Width-2,maxx);
+ maxy=MIN(Height-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]=deltas[i]->QuarterSize();
+ FloatBitMap_t *tmp=QuarterSize();
+ tmp->Poisson(lowdeltas,n_iters*4,flags);
+ // now, propagate results from tmp to us
+ for(int x=0;x<tmp->Width;x++)
+ for(int y=0;y<tmp->Height;y++)
+ for(int xi=0;xi<2;xi++)
+ for(int yi=0;yi<2;yi++)
+ if (Alpha(x*2+xi,y*2+yi))
+ {
+ for(int c=0;c<3;c++)
+ Pixel(x*2+xi,y*2+yi,c)=
+ FLerp(Pixel(x*2+xi,y*2+yi,c),tmp->Pixel(x,y,c),Alpha(x*2+xi,y*2+yi));
+ }
+ char fname[80];
+ sprintf(fname,"sub%dx%d.tga",tmp->Width,tmp->Height);
+ tmp->WriteTGAFile(fname);
+ sprintf(fname,"submrg%dx%d.tga",tmp->Width,tmp->Height);
+ 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))
+ {
+ for(int c=0;c<3;c++)
+ {
+ float desired=0.0;
+ for(int i=0;i<NDELTAS;i++)
+ desired+=deltas[i]->Pixel(x,y,c)+cursrc->Pixel(x+dx[i],y+dy[i],c);
+ desired*=(1.0/NDELTAS);
+ // desired=FLerp(Pixel(x,y,c),desired,Alpha(x,y));
+ curdst->Pixel(x,y,c)=FLerp(cursrc->Pixel(x,y,c),desired,0.5);
+ error+=SQ(desired-cursrc->Pixel(x,y,c));
+ }
+ }
+ SWAP(cursrc,curdst);
+ }
+ }
+ }
+ // paste result
+ for(int x=0;x<Width;x++)
+ {
+ for(int y=0;y<Height;y++)
+ {
+ for(int c=0;c<3;c++)
+ {
+ Pixel(x,y,c)=curdst->Pixel(x,y,c);
+ }
+ }
+ }
+}
+
+
diff --git a/bitmap/float_bm2.cpp b/bitmap/float_bm2.cpp
new file mode 100644
index 0000000..2e113c7
--- /dev/null
+++ b/bitmap/float_bm2.cpp
@@ -0,0 +1,144 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//===========================================================================//
+
+#include <tier0/platform.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include <stdlib.h>
+#include "bitmap/float_bm.h"
+
+
+static float ScaleValue(float f, float overbright)
+{
+ // map a value between 0..255 to the scale factor
+ int ival=f;
+ return ival*(overbright/255.0);
+}
+
+static float IScaleValue(float f, float overbright)
+{
+ f*=(1.0/overbright);
+ int ival=min(255,(int)ceil(f*255.0));
+ return ival;
+}
+
+void MaybeSetScaleVaue(FloatBitMap_t const &orig, FloatBitMap_t &newbm, int x, int y,
+ float newscale, float overbright)
+{
+ // clamp the given scale value to the legal range for that pixel and regnerate the rgb
+ // components.
+ float maxc=max(max(orig.Pixel(x,y,0),orig.Pixel(x,y,1)),orig.Pixel(x,y,2));
+ if (maxc==0.0)
+ {
+ // pixel is black. any scale value is fine.
+ newbm.Pixel(x,y,3)=newscale;
+ for(int c=0;c<3;c++)
+ newbm.Pixel(x,y,c)=0;
+ }
+ else
+ {
+// float desired_floatscale=maxc;
+ float scale_we_will_get=ScaleValue(newscale,overbright);
+// if (scale_we_will_get >= desired_floatscale )
+ {
+ newbm.Pixel(x,y,3)=newscale;
+ for(int c=0;c<3;c++)
+ newbm.Pixel(x,y,c)=orig.Pixel(x,y,c)/(scale_we_will_get);
+ }
+ }
+}
+
+
+void FloatBitMap_t::Uncompress(float overbright)
+{
+ for(int y=0;y<Height;y++)
+ for(int x=0;x<Width;x++)
+ {
+ int iactual_alpha_value=255.0*Pixel(x,y,3);
+ float actual_alpha_value=iactual_alpha_value*(1.0/255.0);
+ for(int c=0;c<3;c++)
+ {
+ int iactual_color_value=255.0*Pixel(x,y,c);
+ float actual_color_value=iactual_color_value*(1.0/255.0);
+ Pixel(x,y,c)=actual_alpha_value*actual_color_value*overbright;
+ }
+ }
+}
+
+#define GAUSSIAN_WIDTH 5
+#define SQ(x) ((x)*(x))
+
+void FloatBitMap_t::CompressTo8Bits(float overbright)
+{
+ FloatBitMap_t TmpFBM(Width,Height);
+ // first, saturate to max overbright
+ for(int y=0;y<Height;y++)
+ for(int x=0;x<Width;x++)
+ for(int c=0;c<3;c++)
+ Pixel(x,y,c)=min(overbright,Pixel(x,y,c));
+ // first pass - choose nominal scale values to convert to rgb,scale
+ for(int y=0;y<Height;y++)
+ for(int x=0;x<Width;x++)
+ {
+ // determine maximum component
+ float maxc=max(max(Pixel(x,y,0),Pixel(x,y,1)),Pixel(x,y,2));
+ if (maxc==0)
+ {
+ for(int c=0;c<4;c++)
+ TmpFBM.Pixel(x,y,c)=0;
+ }
+ else
+ {
+ float desired_floatscale=maxc;
+ float closest_iscale=IScaleValue(desired_floatscale, overbright);
+ float scale_value_we_got=ScaleValue(closest_iscale, overbright );
+ TmpFBM.Pixel(x,y,3)=closest_iscale;
+ for(int c=0;c<3;c++)
+ TmpFBM.Pixel(x,y,c)=Pixel(x,y,c)/scale_value_we_got;
+ }
+ }
+ // now, refine scale values
+#ifdef FILTER_TO_REDUCE_LERP_ARTIFACTS
+// I haven't been able to come up with a filter which eleiminates objectionable artifacts on all
+// source textures. So, I've gone to doing the lerping in the shader.
+ int pass=0;
+ while(pass<1)
+ {
+ FloatBitMap_t temp_filtered(&TmpFBM);
+ for(int y=0;y<Height;y++)
+ {
+ for(int x=0;x<Width;x++)
+ {
+ float sum_scales=0.0;
+ float sum_weights=0.0;
+ for(int yofs=-GAUSSIAN_WIDTH;yofs<=GAUSSIAN_WIDTH;yofs++)
+ for(int xofs=-GAUSSIAN_WIDTH;xofs<=GAUSSIAN_WIDTH;xofs++)
+ {
+ float r=0.456*GAUSSIAN_WIDTH;
+ r=0.26*GAUSSIAN_WIDTH;
+ float x1=xofs/r;
+ float y1=yofs/r;
+ float a=(SQ(x1)+SQ(y1))/(2.0*SQ(r));
+ float w=exp(-a);
+ sum_scales+=w*TmpFBM.PixelClamped(x+xofs,y+yofs,3);
+ sum_weights+=w;
+ }
+ int new_trial_scale=sum_scales*(1.0/sum_weights);
+ MaybeSetScaleVaue(*this,temp_filtered,x,y,new_trial_scale,overbright);
+ }
+ }
+ pass++;
+ memcpy(TmpFBM.RGBAData,temp_filtered.RGBAData,Width*Height*4*sizeof(float));
+ }
+#endif
+
+ memcpy(RGBAData,TmpFBM.RGBAData,Width*Height*4*sizeof(float));
+ // now, map scale to real value
+ for(int y=0;y<Height;y++)
+ for(int x=0;x<Width;x++)
+ Pixel(x,y,3)*=(1.0/255.0);
+}
diff --git a/bitmap/float_bm3.cpp b/bitmap/float_bm3.cpp
new file mode 100644
index 0000000..83d4630
--- /dev/null
+++ b/bitmap/float_bm3.cpp
@@ -0,0 +1,108 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//===========================================================================//
+
+#include <tier0/platform.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include <stdlib.h>
+#include "bitmap/float_bm.h"
+#include "vstdlib/vstdlib.h"
+#include "vstdlib/random.h"
+#include "tier1/strtools.h"
+
+void FloatBitMap_t::InitializeWithRandomPixelsFromAnotherFloatBM(FloatBitMap_t const &other)
+{
+ for(int y=0;y<Height;y++)
+ for(int x=0;x<Width;x++)
+ {
+ float x1=RandomInt(0,other.Width-1);
+ float y1=RandomInt(0,other.Height-1);
+ for(int c=0;c<4;c++)
+ {
+ Pixel(x,y,c)=other.Pixel(x1,y1,c);
+ }
+ }
+}
+
+
+FloatBitMap_t *FloatBitMap_t::QuarterSizeWithGaussian(void) const
+{
+ // generate a new bitmap half on each axis, using a separable gaussian.
+ static float kernel[]={.05,.25,.4,.25,.05};
+ FloatBitMap_t *newbm=new FloatBitMap_t(Width/2,Height/2);
+
+ for(int y=0;y<Height/2;y++)
+ for(int x=0;x<Width/2;x++)
+ {
+ for(int c=0;c<4;c++)
+ {
+ float sum=0;
+ float sumweights=0; // for versatility in handling the
+ // offscreen case
+ for(int xofs=-2;xofs<=2;xofs++)
+ {
+ int orig_x=max(0,min(Width-1,x*2+xofs));
+ for(int yofs=-2;yofs<=2;yofs++)
+ {
+ int orig_y=max(0,min(Height-1,y*2+yofs));
+ float coeff=kernel[xofs+2]*kernel[yofs+2];
+ sum+=Pixel(orig_x,orig_y,c)*coeff;
+ sumweights+=coeff;
+ }
+ }
+ newbm->Pixel(x,y,c)=sum/sumweights;
+ }
+ }
+ return newbm;
+}
+
+FloatImagePyramid_t::FloatImagePyramid_t(FloatBitMap_t const &src, ImagePyramidMode_t mode)
+{
+ memset(m_pLevels,0,sizeof(m_pLevels));
+ m_nLevels=1;
+ m_pLevels[0]=new FloatBitMap_t(&src);
+ ReconstructLowerResolutionLevels(0);
+}
+
+void FloatImagePyramid_t::ReconstructLowerResolutionLevels(int start_level)
+{
+ while( (m_pLevels[start_level]->Width>1) && (m_pLevels[start_level]->Height>1) )
+ {
+ if (m_pLevels[start_level+1])
+ delete m_pLevels[start_level+1];
+ m_pLevels[start_level+1]=m_pLevels[start_level]->QuarterSizeWithGaussian();
+ start_level++;
+ }
+ m_nLevels=start_level+1;
+}
+
+float & FloatImagePyramid_t::Pixel(int x, int y, int component, int level) const
+{
+ assert(level<m_nLevels);
+ x<<=level;
+ y<<=level;
+ return m_pLevels[level]->Pixel(x,y,component);
+}
+
+void FloatImagePyramid_t::WriteTGAs(char const *basename) const
+{
+ for(int l=0;l<m_nLevels;l++)
+ {
+ char bname_out[1024];
+ Q_snprintf(bname_out,sizeof(bname_out),"%s_%02d.tga",basename,l);
+ m_pLevels[l]->WriteTGAFile(bname_out);
+ }
+
+}
+
+
+FloatImagePyramid_t::~FloatImagePyramid_t(void)
+{
+ for(int l=0;l<m_nLevels;l++)
+ if (m_pLevels[l])
+ delete m_pLevels[l];
+}
diff --git a/bitmap/float_bm4.cpp b/bitmap/float_bm4.cpp
new file mode 100644
index 0000000..7130b27
--- /dev/null
+++ b/bitmap/float_bm4.cpp
@@ -0,0 +1,350 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//===========================================================================//
+
+#include <tier0/platform.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include <stdlib.h>
+#include "bitmap/float_bm.h"
+#include "vstdlib/vstdlib.h"
+#include "raytrace.h"
+#include "mathlib/bumpvects.h"
+#include "mathlib/halton.h"
+#include "tier0/threadtools.h"
+#include "tier0/progressbar.h"
+
+// In order to handle intersections with wrapped copies, we repeat the bitmap triangles this many
+// times
+#define NREPS_TILE 1
+extern int n_intersection_calculations;
+
+
+
+struct SSBumpCalculationContext // what each thread needs to see
+{
+ RayTracingEnvironment *m_pRtEnv;
+ FloatBitMap_t *ret_bm; // the bitmnap we are building
+ FloatBitMap_t const *src_bm;
+ int nrays_to_trace_per_pixel;
+ float bump_scale;
+ Vector *trace_directions; // light source directions to trace
+ Vector *normals;
+ int min_y; // range of scanlines to computer for
+ int max_y;
+ uint32 m_nOptionFlags;
+ int thread_number;
+};
+
+
+static unsigned SSBumpCalculationThreadFN( void * ctx1 )
+{
+ SSBumpCalculationContext *ctx = ( SSBumpCalculationContext * ) ctx1;
+
+ RayStream ray_trace_stream_ctx;
+
+ RayTracingSingleResult * rslts = new
+ RayTracingSingleResult[ctx->ret_bm->Width * ctx->nrays_to_trace_per_pixel];
+
+
+ for( int y = ctx->min_y; y <= ctx->max_y; y++ )
+ {
+ if ( ctx->thread_number == 0 )
+ ReportProgress("Computing output",(1+ctx->max_y-ctx->min_y),y-ctx->min_y);
+ for( int r = 0; r < ctx->nrays_to_trace_per_pixel; r++ )
+ {
+ for( int x = 0; x < ctx->ret_bm->Width; x++ )
+ {
+ Vector surf_pnt( x, y, ctx->bump_scale * ctx->src_bm->Pixel( x, y, 3 ) );
+ // move the ray origin up a hair
+ surf_pnt.z += 0.55;
+ Vector trace_end = surf_pnt;
+ Vector trace_dir = ctx->trace_directions[ r ];
+ trace_dir *= ( 1 + NREPS_TILE * 2 ) * max( ctx->src_bm->Width, ctx->src_bm->Height );
+ trace_end += trace_dir;
+ ctx->m_pRtEnv->AddToRayStream( ray_trace_stream_ctx, surf_pnt, trace_end,
+ & ( rslts[ r + ctx->nrays_to_trace_per_pixel * ( x )] ));
+ }
+ }
+ if ( ctx->nrays_to_trace_per_pixel )
+ ctx->m_pRtEnv->FinishRayStream( ray_trace_stream_ctx );
+ // now, all ray tracing results are in the results buffer. Determine the visible self-shadowed
+ // bump map lighting at each vertex in each basis direction
+ for( int x = 0; x < ctx->src_bm->Width; x++ )
+ {
+ int nNumChannels = ( ctx->m_nOptionFlags & SSBUMP_OPTION_NONDIRECTIONAL ) ? 1 : 3;
+ for( int c = 0; c < nNumChannels; c++ )
+ {
+ float sum_dots = 0;
+ float sum_possible_dots = 0;
+ Vector ldir = g_localBumpBasis[c];
+ float ndotl = DotProduct( ldir, ctx->normals[x + y * ctx->src_bm->Width] );
+ if ( ndotl < 0 )
+ ctx->ret_bm->Pixel( x, y, c ) = 0;
+ else
+ {
+ if ( ctx->nrays_to_trace_per_pixel )
+ {
+ RayTracingSingleResult *this_rslt =
+ rslts + ctx->nrays_to_trace_per_pixel * ( x );
+ for( int r = 0; r < ctx->nrays_to_trace_per_pixel; r++ )
+ {
+ float dot;
+ if ( ctx->m_nOptionFlags & SSBUMP_OPTION_NONDIRECTIONAL )
+ dot = ctx->trace_directions[r].z;
+ else
+ dot = DotProduct( ldir, ctx->trace_directions[r] );
+ if ( dot > 0 )
+ {
+ sum_possible_dots += dot;
+ if ( this_rslt[r].HitID == - 1 )
+ sum_dots += dot;
+ }
+ }
+ }
+ else
+ {
+ sum_dots = sum_possible_dots = 1.0;
+ }
+ ctx->ret_bm->Pixel( x, y, c ) = ( ndotl * sum_dots ) / sum_possible_dots;
+ }
+ }
+ if ( ctx->m_nOptionFlags & SSBUMP_OPTION_NONDIRECTIONAL )
+ {
+ ctx->ret_bm->Pixel( x, y, 1 ) = ctx->ret_bm->Pixel( x, y, 0 ); // copy height
+ ctx->ret_bm->Pixel( x, y, 2 ) = ctx->ret_bm->Pixel( x, y, 0 ); // copy height
+ ctx->ret_bm->Pixel( x, y, 3 ) = ctx->ret_bm->Pixel( x, y, 0 ); // copy height
+ }
+ else
+ {
+ ctx->ret_bm->Pixel( x, y, 3 ) = ctx->src_bm->Pixel( x, y, 3 ); // copy height
+ }
+ }
+ }
+ delete[] rslts;
+ return 0;
+}
+
+void FloatBitMap_t::ComputeVertexPositionsAndNormals( float flHeightScale, Vector **ppPosOut, Vector **ppNormalOut ) const
+{
+ Vector *verts = new Vector[Width * Height];
+ // first, calculate vertex positions
+ for( int y = 0; y < Height; y++ )
+ for( int x = 0; x < Width; x++ )
+ {
+ Vector * out = verts + x + y * Width;
+ out->x = x;
+ out->y = y;
+ out->z = flHeightScale * Pixel( x, y, 3 );
+ }
+
+ Vector *normals = new Vector[Width * Height];
+ // now, calculate normals, smoothed
+ for( int y = 0; y < Height; y++ )
+ for( int x = 0; x < Width; x++ )
+ {
+ // now, calculcate average normal
+ Vector avg_normal( 0, 0, 0 );
+ for( int xofs =- 1;xofs <= 1;xofs++ )
+ for( int yofs =- 1;yofs <= 1;yofs++ )
+ {
+ int x0 = ( x + xofs );
+ if ( x0 < 0 )
+ x0 += Width;
+ int y0 = ( y + yofs );
+ if ( y0 < 0 )
+ y0 += Height;
+ x0 = x0 % Width;
+ y0 = y0 % Height;
+ int x1 = ( x0 + 1 ) % Width;
+ int y1 = ( y0 + 1 ) % Height;
+ // now, form the two triangles from this vertex
+ Vector p0 = verts[x0 + y0 * Width];
+ Vector e1 = verts[x1 + y0 * Width];
+ e1 -= p0;
+ Vector e2 = verts[x0 + y1 * Width];
+ e2 -= p0;
+ Vector n1;
+ CrossProduct( e1, e2, n1 );
+ if ( n1.z < 0 )
+ n1.Negate();
+ e1 = verts[x + y1 * Width];
+ e1 -= p0;
+ e2 = verts[x1 + y1 * Width];
+ e2 -= p0;
+ Vector n2;
+ CrossProduct( e1, e2, n2 );
+ if ( n2.z < 0 )
+ n2.Negate();
+ n1.NormalizeInPlace();
+ n2.NormalizeInPlace();
+ avg_normal += n1;
+ avg_normal += n2;
+ }
+ avg_normal.NormalizeInPlace();
+ normals[x + y * Width]= avg_normal;
+ }
+ *ppPosOut = verts;
+ *ppNormalOut = normals;
+}
+
+FloatBitMap_t *FloatBitMap_t::ComputeSelfShadowedBumpmapFromHeightInAlphaChannel(
+ float bump_scale, int nrays_to_trace_per_pixel,
+ uint32 nOptionFlags ) const
+{
+
+ // first, add all the triangles from the height map to the "world".
+ // we will make multiple copies to handle wrapping
+ int tcnt = 1;
+
+ Vector * verts;
+ Vector * normals;
+ ComputeVertexPositionsAndNormals( bump_scale, & verts, & normals );
+
+ RayTracingEnvironment rtEnv;
+ rtEnv.Flags |= RTE_FLAGS_DONT_STORE_TRIANGLE_COLORS; // save some ram
+
+ if ( nrays_to_trace_per_pixel )
+ {
+ rtEnv.MakeRoomForTriangles( ( 1 + 2 * NREPS_TILE ) * ( 1 + 2 * NREPS_TILE ) * 2 * Height * Width );
+
+ // now, add a whole mess of triangles to trace against
+ for( int tilex =- NREPS_TILE; tilex <= NREPS_TILE; tilex++ )
+ for( int tiley =- NREPS_TILE; tiley <= NREPS_TILE; tiley++ )
+ {
+ int min_x = 0;
+ int max_x = Width - 1;
+ int min_y = 0;
+ int max_y = Height - 1;
+ if ( tilex < 0 )
+ min_x = Width / 2;
+ if ( tilex > 0 )
+ max_x = Width / 2;
+ if ( tiley < 0 )
+ min_y = Height / 2;
+ if ( tiley > 0 )
+ max_y = Height / 2;
+ for( int y = min_y; y <= max_y; y++ )
+ for( int x = min_x; x <= max_x; x++ )
+ {
+ Vector ofs( tilex * Width, tiley * Height, 0 );
+ int x1 = ( x + 1 ) % Width;
+ int y1 = ( y + 1 ) % Height;
+ Vector v0 = verts[x + y * Width];
+ Vector v1 = verts[x1 + y * Width];
+ Vector v2 = verts[x1 + y1 * Width];
+ Vector v3 = verts[x + y1 * Width];
+ v0.x = x; v0.y = y;
+ v1.x = x + 1; v1.y = y;
+ v2.x = x + 1; v2.y = y + 1;
+ v3.x = x; v3.y = y + 1;
+ v0 += ofs; v1 += ofs; v2 += ofs; v3 += ofs;
+ rtEnv.AddTriangle( tcnt++, v0, v1, v2, Vector( 1, 1, 1 ) );
+ rtEnv.AddTriangle( tcnt++, v0, v3, v2, Vector( 1, 1, 1 ) );
+ }
+ }
+ //printf("added %d triangles\n",tcnt-1);
+ ReportProgress("Creating kd-tree",0,0);
+ rtEnv.SetupAccelerationStructure();
+ // ok, now we have built a structure for ray intersection. we will take advantage
+ // of the SSE ray tracing code by intersecting rays as a batch.
+ }
+
+ // We need to calculate for each vertex (i.e. pixel) of the heightmap, how "much" of the world
+ // it can see in each basis direction. we will do this by sampling a sphere of rays around the
+ // vertex, and using dot-product weighting to measure the lighting contribution in each basis
+ // direction. note that the surface normal is not used here. The surface normal will end up
+ // being reflected in the result because of rays being blocked when they try to pass through
+ // the planes of the triangles touching the vertex.
+
+ // note that there is no reason inter-bounced lighting could not be folded into this
+ // calculation.
+
+ FloatBitMap_t * ret = new FloatBitMap_t( Width, Height );
+
+
+ Vector *trace_directions=new Vector[nrays_to_trace_per_pixel];
+ DirectionalSampler_t my_sphere_sampler;
+ for( int r=0; r < nrays_to_trace_per_pixel; r++)
+ {
+ Vector trace_dir=my_sphere_sampler.NextValue();
+// trace_dir=Vector(1,0,0);
+ trace_dir.z=fabs(trace_dir.z); // upwards facing only
+ trace_directions[ r ]= trace_dir;
+ }
+
+ volatile SSBumpCalculationContext ctxs[32];
+ ctxs[0].m_pRtEnv =& rtEnv;
+ ctxs[0].ret_bm = ret;
+ ctxs[0].src_bm = this;
+ ctxs[0].nrays_to_trace_per_pixel = nrays_to_trace_per_pixel;
+ ctxs[0].bump_scale = bump_scale;
+ ctxs[0].trace_directions = trace_directions;
+ ctxs[0].normals = normals;
+ ctxs[0].min_y = 0;
+ ctxs[0].max_y = Height - 1;
+ ctxs[0].m_nOptionFlags = nOptionFlags;
+ int nthreads = min( 32, (int)GetCPUInformation()->m_nPhysicalProcessors );
+
+ ThreadHandle_t waithandles[32];
+ int starty = 0;
+ int ystep = Height / nthreads;
+ for( int t = 0;t < nthreads; t++ )
+ {
+ if ( t )
+ memcpy( (void * ) ( & ctxs[t] ), ( void * ) & ctxs[0], sizeof( ctxs[0] ));
+ ctxs[t].thread_number = t;
+ ctxs[t].min_y = starty;
+ if ( t != nthreads - 1 )
+ ctxs[t].max_y = min( Height - 1, starty + ystep - 1 );
+ else
+ ctxs[t].max_y = Height - 1;
+ waithandles[t]= CreateSimpleThread( SSBumpCalculationThreadFN, ( SSBumpCalculationContext * ) & ctxs[t] );
+ starty += ystep;
+ }
+ for(int t=0;t<nthreads;t++)
+ {
+ ThreadJoin( waithandles[t] );
+ }
+ if ( nOptionFlags & SSBUMP_MOD2X_DETAIL_TEXTURE )
+ {
+ const float flOutputScale = 0.5 * ( 1.0 / .57735026 ); // normalize so that a flat normal yields 0.5
+ // scale output weights by color channel
+ for( int nY = 0; nY < Height; nY++ )
+ for( int nX = 0; nX < Width; nX++ )
+ {
+ float flScale = flOutputScale * (2.0/3.0) * ( Pixel( nX, nY, 0 ) + Pixel( nX, nY, 1 ) + Pixel( nX, nY, 2 ) );
+ ret->Pixel( nX, nY, 0 ) *= flScale;
+ ret->Pixel( nX, nY, 1 ) *= flScale;
+ ret->Pixel( nX, nY, 2 ) *= flScale;
+ }
+ }
+
+ delete[] verts;
+ delete[] trace_directions;
+ delete[] normals;
+ return ret; // destructor will clean up rtenv
+}
+
+// generate a conventional normal map from a source with height stored in alpha.
+FloatBitMap_t *FloatBitMap_t::ComputeBumpmapFromHeightInAlphaChannel( float bump_scale ) const
+{
+ Vector *verts;
+ Vector *normals;
+ ComputeVertexPositionsAndNormals( bump_scale, &verts, &normals );
+ FloatBitMap_t *ret=new FloatBitMap_t( Width, Height );
+ for( int y = 0; y < Height; y++ )
+ for( int x = 0; x < Width; x++ )
+ {
+ Vector const & N = normals[ x + y * Width ];
+ ret->Pixel( x, y, 0 ) = 0.5+ 0.5 * N.x;
+ ret->Pixel( x, y, 1 ) = 0.5+ 0.5 * N.y;
+ ret->Pixel( x, y, 2 ) = 0.5+ 0.5 * N.z;
+ ret->Pixel( x, y, 3 ) = Pixel( x, y, 3 );
+ }
+ return ret;
+}
+
diff --git a/bitmap/float_bm_bilateral_filter.cpp b/bitmap/float_bm_bilateral_filter.cpp
new file mode 100644
index 0000000..4d45eb9
--- /dev/null
+++ b/bitmap/float_bm_bilateral_filter.cpp
@@ -0,0 +1,95 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//===========================================================================//
+
+#include <tier0/platform.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include <stdlib.h>
+#include "bitmap/float_bm.h"
+#include <tier2/tier2.h>
+#include "tier0/threadtools.h"
+#include "tier0/progressbar.h"
+
+struct TBFCalculationContext
+{
+ int min_y,max_y; // range to calculate in this thread
+ int thread_number;
+ int radius_in_pixels;
+ float edge_threshold_value;
+ FloatBitMap_t const *orig_bm;
+ FloatBitMap_t *dest_bm;
+};
+
+static unsigned TBFCalculationThreadFN( void *ctx1 )
+{
+ TBFCalculationContext *ctx = (TBFCalculationContext *) ctx1;
+ for(int y=ctx->min_y; y <= ctx->max_y; y++)
+ {
+ if ( ctx->thread_number == 0 )
+ ReportProgress("Performing bilateral filter",(1+ctx->max_y-ctx->min_y),
+ y-ctx->min_y);
+ for(int x=0; x < ctx->dest_bm->Width; x++)
+ for(int c=0;c<4;c++)
+ {
+ float sum_weights=0;
+ float filter_sum=0;
+ float centerp=ctx->orig_bm->Pixel(x,y,c);
+ for(int iy=-ctx->radius_in_pixels; iy <= ctx->radius_in_pixels; iy++)
+ for(int ix=-ctx->radius_in_pixels; ix <= ctx->radius_in_pixels; ix++)
+ {
+ float this_p=ctx->orig_bm->PixelWrapped(x+ix,y+iy,c);
+
+ // caluclate the g() term. We use a gaussian
+ float exp1=(ix*ix+iy*iy)*(1.0/(2.0*ctx->radius_in_pixels*.033));
+ float g=exp(-exp1);
+ // calculate the "similarity" term. We use a triangle filter
+ float s=1.0;
+ float cdiff=fabs(centerp-this_p);
+ s= (cdiff>ctx->edge_threshold_value)?0:
+ FLerp(1,0,0,ctx->edge_threshold_value,cdiff);
+ sum_weights += s*g;
+ filter_sum += s*g*this_p;
+ }
+ ctx->dest_bm->Pixel(x,y,c)=filter_sum/sum_weights;
+ }
+ }
+ return 0;
+}
+
+void FloatBitMap_t::TileableBilateralFilter( int radius_in_pixels,
+ float edge_threshold_value )
+{
+ FloatBitMap_t orig( this ); // need a copy for the source
+ TBFCalculationContext ctxs[32];
+ ctxs[0].radius_in_pixels = radius_in_pixels;
+ ctxs[0].edge_threshold_value = edge_threshold_value;
+ ctxs[0].orig_bm = &orig;
+ ctxs[0].dest_bm = this;
+ int nthreads = min( 32, (int)GetCPUInformation()->m_nPhysicalProcessors );
+ ThreadHandle_t waithandles[32];
+ int starty=0;
+ int ystep=Height/nthreads;
+
+ for(int t=0;t<nthreads;t++)
+ {
+ if (t)
+ ctxs[t]=ctxs[0];
+ ctxs[t].thread_number=t;
+ ctxs[t].min_y=starty;
+ if (t != nthreads-1)
+ ctxs[t].max_y=min(Height-1,starty+ystep-1);
+ else
+ ctxs[t].max_y=Height-1;
+ waithandles[t]=CreateSimpleThread(TBFCalculationThreadFN, &ctxs[t]);
+ starty+=ystep;
+ }
+ for(int t=0;t<nthreads;t++)
+ {
+ ThreadJoin( waithandles[t] );
+ }
+}
+
diff --git a/bitmap/float_cube.cpp b/bitmap/float_cube.cpp
new file mode 100644
index 0000000..df7726a
--- /dev/null
+++ b/bitmap/float_cube.cpp
@@ -0,0 +1,119 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+#include <tier0/platform.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include <stdlib.h>
+#include "bitmap/float_bm.h"
+#include <filesystem.h>
+#include <mathlib/vector.h>
+
+static Vector face_xvector[6]={ // direction of x pixels on face
+ Vector(-1,0,0), // back
+ Vector(1,0,0), // down
+ Vector(1,0,0), // front
+ Vector(0,1,0), // left
+ Vector(0,-1,0), // right
+ Vector(1,0,0) // up
+};
+
+static Vector face_yvector[6]={ // direction of y pixels on face
+ Vector(0,0,-1), // back
+ Vector(0,1,0), // down
+ Vector(0,0,-1), // front
+ Vector(0,0,-1), // left
+ Vector(0,0,-1), // right
+ Vector(0,-1,0) // up
+};
+
+
+static Vector face_zvector[6]={
+ Vector(1,1,1), // back
+ Vector(-1,-1,-1), // down
+ Vector(-1,-1,1), // front
+ Vector(1,-1,1), // left
+ Vector(-1,1,1), // right
+ Vector(-1,1,1) // up
+};
+
+
+static char const *namepts[6]={"%sbk.pfm","%sdn.pfm","%sft.pfm","%slf.pfm","%srt.pfm","%sup.pfm"};
+
+FloatCubeMap_t::FloatCubeMap_t(char const *basename)
+{
+ for(int f=0;f<6;f++)
+ {
+ char fnamebuf[512];
+ sprintf(fnamebuf,namepts[f],basename);
+ face_maps[f].LoadFromPFM(fnamebuf);
+ }
+}
+
+void FloatCubeMap_t::WritePFMs(char const *basename)
+{
+ for(int f=0;f<6;f++)
+ {
+ char fnamebuf[512];
+ sprintf(fnamebuf,namepts[f],basename);
+ face_maps[f].WritePFM(fnamebuf);
+ }
+}
+
+Vector FloatCubeMap_t::PixelDirection(int face, int x, int y)
+{
+ FloatBitMap_t const &bm=face_maps[face];
+ float xc=x*1.0/(bm.Width-1);
+ float yc=y*1.0/(bm.Height-1);
+ Vector dir=2*xc*face_xvector[face]+
+ 2*yc*face_yvector[face]+face_zvector[face];
+ VectorNormalize(dir);
+ return dir;
+}
+
+Vector FloatCubeMap_t::FaceNormal(int face)
+{
+ float xc=0.5;
+ float yc=0.5;
+ Vector dir=2*xc*face_xvector[face]+
+ 2*yc*face_yvector[face]+face_zvector[face];
+ VectorNormalize(dir);
+ return dir;
+}
+
+void FloatCubeMap_t::Resample( FloatCubeMap_t &out, float flPhongExponent )
+{
+ // terribly slow brute force algorithm just so I can try it out
+ for(int dface=0;dface<6;dface++)
+ {
+ for(int dy=0;dy<out.face_maps[dface].Height;dy++)
+ for(int dx=0;dx<out.face_maps[dface].Width;dx++)
+ {
+ float sum_weights=0;
+ float sum_rgb[3]={0,0,0};
+ for(int sface=0;sface<6;sface++)
+ {
+ // easy 15% optimization - check if faces point away from each other
+ if (DotProduct(FaceNormal(sface),FaceNormal(sface))>-0.9)
+ {
+ Vector ddir=out.PixelDirection(dface,dx,dy);
+ for(int sy=0;sy<face_maps[sface].Height;sy++)
+ for(int sx=0;sx<face_maps[sface].Width;sx++)
+ {
+ float dp=DotProduct(ddir,PixelDirection(sface,sx,sy));
+ if (dp>0.0)
+ {
+ dp=pow( dp, flPhongExponent );
+ sum_weights += dp;
+ for(int c=0;c<3;c++)
+ sum_rgb[c] += dp*face_maps[sface].Pixel( sx, sy, c );
+ }
+ }
+ }
+ }
+ for(int c=0;c<3;c++)
+ out.face_maps[dface].Pixel( dx, dy, c )=sum_rgb[c]*(1.0/sum_weights);
+ }
+ }
+}
+
+
diff --git a/bitmap/imageformat.cpp b/bitmap/imageformat.cpp
new file mode 100644
index 0000000..bce9b67
--- /dev/null
+++ b/bitmap/imageformat.cpp
@@ -0,0 +1,526 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#if defined( _WIN32 ) && !defined( _X360 ) && !defined( DX_TO_GL_ABSTRACTION )
+#include <windows.h>
+#include "../dx9sdk/include/d3d9types.h"
+#endif
+#include "bitmap/imageformat.h"
+#include "basetypes.h"
+#include "tier0/dbg.h"
+#include <malloc.h>
+#include <memory.h>
+#include "nvtc.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"
+
+//-----------------------------------------------------------------------------
+// Various important function types for each color format
+//-----------------------------------------------------------------------------
+static const ImageFormatInfo_t g_ImageFormatInfo[] =
+{
+ { "UNKNOWN", 0, 0, 0, 0, 0, false }, // IMAGE_FORMAT_UNKNOWN,
+ { "RGBA8888", 4, 8, 8, 8, 8, false }, // IMAGE_FORMAT_RGBA8888,
+ { "ABGR8888", 4, 8, 8, 8, 8, false }, // IMAGE_FORMAT_ABGR8888,
+ { "RGB888", 3, 8, 8, 8, 0, false }, // IMAGE_FORMAT_RGB888,
+ { "BGR888", 3, 8, 8, 8, 0, false }, // IMAGE_FORMAT_BGR888,
+ { "RGB565", 2, 5, 6, 5, 0, false }, // IMAGE_FORMAT_RGB565,
+ { "I8", 1, 0, 0, 0, 0, false }, // IMAGE_FORMAT_I8,
+ { "IA88", 2, 0, 0, 0, 8, false }, // IMAGE_FORMAT_IA88
+ { "P8", 1, 0, 0, 0, 0, false }, // IMAGE_FORMAT_P8
+ { "A8", 1, 0, 0, 0, 8, false }, // IMAGE_FORMAT_A8
+ { "RGB888_BLUESCREEN", 3, 8, 8, 8, 0, false }, // IMAGE_FORMAT_RGB888_BLUESCREEN
+ { "BGR888_BLUESCREEN", 3, 8, 8, 8, 0, false }, // IMAGE_FORMAT_BGR888_BLUESCREEN
+ { "ARGB8888", 4, 8, 8, 8, 8, false }, // IMAGE_FORMAT_ARGB8888
+ { "BGRA8888", 4, 8, 8, 8, 8, false }, // IMAGE_FORMAT_BGRA8888
+ { "DXT1", 0, 0, 0, 0, 0, true }, // IMAGE_FORMAT_DXT1
+ { "DXT3", 0, 0, 0, 0, 8, true }, // IMAGE_FORMAT_DXT3
+ { "DXT5", 0, 0, 0, 0, 8, true }, // IMAGE_FORMAT_DXT5
+ { "BGRX8888", 4, 8, 8, 8, 0, false }, // IMAGE_FORMAT_BGRX8888
+ { "BGR565", 2, 5, 6, 5, 0, false }, // IMAGE_FORMAT_BGR565
+ { "BGRX5551", 2, 5, 5, 5, 0, false }, // IMAGE_FORMAT_BGRX5551
+ { "BGRA4444", 2, 4, 4, 4, 4, false }, // IMAGE_FORMAT_BGRA4444
+ { "DXT1_ONEBITALPHA", 0, 0, 0, 0, 0, true }, // IMAGE_FORMAT_DXT1_ONEBITALPHA
+ { "BGRA5551", 2, 5, 5, 5, 1, false }, // IMAGE_FORMAT_BGRA5551
+ { "UV88", 2, 8, 8, 0, 0, false }, // IMAGE_FORMAT_UV88
+ { "UVWQ8888", 4, 8, 8, 8, 8, false }, // IMAGE_FORMAT_UVWQ8899
+ { "RGBA16161616F", 8, 16, 16, 16, 16, false }, // IMAGE_FORMAT_RGBA16161616F
+ { "RGBA16161616", 8, 16, 16, 16, 16, false }, // IMAGE_FORMAT_RGBA16161616
+ { "IMAGE_FORMAT_UVLX8888", 4, 8, 8, 8, 8, false }, // IMAGE_FORMAT_UVLX8899
+ { "IMAGE_FORMAT_R32F", 4, 32, 0, 0, 0, false }, // IMAGE_FORMAT_R32F
+ { "IMAGE_FORMAT_RGB323232F", 12, 32, 32, 32, 0, false }, // IMAGE_FORMAT_RGB323232F
+ { "IMAGE_FORMAT_RGBA32323232F", 16, 32, 32, 32, 32, false }, // IMAGE_FORMAT_RGBA32323232F
+
+ // Vendor-dependent depth formats used for shadow depth mapping
+ { "NV_DST16", 2, 16, 0, 0, 0, false }, // IMAGE_FORMAT_NV_DST16
+ { "NV_DST24", 4, 24, 0, 0, 0, false }, // IMAGE_FORMAT_NV_DST24
+ { "NV_INTZ", 4, 8, 8, 8, 8, false }, // IMAGE_FORMAT_NV_INTZ
+ { "NV_RAWZ", 4, 24, 0, 0, 0, false }, // IMAGE_FORMAT_NV_RAWZ
+ { "ATI_DST16", 2, 16, 0, 0, 0, false }, // IMAGE_FORMAT_ATI_DST16
+ { "ATI_DST24", 4, 24, 0, 0, 0, false }, // IMAGE_FORMAT_ATI_DST24
+ { "NV_NULL", 4, 8, 8, 8, 8, false }, // IMAGE_FORMAT_NV_NULL
+
+ // Vendor-dependent compressed formats typically used for normal map compression
+ { "ATI1N", 0, 0, 0, 0, 0, true }, // IMAGE_FORMAT_ATI1N
+ { "ATI2N", 0, 0, 0, 0, 0, true }, // IMAGE_FORMAT_ATI2N
+
+#ifdef _X360
+ { "X360_DST16", 2, 16, 0, 0, 0, false }, // IMAGE_FORMAT_X360_DST16
+ { "X360_DST24", 4, 24, 0, 0, 0, false }, // IMAGE_FORMAT_X360_DST24
+ { "X360_DST24F", 4, 24, 0, 0, 0, false }, // IMAGE_FORMAT_X360_DST24F
+ { "LINEAR_BGRX8888", 4, 8, 8, 8, 8, false }, // IMAGE_FORMAT_LINEAR_BGRX8888
+ { "LINEAR_RGBA8888", 4, 8, 8, 8, 8, false }, // IMAGE_FORMAT_LINEAR_RGBA8888
+ { "LINEAR_ABGR8888", 4, 8, 8, 8, 8, false }, // IMAGE_FORMAT_LINEAR_ABGR8888
+ { "LINEAR_ARGB8888", 4, 8, 8, 8, 8, false }, // IMAGE_FORMAT_LINEAR_ARGB8888
+ { "LINEAR_BGRA8888", 4, 8, 8, 8, 8, false }, // IMAGE_FORMAT_LINEAR_BGRA8888
+ { "LINEAR_RGB888", 3, 8, 8, 8, 0, false }, // IMAGE_FORMAT_LINEAR_RGB888
+ { "LINEAR_BGR888", 3, 8, 8, 8, 0, false }, // IMAGE_FORMAT_LINEAR_BGR888
+ { "LINEAR_BGRX5551", 2, 5, 5, 5, 0, false }, // IMAGE_FORMAT_LINEAR_BGRX5551
+ { "LINEAR_I8", 1, 0, 0, 0, 0, false }, // IMAGE_FORMAT_LINEAR_I8
+ { "LINEAR_RGBA16161616", 8, 16, 16, 16, 16, false }, // IMAGE_FORMAT_LINEAR_RGBA16161616
+
+ { "LE_BGRX8888", 4, 8, 8, 8, 8, false }, // IMAGE_FORMAT_LE_BGRX8888
+ { "LE_BGRA8888", 4, 8, 8, 8, 8, false }, // IMAGE_FORMAT_LE_BGRA8888
+#endif
+
+ { "DXT1_RUNTIME", 0, 0, 0, 0, 0, true, }, // IMAGE_FORMAT_DXT1_RUNTIME
+ { "DXT5_RUNTIME", 0, 0, 0, 0, 8, true, }, // IMAGE_FORMAT_DXT5_RUNTIME
+};
+
+
+namespace ImageLoader
+{
+
+//-----------------------------------------------------------------------------
+// Returns info about each image format
+//-----------------------------------------------------------------------------
+const ImageFormatInfo_t& ImageFormatInfo( ImageFormat fmt )
+{
+ Assert( ( NUM_IMAGE_FORMATS + 1 ) == sizeof( g_ImageFormatInfo ) / sizeof( g_ImageFormatInfo[0] ) );
+ Assert( unsigned( fmt + 1 ) <= ( NUM_IMAGE_FORMATS ) );
+ return g_ImageFormatInfo[ fmt + 1 ];
+}
+
+int GetMemRequired( int width, int height, int depth, ImageFormat imageFormat, bool mipmap )
+{
+ if ( depth <= 0 )
+ {
+ depth = 1;
+ }
+
+ if ( !mipmap )
+ {
+ // Block compressed formats
+
+ if ( IsCompressed( imageFormat ) )
+ {
+/*
+ DDSURFACEDESC desc;
+ memset( &desc, 0, sizeof(desc) );
+
+ DWORD dwEncodeType;
+ dwEncodeType = GetDXTCEncodeType( imageFormat );
+ desc.dwSize = sizeof( desc );
+ desc.dwFlags = DDSD_WIDTH | DDSD_HEIGHT;
+ desc.dwWidth = width;
+ desc.dwHeight = height;
+ return S3TCgetEncodeSize( &desc, dwEncodeType );
+*/
+ Assert( ( width < 4 ) || !( width % 4 ) );
+ Assert( ( height < 4 ) || !( height % 4 ) );
+ Assert( ( depth < 4 ) || !( depth % 4 ) );
+ if ( width < 4 && width > 0 )
+ {
+ width = 4;
+ }
+ if ( height < 4 && height > 0 )
+ {
+ height = 4;
+ }
+ if ( depth < 4 && depth > 1 )
+ {
+ depth = 4;
+ }
+ int numBlocks = ( width * height ) >> 4;
+ numBlocks *= depth;
+ switch ( imageFormat )
+ {
+ case IMAGE_FORMAT_DXT1:
+ case IMAGE_FORMAT_DXT1_RUNTIME:
+ case IMAGE_FORMAT_ATI1N:
+ return numBlocks * 8;
+
+ case IMAGE_FORMAT_DXT3:
+ case IMAGE_FORMAT_DXT5:
+ case IMAGE_FORMAT_DXT5_RUNTIME:
+ case IMAGE_FORMAT_ATI2N:
+ return numBlocks * 16;
+ }
+
+ Assert( 0 );
+ return 0;
+ }
+
+ return width * height * depth * SizeInBytes( imageFormat );
+ }
+
+ // Mipmap version
+ int memSize = 0;
+ while ( 1 )
+ {
+ memSize += GetMemRequired( width, height, depth, imageFormat, false );
+ if ( width == 1 && height == 1 && depth == 1 )
+ {
+ break;
+ }
+ width >>= 1;
+ height >>= 1;
+ depth >>= 1;
+ if ( width < 1 )
+ {
+ width = 1;
+ }
+ if ( height < 1 )
+ {
+ height = 1;
+ }
+ if ( depth < 1 )
+ {
+ depth = 1;
+ }
+ }
+
+ return memSize;
+}
+
+int GetMipMapLevelByteOffset( int width, int height, ImageFormat imageFormat, int skipMipLevels )
+{
+ int offset = 0;
+
+ while( skipMipLevels > 0 )
+ {
+ offset += width * height * SizeInBytes(imageFormat);
+ if( width == 1 && height == 1 )
+ {
+ break;
+ }
+ width >>= 1;
+ height >>= 1;
+ if( width < 1 )
+ {
+ width = 1;
+ }
+ if( height < 1 )
+ {
+ height = 1;
+ }
+ skipMipLevels--;
+ }
+ return offset;
+}
+
+void GetMipMapLevelDimensions( int *width, int *height, int skipMipLevels )
+{
+ while( skipMipLevels > 0 )
+ {
+ if( *width == 1 && *height == 1 )
+ {
+ break;
+ }
+ *width >>= 1;
+ *height >>= 1;
+ if( *width < 1 )
+ {
+ *width = 1;
+ }
+ if( *height < 1 )
+ {
+ *height = 1;
+ }
+ skipMipLevels--;
+ }
+}
+
+int GetNumMipMapLevels( int width, int height, int depth )
+{
+ if ( depth <= 0 )
+ {
+ depth = 1;
+ }
+
+ if( width < 1 || height < 1 || depth < 1 )
+ return 0;
+
+ int numMipLevels = 1;
+ while( 1 )
+ {
+ if( width == 1 && height == 1 && depth == 1 )
+ break;
+
+ width >>= 1;
+ height >>= 1;
+ depth >>= 1;
+ if( width < 1 )
+ {
+ width = 1;
+ }
+ if( height < 1 )
+ {
+ height = 1;
+ }
+ if( depth < 1 )
+ {
+ depth = 1;
+ }
+ numMipLevels++;
+ }
+ return numMipLevels;
+}
+
+// Turn off warning about FOURCC formats below...
+#pragma warning (disable:4063)
+
+#ifdef DX_TO_GL_ABSTRACTION
+#ifndef MAKEFOURCC
+#define MAKEFOURCC(ch0, ch1, ch2, ch3) \
+ ((DWORD)(BYTE)(ch0) | ((DWORD)(BYTE)(ch1) << 8) | \
+ ((DWORD)(BYTE)(ch2) << 16) | ((DWORD)(BYTE)(ch3) << 24 ))
+#endif //defined(MAKEFOURCC)
+#endif
+//-----------------------------------------------------------------------------
+// convert back and forth from D3D format to ImageFormat, regardless of
+// whether it's supported or not
+//-----------------------------------------------------------------------------
+ImageFormat D3DFormatToImageFormat( D3DFORMAT format )
+{
+#if defined( _X360 )
+ if ( IS_D3DFORMAT_SRGB( format ) )
+ {
+ // sanitize the format from possible sRGB state for comparison purposes
+ format = MAKE_NON_SRGB_FMT( format );
+ }
+#endif
+
+ switch ( format )
+ {
+#if !defined( _X360 )
+ case D3DFMT_R8G8B8:
+ return IMAGE_FORMAT_BGR888;
+#endif
+ case D3DFMT_A8R8G8B8:
+ return IMAGE_FORMAT_BGRA8888;
+ case D3DFMT_X8R8G8B8:
+ return IMAGE_FORMAT_BGRX8888;
+ case D3DFMT_R5G6B5:
+ return IMAGE_FORMAT_BGR565;
+ case D3DFMT_X1R5G5B5:
+ return IMAGE_FORMAT_BGRX5551;
+ case D3DFMT_A1R5G5B5:
+ return IMAGE_FORMAT_BGRA5551;
+ case D3DFMT_A4R4G4B4:
+ return IMAGE_FORMAT_BGRA4444;
+ case D3DFMT_L8:
+ return IMAGE_FORMAT_I8;
+ case D3DFMT_A8L8:
+ return IMAGE_FORMAT_IA88;
+ case D3DFMT_A8:
+ return IMAGE_FORMAT_A8;
+ case D3DFMT_DXT1:
+ return IMAGE_FORMAT_DXT1;
+ case D3DFMT_DXT3:
+ return IMAGE_FORMAT_DXT3;
+ case D3DFMT_DXT5:
+ return IMAGE_FORMAT_DXT5;
+ case D3DFMT_V8U8:
+ return IMAGE_FORMAT_UV88;
+ case D3DFMT_Q8W8V8U8:
+ return IMAGE_FORMAT_UVWQ8888;
+ case D3DFMT_X8L8V8U8:
+ return IMAGE_FORMAT_UVLX8888;
+ case D3DFMT_A16B16G16R16F:
+ return IMAGE_FORMAT_RGBA16161616F;
+ case D3DFMT_A16B16G16R16:
+ return IMAGE_FORMAT_RGBA16161616;
+ case D3DFMT_R32F:
+ return IMAGE_FORMAT_R32F;
+ case D3DFMT_A32B32G32R32F:
+ return IMAGE_FORMAT_RGBA32323232F;
+
+ // DST and FOURCC formats mapped back to ImageFormat (for vendor-dependent shadow depth textures)
+ case (D3DFORMAT)(MAKEFOURCC('R','A','W','Z')):
+ return IMAGE_FORMAT_NV_RAWZ;
+ case (D3DFORMAT)(MAKEFOURCC('I','N','T','Z')):
+ return IMAGE_FORMAT_NV_INTZ;
+ case (D3DFORMAT)(MAKEFOURCC('N','U','L','L')):
+ return IMAGE_FORMAT_NV_NULL;
+ case D3DFMT_D16:
+#if !defined( _X360 )
+ return IMAGE_FORMAT_NV_DST16;
+#else
+ return IMAGE_FORMAT_X360_DST16;
+#endif
+ case D3DFMT_D24S8:
+#if !defined( _X360 )
+ return IMAGE_FORMAT_NV_DST24;
+#else
+ return IMAGE_FORMAT_X360_DST24;
+#endif
+ case (D3DFORMAT)(MAKEFOURCC('D','F','1','6')):
+ return IMAGE_FORMAT_ATI_DST16;
+ case (D3DFORMAT)(MAKEFOURCC('D','F','2','4')):
+ return IMAGE_FORMAT_ATI_DST24;
+
+ // ATIxN FOURCC formats mapped back to ImageFormat
+ case (D3DFORMAT)(MAKEFOURCC('A','T','I','1')):
+ return IMAGE_FORMAT_ATI1N;
+ case (D3DFORMAT)(MAKEFOURCC('A','T','I','2')):
+ return IMAGE_FORMAT_ATI2N;
+
+#if defined( _X360 )
+ case D3DFMT_LIN_A8R8G8B8:
+ return IMAGE_FORMAT_LINEAR_BGRA8888;
+ case D3DFMT_LIN_X8R8G8B8:
+ return IMAGE_FORMAT_LINEAR_BGRX8888;
+ case D3DFMT_LIN_X1R5G5B5:
+ return IMAGE_FORMAT_LINEAR_BGRX5551;
+ case D3DFMT_LIN_L8:
+ return IMAGE_FORMAT_LINEAR_I8;
+ case D3DFMT_LIN_A16B16G16R16:
+ return IMAGE_FORMAT_LINEAR_RGBA16161616;
+
+ case D3DFMT_LE_X8R8G8B8:
+ return IMAGE_FORMAT_LE_BGRX8888;
+
+ case D3DFMT_LE_A8R8G8B8:
+ return IMAGE_FORMAT_LE_BGRA8888;
+
+ case D3DFMT_D24FS8:
+ return IMAGE_FORMAT_X360_DST24F;
+#endif
+ }
+
+ Assert( 0 );
+
+ return IMAGE_FORMAT_UNKNOWN;
+}
+
+D3DFORMAT ImageFormatToD3DFormat( ImageFormat format )
+{
+ // This doesn't care whether it's supported or not
+ switch ( format )
+ {
+ case IMAGE_FORMAT_BGR888:
+#if !defined( _X360 )
+ return D3DFMT_R8G8B8;
+#else
+ return D3DFMT_UNKNOWN;
+#endif
+ case IMAGE_FORMAT_BGRA8888:
+ return D3DFMT_A8R8G8B8;
+ case IMAGE_FORMAT_BGRX8888:
+ return D3DFMT_X8R8G8B8;
+ case IMAGE_FORMAT_BGR565:
+ return D3DFMT_R5G6B5;
+ case IMAGE_FORMAT_BGRX5551:
+ return D3DFMT_X1R5G5B5;
+ case IMAGE_FORMAT_BGRA5551:
+ return D3DFMT_A1R5G5B5;
+ case IMAGE_FORMAT_BGRA4444:
+ return D3DFMT_A4R4G4B4;
+ case IMAGE_FORMAT_I8:
+ return D3DFMT_L8;
+ case IMAGE_FORMAT_IA88:
+ return D3DFMT_A8L8;
+ case IMAGE_FORMAT_A8:
+ return D3DFMT_A8;
+ case IMAGE_FORMAT_DXT1:
+ case IMAGE_FORMAT_DXT1_ONEBITALPHA:
+ return D3DFMT_DXT1;
+ case IMAGE_FORMAT_DXT3:
+ return D3DFMT_DXT3;
+ case IMAGE_FORMAT_DXT5:
+ return D3DFMT_DXT5;
+ case IMAGE_FORMAT_UV88:
+ return D3DFMT_V8U8;
+ case IMAGE_FORMAT_UVWQ8888:
+ return D3DFMT_Q8W8V8U8;
+ case IMAGE_FORMAT_UVLX8888:
+ return D3DFMT_X8L8V8U8;
+ case IMAGE_FORMAT_RGBA16161616F:
+ return D3DFMT_A16B16G16R16F;
+ case IMAGE_FORMAT_RGBA16161616:
+ return D3DFMT_A16B16G16R16;
+ case IMAGE_FORMAT_R32F:
+ return D3DFMT_R32F;
+ case IMAGE_FORMAT_RGBA32323232F:
+ return D3DFMT_A32B32G32R32F;
+
+ // ImageFormat mapped to vendor-dependent FOURCC formats (for shadow depth textures)
+ case IMAGE_FORMAT_NV_RAWZ:
+ return (D3DFORMAT)(MAKEFOURCC('R','A','W','Z'));
+ case IMAGE_FORMAT_NV_INTZ:
+ return (D3DFORMAT)(MAKEFOURCC('I','N','T','Z'));
+ case IMAGE_FORMAT_NV_NULL:
+ return (D3DFORMAT)(MAKEFOURCC('N','U','L','L'));
+ case IMAGE_FORMAT_NV_DST16:
+ return D3DFMT_D16;
+ case IMAGE_FORMAT_NV_DST24:
+ return D3DFMT_D24S8;
+ case IMAGE_FORMAT_ATI_DST16:
+ return (D3DFORMAT)(MAKEFOURCC('D','F','1','6'));
+ case IMAGE_FORMAT_ATI_DST24:
+ return (D3DFORMAT)(MAKEFOURCC('D','F','2','4'));
+
+ // ImageFormats mapped to ATIxN FOURCC
+ case IMAGE_FORMAT_ATI1N:
+ return (D3DFORMAT)(MAKEFOURCC('A','T','I','1'));
+ case IMAGE_FORMAT_ATI2N:
+ return (D3DFORMAT)(MAKEFOURCC('A','T','I','2'));
+
+#if defined( _X360 )
+ case IMAGE_FORMAT_LINEAR_BGRA8888:
+ return D3DFMT_LIN_A8R8G8B8;
+ case IMAGE_FORMAT_LINEAR_BGRX8888:
+ return D3DFMT_LIN_X8R8G8B8;
+ case IMAGE_FORMAT_LINEAR_BGRX5551:
+ return D3DFMT_LIN_X1R5G5B5;
+ case IMAGE_FORMAT_LINEAR_I8:
+ return D3DFMT_LIN_L8;
+ case IMAGE_FORMAT_LINEAR_RGBA16161616:
+ return D3DFMT_LIN_A16B16G16R16;
+ case IMAGE_FORMAT_LE_BGRX8888:
+ return D3DFMT_LE_X8R8G8B8;
+ case IMAGE_FORMAT_LE_BGRA8888:
+ return D3DFMT_LE_A8R8G8B8;
+ case IMAGE_FORMAT_X360_DST16:
+ return D3DFMT_D16;
+ case IMAGE_FORMAT_X360_DST24:
+ return D3DFMT_D24S8;
+ case IMAGE_FORMAT_X360_DST24F:
+ return D3DFMT_D24FS8;
+#endif
+
+ case IMAGE_FORMAT_DXT1_RUNTIME:
+ return D3DFMT_DXT1;
+ case IMAGE_FORMAT_DXT5_RUNTIME:
+ return D3DFMT_DXT5;
+ }
+
+ Assert( 0 );
+
+ return D3DFMT_UNKNOWN;
+}
+
+#pragma warning (default:4063)
+
+} // ImageLoader namespace ends
+
diff --git a/bitmap/psd.cpp b/bitmap/psd.cpp
new file mode 100644
index 0000000..0970356
--- /dev/null
+++ b/bitmap/psd.cpp
@@ -0,0 +1,571 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//===========================================================================//
+
+#include "bitmap/psd.h"
+#include "tier0/dbg.h"
+#include "tier1/utlbuffer.h"
+#include "filesystem.h"
+#include "tier2/tier2.h"
+#include "tier2/utlstreambuffer.h"
+#include "bitmap/imageformat.h"
+#include "bitmap/bitmap.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+//-----------------------------------------------------------------------------
+// The PSD signature bytes
+//-----------------------------------------------------------------------------
+#define PSD_SIGNATURE 0x38425053
+#define PSD_IMGRES_SIGNATURE 0x3842494D
+
+//-----------------------------------------------------------------------------
+// Format of the PSD header on disk
+// NOTE: PSD file header, everything is bigendian
+//-----------------------------------------------------------------------------
+#pragma pack (1)
+
+enum PSDMode_t
+{
+ MODE_GREYSCALE = 1,
+ MODE_PALETTIZED = 2,
+ MODE_RGBA = 3,
+ MODE_CMYK = 4,
+ MODE_MULTICHANNEL = 7,
+ MODE_LAB = 9,
+
+ MODE_COUNT = 10,
+};
+
+//////////////////////////////////////////////////////////////////////////
+//
+// BEGIN PSD FILE:
+//
+// PSDHeader_t
+// unsigned int numBytesPalette;
+// byte palette[ numBytesPalette ]; = { (all red palette entries), (all green palette entries), (all blue palette entries) }, where numEntries = numBytesPalette/3;
+// unsigned int numBytesImgResources;
+// byte imgresources[ numBytesImgResources ]; = { sequence of PSDImgResEntry_t }
+// unsigned int numBytesLayers;
+// byte layers[ numBytesLayers ];
+// unsigned short uCompressionInfo;
+// < ~ image data ~ >
+//
+// END PSD FILE
+//
+//////////////////////////////////////////////////////////////////////////
+
+struct PSDHeader_t
+{
+ unsigned int m_nSignature;
+ unsigned short m_nVersion;
+ unsigned char m_pReserved[6];
+ unsigned short m_nChannels;
+ unsigned int m_nRows;
+ unsigned int m_nColumns;
+ unsigned short m_nDepth;
+ unsigned short m_nMode;
+};
+
+struct PSDPalette_t
+{
+ unsigned char *m_pRed;
+ unsigned char *m_pGreen;
+ unsigned char *m_pBlue;
+};
+
+//-----------------------------------------------------------------------------
+// NOTE: This is how we could load files using file mapping
+//-----------------------------------------------------------------------------
+//HANDLE File = CreateFile(FileName,GENERIC_READ,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
+//Assert(File != INVALID_HANDLE_VALUE);
+//HANDLE FileMap = CreateFileMapping(File,0,PAGE_READONLY,0,0,0);
+//Assert(FileMap != INVALID_HANDLE_VALUE);
+//void *FileData = MapViewOfFile(FileMap,FILE_MAP_READ,0,0,0);
+
+
+//-----------------------------------------------------------------------------
+// Is it a PSD file?
+//-----------------------------------------------------------------------------
+bool IsPSDFile( CUtlBuffer &buf )
+{
+ int nGet = buf.TellGet();
+ PSDHeader_t header;
+ buf.Get( &header, sizeof(header) );
+ buf.SeekGet( CUtlBuffer::SEEK_HEAD, nGet );
+
+ if ( BigLong( header.m_nSignature ) != PSD_SIGNATURE )
+ return false;
+ if ( BigShort( header.m_nVersion ) != 1 )
+ return false;
+ return ( BigShort( header.m_nDepth ) == 8 );
+}
+
+bool IsPSDFile( const char *pFileName, const char *pPathID )
+{
+ CUtlBuffer buf;
+ if ( !g_pFullFileSystem->ReadFile( pFileName, pPathID, buf, sizeof(PSDHeader_t) ) )
+ {
+ Warning( "Unable to read file %s\n", pFileName );
+ return false;
+ }
+ return IsPSDFile( buf );
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns information about the PSD file
+//-----------------------------------------------------------------------------
+bool PSDGetInfo( CUtlBuffer &buf, int *pWidth, int *pHeight, ImageFormat *pImageFormat, float *pSourceGamma )
+{
+ int nGet = buf.TellGet();
+ PSDHeader_t header;
+ buf.Get( &header, sizeof(header) );
+ buf.SeekGet( CUtlBuffer::SEEK_HEAD, nGet );
+
+ if ( BigLong( header.m_nSignature ) != PSD_SIGNATURE )
+ return false;
+ if ( BigShort( header.m_nVersion ) != 1 )
+ return false;
+ if ( BigShort( header.m_nDepth ) != 8 )
+ return false;
+
+ *pWidth = BigLong( header.m_nColumns );
+ *pHeight = BigLong( header.m_nRows );
+ *pImageFormat = BigShort( header.m_nChannels ) == 3 ? IMAGE_FORMAT_RGB888 : IMAGE_FORMAT_RGBA8888;
+ *pSourceGamma = ARTWORK_GAMMA;
+
+ return true;
+}
+
+bool PSDGetInfo( const char *pFileName, const char *pPathID, int *pWidth, int *pHeight, ImageFormat *pImageFormat, float *pSourceGamma )
+{
+ CUtlBuffer buf;
+ if ( !g_pFullFileSystem->ReadFile( pFileName, pPathID, buf, sizeof(PSDHeader_t) ) )
+ {
+ Warning( "Unable to read file %s\n", pFileName );
+ return false;
+ }
+ return PSDGetInfo( buf, pWidth, pHeight, pImageFormat, pSourceGamma );
+}
+
+//-----------------------------------------------------------------------------
+// Get PSD file image resources
+//-----------------------------------------------------------------------------
+PSDImageResources PSDGetImageResources( CUtlBuffer &buf )
+{
+ int nGet = buf.TellGet();
+
+ // Header
+ PSDHeader_t header;
+ buf.Get( &header, sizeof( header ) );
+
+ // Then palette
+ unsigned int numBytesPalette = BigLong( buf.GetUnsignedInt() );
+ buf.SeekGet( CUtlBuffer::SEEK_CURRENT, numBytesPalette );
+
+ // Then image resources
+ unsigned int numBytesImgResources = BigLong( buf.GetUnsignedInt() );
+ PSDImageResources imgres( numBytesImgResources, ( unsigned char * ) buf.PeekGet() );
+
+ // Restore the seek
+ buf.SeekGet( CUtlBuffer::SEEK_HEAD, nGet );
+
+ return imgres;
+}
+
+//-----------------------------------------------------------------------------
+// Converts from CMYK to RGB
+//-----------------------------------------------------------------------------
+static inline void CMYKToRGB( RGBA8888_t &color )
+{
+ unsigned char nCyan = 255 - color.r;
+ unsigned char nMagenta = 255 - color.g;
+ unsigned char nYellow = 255 - color.b;
+ unsigned char nBlack = 255 - color.a;
+
+ int nCyanBlack = (int)nCyan + (int)nBlack;
+ int nMagentaBlack = (int)nMagenta + (int)nBlack;
+ int nYellowBlack = (int)nYellow + (int)nBlack;
+ color.r = ( nCyanBlack < 255 ) ? 255 - nCyanBlack : 0;
+ color.g = ( nMagentaBlack < 255 ) ? 255 - nMagentaBlack : 0;
+ color.b = ( nYellowBlack < 255 ) ? 255 - nYellowBlack : 0;
+ color.a = 255;
+}
+
+
+//-----------------------------------------------------------------------------
+// Deals with uncompressed channels
+//-----------------------------------------------------------------------------
+static void PSDConvertToRGBA8888( int nChannelsCount, PSDMode_t mode, PSDPalette_t &palette, Bitmap_t &bitmap )
+{
+ bool bShouldFillInAlpha = false;
+ unsigned char *pDest = bitmap.GetBits();
+
+ switch( mode )
+ {
+ case MODE_RGBA:
+ bShouldFillInAlpha = ( nChannelsCount == 3 );
+ break;
+
+ case MODE_PALETTIZED:
+ {
+ // Convert from palette
+ bShouldFillInAlpha = ( nChannelsCount == 1 );
+ for( int j=0; j < bitmap.Height(); ++j )
+ {
+ for ( int k = 0; k < bitmap.Width(); ++k, pDest += 4 )
+ {
+ unsigned char nPaletteIndex = pDest[0];
+ pDest[0] = palette.m_pRed[nPaletteIndex];
+ pDest[1] = palette.m_pGreen[nPaletteIndex];
+ pDest[2] = palette.m_pBlue[nPaletteIndex];
+ }
+ }
+ }
+ break;
+
+ case MODE_GREYSCALE:
+ {
+ // Monochrome
+ bShouldFillInAlpha = ( nChannelsCount == 1 );
+ for( int j=0; j < bitmap.Height(); ++j )
+ {
+ for ( int k = 0; k < bitmap.Width(); ++k, pDest += 4 )
+ {
+ pDest[1] = pDest[0];
+ pDest[2] = pDest[0];
+ }
+ }
+ }
+ break;
+
+ case MODE_CMYK:
+ {
+ // NOTE: The conversion will fill in alpha by default
+ bShouldFillInAlpha = false;
+ for( int j=0; j < bitmap.Height(); ++j )
+ {
+ for ( int k = 0; k < bitmap.Width(); ++k, pDest += 4 )
+ {
+ CMYKToRGB( *((RGBA8888_t*)pDest) );
+ }
+ }
+ }
+ break;
+ }
+
+ if ( bShouldFillInAlpha )
+ {
+ // No alpha channel, fill in white
+ unsigned char *pDestAlpha = bitmap.GetBits();
+ for( int j=0; j < bitmap.Height(); ++j )
+ {
+ for ( int k = 0; k < bitmap.Width(); ++k, pDestAlpha += 4 )
+ {
+ pDestAlpha[3] = 0xFF;
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Deals with uncompressed channels
+//-----------------------------------------------------------------------------
+static int s_pChannelIndex[MODE_COUNT+1][4] =
+{
+ { -1, -1, -1, -1 },
+ { 0, 3, -1, -1 }, // MODE_GREYSCALE
+ { 0, 3, -1, -1 }, // MODE_PALETTIZED
+ { 0, 1, 2, 3 }, // MODE_RGBA
+ { 0, 1, 2, 3 }, // MODE_CMYK
+ { -1, -1, -1, -1 },
+ { -1, -1, -1, -1 },
+ { -1, -1, -1, -1 }, // MODE_MULTICHANNEL
+ { -1, -1, -1, -1 },
+ { -1, -1, -1, -1 }, // MODE_LAB
+ { 3, -1, -1, -1 }, // Secret second pass mode for CMYK
+};
+
+
+static void PSDReadUncompressedChannels( CUtlBuffer &buf, int nChannelsCount, PSDMode_t mode, PSDPalette_t &palette, Bitmap_t &bitmap )
+{
+ unsigned char *pChannelRow = (unsigned char*)_alloca( bitmap.Width() );
+ for ( int i=0; i<nChannelsCount; ++i )
+ {
+ int nIndex = s_pChannelIndex[mode][i];
+ Assert( nIndex != -1 );
+
+ unsigned char *pDest = bitmap.GetBits();
+ for( int j=0; j < bitmap.Height(); ++j )
+ {
+ buf.Get( pChannelRow, bitmap.Width() );
+
+ // Collate the channels together
+ for ( int k = 0; k < bitmap.Width(); ++k, pDest += 4 )
+ {
+ pDest[nIndex] = pChannelRow[k];
+ }
+ }
+ }
+
+ PSDConvertToRGBA8888( nChannelsCount, mode, palette, bitmap );
+}
+
+
+//-----------------------------------------------------------------------------
+// Deals with compressed channels
+//-----------------------------------------------------------------------------
+static void PSDReadCompressedChannels( CUtlBuffer &buf, int nChannelsCount, PSDMode_t mode, PSDPalette_t &palette, Bitmap_t &bitmap )
+{
+ unsigned char *pChannelRow = (unsigned char*)_alloca( bitmap.Width() );
+ for ( int i=0; i<nChannelsCount; ++i )
+ {
+ int nIndex = s_pChannelIndex[mode][i];
+ Assert( nIndex != -1 );
+
+ unsigned char *pDest = bitmap.GetBits();
+ for( int j=0; j < bitmap.Height(); ++j )
+ {
+ unsigned char *pSrc = pChannelRow;
+ unsigned int nPixelsRemaining = bitmap.Width();
+ while ( nPixelsRemaining > 0 )
+ {
+ int nCount = buf.GetChar();
+ if ( nCount >= 0 )
+ {
+ // If nCount is between 0 + 7F, it means copy the next nCount+1 bytes directly
+ ++nCount;
+ Assert( (unsigned int)nCount <= nPixelsRemaining );
+ buf.Get( pSrc, nCount );
+ }
+ else
+ {
+ // If nCount is between 80 and FF, it means replicate the next byte -Count+1 times
+ nCount = -nCount + 1;
+ Assert( (unsigned int)nCount <= nPixelsRemaining );
+ unsigned char nPattern = buf.GetUnsignedChar();
+ memset( pSrc, nPattern, nCount );
+ }
+ pSrc += nCount;
+ nPixelsRemaining -= nCount;
+ }
+ Assert( nPixelsRemaining == 0 );
+
+ // Collate the channels together
+ for ( int k = 0; k < bitmap.Width(); ++k, pDest += 4 )
+ {
+ pDest[nIndex] = pChannelRow[k];
+ }
+ }
+ }
+
+ PSDConvertToRGBA8888( nChannelsCount, mode, palette, bitmap );
+}
+
+
+//-----------------------------------------------------------------------------
+// Reads the PSD file into the specified buffer
+//-----------------------------------------------------------------------------
+bool PSDReadFileRGBA8888( CUtlBuffer &buf, Bitmap_t &bitmap )
+{
+ PSDHeader_t header;
+ buf.Get( &header, sizeof(header) );
+
+ if ( BigLong( header.m_nSignature ) != PSD_SIGNATURE )
+ return false;
+ if ( BigShort( header.m_nVersion ) != 1 )
+ return false;
+ if ( BigShort( header.m_nDepth ) != 8 )
+ return false;
+
+ PSDMode_t mode = (PSDMode_t)BigShort( header.m_nMode );
+ int nChannelsCount = BigShort( header.m_nChannels );
+
+ if ( mode == MODE_MULTICHANNEL || mode == MODE_LAB )
+ return false;
+
+ switch ( mode )
+ {
+ case MODE_RGBA:
+ if ( nChannelsCount < 3 )
+ return false;
+ break;
+
+ case MODE_GREYSCALE:
+ case MODE_PALETTIZED:
+ if ( nChannelsCount != 1 && nChannelsCount != 2 )
+ return false;
+ break;
+
+ case MODE_CMYK:
+ if ( nChannelsCount < 4 )
+ return false;
+ break;
+
+ default:
+ Warning( "Unsupported PSD color mode!\n" );
+ return false;
+ }
+
+ int nWidth = BigLong( header.m_nColumns );
+ int nHeight = BigLong( header.m_nRows );
+
+ // Skip parts of memory we don't care about
+ int nColorModeSize = BigLong( buf.GetUnsignedInt() );
+ Assert( nColorModeSize % 3 == 0 );
+ unsigned char *pPaletteBits = (unsigned char*)_alloca( nColorModeSize );
+ PSDPalette_t palette;
+ palette.m_pRed = palette.m_pGreen = palette.m_pBlue = 0;
+ if ( nColorModeSize )
+ {
+ int nPaletteSize = nColorModeSize / 3;
+ buf.Get( pPaletteBits, nColorModeSize );
+ palette.m_pRed = pPaletteBits;
+ palette.m_pGreen = palette.m_pRed + nPaletteSize;
+ palette.m_pBlue = palette.m_pGreen + nPaletteSize;
+ }
+ int nImageResourcesSize = BigLong( buf.GetUnsignedInt() );
+ buf.SeekGet( CUtlBuffer::SEEK_CURRENT, nImageResourcesSize );
+ int nLayersSize = BigLong( buf.GetUnsignedInt() );
+ buf.SeekGet( CUtlBuffer::SEEK_CURRENT, nLayersSize );
+
+ unsigned short nCompressionType = BigShort( buf.GetShort() );
+
+ bitmap.Init( nWidth, nHeight, IMAGE_FORMAT_RGBA8888 );
+
+ bool bSecondPassCMYKA = ( nChannelsCount > 4 && mode == MODE_CMYK );
+ if ( nCompressionType == 0 )
+ {
+ PSDReadUncompressedChannels( buf, ( nChannelsCount > 4 ) ? 4 : nChannelsCount, mode, palette, bitmap );
+ }
+ else
+ {
+ // Skip the data that indicates the length of each compressed row in bytes
+ // NOTE: There are two bytes per row per channel
+ unsigned int nLineLengthData = sizeof(unsigned short) * bitmap.Height() * nChannelsCount;
+ buf.SeekGet( CUtlBuffer::SEEK_CURRENT, nLineLengthData );
+ PSDReadCompressedChannels( buf, ( nChannelsCount > 4 ) ? 4 : nChannelsCount, mode, palette, bitmap );
+ }
+
+ // Read the alpha in a second pass for CMYKA
+ if ( bSecondPassCMYKA )
+ {
+ if ( nCompressionType == 0 )
+ {
+ PSDReadUncompressedChannels( buf, 1, MODE_COUNT, palette, bitmap );
+ }
+ else
+ {
+ PSDReadCompressedChannels( buf, 1, MODE_COUNT, palette, bitmap );
+ }
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Loads the heightfield from a file
+//-----------------------------------------------------------------------------
+bool PSDReadFileRGBA8888( const char *pFileName, const char *pPathID, Bitmap_t &bitmap )
+{
+ CUtlStreamBuffer buf( pFileName, pPathID, CUtlBuffer::READ_ONLY );
+ if ( !g_pFullFileSystem->ReadFile( pFileName, pPathID, buf, sizeof(PSDHeader_t) ) )
+ {
+ Warning( "Unable to read file %s\n", pFileName );
+ return false;
+ }
+ return PSDReadFileRGBA8888( buf, bitmap );
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+//
+// PSD Helper structs implementation
+//
+//////////////////////////////////////////////////////////////////////////
+
+PSDImageResources::ResElement PSDImageResources::FindElement( Resource eType ) const
+{
+ ResElement res;
+ memset( &res, 0, sizeof( res ) );
+
+ unsigned char const *pvBuffer = m_pvBuffer, * const pvBufferEnd = m_pvBuffer + m_numBytes;
+ while ( pvBuffer < pvBufferEnd )
+ {
+ // 4 : signature
+ // 2 : type
+ // 4 : reserved
+ // 2 : length
+ // bytes[ length ]
+
+ unsigned long uSignature = BigLong( *( unsigned long * )( pvBuffer ) );
+ pvBuffer += 4;
+ if ( uSignature != PSD_IMGRES_SIGNATURE )
+ break;
+
+ unsigned short uType = BigShort( *( unsigned short * )( pvBuffer ) );
+ pvBuffer += 6;
+
+ unsigned short uLength = BigShort( *( unsigned short * )( pvBuffer ) );
+ pvBuffer += 2;
+
+ if ( uType == eType )
+ {
+ res.m_eType = eType;
+ res.m_numBytes = uLength;
+ res.m_pvData = pvBuffer;
+ break;
+ }
+ else
+ {
+ pvBuffer += ( ( uLength + 1 ) &~1 );
+ }
+ }
+
+ return res;
+}
+
+PSDResFileInfo::ResFileInfoElement PSDResFileInfo::FindElement( ResFileInfo eType ) const
+{
+ ResFileInfoElement res;
+ memset( &res, 0, sizeof( res ) );
+
+ unsigned char const *pvBuffer = m_res.m_pvData, * const pvBufferEnd = pvBuffer + m_res.m_numBytes;
+ while ( pvBuffer < pvBufferEnd )
+ {
+ // 2 : = 0x1C02
+ // 1 : type
+ // 2 : length
+ // bytes[ length ]
+
+ unsigned short uResLabel = BigShort( *( unsigned short * )( pvBuffer ) );
+ pvBuffer += 2;
+
+ unsigned char uType = *pvBuffer;
+ pvBuffer += 1;
+
+ unsigned short uLength = BigShort( *( unsigned short * )( pvBuffer ) );
+ pvBuffer += 2;
+
+ if ( uType == eType && uResLabel == 0x1C02 )
+ {
+ res.m_eType = eType;
+ res.m_numBytes = uLength;
+ res.m_pvData = pvBuffer;
+ break;
+ }
+ else
+ {
+ pvBuffer += uLength;
+ }
+ }
+
+ return res;
+}
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
+
diff --git a/bitmap/tgaloader.cpp b/bitmap/tgaloader.cpp
new file mode 100644
index 0000000..29b0c31
--- /dev/null
+++ b/bitmap/tgaloader.cpp
@@ -0,0 +1,1001 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include <stdio.h>
+#include "bitmap/tgaloader.h"
+#include "tier0/dbg.h"
+#include "basetypes.h"
+#include <math.h>
+#include "tier1/utlvector.h"
+#include "tier1/utlbuffer.h"
+#include "filesystem.h"
+#include "tier2/tier2.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+namespace TGALoader
+{
+
+//-----------------------------------------------------------------------------
+// Format of the TGA header on disk
+//-----------------------------------------------------------------------------
+
+#pragma pack (1)
+
+struct TGAHeader_t
+{
+ unsigned char id_length;
+ unsigned char colormap_type;
+ unsigned char image_type;
+ unsigned short colormap_index;
+ unsigned short colormap_length;
+ unsigned char colormap_size;
+ unsigned short x_origin;
+ unsigned short y_origin;
+ unsigned short width;
+ unsigned short height;
+ unsigned char pixel_size;
+ unsigned char attributes;
+};
+
+
+//-----------------------------------------------------------------------------
+// read a row into an RGBA8888 array.
+//-----------------------------------------------------------------------------
+
+typedef void (*ReadRowFunc_t)( CUtlBuffer& buf, TGAHeader_t const& header, unsigned char* pDstMemory );
+
+
+//-----------------------------------------------------------------------------
+// output a RGBA8888 row into the destination format.
+//-----------------------------------------------------------------------------
+
+typedef void (*OutputRowFunc_t)( CUtlBuffer& buf, TGAHeader_t const& header, unsigned char* pDstMemory );
+
+
+//-----------------------------------------------------------------------------
+// Important constants
+//-----------------------------------------------------------------------------
+
+#define TGA_MAX_COLORMAP_SIZE ( 256 * 4 )
+#define TGA_MAX_ROW_LENGTH_IN_PIXELS IMAGE_MAX_DIM
+
+
+//-----------------------------------------------------------------------------
+// Globals... blech
+//-----------------------------------------------------------------------------
+static unsigned char g_ColorMap[TGA_MAX_COLORMAP_SIZE];
+
+// run-length state from row to row for RLE images
+static bool g_IsRunLengthPacket;
+static int g_PixelsLeftInPacket;
+
+static unsigned char g_SrcGammaTable[256];
+static unsigned char g_DstGammaTable[256];
+
+typedef CUtlMemory<unsigned char> CTempImage;
+
+
+//-----------------------------------------------------------------------------
+// Reads in a file, sticks it into a UtlVector
+//-----------------------------------------------------------------------------
+
+static bool ReadFile( char const* pFileName, CTempImage& image, int maxbytes = -1 )
+{
+ Assert( pFileName );
+ Assert( g_pFullFileSystem );
+ if( !g_pFullFileSystem )
+ {
+ return false;
+ }
+
+ FileHandle_t fileHandle;
+ fileHandle = g_pFullFileSystem->Open( pFileName, "rb" );
+ if( !fileHandle )
+ return false;
+
+ // How big is the file?
+ long pos;
+ if (maxbytes < 0)
+ {
+ pos = g_pFullFileSystem->Size( fileHandle );
+ }
+ else
+ {
+ pos = maxbytes;
+ }
+
+ // Allocate enough space
+ image.EnsureCapacity( pos );
+
+ // Back to the start of the file
+ g_pFullFileSystem->Seek( fileHandle, 0, FILESYSTEM_SEEK_HEAD );
+
+ // Read the file into the vector memory
+ int len = g_pFullFileSystem->Read( image.Base(), pos, fileHandle );
+
+ // Close the file
+ g_pFullFileSystem->Close( fileHandle );
+
+ // It's an error if we didn't read in enough goodies
+ return len == pos;
+}
+
+
+//-----------------------------------------------------------------------------
+// Reads in the TGA Header
+//-----------------------------------------------------------------------------
+static void ReadHeader( CUtlBuffer& buf, TGAHeader_t& header )
+{
+ buf.Get( &header, sizeof(TGAHeader_t) );
+}
+
+
+//-----------------------------------------------------------------------------
+// Figures out TGA information
+//-----------------------------------------------------------------------------
+bool GetInfo( CUtlBuffer& buf, int *width, int *height,
+ ImageFormat *imageFormat, float *sourceGamma )
+{
+ TGAHeader_t header;
+
+ ReadHeader( buf, header );
+
+ switch( header.image_type )
+ {
+ case 1: // 8 bit uncompressed TGA image
+ case 3: // 8 bit monochrome uncompressed TGA image
+ case 9: // 8 bit compressed TGA image
+ *imageFormat = IMAGE_FORMAT_I8;
+ break;
+ case 2: // 24/32 bit uncompressed TGA image
+ case 10: // 24/32 bit compressed TGA image
+ if( header.pixel_size == 32 )
+ {
+ *imageFormat = IMAGE_FORMAT_ABGR8888;
+ }
+ else if( header.pixel_size == 24 )
+ {
+ *imageFormat = IMAGE_FORMAT_BGR888;
+ }
+ else
+ {
+ return false;
+ }
+ break;
+
+ default:
+ return false;
+ break;
+ }
+
+ *width = header.width;
+ *height = header.height;
+ *sourceGamma = ARTWORK_GAMMA;
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the minimum amount you have to load to get information about the TGA file
+//-----------------------------------------------------------------------------
+int TGAHeaderSize()
+{
+ return sizeof( TGAHeader_t );
+}
+
+
+//-----------------------------------------------------------------------------
+// Gets info about a TGA file
+//-----------------------------------------------------------------------------
+bool GetInfo( char const* pFileName, int *width, int *height,
+ ImageFormat *imageFormat, float *sourceGamma )
+{
+ // temporary memory
+ CTempImage image;
+
+ // try to read in the file
+ if (!ReadFile( pFileName, image, sizeof(TGAHeader_t) ))
+ return false;
+
+ // Serialization buffer
+ CUtlBuffer buf( image.Base(), image.NumAllocated(), CUtlBuffer::READ_ONLY );
+
+ return GetInfo( buf, width, height, imageFormat, sourceGamma );
+}
+
+
+//-----------------------------------------------------------------------------
+// Various output methods
+//-----------------------------------------------------------------------------
+
+void OutputRowRGBA8888( CUtlBuffer& buf, TGAHeader_t const& header, unsigned char* pDst )
+{
+ for( int i = 0; i < header.width; ++i, pDst += 4 )
+ {
+ unsigned char* pSrc = (unsigned char*)buf.PeekGet();
+ pDst[0] = pSrc[0];
+ pDst[1] = pSrc[1];
+ pDst[2] = pSrc[2];
+ pDst[3] = pSrc[3];
+ buf.SeekGet( CUtlBuffer::SEEK_CURRENT, 4 );
+ }
+}
+
+void OutputRowABGR8888( CUtlBuffer& buf, TGAHeader_t const& header, unsigned char* pDst )
+{
+ for( int i = 0; i < header.width; ++i, pDst += 4 )
+ {
+ unsigned char* pSrc = (unsigned char*)buf.PeekGet();
+ pDst[3] = pSrc[0];
+ pDst[2] = pSrc[1];
+ pDst[1] = pSrc[2];
+ pDst[0] = pSrc[3];
+ buf.SeekGet( CUtlBuffer::SEEK_CURRENT, 4 );
+ }
+}
+
+void OutputRowRGB888( CUtlBuffer& buf, TGAHeader_t const& header, unsigned char* pDst )
+{
+ for( int i = 0; i < header.width; ++i, pDst += 3 )
+ {
+ unsigned char* pSrc = (unsigned char*)buf.PeekGet();
+ pDst[0] = pSrc[0];
+ pDst[1] = pSrc[1];
+ pDst[2] = pSrc[2];
+ buf.SeekGet( CUtlBuffer::SEEK_CURRENT, 4 );
+ }
+}
+
+void OutputRowBGR888( CUtlBuffer& buf, TGAHeader_t const& header, unsigned char* pDst )
+{
+ for( int i = 0; i < header.width; ++i, pDst += 3 )
+ {
+ unsigned char* pSrc = (unsigned char*)buf.PeekGet();
+ pDst[2] = pSrc[0];
+ pDst[1] = pSrc[1];
+ pDst[0] = pSrc[2];
+ buf.SeekGet( CUtlBuffer::SEEK_CURRENT, 4 );
+ }
+}
+
+void OutputRowRGB565( CUtlBuffer& buf, TGAHeader_t const& header, unsigned char* pDst )
+{
+ Assert( 0 );
+}
+
+void OutputRowI8( CUtlBuffer& buf, TGAHeader_t const& header, unsigned char* pDst )
+{
+ for( int i = 0; i < header.width; ++i, ++pDst )
+ {
+ unsigned char* pSrc = (unsigned char*)buf.PeekGet();
+
+ if( ( pSrc[0] == pSrc[1] ) && ( pSrc[1] == pSrc[2] ) )
+ {
+ pDst[0] = pSrc[0];
+ }
+ else
+ {
+ pDst[0] = ( unsigned char )( 0.299f * pSrc[0] + 0.587f * pSrc[1] + 0.114f * pSrc[2] );
+ }
+
+ buf.SeekGet( CUtlBuffer::SEEK_CURRENT, 4 );
+ }
+}
+
+void OutputRowIA88( CUtlBuffer& buf, TGAHeader_t const& header, unsigned char* pDst )
+{
+ for( int i = 0; i < header.width; ++i, pDst += 2 )
+ {
+ unsigned char* pSrc = (unsigned char*)buf.PeekGet();
+
+ if( ( pSrc[0] == pSrc[1] ) && ( pSrc[1] == pSrc[2] ) )
+ {
+ pDst[0] = pSrc[0];
+ }
+ else
+ {
+ pDst[0] = ( unsigned char )( 0.299f * pSrc[0] + 0.587f * pSrc[1] + 0.114f * pSrc[2] );
+ }
+ pDst[1] = pSrc[3];
+
+ buf.SeekGet( CUtlBuffer::SEEK_CURRENT, 4 );
+ }
+}
+
+void OutputRowA8( CUtlBuffer& buf, TGAHeader_t const& header, unsigned char* pDst )
+{
+ for( int i = 0; i < header.width; ++i, ++pDst )
+ {
+ unsigned char* pSrc = (unsigned char*)buf.PeekGet();
+ pDst[0] = pSrc[3];
+ buf.SeekGet( CUtlBuffer::SEEK_CURRENT, 4 );
+ }
+}
+
+void OutputRowRGB888BlueScreen( CUtlBuffer& buf, TGAHeader_t const& header, unsigned char* pDst )
+{
+ for( int i = 0; i < header.width; ++i, pDst += 3 )
+ {
+ unsigned char* pSrc = (unsigned char*)buf.PeekGet();
+ pDst[0] = (unsigned char)(( ( int )pSrc[0] * ( int )pSrc[3] ) >> 8);
+ pDst[1] = (unsigned char)(( ( int )pSrc[1] * ( int )pSrc[3] ) >> 8);
+ pDst[2] = (( ( ( ( int )pSrc[2] * ( int )pSrc[3] ) ) >> 8 ) + ( 255 - pSrc[3] ));
+ buf.SeekGet( CUtlBuffer::SEEK_CURRENT, 4 );
+ }
+}
+
+void OutputRowBGR888BlueScreen( CUtlBuffer& buf, TGAHeader_t const& header, unsigned char* pDst )
+{
+ for( int i = 0; i < header.width; ++i, pDst += 3 )
+ {
+ unsigned char* pSrc = (unsigned char*)buf.PeekGet();
+ pDst[2] = (unsigned char)(( ( int )pSrc[0] * ( int )pSrc[3] ) >> 8);
+ pDst[1] = (unsigned char)(( ( int )pSrc[1] * ( int )pSrc[3] ) >> 8);
+ pDst[0] = (unsigned char)(( ( ( ( int )pSrc[2] * ( int )pSrc[3] ) ) >> 8 ) + ( 255 - pSrc[3] ));
+ buf.SeekGet( CUtlBuffer::SEEK_CURRENT, 4 );
+ }
+}
+
+void OutputRowARGB8888( CUtlBuffer& buf, TGAHeader_t const& header, unsigned char* pDst )
+{
+ for( int i = 0; i < header.width; ++i, pDst += 4 )
+ {
+ unsigned char* pSrc = (unsigned char*)buf.PeekGet();
+ pDst[0] = pSrc[3];
+ pDst[1] = pSrc[0];
+ pDst[2] = pSrc[1];
+ pDst[3] = pSrc[2];
+ buf.SeekGet( CUtlBuffer::SEEK_CURRENT, 4 );
+ }
+}
+
+void OutputRowBGRA8888( CUtlBuffer& buf, TGAHeader_t const& header, unsigned char* pDst )
+{
+ for( int i = 0; i < header.width; ++i, pDst += 4 )
+ {
+ unsigned char* pSrc = (unsigned char*)buf.PeekGet();
+ pDst[0] = pSrc[2];
+ pDst[1] = pSrc[1];
+ pDst[2] = pSrc[0];
+ pDst[3] = pSrc[3];
+ buf.SeekGet( CUtlBuffer::SEEK_CURRENT, 4 );
+ }
+}
+
+void OutputRowBGRX8888( CUtlBuffer& buf, TGAHeader_t const& header, unsigned char* pDst )
+{
+ for( int i = 0; i < header.width; ++i, pDst += 4 )
+ {
+ unsigned char* pSrc = (unsigned char*)buf.PeekGet();
+ pDst[0] = pSrc[2];
+ pDst[1] = pSrc[1];
+ pDst[2] = pSrc[0];
+ pDst[3] = 255;
+ buf.SeekGet( CUtlBuffer::SEEK_CURRENT, 4 );
+ }
+}
+
+void OutputRowBGR565( CUtlBuffer& buf, TGAHeader_t const& header, unsigned char* pDst )
+{
+ for( int i = 0; i < header.width; ++i, pDst += 2 )
+ {
+ unsigned char* pSrc = (unsigned char*)buf.PeekGet();
+ unsigned short rgba = (pSrc[2] & 0x1F) | ((pSrc[1] & 0x3F) << 5) |
+ ((pSrc[0] & 0x1F) << 11);
+
+ pDst[0] = rgba & 0xFF;
+ pDst[1] = rgba >> 8;
+ buf.SeekGet( CUtlBuffer::SEEK_CURRENT, 4 );
+ }
+}
+
+void OutputRowBGRX5551( CUtlBuffer& buf, TGAHeader_t const& header, unsigned char* pDst )
+{
+ for( int i = 0; i < header.width; ++i, pDst += 2 )
+ {
+ unsigned char* pSrc = (unsigned char*)buf.PeekGet();
+ unsigned short rgba = (pSrc[2] & 0x1F) | ((pSrc[1] & 0x1F) << 5) |
+ ((pSrc[0] & 0x1F) << 10) | 0x8000;
+
+ pDst[0] = rgba & 0xFF;
+ pDst[1] = rgba >> 8;
+ buf.SeekGet( CUtlBuffer::SEEK_CURRENT, 4 );
+ }
+}
+
+static OutputRowFunc_t GetOutputRowFunc( ImageFormat imageFormat )
+{
+ switch( imageFormat )
+ {
+ case IMAGE_FORMAT_RGBA8888:
+ return &OutputRowRGBA8888;
+ case IMAGE_FORMAT_ABGR8888:
+ return &OutputRowABGR8888;
+ case IMAGE_FORMAT_RGB888:
+ return &OutputRowRGB888;
+ case IMAGE_FORMAT_BGR888:
+ return &OutputRowBGR888;
+ case IMAGE_FORMAT_RGB565:
+ return &OutputRowRGB565;
+ case IMAGE_FORMAT_I8:
+ return &OutputRowI8;
+ case IMAGE_FORMAT_IA88:
+ return &OutputRowIA88;
+ case IMAGE_FORMAT_A8:
+ return &OutputRowA8;
+ case IMAGE_FORMAT_RGB888_BLUESCREEN:
+ return &OutputRowRGB888BlueScreen;
+ case IMAGE_FORMAT_BGR888_BLUESCREEN:
+ return &OutputRowBGR888BlueScreen;
+ case IMAGE_FORMAT_ARGB8888:
+ return &OutputRowARGB8888;
+ case IMAGE_FORMAT_BGRA8888:
+ return &OutputRowBGRA8888;
+ case IMAGE_FORMAT_BGRX8888:
+ return &OutputRowBGRX8888;
+ case IMAGE_FORMAT_BGR565:
+ return &OutputRowBGR565;
+ case IMAGE_FORMAT_BGRX5551:
+ return &OutputRowBGRX5551;
+#ifdef _X360
+ case IMAGE_FORMAT_LINEAR_RGB888:
+ return &OutputRowRGB888;
+ case IMAGE_FORMAT_LINEAR_BGR888:
+ return &OutputRowBGR888;
+#endif
+ default:
+ return NULL;
+ break;
+ }
+}
+
+#if 0
+static void InitSourceGammaConversionTable( float srcGamma )
+{
+ static float lastSrcGamma = -1;
+ if (lastSrcGamma == srcGamma)
+ return;
+
+ lastSrcGamma = srcGamma;
+ ImageLoader::ConstructGammaTable( g_SrcGammaTable, srcGamma, 1.0f );
+}
+
+static void InitDestGammaConversionTable( float dstGamma )
+{
+ static float lastDstGamma = -1;
+ if (lastDstGamma == dstGamma)
+ return;
+
+ lastDstGamma = dstGamma;
+ ImageLoader::ConstructGammaTable( g_DstGammaTable, 1.0f, dstGamma );
+}
+#endif
+
+//-----------------------------------------------------------------------------
+// Reads an 8-bit palettized TGA image
+//-----------------------------------------------------------------------------
+
+void ReadRow8BitUncompressedWithColormap( CUtlBuffer& buf,
+ TGAHeader_t const& header, unsigned char* pDst )
+{
+ int i;
+ unsigned char* colormapEntry;
+
+ switch( header.colormap_size )
+ {
+ case 8:
+ for( i = 0; i < header.width; ++i, pDst += 4 )
+ {
+ int pal = buf.GetUnsignedChar();
+
+ colormapEntry = &g_ColorMap[pal];
+ pDst[0] = colormapEntry[0];
+ pDst[1] = colormapEntry[0];
+ pDst[2] = colormapEntry[0];
+ pDst[3] = 255;
+ }
+ break;
+
+ case 24:
+ for( i = 0; i < header.width; ++i, pDst += 4 )
+ {
+ int pal = buf.GetUnsignedChar();
+
+ colormapEntry = &g_ColorMap[pal * 3];
+ pDst[0] = colormapEntry[2];
+ pDst[1] = colormapEntry[1];
+ pDst[2] = colormapEntry[0];
+ pDst[3] = 255;
+ }
+ break;
+
+ case 32:
+ for( i = 0; i < header.width; ++i, pDst += 4 )
+ {
+ int pal = buf.GetUnsignedChar();
+
+ colormapEntry = &g_ColorMap[pal * 4];
+ pDst[0] = colormapEntry[3];
+ pDst[1] = colormapEntry[2];
+ pDst[2] = colormapEntry[1];
+ pDst[3] = colormapEntry[0];
+ }
+ break;
+
+ default:
+ Assert( 0 );
+ break;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Reads an 8-bit greyscale TGA image
+//-----------------------------------------------------------------------------
+
+void ReadRow8BitUncompressedWithoutColormap( CUtlBuffer& buf,
+ TGAHeader_t const& header, unsigned char* pDst )
+{
+ for( int i = 0; i < header.width; ++i, pDst += 4 )
+ {
+ pDst[0] = pDst[1] = pDst[2] = buf.GetUnsignedChar();
+ pDst[3] = 255;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Reads a 24-bit TGA image
+//-----------------------------------------------------------------------------
+
+void ReadRow24BitUncompressedWithoutColormap( CUtlBuffer& buf,
+ TGAHeader_t const& header, unsigned char* pDst )
+{
+ for( int i = 0; i < header.width; ++i, pDst += 4 )
+ {
+ pDst[2] = buf.GetUnsignedChar();
+ pDst[1] = buf.GetUnsignedChar();
+ pDst[0] = buf.GetUnsignedChar();
+ pDst[3] = 255;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Reads a 32-bit TGA image
+//-----------------------------------------------------------------------------
+
+void ReadRow32BitUncompressedWithoutColormap( CUtlBuffer& buf,
+ TGAHeader_t const& header, unsigned char* pDst )
+{
+ for( int i = 0; i < header.width; ++i, pDst += 4 )
+ {
+ pDst[2] = buf.GetUnsignedChar();
+ pDst[1] = buf.GetUnsignedChar();
+ pDst[0] = buf.GetUnsignedChar();
+ pDst[3] = buf.GetUnsignedChar();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Decompresses a run-length encoded row of bytes
+//-----------------------------------------------------------------------------
+
+static void DecompressRow( CUtlBuffer& buf, TGAHeader_t const& header, unsigned char* pDst )
+{
+ int bytesPerPixel = header.pixel_size >> 3;
+ int pixelsLeftInRow = header.width;
+ int numPixelsToProcess;
+
+#ifdef DBGFLAG_ASSERT
+ unsigned char *pLast = pDst + header.width * bytesPerPixel;
+#endif
+
+ unsigned char repeat[4] = {};
+ do
+ {
+ if( !g_PixelsLeftInPacket )
+ {
+ // start a new packet.
+ unsigned char packetHeader = buf.GetUnsignedChar();
+ g_PixelsLeftInPacket = 1 + ( packetHeader & 0x7f );
+ if( packetHeader & 0x80 )
+ {
+ g_IsRunLengthPacket = true;
+
+ // Read what I'm supposed to repeat
+ for (int i = 0; i < bytesPerPixel; ++i)
+ {
+ repeat[i] = buf.GetUnsignedChar();
+ }
+ }
+ else
+ {
+ g_IsRunLengthPacket = false;
+ }
+ }
+
+ // already in the middle of a packet of data.
+ numPixelsToProcess = g_PixelsLeftInPacket;
+ if( numPixelsToProcess > pixelsLeftInRow )
+ {
+ numPixelsToProcess = pixelsLeftInRow;
+ }
+ if( g_IsRunLengthPacket )
+ {
+ for( int i = numPixelsToProcess; --i >= 0; pDst += bytesPerPixel )
+ {
+ for (int j = 0; j < bytesPerPixel; ++j )
+ {
+ pDst[j] = repeat[j];
+ }
+ }
+ }
+ else
+ {
+ buf.Get( pDst, numPixelsToProcess * bytesPerPixel );
+ pDst += numPixelsToProcess * bytesPerPixel;
+ }
+
+ g_PixelsLeftInPacket -= numPixelsToProcess;
+ pixelsLeftInRow -= numPixelsToProcess;
+
+ } while( pixelsLeftInRow );
+
+ Assert( pDst == pLast );
+}
+
+
+//-----------------------------------------------------------------------------
+// Reads a compressed 8-bit palettized TGA image
+//-----------------------------------------------------------------------------
+void ReadRow8BitCompressedWithColormap( CUtlBuffer& buf,
+ TGAHeader_t const& header, unsigned char* pDst )
+{
+ unsigned char rowI_8[TGA_MAX_ROW_LENGTH_IN_PIXELS];
+
+ DecompressRow( buf, header, rowI_8 );
+
+ CUtlBuffer uncompressedBuf( rowI_8, TGA_MAX_ROW_LENGTH_IN_PIXELS, CUtlBuffer::READ_ONLY );
+ ReadRow8BitUncompressedWithColormap( uncompressedBuf, header, pDst );
+}
+
+
+//-----------------------------------------------------------------------------
+// Reads a compressed 8-bit greyscale TGA image
+//-----------------------------------------------------------------------------
+void ReadRow8BitCompressedWithoutColormap( CUtlBuffer& buf,
+ TGAHeader_t const& header, unsigned char* pDst )
+{
+ unsigned char rowI_8[TGA_MAX_ROW_LENGTH_IN_PIXELS];
+
+ DecompressRow( buf, header, rowI_8 );
+
+ CUtlBuffer uncompressedBuf( rowI_8, TGA_MAX_ROW_LENGTH_IN_PIXELS, CUtlBuffer::READ_ONLY );
+ ReadRow8BitUncompressedWithoutColormap( uncompressedBuf, header, pDst );
+}
+
+//-----------------------------------------------------------------------------
+// Reads a compressed 24-bit TGA image
+//-----------------------------------------------------------------------------
+
+void ReadRow24BitCompressedWithoutColormap( CUtlBuffer& buf,
+ TGAHeader_t const& header, unsigned char* pDst )
+{
+ unsigned char rowBGR_888[TGA_MAX_ROW_LENGTH_IN_PIXELS * 3];
+
+ DecompressRow( buf, header, rowBGR_888 );
+
+ CUtlBuffer uncompressedBuf( rowBGR_888, TGA_MAX_ROW_LENGTH_IN_PIXELS * 3, CUtlBuffer::READ_ONLY );
+ ReadRow24BitUncompressedWithoutColormap( uncompressedBuf, header, pDst );
+}
+
+//-----------------------------------------------------------------------------
+// Reads a compressed 32-bit TGA image
+//-----------------------------------------------------------------------------
+
+void ReadRow32BitCompressedWithoutColormap( CUtlBuffer& buf,
+ TGAHeader_t const& header, unsigned char* pDst )
+{
+ unsigned char rowBGRA_8888[TGA_MAX_ROW_LENGTH_IN_PIXELS << 2];
+
+ DecompressRow( buf, header, rowBGRA_8888 );
+
+ CUtlBuffer uncompressedBuf( rowBGRA_8888, TGA_MAX_ROW_LENGTH_IN_PIXELS << 2, CUtlBuffer::READ_ONLY );
+ ReadRow32BitUncompressedWithoutColormap( uncompressedBuf, header, pDst );
+}
+
+//-----------------------------------------------------------------------------
+// Method used to read the TGA
+//-----------------------------------------------------------------------------
+
+static ReadRowFunc_t GetReadRowFunc( TGAHeader_t const& header )
+{
+ switch( header.image_type )
+ {
+ case 1: // 8 bit uncompressed TGA image
+ case 3: // 8 bit monochrome uncompressed TGA image
+ if( header.colormap_length )
+ {
+ return &ReadRow8BitUncompressedWithColormap;
+ }
+ else
+ {
+ return &ReadRow8BitUncompressedWithoutColormap;
+ }
+ case 9: // 8 bit compressed TGA image
+ if( header.colormap_length )
+ {
+ return &ReadRow8BitCompressedWithColormap;
+ }
+ else
+ {
+ return &ReadRow8BitCompressedWithoutColormap;
+ }
+ case 2: // 24/32 bit uncompressed TGA image
+ switch( header.pixel_size )
+ {
+ case 24:
+ return &ReadRow24BitUncompressedWithoutColormap;
+ break;
+ case 32:
+ return &ReadRow32BitUncompressedWithoutColormap;
+ break;
+ default:
+ //Error( "unsupported tga colordepth: %d", TGAHeader_t.pixel_size" );
+ return 0;
+ break;
+ }
+ case 10: // 24/32 bit compressed TGA image
+ if( header.colormap_length )
+ {
+ // Error( "colormaps not support with 24/32 bit TGAs." );
+ return 0;
+ }
+ else
+ {
+ switch( header.pixel_size )
+ {
+ case 24:
+ return &ReadRow24BitCompressedWithoutColormap;
+ break;
+ case 32:
+ return &ReadRow32BitCompressedWithoutColormap;
+ break;
+ default:
+ //Error( "unsupported tga colordepth: %d", TGAHeader_t.pixel_size" );
+ return NULL;
+ break;
+ }
+ }
+ default:
+ // Error( "unsupported tga pixel format" );
+ return 0;
+ break;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Reads the color map
+//-----------------------------------------------------------------------------
+
+static bool ReadColormap( CUtlBuffer& buf, TGAHeader_t const& header )
+{
+ int numColormapBytes = header.colormap_length * ( header.colormap_size >> 3 );
+ if( numColormapBytes > TGA_MAX_COLORMAP_SIZE )
+ {
+ // Error( "colormap bigger than TGA_MAX_COLORMAP_SIZE" );
+ return false;
+ }
+
+ // read colormap
+ buf.Get( g_ColorMap, numColormapBytes );
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Reads the source image
+//-----------------------------------------------------------------------------
+static bool ReadSourceImage( CUtlBuffer& buf, TGAHeader_t& header, CTempImage& image )
+{
+ // Figure out our reading and riting
+ ReadRowFunc_t ReadRowFunc = GetReadRowFunc( header );
+ if( !ReadRowFunc )
+ return false;
+
+ // HACK: Fixme: We really shouldn't be using globals here
+ // Init RLE vars
+ g_PixelsLeftInPacket = 0;
+
+ // Only allocate the memory once
+ int memRequired = ImageLoader::GetMemRequired( header.width, header.height, 1,
+ IMAGE_FORMAT_RGBA8888, false );
+ image.EnsureCapacity( memRequired );
+
+ // read each row and process it. Note the image is upside-down from
+ // the way we want it.
+ unsigned char* pDstBits;
+ // flip the image vertically if necessary.
+ if (header.attributes & 0x20)
+ {
+ for( int row = 0; row < header.height; ++row )
+ {
+ pDstBits = image.Base() +
+ row * header.width * ImageLoader::SizeInBytes(IMAGE_FORMAT_RGBA8888);
+ ReadRowFunc( buf, header, pDstBits );
+ }
+ }
+ else
+ {
+ for( int row = header.height; --row >= 0; )
+ {
+ pDstBits = image.Base() +
+ row * header.width * ImageLoader::SizeInBytes(IMAGE_FORMAT_RGBA8888);
+ ReadRowFunc( buf, header, pDstBits );
+ }
+ }
+
+ return true;
+}
+
+#if 0
+//-----------------------------------------------------------------------------
+// Outputs the final image
+//-----------------------------------------------------------------------------
+static bool OutputImage( CTempImage& image, TGAHeader_t& header,
+ ImageFormat imageFormat, unsigned char* pDst )
+{
+ // How do we write?
+ OutputRowFunc_t OutputRowFunc = GetOutputRowFunc( imageFormat );
+ if( !OutputRowFunc )
+ return false;
+
+ CUtlBuffer buf( image.Base(), image.NumAllocated(), CUtlBuffer::READ_ONLY );
+ unsigned char* pDstBits;
+ for( int row = 0; row < header.height; ++row )
+ {
+ pDstBits = pDst +
+ row * header.width * ImageLoader::SizeInBytes(imageFormat);
+ OutputRowFunc( buf, header, pDstBits );
+ }
+
+ return true;
+}
+#endif
+
+//-----------------------------------------------------------------------------
+// Parses the lovely bits previously read from disk
+//-----------------------------------------------------------------------------
+bool Load( unsigned char *pOutputImage, CUtlBuffer& buf, int width,
+ int height, ImageFormat imageFormat, float targetGamma, bool mipmap )
+{
+ TGAHeader_t header;
+
+ // Read the TGA header
+ ReadHeader( buf, header );
+
+ // skip TARGA image comment
+ if( header.id_length != 0 )
+ {
+ buf.SeekGet( CUtlBuffer::SEEK_CURRENT, header.id_length );
+ }
+
+ // Read the color map for palettized images
+ if( header.colormap_length != 0 )
+ {
+ if (!ReadColormap( buf, header ))
+ return false;
+ }
+
+ // Stores the RGBA8888 temp version of the image which we'll use to
+ // to do mipmapping...
+ CTempImage tmpImage;
+ if (!ReadSourceImage( buf, header, tmpImage ))
+ return false;
+
+ // Erg... what if header.width * header.height > width * height?
+ // Then don't do anything, this is an error condition...
+ if ((width * height) < (header.width * header.height))
+ return false;
+
+ // Now that we've got the source image, generate the mip-map levels
+ ImageLoader::GenerateMipmapLevels( tmpImage.Base(), pOutputImage,
+ header.width, header.height, 1, imageFormat, ARTWORK_GAMMA, targetGamma,
+ mipmap ? 0 : 1 );
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Reads a TGA image from a file
+//-----------------------------------------------------------------------------
+bool Load( unsigned char *pOutputImage, const char *pFileName, int width, int height,
+ ImageFormat imageFormat, float targetGamma, bool mipmap )
+{
+ Assert( pOutputImage && pFileName );
+
+ // memory for the file
+ CTempImage vec;
+
+ // Read that puppy in!
+ if (!ReadFile( pFileName, vec ))
+ return false;
+
+ // Make an unserialization buffer
+ CUtlBuffer buf( vec.Base(), vec.NumAllocated(), CUtlBuffer::READ_ONLY );
+
+ // Do the dirty deed
+ return Load( pOutputImage, buf, width, height, imageFormat, targetGamma, mipmap );
+}
+
+
+//-----------------------------------------------------------------------------
+// Creates a map in linear space
+//-----------------------------------------------------------------------------
+bool LoadRGBA8888( CUtlBuffer& buf, CUtlMemory<unsigned char> &outputData, int &outWidth, int &outHeight )
+{
+ TGAHeader_t header;
+
+ // Read the TGA header
+ ReadHeader( buf, header );
+
+ // skip TARGA image comment
+ if( header.id_length != 0 )
+ {
+ buf.SeekGet( CUtlBuffer::SEEK_CURRENT, header.id_length );
+ }
+
+ // Read the color map for palettized images
+ if( header.colormap_length != 0 )
+ {
+ if (!ReadColormap( buf, header ))
+ return false;
+ }
+
+ // Stores the RGBA8888 temp version of the image which we'll use to
+ // to do mipmapping...
+ int memSize = ImageLoader::GetMemRequired(
+ header.width, header.height, 1, IMAGE_FORMAT_RGBA8888, false );
+
+ outputData.EnsureCapacity( memSize );
+ if (!ReadSourceImage( buf, header, outputData ))
+ return false;
+
+ outWidth = header.width;
+ outHeight = header.height;
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Reads a TGA, keeps it in RGBA8888
+//-----------------------------------------------------------------------------
+
+bool LoadRGBA8888( const char *pFileName, CUtlMemory<unsigned char> &outputData, int &outWidth, int &outHeight )
+{
+ Assert( pFileName );
+
+ // memory for the file
+ CTempImage vec;
+
+ // Read that puppy in!
+ if (!ReadFile( pFileName, vec ))
+ return false;
+
+ // Make an unserialization buffer
+ CUtlBuffer buf( vec.Base(), vec.NumAllocated(), CUtlBuffer::READ_ONLY );
+
+ // Do the dirty deed
+ return LoadRGBA8888( buf, outputData, outWidth, outHeight );
+}
+
+} // end namespace TGALoader
+
diff --git a/bitmap/tgawriter.cpp b/bitmap/tgawriter.cpp
new file mode 100644
index 0000000..a73c996
--- /dev/null
+++ b/bitmap/tgawriter.cpp
@@ -0,0 +1,353 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//===========================================================================//
+
+#include <stdlib.h>
+#include <stdio.h>
+#include "tier0/dbg.h"
+#include <malloc.h>
+#include "filesystem.h"
+#include "bitmap/tgawriter.h"
+#include "tier1/utlbuffer.h"
+#include "bitmap/imageformat.h"
+#include "tier2/tier2.h"
+#include "tier2/fileutils.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+namespace TGAWriter
+{
+
+#pragma pack(1)
+struct TGAHeader_t
+{
+ unsigned char id_length;
+ unsigned char colormap_type;
+ unsigned char image_type;
+ unsigned short colormap_index;
+ unsigned short colormap_length;
+ unsigned char colormap_size;
+ unsigned short x_origin;
+ unsigned short y_origin;
+ unsigned short width;
+ unsigned short height;
+ unsigned char pixel_size;
+ unsigned char attributes;
+};
+#pragma pack()
+
+#define fputc myfputc
+#define fwrite myfwrite
+
+static void fputLittleShort( unsigned short s, CUtlBuffer &buffer )
+{
+ buffer.PutChar( s & 0xff );
+ buffer.PutChar( s >> 8 );
+}
+
+static inline void myfputc( unsigned char c, FileHandle_t fileHandle )
+{
+ g_pFullFileSystem->Write( &c, 1, fileHandle );
+}
+
+static inline void myfwrite( void const *data, int size1, int size2, FileHandle_t fileHandle )
+{
+ g_pFullFileSystem->Write( data, size1 * size2, fileHandle );
+}
+
+
+//-----------------------------------------------------------------------------
+// FIXME: assumes that we don't need to do gamma correction.
+//-----------------------------------------------------------------------------
+bool WriteToBuffer( unsigned char *pImageData, CUtlBuffer &buffer, int width, int height,
+ ImageFormat srcFormat, ImageFormat dstFormat )
+{
+ TGAHeader_t header;
+
+ // Fix the dstFormat to match what actually is going to go into the file
+ switch( dstFormat )
+ {
+ case IMAGE_FORMAT_RGB888:
+ dstFormat = IMAGE_FORMAT_BGR888;
+ break;
+#if defined( _X360 )
+ case IMAGE_FORMAT_LINEAR_RGB888:
+ dstFormat = IMAGE_FORMAT_LINEAR_BGR888;
+ break;
+#endif
+ case IMAGE_FORMAT_RGBA8888:
+ dstFormat = IMAGE_FORMAT_BGRA8888;
+ break;
+ }
+
+ header.id_length = 0; // comment length
+ header.colormap_type = 0; // ???
+
+ switch( dstFormat )
+ {
+ case IMAGE_FORMAT_BGR888:
+#if defined( _X360 )
+ case IMAGE_FORMAT_LINEAR_BGR888:
+#endif
+ header.image_type = 2; // 24/32 bit uncompressed TGA
+ header.pixel_size = 24;
+ break;
+ case IMAGE_FORMAT_BGRA8888:
+ header.image_type = 2; // 24/32 bit uncompressed TGA
+ header.pixel_size = 32;
+ break;
+ case IMAGE_FORMAT_I8:
+ header.image_type = 1; // 8 bit uncompressed TGA
+ header.pixel_size = 8;
+ break;
+ default:
+ return false;
+ break;
+ }
+
+ header.colormap_index = 0;
+ header.colormap_length = 0;
+ header.colormap_size = 0;
+ header.x_origin = 0;
+ header.y_origin = 0;
+ header.width = ( unsigned short )width;
+ header.height = ( unsigned short )height;
+ header.attributes = 0x20; // Makes it so we don't have to vertically flip the image
+
+ buffer.PutChar( header.id_length );
+ buffer.PutChar( header.colormap_type );
+ buffer.PutChar( header.image_type );
+ fputLittleShort( header.colormap_index, buffer );
+ fputLittleShort( header.colormap_length, buffer );
+ buffer.PutChar( header.colormap_size );
+ fputLittleShort( header.x_origin, buffer );
+ fputLittleShort( header.y_origin, buffer );
+ fputLittleShort( header.width, buffer );
+ fputLittleShort( header.height, buffer );
+ buffer.PutChar( header.pixel_size );
+ buffer.PutChar( header.attributes );
+
+ int nSizeInBytes = width * height * ImageLoader::SizeInBytes( dstFormat );
+ buffer.EnsureCapacity( buffer.TellPut() + nSizeInBytes );
+ unsigned char *pDst = (unsigned char*)buffer.PeekPut();
+
+ if ( !ImageLoader::ConvertImageFormat( pImageData, srcFormat, pDst, dstFormat, width, height ) )
+ return false;
+
+ buffer.SeekPut( CUtlBuffer::SEEK_CURRENT, nSizeInBytes );
+ return true;
+}
+
+bool WriteDummyFileNoAlloc( const char *fileName, int width, int height, enum ImageFormat dstFormat )
+{
+ TGAHeader_t tgaHeader;
+
+ Assert( g_pFullFileSystem );
+ if( !g_pFullFileSystem )
+ {
+ return false;
+ }
+ COutputFile fp( fileName );
+
+ int nBytesPerPixel, nImageType, nPixelSize;
+
+ switch( dstFormat )
+ {
+ case IMAGE_FORMAT_BGR888:
+#if defined( _X360 )
+ case IMAGE_FORMAT_LINEAR_BGR888:
+#endif
+ nBytesPerPixel = 3; // 24/32 bit uncompressed TGA
+ nPixelSize = 24;
+ nImageType = 2;
+ break;
+ case IMAGE_FORMAT_BGRA8888:
+ nBytesPerPixel = 4; // 24/32 bit uncompressed TGA
+ nPixelSize = 32;
+ nImageType = 2;
+ break;
+ case IMAGE_FORMAT_I8:
+ nBytesPerPixel = 1; // 8 bit uncompressed TGA
+ nPixelSize = 8;
+ nImageType = 1;
+ break;
+ default:
+ return false;
+ break;
+ }
+
+ memset( &tgaHeader, 0, sizeof(tgaHeader) );
+ tgaHeader.id_length = 0;
+ tgaHeader.image_type = (unsigned char) nImageType;
+ tgaHeader.width = (unsigned short) width;
+ tgaHeader.height = (unsigned short) height;
+ tgaHeader.pixel_size = (unsigned char) nPixelSize;
+ tgaHeader.attributes = 0x20;
+
+ // Write the Targa header
+ fp.Write( &tgaHeader, sizeof(TGAHeader_t) );
+
+ // Write out width * height black pixels
+ unsigned char black[4] = { 0x1E, 0x9A, 0xFF, 0x00 };
+ for (int i = 0; i < width * height; i++)
+ {
+ fp.Write( black, nBytesPerPixel );
+ }
+
+ return true;
+}
+
+bool WriteTGAFile( const char *fileName, int width, int height, enum ImageFormat srcFormat, uint8 const *srcData, int nStride )
+{
+ TGAHeader_t tgaHeader;
+
+ COutputFile fp( fileName );
+
+ int nBytesPerPixel, nImageType, nPixelSize;
+
+ bool bMustConvert = false;
+ ImageFormat dstFormat = srcFormat;
+
+ switch( srcFormat )
+ {
+ case IMAGE_FORMAT_BGR888:
+#if defined( _X360 )
+ case IMAGE_FORMAT_LINEAR_BGR888:
+#endif
+ nBytesPerPixel = 3; // 24/32 bit uncompressed TGA
+ nPixelSize = 24;
+ nImageType = 2;
+ break;
+ case IMAGE_FORMAT_BGRA8888:
+ nBytesPerPixel = 4; // 24/32 bit uncompressed TGA
+ nPixelSize = 32;
+ nImageType = 2;
+ break;
+ case IMAGE_FORMAT_RGBA8888:
+ bMustConvert = true;
+ dstFormat = IMAGE_FORMAT_BGRA8888;
+ nBytesPerPixel = 4; // 24/32 bit uncompressed TGA
+ nPixelSize = 32;
+ nImageType = 2;
+ break;
+ case IMAGE_FORMAT_I8:
+ nBytesPerPixel = 1; // 8 bit uncompressed TGA
+ nPixelSize = 8;
+ nImageType = 1;
+ break;
+ default:
+ return false;
+ break;
+ }
+
+ memset( &tgaHeader, 0, sizeof(tgaHeader) );
+ tgaHeader.id_length = 0;
+ tgaHeader.image_type = (unsigned char) nImageType;
+ tgaHeader.width = (unsigned short) width;
+ tgaHeader.height = (unsigned short) height;
+ tgaHeader.pixel_size = (unsigned char) nPixelSize;
+ tgaHeader.attributes = 0x20;
+
+ // Write the Targa header
+ fp.Write( &tgaHeader, sizeof(TGAHeader_t) );
+
+ // Write out image data
+ if ( bMustConvert )
+ {
+ uint8 *pLineBuf = new uint8[ nBytesPerPixel * width ];
+ while( height-- )
+ {
+ ImageLoader::ConvertImageFormat( srcData, srcFormat, pLineBuf, dstFormat, width, 1 );
+ fp.Write( pLineBuf, nBytesPerPixel * width );
+ srcData += nStride;
+ }
+ delete[] pLineBuf;
+ }
+ else
+ {
+ while( height-- )
+ {
+ fp.Write( srcData, nBytesPerPixel * width );
+ srcData += nStride;
+ }
+
+ }
+ return true;
+}
+
+
+bool WriteRectNoAlloc( unsigned char *pImageData, const char *fileName, int nXOrigin, int nYOrigin, int width, int height, int nStride, enum ImageFormat srcFormat )
+{
+ Assert( g_pFullFileSystem );
+ if( !g_pFullFileSystem )
+ {
+ return false;
+ }
+ FileHandle_t fp;
+ fp = g_pFullFileSystem->Open( fileName, "r+b" );
+
+ //
+ // Read in the targa header
+ //
+ TGAHeader_t tgaHeader;
+ g_pFullFileSystem->Read( &tgaHeader, sizeof(tgaHeader), fp );
+
+
+ int nBytesPerPixel, nPixelSize;
+
+ switch( srcFormat )
+ {
+ case IMAGE_FORMAT_BGR888:
+#if defined( _X360 )
+ case IMAGE_FORMAT_LINEAR_BGR888:
+#endif
+ nBytesPerPixel = 3; // 24/32 bit uncompressed TGA
+ nPixelSize = 24;
+ break;
+ case IMAGE_FORMAT_BGRA8888:
+ nBytesPerPixel = 4; // 24/32 bit uncompressed TGA
+ nPixelSize = 32;
+ break;
+ case IMAGE_FORMAT_I8:
+ nBytesPerPixel = 1; // 8 bit uncompressed TGA
+ nPixelSize = 8;
+ break;
+ default:
+ return false;
+ break;
+ }
+
+ // Verify src data matches the targa we're going to write into
+ if ( nPixelSize != tgaHeader.pixel_size )
+ {
+ Warning( "TGA doesn't match source data.\n" );
+ return false;
+ }
+
+
+ // Seek to the origin of the target subrect from the beginning of the file
+ g_pFullFileSystem->Seek( fp, nBytesPerPixel * (tgaHeader.width * nYOrigin + nXOrigin), FILESYSTEM_SEEK_CURRENT );
+
+ unsigned char *pSrc = pImageData;
+
+ // Run through each scanline of the incoming rect
+ for (int row=0; row < height; row++ )
+ {
+ g_pFullFileSystem->Write( pSrc, nBytesPerPixel * width, fp );
+
+ // Advance src pointer to next scanline
+ pSrc += nBytesPerPixel * nStride;
+
+ // Seek ahead in the file
+ g_pFullFileSystem->Seek( fp, nBytesPerPixel * ( tgaHeader.width - width ), FILESYSTEM_SEEK_CURRENT );
+ }
+
+ g_pFullFileSystem->Close( fp );
+ return true;
+}
+
+} // end namespace TGAWriter
+