|
|
//====== Copyright � 1996-2004, Valve Corporation, All rights reserved. =======
//
// Purpose:
//
//=============================================================================
#include "materialobjects/dmeimage.h"
#include "datamodel/dmelementfactoryhelper.h"
#include "bitmap/imageformat.h"
//-----------------------------------------------------------------------------
// Expose this class to the scene database
//-----------------------------------------------------------------------------
IMPLEMENT_ELEMENT_FACTORY( DmeImage, CDmeImage );
//-----------------------------------------------------------------------------
// Constructor, destructor
//-----------------------------------------------------------------------------
void CDmeImage::OnConstruction() { m_nWidth.Init( this, "width", FATTRIB_HIDDEN ); m_nHeight.Init( this, "height", FATTRIB_HIDDEN ); m_nDepth.Init( this, "depth", FATTRIB_HIDDEN ); m_nFormat.Init( this, "format", FATTRIB_HIDDEN ); m_flGamma.Init( this, "gamma", FATTRIB_HIDDEN ); m_Bits.Init( this, "bits", FATTRIB_HIDDEN | FATTRIB_HAS_CALLBACK ); m_Mode = DMEIMAGE_STORAGE_NONE; m_bInModification = false; m_bInFloatBitmapModification = false; m_bIgnoreChangedBitsAttribute = false; m_hModify = NULL; }
void CDmeImage::OnDestruction() { Assert( !m_bInModification && !m_bInFloatBitmapModification ); }
//-----------------------------------------------------------------------------
// Used to make sure the attribute is well-behaved
//-----------------------------------------------------------------------------
void CDmeImage::OnAttributeChanged( CDmAttribute *pAttribute ) { BaseClass::OnAttributeChanged( pAttribute ); if ( pAttribute == m_Bits.GetAttribute() ) { if ( !m_bIgnoreChangedBitsAttribute ) { // If the attribute changed (undo), the attribute contains the bits;
// discard the float bitmap state
if ( m_Mode == DMEIMAGE_STORAGE_FLOAT_BITMAP ) { SetFloatBitmapStorageMode( false, true ); } } } }
void CDmeImage::OnElementUnserialized() { BaseClass::OnElementUnserialized();
// After reading, the attribute contains the bits; discard anything else
if ( m_Mode != DMEIMAGE_STORAGE_ATTRIBUTE ) { SetFloatBitmapStorageMode( false, true ); } }
void CDmeImage::OnElementSerialized() { BaseClass::OnElementSerialized();
// Prior to serialization, make sure the bits attribute is holding the current bits
if ( m_Mode == DMEIMAGE_STORAGE_FLOAT_BITMAP ) { SetFloatBitmapStorageMode( false ); } }
//-----------------------------------------------------------------------------
// Initializes the buffer, doesn't allocate space
//-----------------------------------------------------------------------------
void CDmeImage::Init( int nWidth, int nHeight, int nDepth, ImageFormat fmt, float flGamma ) { if ( IsUsingFloatBitmapStorageMode() ) { m_ComputeBits.Shutdown(); } m_Bits.Set( NULL, 0 );
m_nWidth = nWidth; m_nHeight = nHeight; m_nDepth = nDepth; m_nFormat = fmt; m_flGamma = flGamma; m_Mode = DMEIMAGE_STORAGE_NONE; }
//-----------------------------------------------------------------------------
// Image format
//-----------------------------------------------------------------------------
ImageFormat CDmeImage::Format() const { return (ImageFormat)( m_nFormat.Get() ); }
const char *CDmeImage::FormatName() const { return ImageLoader::GetName( Format() ); }
//-----------------------------------------------------------------------------
// returns the size of one row
//-----------------------------------------------------------------------------
int CDmeImage::RowSizeInBytes( ) const { return ImageLoader::GetMemRequired( m_nWidth, 1, 1, Format(), false ); }
//-----------------------------------------------------------------------------
// returns the size of one z slice
//-----------------------------------------------------------------------------
int CDmeImage::ZSliceSizeInBytes( ) const { return ImageLoader::GetMemRequired( m_nWidth, m_nHeight, 1, Format(), false ); }
//-----------------------------------------------------------------------------
// returns the total size of the image
//-----------------------------------------------------------------------------
int CDmeImage::SizeInBytes( ) const { return ImageLoader::GetMemRequired( m_nWidth, m_nHeight, m_nDepth, Format(), false ); }
//-----------------------------------------------------------------------------
// Converts the image into its requested storage mode
//-----------------------------------------------------------------------------
void CDmeImage::SetFloatBitmapStorageMode( bool bFloatBitmap, bool bDiscardContents ) { Assert( !m_bInModification && !m_bInFloatBitmapModification );
StorageMode_t nMode = ( bFloatBitmap ) ? DMEIMAGE_STORAGE_FLOAT_BITMAP : DMEIMAGE_STORAGE_ATTRIBUTE; if ( nMode == m_Mode ) return;
// Do this to avoid re-entrancy problems in BeginModification
StorageMode_t nOldMode = m_Mode; m_Mode = nMode;
switch( m_Mode ) { case DMEIMAGE_STORAGE_NONE: break;
case DMEIMAGE_STORAGE_ATTRIBUTE: if ( !bDiscardContents && ( nOldMode == DMEIMAGE_STORAGE_FLOAT_BITMAP ) ) { m_nWidth = m_ComputeBits.NumCols(); m_nHeight = m_ComputeBits.NumRows(); m_nDepth = m_ComputeBits.NumSlices();
CUtlBinaryBlock &buf = BeginModification(); buf.SetLength( SizeInBytes() ); m_ComputeBits.WriteToBuffer( buf.Get(), buf.Length(), Format(), Gamma() ); EndModification(); } else { // Question: If we're in float bitmap mode, but we discard contents,
// should we copy back the dimensions?
CUtlBinaryBlock &buf = BeginModification(); buf.SetLength( SizeInBytes() ); EndModification(); } m_ComputeBits.Shutdown(); break;
case DMEIMAGE_STORAGE_FLOAT_BITMAP: const ImageFormatInfo_t &info = ImageLoader::ImageFormatInfo( Format() ); int nMask = 0; if ( info.m_nNumRedBits > 0 ) nMask |= FBM_ATTR_RED_MASK; if ( info.m_nNumGreenBits > 0 ) nMask |= FBM_ATTR_GREEN_MASK; if ( info.m_nNumBlueBits > 0 ) nMask |= FBM_ATTR_BLUE_MASK; if ( info.m_nNumAlphaBits > 0 ) nMask |= FBM_ATTR_ALPHA_MASK;
m_ComputeBits.Init( m_nWidth, m_nHeight, m_nDepth, nMask ); if ( !bDiscardContents && ( nOldMode == DMEIMAGE_STORAGE_ATTRIBUTE ) ) { m_ComputeBits.LoadFromBuffer( m_Bits.Get(), m_Bits.Length(), Format(), Gamma() ); } break; } }
//-----------------------------------------------------------------------------
// Copies the bits in whatever form they are currently in
//-----------------------------------------------------------------------------
void CDmeImage::CopyFrom( CDmeImage *pSrcImage, ImageFormat fmt ) { if ( fmt == IMAGE_FORMAT_UNKNOWN ) { fmt = pSrcImage->Format(); }
Init( pSrcImage->Width(), pSrcImage->Height(), pSrcImage->Depth(), fmt, pSrcImage->Gamma() );
if ( !pSrcImage->HasImageData() ) return;
if ( pSrcImage->IsUsingFloatBitmapStorageMode() ) { SetFloatBitmapStorageMode( true, true ); m_ComputeBits.LoadFromFloatBitmap( pSrcImage->FloatBitmap() ); } else { if ( pSrcImage->Format() == Format() ) { SetImageBits( pSrcImage->ImageBits(), pSrcImage->SizeInBytes() ); } else { int nMemory = ImageLoader::GetMemRequired( Width(), Height(), Depth(), 1, Format() ); CUtlBinaryBlock &bits = BeginModification(); bits.SetLength( nMemory ); ImageLoader::ConvertImageFormat( (const uint8*)pSrcImage->ImageBits(), pSrcImage->Format(), (uint8*)bits.Get(), Format(), Width(), Height() * Depth() ); EndModification(); } } }
//-----------------------------------------------------------------------------
// Color converts the image into the destination format.
//-----------------------------------------------------------------------------
void CDmeImage::ConvertFormat( ImageFormat fmt ) { if ( fmt == m_nFormat ) return;
if ( ImageLoader::IsCompressed( Format() ) ) { // Cannot convert from a compressed format
Assert( 0 ); return; }
if ( ImageLoader::IsCompressed( fmt ) ) { // Not supported for compressed textures
Assert( 0 ); return; }
// NOTE: Not super fast, needs to copy the data here even if we use
// the faster ImageLoader::ConvertImageFormat calls. And those
// don't support volume textures. So, screw it. Doing an easier implementation.
// This will convert it to 32323232F, and on return, it will convert
// to the desired format.
if ( m_Mode == DMEIMAGE_STORAGE_ATTRIBUTE ) { SetFloatBitmapStorageMode( true ); }
m_nFormat = fmt; }
//-----------------------------------------------------------------------------
// Sets the color of every pixel
//-----------------------------------------------------------------------------
void CDmeImage::Clear( float r, float g, float b, float a ) { SetFloatBitmapStorageMode( true, true ); m_ComputeBits.Clear( r, g, b, a ); }
//-----------------------------------------------------------------------------
// Compresses an image into this image
//-----------------------------------------------------------------------------
void CDmeImage::CompressImage( CDmeImage *pSrcImage, ImageFormat fmt ) { if ( ImageLoader::IsCompressed( pSrcImage->Format() ) ) { // Cannot convert from a compressed format
Assert( 0 ); return; }
// Must be a compressed format
if ( !ImageLoader::IsCompressed( fmt ) ) { Assert( 0 ); return; }
Init( pSrcImage->Width(), pSrcImage->Height(), pSrcImage->Depth(), fmt, pSrcImage->Gamma() );
// We can only convert from a few well-known formats
pSrcImage->ConvertFormat( IMAGE_FORMAT_RGBA8888 );
int nSrcFaceStride = pSrcImage->ZSliceSizeInBytes(); int nDstFaceStride = ZSliceSizeInBytes();
CUtlBinaryBlock &dstBlock = BeginModification(); dstBlock.SetLength( SizeInBytes() );
const uint8* pSrcData = reinterpret_cast< const uint8* >( pSrcImage->ImageBits() ); uint8* pDstData = reinterpret_cast< uint8* >( dstBlock.Get() ); for ( int z = 0; z < Depth(); ++z, pSrcData += nSrcFaceStride, pDstData += nDstFaceStride ) { ImageLoader::ConvertImageFormat( pSrcData, pSrcImage->Format(), pDstData, fmt, pSrcImage->Width(), pSrcImage->Height() ); } EndModification(); }
//-----------------------------------------------------------------------------
// Reinterprets the image as a new color format; no work is done
//-----------------------------------------------------------------------------
void CDmeImage::ReinterpetFormat( ImageFormat fmt ) { if ( ImageLoader::SizeInBytes( fmt ) != ImageLoader::SizeInBytes( Format() ) ) { // Src + dst format must be the same size for this to work!
Assert( 0 ); return; }
m_nFormat = fmt; }
//-----------------------------------------------------------------------------
// Copies bits into the image bits buffer
//-----------------------------------------------------------------------------
void CDmeImage::SetImageBits( const void *pBits, int nSize ) { SetFloatBitmapStorageMode( false, true ); m_Bits.Set( pBits, nSize ); }
//-----------------------------------------------------------------------------
// Used for bit modification
//-----------------------------------------------------------------------------
CUtlBinaryBlock &CDmeImage::BeginModification( ) { SetFloatBitmapStorageMode( false ); Assert( !m_bInModification && !m_bInFloatBitmapModification ); m_bInModification = true; return m_Bits.GetAttribute()->BeginModifyValueInPlace< CUtlBinaryBlock >( &m_hModify ); }
void CDmeImage::EndModification( ) { Assert( m_bInModification && !m_bInFloatBitmapModification ); m_bInModification = false; m_bIgnoreChangedBitsAttribute = true; m_Bits.GetAttribute()->EndModifyValueInPlace< CUtlBinaryBlock >( m_hModify ); m_bIgnoreChangedBitsAttribute = false; m_hModify = NULL; }
//-----------------------------------------------------------------------------
// Used for float bitmap modification
//-----------------------------------------------------------------------------
FloatBitMap_t &CDmeImage::BeginFloatBitmapModification( ) { SetFloatBitmapStorageMode( true ); Assert( !m_bInModification && !m_bInFloatBitmapModification ); m_bInFloatBitmapModification = true; return m_ComputeBits; }
void CDmeImage::EndFloatBitmapModification( ) { Assert( m_bInFloatBitmapModification && !m_bInModification ); m_bInFloatBitmapModification = false; }
//-----------------------------------------------------------------------------
// Image data
//-----------------------------------------------------------------------------
const FloatBitMap_t *CDmeImage::FloatBitmap() { SetFloatBitmapStorageMode( true ); return &m_ComputeBits; }
//-----------------------------------------------------------------------------
// Creates an image 1/4 size of the source using a box filter
//-----------------------------------------------------------------------------
void CDmeImage::QuarterSize( CDmeImage *pSrcImage ) { SetFloatBitmapStorageMode( true, true ); pSrcImage->SetFloatBitmapStorageMode( true ); pSrcImage->m_ComputeBits.QuarterSize( &m_ComputeBits ); m_nFormat = pSrcImage->Format(); m_flGamma = pSrcImage->Gamma(); }
//-----------------------------------------------------------------------------
// Downsample using nice filter (NOTE: Dest bitmap needs to have been initialized w/ final size)
//-----------------------------------------------------------------------------
void CDmeImage::DownsampleNiceFiltered( const DownsampleInfo_t& info, CDmeImage *pSrcImage ) { SetFloatBitmapStorageMode( true, true ); pSrcImage->SetFloatBitmapStorageMode( true ); pSrcImage->m_ComputeBits.DownsampleNiceFiltered( info, &m_ComputeBits ); m_nFormat = pSrcImage->Format(); m_flGamma = pSrcImage->Gamma(); }
//-----------------------------------------------------------------------------
// Expose this class to the scene database
//-----------------------------------------------------------------------------
IMPLEMENT_ELEMENT_FACTORY( DmeImageArray, CDmeImageArray );
//-----------------------------------------------------------------------------
// Constructor, destructor
//-----------------------------------------------------------------------------
void CDmeImageArray::OnConstruction() { m_Images.Init( this, "images" ); }
void CDmeImageArray::OnDestruction() { }
//-----------------------------------------------------------------------------
// Image format
//-----------------------------------------------------------------------------
int CDmeImageArray::ImageCount() const { return m_Images.Count(); }
CDmeImage *CDmeImageArray::GetImage( int nIndex ) const { return m_Images[nIndex]; }
bool CDmeImageArray::IsConsistent( int nWidth, int nHeight, int nDepth, ImageFormat fmt ) const { if ( ImageCount() == 0 ) return true;
CDmeImage *pFirstImage = m_Images[0]; return ( nWidth == pFirstImage->Width() && nHeight == pFirstImage->Height() && fmt == pFirstImage->Format() && nDepth == pFirstImage->Depth() ); }
void CDmeImageArray::AddImage( CDmeImage *pImage ) { if ( !IsConsistent( pImage->Width(), pImage->Height(), pImage->Depth(), pImage->Format() ) ) { Warning( "Attempted to add different size/format images to the image array!\n" ); return; }
m_Images.AddToTail( pImage ); }
CDmeImage *CDmeImageArray::AddImage( ) { CDmeImage *pImage = CreateElement< CDmeImage >( "image", GetFileId() ); if ( ImageCount() > 0 ) { CDmeImage *pFirstImage = m_Images[0]; pImage->Init( pFirstImage->Width(), pFirstImage->Height(), pFirstImage->Depth(), pFirstImage->Format(), pFirstImage->Gamma() ); }
AddImage( pImage ); return pImage; }
// Gets dimensions
int CDmeImageArray::Width() const { return ( ImageCount() > 0 ) ? m_Images[0]->Width() : -1; }
int CDmeImageArray::Height() const { return ( ImageCount() > 0 ) ? m_Images[0]->Height() : -1; }
int CDmeImageArray::Depth() const { return ( ImageCount() > 0 ) ? m_Images[0]->Depth() : -1; }
ImageFormat CDmeImageArray::Format() const { return ( ImageCount() > 0 ) ? m_Images[0]->Format() : IMAGE_FORMAT_UNKNOWN; }
|