uid issue
This commit is contained in:
778
bitmap/bitmap.cpp
Normal file
778
bitmap/bitmap.cpp
Normal file
@ -0,0 +1,778 @@
|
||||
//======= Copyright 1996-2005, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "bitmap/bitmap.h"
|
||||
#include "dbg.h"
|
||||
#include "utlbuffer.h"
|
||||
#include "bitmap/psd.h"
|
||||
#include "bitmap/tgaloader.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(y,0), 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 );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Returns true if it's a PFM file
|
||||
//-----------------------------------------------------------------------------
|
||||
bool IsPFMFile( CUtlBuffer &buf )
|
||||
{
|
||||
int nGet = buf.TellGet();
|
||||
char c0 = buf.GetChar();
|
||||
char c1 = buf.GetChar();
|
||||
char c2 = buf.GetChar();
|
||||
buf.SeekGet( CUtlBuffer::SEEK_HEAD, nGet );
|
||||
|
||||
return ( c0 == 'P' && ( c1 == 'F' || c1 == 'f' ) && c2 == 0xA );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// This reads an integer from a binary CUtlBuffer.
|
||||
//-----------------------------------------------------------------------------
|
||||
static int ReadIntFromUtlBuffer( CUtlBuffer &buf )
|
||||
{
|
||||
int val = 0;
|
||||
int c;
|
||||
while( buf.IsValid() )
|
||||
{
|
||||
c = buf.GetChar();
|
||||
if( c >= '0' && c <= '9' )
|
||||
{
|
||||
val = val * 10 + ( c - '0' );
|
||||
}
|
||||
else
|
||||
{
|
||||
buf.SeekGet( CUtlBuffer::SEEK_CURRENT, -1 );
|
||||
break;
|
||||
}
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline bool IsWhitespace( char c )
|
||||
{
|
||||
return c == ' ' || c == '\t' || c == 10;
|
||||
}
|
||||
|
||||
static void EatWhiteSpace( CUtlBuffer &buf )
|
||||
{
|
||||
while( buf.IsValid() )
|
||||
{
|
||||
int c = buf.GetChar();
|
||||
if( !IsWhitespace( c ) )
|
||||
{
|
||||
buf.SeekGet( CUtlBuffer::SEEK_CURRENT, -1 );
|
||||
return;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Reads PFM info + advances to the texture bits
|
||||
//-----------------------------------------------------------------------------
|
||||
bool PFMGetInfo_AndAdvanceToTextureBits( CUtlBuffer &pfmBuffer, int &nWidth, int &nHeight, ImageFormat &imageFormat )
|
||||
{
|
||||
pfmBuffer.SeekGet( CUtlBuffer::SEEK_HEAD, 0 );
|
||||
if( pfmBuffer.GetChar() != 'P' )
|
||||
{
|
||||
Assert( 0 );
|
||||
return false;
|
||||
}
|
||||
char c = pfmBuffer.GetChar();
|
||||
if ( c == 'F' )
|
||||
{
|
||||
imageFormat = IMAGE_FORMAT_RGB323232F;
|
||||
}
|
||||
else if ( c == 'f' )
|
||||
{
|
||||
imageFormat = IMAGE_FORMAT_R32F;
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert( 0 );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( pfmBuffer.GetChar() != 0xa )
|
||||
{
|
||||
Assert( 0 );
|
||||
return false;
|
||||
}
|
||||
nWidth = ReadIntFromUtlBuffer( pfmBuffer );
|
||||
EatWhiteSpace( pfmBuffer );
|
||||
nHeight = ReadIntFromUtlBuffer( pfmBuffer );
|
||||
|
||||
// eat crap until the next newline
|
||||
while( pfmBuffer.IsValid() && pfmBuffer.GetChar() != 0xa )
|
||||
{
|
||||
}
|
||||
|
||||
int nScale = ReadIntFromUtlBuffer( pfmBuffer );
|
||||
|
||||
// eat crap until the next newline
|
||||
while( pfmBuffer.IsValid() && pfmBuffer.GetChar() != 0xa )
|
||||
{
|
||||
}
|
||||
|
||||
if ( nScale > 0 )
|
||||
{
|
||||
pfmBuffer.SetBigEndian( true );
|
||||
}
|
||||
|
||||
// Here, the buffer should be at the start of the texture data
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Loads a PFM file into a Bitmap_t as RGBA32323232F data
|
||||
// PFM source data doesn't have alpha, so we put 1.0f into alpha
|
||||
//-----------------------------------------------------------------------------
|
||||
bool PFMReadFileRGBA32323232F( CUtlBuffer &fileBuffer, Bitmap_t &bitmap, float pfmScale )
|
||||
{
|
||||
int nWidth = 0, nHeight = 0;
|
||||
ImageFormat imageFormat = IMAGE_FORMAT_UNKNOWN;
|
||||
PFMGetInfo_AndAdvanceToTextureBits( fileBuffer, nWidth, nHeight, imageFormat ); // Read the header (advances us to the texture bits)
|
||||
Assert( imageFormat == IMAGE_FORMAT_RGB323232F );
|
||||
|
||||
bitmap.Init( nWidth, nHeight, IMAGE_FORMAT_RGBA32323232F ); // Init the bitmap
|
||||
|
||||
// NOTE: PFMs are displayed *UPSIDE-DOWN* in Photoshop! (as of CS2... HDRShop gets it right)
|
||||
// Reading rows in reverse order here is the correct thing to do:
|
||||
for( int y = ( nHeight - 1 ); y >= 0; y-- )
|
||||
{
|
||||
Assert( fileBuffer.IsValid() );
|
||||
if ( !fileBuffer.IsValid() )
|
||||
return false;
|
||||
|
||||
float *pOut = ( (float *)bitmap.GetBits() ) + y*nWidth*4; // Point to output row
|
||||
|
||||
for ( int x = 0; x < nWidth; x++ ) // For every RGB input color
|
||||
{
|
||||
fileBuffer.Get( pOut, sizeof( float ) ); // Get red
|
||||
*pOut *= pfmScale; // Scale into output
|
||||
pOut++;
|
||||
fileBuffer.Get( pOut, sizeof( float ) ); // Get green
|
||||
*pOut *= pfmScale; // Scale into output
|
||||
pOut++;
|
||||
fileBuffer.Get( pOut, sizeof( float ) ); // Get blue
|
||||
*pOut *= pfmScale; // Scale into output
|
||||
pOut++;
|
||||
*pOut = 1.0f; // 1.0f into alpha
|
||||
pOut++;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Loads a PFM file into a Bitmap_t as RGB323232F data
|
||||
//-----------------------------------------------------------------------------
|
||||
bool PFMReadFileRGB323232F( CUtlBuffer &fileBuffer, Bitmap_t &bitmap, float pfmScale )
|
||||
{
|
||||
// Read the header (advances us to the texture bits)
|
||||
int nWidth = 0, nHeight = 0;
|
||||
ImageFormat imageFormat = IMAGE_FORMAT_UNKNOWN;
|
||||
PFMGetInfo_AndAdvanceToTextureBits( fileBuffer, nWidth, nHeight, imageFormat );
|
||||
Assert( imageFormat == IMAGE_FORMAT_RGB323232F );
|
||||
|
||||
// Init the bitmap
|
||||
bitmap.Init( nWidth, nHeight, IMAGE_FORMAT_RGB323232F );
|
||||
|
||||
// Read the texels
|
||||
for( int y = ( nHeight - 1 ); y >= 0; y-- )
|
||||
{
|
||||
Assert( fileBuffer.IsValid() );
|
||||
if ( !fileBuffer.IsValid() )
|
||||
return false;
|
||||
|
||||
// NOTE: PFMs are displayed *UPSIDE-DOWN* in Photoshop! (as of CS2... HDRShop gets it right)
|
||||
// Reading rows in reverse order here is the correct thing to do:
|
||||
float *pRow = ( (float *)bitmap.GetBits() ) + y*nWidth*3;
|
||||
|
||||
fileBuffer.Get( pRow, nWidth*3*sizeof( float ) );
|
||||
for ( int x = 0; x < nWidth*3; x++ )
|
||||
{
|
||||
pRow[ x ] *= pfmScale;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Loads the x channel of a PFM file into a Bitmap_t as R32F data
|
||||
//-----------------------------------------------------------------------------
|
||||
bool PFMReadFileR32F( CUtlBuffer &fileBuffer, Bitmap_t &bitmap, float pfmScale )
|
||||
{
|
||||
// Read the header (advances us to the texture bits)
|
||||
int nWidth, nHeight;
|
||||
ImageFormat fileImageFormat; // Format of data in file
|
||||
PFMGetInfo_AndAdvanceToTextureBits( fileBuffer, nWidth, nHeight, fileImageFormat );
|
||||
Assert( fileImageFormat == IMAGE_FORMAT_RGB323232F );
|
||||
|
||||
// Init the bitmap
|
||||
bitmap.Init( nWidth, nHeight, IMAGE_FORMAT_R32F );
|
||||
|
||||
float flMin = FLOAT32_MAX;
|
||||
float flMax = FLOAT32_MIN;
|
||||
|
||||
// Read the texels, snarfing out just the x component
|
||||
for( int y = ( nHeight - 1 ); y >= 0; y-- )
|
||||
{
|
||||
Assert( fileBuffer.IsValid() );
|
||||
if ( !fileBuffer.IsValid() )
|
||||
return false;
|
||||
|
||||
// NOTE: PFMs are displayed *UPSIDE-DOWN* in Photoshop! (as of CS2... HDRShop gets it right)
|
||||
// Reading rows in reverse order here is the correct thing to do:
|
||||
float *pRow = ( (float *)bitmap.GetBits() ) + y*nWidth;
|
||||
|
||||
for ( int x = 0; x < nWidth; x++ )
|
||||
{
|
||||
fileBuffer.Get( pRow+x, sizeof( float ) ); // Grab x component and scale
|
||||
pRow[x] *= pfmScale;
|
||||
|
||||
flMin = MIN( pRow[x], flMin );
|
||||
flMax = MAX( pRow[x], flMax );
|
||||
|
||||
float flDummy[2];
|
||||
fileBuffer.Get( &flDummy, 2*sizeof( float ) ); // Jump past y and z components in file
|
||||
}
|
||||
}
|
||||
|
||||
printf( "Displacement Range: (%g, %g)\n", flMin, flMax );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Loads a PFM file into a Bitmap_t
|
||||
//-----------------------------------------------------------------------------
|
||||
bool PFMReadFile( CUtlBuffer &buf, Bitmap_t *pBitmap )
|
||||
{
|
||||
// Read the header (advances us to the texture bits)
|
||||
int nWidth = 0, nHeight = 0;
|
||||
ImageFormat fmt = IMAGE_FORMAT_UNKNOWN;
|
||||
PFMGetInfo_AndAdvanceToTextureBits( buf, nWidth, nHeight, fmt );
|
||||
|
||||
// Init the bitmap
|
||||
pBitmap->Init( nWidth, nHeight, fmt );
|
||||
|
||||
int nRowBytes = ImageLoader::GetMemRequired( nWidth, 1, 1, 1, fmt );
|
||||
|
||||
// Read the texels
|
||||
for( int y = ( nHeight - 1 ); y >= 0; y-- )
|
||||
{
|
||||
Assert( buf.IsValid() );
|
||||
if ( !buf.IsValid() )
|
||||
return false;
|
||||
|
||||
// NOTE: PFMs are displayed *UPSIDE-DOWN* in Photoshop! (as of CS2... HDRShop gets it right)
|
||||
// Reading rows in reverse order here is the correct thing to do:
|
||||
void *pDest = pBitmap->GetBits() + y * nRowBytes;
|
||||
buf.Get( pDest, nRowBytes );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Loads a bitmap from an arbitrary file: could be a TGA, PSD, or PFM.
|
||||
// LoadBitmap autodetects which type
|
||||
//-----------------------------------------------------------------------------
|
||||
BitmapFileType_t LoadBitmapFile( CUtlBuffer &buf, Bitmap_t *pBitmap )
|
||||
{
|
||||
if ( IsPSDFile( buf ) )
|
||||
{
|
||||
// FIXME: Make it actually load the true format
|
||||
if ( !PSDReadFileRGBA8888( buf, *pBitmap ) )
|
||||
return BITMAP_FILE_TYPE_UNKNOWN;
|
||||
return BITMAP_FILE_TYPE_PSD;
|
||||
}
|
||||
|
||||
if ( IsPFMFile( buf ) )
|
||||
{
|
||||
if ( !PFMReadFile( buf, pBitmap ) )
|
||||
return BITMAP_FILE_TYPE_UNKNOWN;
|
||||
return BITMAP_FILE_TYPE_PFM;
|
||||
}
|
||||
|
||||
// It's not really possible to detect TGAness.. there's no identifier
|
||||
// Assume TGA file here
|
||||
int nWidth, nHeight;
|
||||
ImageFormat fmt;
|
||||
float flGamma;
|
||||
int nGet = buf.TellGet();
|
||||
bool bOk = TGALoader::GetInfo( buf, &nWidth, &nHeight, &fmt, &flGamma );
|
||||
buf.SeekGet( CUtlBuffer::SEEK_HEAD, nGet );
|
||||
if ( !bOk )
|
||||
return BITMAP_FILE_TYPE_UNKNOWN;
|
||||
|
||||
// FIXME: TGALoader is incredibly inefficient when trying to just get
|
||||
// the bits as-is as it loads into RGBA8888 and then color converts out
|
||||
pBitmap->Init( nWidth, nHeight, fmt );
|
||||
if ( !TGALoader::Load( pBitmap->GetBits(), buf, nWidth, nHeight, fmt, flGamma, false ) )
|
||||
return BITMAP_FILE_TYPE_UNKNOWN;
|
||||
|
||||
return BITMAP_FILE_TYPE_TGA;
|
||||
}
|
||||
|
Reference in New Issue
Block a user