|
|
//========= 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 ); }
|