This commit is contained in:
FluorescentCIAAfricanAmerican
2020-04-22 12:56:21 -04:00
commit 3bf9df6b27
15370 changed files with 5489726 additions and 0 deletions

260
bitmap/ImageByteSwap.cpp Normal file
View File

@ -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
}
}

461
bitmap/bitmap.cpp Normal file
View File

@ -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 );
}

57
bitmap/bitmap.vpc Normal file
View File

@ -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
}
}

2373
bitmap/colorconversion.cpp Normal file

File diff suppressed because it is too large Load Diff

729
bitmap/float_bm.cpp Normal file
View File

@ -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);
}
}
}
}

144
bitmap/float_bm2.cpp Normal file
View File

@ -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);
}

108
bitmap/float_bm3.cpp Normal file
View File

@ -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];
}

350
bitmap/float_bm4.cpp Normal file
View File

@ -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;
}

View File

@ -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] );
}
}

119
bitmap/float_cube.cpp Normal file
View File

@ -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);
}
}
}

526
bitmap/imageformat.cpp Normal file
View File

@ -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

571
bitmap/psd.cpp Normal file
View File

@ -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;
}

919
bitmap/resample.cpp Normal file
View File

@ -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

1001
bitmap/tgaloader.cpp Normal file

File diff suppressed because it is too large Load Diff

353
bitmap/tgawriter.cpp Normal file
View File

@ -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