diff options
Diffstat (limited to 'bitmap')
| -rw-r--r-- | bitmap/ImageByteSwap.cpp | 260 | ||||
| -rw-r--r-- | bitmap/bitmap.cpp | 461 | ||||
| -rw-r--r-- | bitmap/bitmap.vpc | 57 | ||||
| -rw-r--r-- | bitmap/colorconversion.cpp | 2373 | ||||
| -rw-r--r-- | bitmap/float_bm.cpp | 729 | ||||
| -rw-r--r-- | bitmap/float_bm2.cpp | 144 | ||||
| -rw-r--r-- | bitmap/float_bm3.cpp | 108 | ||||
| -rw-r--r-- | bitmap/float_bm4.cpp | 350 | ||||
| -rw-r--r-- | bitmap/float_bm_bilateral_filter.cpp | 95 | ||||
| -rw-r--r-- | bitmap/float_cube.cpp | 119 | ||||
| -rw-r--r-- | bitmap/imageformat.cpp | 526 | ||||
| -rw-r--r-- | bitmap/psd.cpp | 571 | ||||
| -rw-r--r-- | bitmap/resample.cpp | 919 | ||||
| -rw-r--r-- | bitmap/tgaloader.cpp | 1001 | ||||
| -rw-r--r-- | bitmap/tgawriter.cpp | 353 |
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 + |