This commit is contained in:
nephacks
2025-06-04 03:22:50 +02:00
parent f234f23848
commit f12416cffd
14243 changed files with 6446499 additions and 26 deletions

277
bitmap/ImageByteSwap.cpp Normal file
View File

@@ -0,0 +1,277 @@
//======= Copyright 1996-2005, 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 )
#if defined( XDK_INSTALLED )
// 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
#include "d3d9.h"
#include "XGraphics.h"
#endif
#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, VtfConsoleFormatType_t targetConsole, int width, int stride )
{
Assert( IsFormatValidForConversion( imageFormat ) );
#ifndef DX_TO_GL_ABSTRACTION
if ( IsPC() )
{
// running as a win32 tool, data is in expected order
// for conversion code
return;
}
// If licensees don't have the XDK installed they are not going to be able to do image conversion and shouldn't need to
#if defined (XDK_INSTALLED)
// 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 // XDK_INSTALLED
#endif // COMPILER_MSVC32
}
//-----------------------------------------------------------------------------
// 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, VtfConsoleFormatType_t targetConsole, int width, int stride )
{
Assert( IsFormatValidForConversion( imageFormat ) );
#ifndef DX_TO_GL_ABSTRACTION
// If licensees don't have the XDK installed they are not going to be able to do image conversion and shouldn't need to
#if defined (XDK_INSTALLED)
// 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_RGBA16161616F:
case IMAGE_FORMAT_RGBA16161616:
if ( IsGameConsole() )
{
// 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:
// Don't endian swap compressed textures for PS3, but swap everything else just like Xbox360
if ( targetConsole == VTF_CONSOLE_PS3 )
return;
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 // XDK_INSTALLED
#endif // COMPILER_MSVC32
}
//-----------------------------------------------------------------------------
// Swaps image bytes.
//-----------------------------------------------------------------------------
void ByteSwapImageData( unsigned char *pImageData, int nImageSize, ImageFormat imageFormat, int width, int stride )
{
Assert( IsFormatValidForConversion( imageFormat ) );
#ifndef DX_TO_GL_ABSTRACTION
// If licensees don't have the XDK installed they are not going to be able to do image conversion and shouldn't need to
#if defined (XDK_INSTALLED)
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 // XDK_INSTALLED
#endif // COMPILER_MSVC32
}
}

View File

@@ -0,0 +1,100 @@
// ----------------------------------------- //
// File generated by VPC //
// ----------------------------------------- //
Source file: F:\csgo_64\cstrike15_src\bitmap\bitmap.cpp
Debug output file: F:\csgo_64\cstrike15_src\bitmap\bitmap.cpp
Release output file: F:\csgo_64\cstrike15_src\bitmap\bitmap.cpp
Containing unity file:
PCH file:
Source file: F:\csgo_64\cstrike15_src\bitmap\colorconversion.cpp
Debug output file: F:\csgo_64\cstrike15_src\bitmap\colorconversion.cpp
Release output file: F:\csgo_64\cstrike15_src\bitmap\colorconversion.cpp
Containing unity file:
PCH file:
Source file: F:\csgo_64\cstrike15_src\common\debug_lib_check.cpp
Debug output file: F:\csgo_64\cstrike15_src\common\debug_lib_check.cpp
Release output file: F:\csgo_64\cstrike15_src\common\debug_lib_check.cpp
Containing unity file:
PCH file:
Source file: F:\csgo_64\cstrike15_src\bitmap\floatbitmap.cpp
Debug output file: F:\csgo_64\cstrike15_src\bitmap\floatbitmap.cpp
Release output file: F:\csgo_64\cstrike15_src\bitmap\floatbitmap.cpp
Containing unity file:
PCH file:
Source file: F:\csgo_64\cstrike15_src\bitmap\floatbitmap2.cpp
Debug output file: F:\csgo_64\cstrike15_src\bitmap\floatbitmap2.cpp
Release output file: F:\csgo_64\cstrike15_src\bitmap\floatbitmap2.cpp
Containing unity file:
PCH file:
Source file: F:\csgo_64\cstrike15_src\bitmap\floatbitmap3.cpp
Debug output file: F:\csgo_64\cstrike15_src\bitmap\floatbitmap3.cpp
Release output file: F:\csgo_64\cstrike15_src\bitmap\floatbitmap3.cpp
Containing unity file:
PCH file:
Source file: F:\csgo_64\cstrike15_src\bitmap\floatbitmap4.cpp
Debug output file: F:\csgo_64\cstrike15_src\bitmap\floatbitmap4.cpp
Release output file: F:\csgo_64\cstrike15_src\bitmap\floatbitmap4.cpp
Containing unity file:
PCH file:
Source file: F:\csgo_64\cstrike15_src\bitmap\floatbitmap_bilateralfilter.cpp
Debug output file: F:\csgo_64\cstrike15_src\bitmap\floatbitmap_bilateralfilter.cpp
Release output file: F:\csgo_64\cstrike15_src\bitmap\floatbitmap_bilateralfilter.cpp
Containing unity file:
PCH file:
Source file: F:\csgo_64\cstrike15_src\bitmap\floatcubemap.cpp
Debug output file: F:\csgo_64\cstrike15_src\bitmap\floatcubemap.cpp
Release output file: F:\csgo_64\cstrike15_src\bitmap\floatcubemap.cpp
Containing unity file:
PCH file:
Source file: F:\csgo_64\cstrike15_src\bitmap\imageformat.cpp
Debug output file: F:\csgo_64\cstrike15_src\bitmap\imageformat.cpp
Release output file: F:\csgo_64\cstrike15_src\bitmap\imageformat.cpp
Containing unity file:
PCH file:
Source file: F:\csgo_64\cstrike15_src\bitmap\psd.cpp
Debug output file: F:\csgo_64\cstrike15_src\bitmap\psd.cpp
Release output file: F:\csgo_64\cstrike15_src\bitmap\psd.cpp
Containing unity file:
PCH file:
Source file: F:\csgo_64\cstrike15_src\bitmap\psheet.cpp
Debug output file: F:\csgo_64\cstrike15_src\bitmap\psheet.cpp
Release output file: F:\csgo_64\cstrike15_src\bitmap\psheet.cpp
Containing unity file:
PCH file:
Source file: F:\csgo_64\cstrike15_src\bitmap\resample.cpp
Debug output file: F:\csgo_64\cstrike15_src\bitmap\resample.cpp
Release output file: F:\csgo_64\cstrike15_src\bitmap\resample.cpp
Containing unity file:
PCH file:
Source file: F:\csgo_64\cstrike15_src\bitmap\texturepacker.cpp
Debug output file: F:\csgo_64\cstrike15_src\bitmap\texturepacker.cpp
Release output file: F:\csgo_64\cstrike15_src\bitmap\texturepacker.cpp
Containing unity file:
PCH file:
Source file: F:\csgo_64\cstrike15_src\bitmap\tgaloader.cpp
Debug output file: F:\csgo_64\cstrike15_src\bitmap\tgaloader.cpp
Release output file: F:\csgo_64\cstrike15_src\bitmap\tgaloader.cpp
Containing unity file:
PCH file:
Source file: F:\csgo_64\cstrike15_src\bitmap\tgawriter.cpp
Debug output file: F:\csgo_64\cstrike15_src\bitmap\tgawriter.cpp
Release output file: F:\csgo_64\cstrike15_src\bitmap\tgawriter.cpp
Containing unity file:
PCH file:

View File

@@ -0,0 +1,16 @@
// ----------------------------------------- //
// File generated by VPC //
// ----------------------------------------- //
Source file: F:\csgo_64\cstrike15_src\common\debug_lib_check.cpp
Debug output file: F:\csgo_64\cstrike15_src\common\debug_lib_check.cpp
Release output file: F:\csgo_64\cstrike15_src\common\debug_lib_check.cpp
Containing unity file:
PCH file:
Source file: F:\csgo_64\cstrike15_src\bitmap\ImageByteSwap.cpp
Debug output file: F:\csgo_64\cstrike15_src\bitmap\ImageByteSwap.cpp
Release output file: F:\csgo_64\cstrike15_src\bitmap\ImageByteSwap.cpp
Containing unity file:
PCH file:

778
bitmap/bitmap.cpp Normal file
View 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;
}

63
bitmap/bitmap.vpc Normal file
View File

@@ -0,0 +1,63 @@
//-----------------------------------------------------------------------------
// BITMAP.VPC
//
// Project Script
//-----------------------------------------------------------------------------
$Macro SRCDIR ".."
$include "$SRCDIR\vpc_scripts\source_lib_base.vpc"
$Configuration
{
$Compiler
{
$AdditionalIncludeDirectories "$BASE;$SRCDIR\dx9sdk\include" [$WINDOWS]
$AdditionalIncludeDirectories "$BASE;$(XEDK)\include\win32" [$WINDOWS && $XDKINSTALLED]
$AdditionalIncludeDirectories "$BASE;$(XEDK)\include\xbox" [$X360]
}
}
$Project "bitmap"
{
$Folder "Source Files"
{
$File "bitmap.cpp"
$File "ImageByteSwap.cpp" [$X360 || $PS3]
$File "colorconversion.cpp"
$File "floatbitmap.cpp"
$File "floatbitmap2.cpp"
$File "floatbitmap3.cpp"
$File "floatbitmap4.cpp" [$WINDOWS]
$File "floatbitmap_bilateralfilter.cpp"
$File "floatcubemap.cpp"
$File "imageformat.cpp"
$File "psd.cpp"
$File "psheet.cpp"
$File "resample.cpp"
$File "tgaloader.cpp"
$File "texturepacker.cpp"
$File "tgawriter.cpp"
}
$Folder "Header Files"
{
$File "$SRCDIR\public\bitmap\bitmap.h"
$File "$SRCDIR\public\bitmap\floatbitmap.h"
$File "$SRCDIR\public\bitmap\imageformat.h"
$File "$SRCDIR\public\bitmap\imageformat_declarations.h"
$File "$SRCDIR\public\bitmap\psd.h"
$File "$SRCDIR\public\bitmap\psheet.h"
$File "$SRCDIR\public\bitmap\texturepacker.h"
$File "$SRCDIR\public\bitmap\tgaloader.h"
$File "$SRCDIR\public\bitmap\tgawriter.h"
$File "$SRCDIR\public\bitmap\stb_dxt.h"
}
$Folder "Link Libraries" [$WINDOWS]
{
$Lib "$SRCDIR\lib\public\nvtc" [!$WIN64]
$Lib "$SRCDIR\lib\public\nvtc64" [$WIN64]
$Lib "bitmap_byteswap"
}
}

View File

@@ -0,0 +1,13 @@
"vpc_cache"
{
"CacheVersion" "1"
"win32"
{
"CRCFile" "bitmap.vcxproj.vpc_crc"
"OutputFiles"
{
"0" "bitmap.vcxproj"
"1" "bitmap.vcxproj.filters"
}
}
}

View File

@@ -0,0 +1,47 @@
//-----------------------------------------------------------------------------
// BITMAP_BYTESWAP.VPC
//
// Project Script
//-----------------------------------------------------------------------------
$Macro SRCDIR ".."
$Macro OUTLIBDIR "$LIBPUBLIC"
$include "$SRCDIR\vpc_scripts\source_lib_base.vpc"
$Configuration
{
$Compiler
{
$AdditionalIncludeDirectories "$BASE;$(XEDK)\include\win32" [$WINDOWS]
}
}
$Configuration "Debug"
{
$General
{
$IntermediateDirectory ".\Debug_BitmapByteswap$PLATSUBDIR" [$WINDOWS]
}
}
$Configuration "Release"
{
$General
{
$IntermediateDirectory ".\Debug_BitmapByteswap$PLATSUBDIR" [$WINDOWS]
}
}
$Project "bitmap_byteswap"
{
$Folder "Source Files"
{
$File "ImageByteSwap.cpp"
}
$Folder "Header Files"
{
$File "$SRCDIR\public\bitmap\imageformat.h"
}
}

View File

@@ -0,0 +1,13 @@
"vpc_cache"
{
"CacheVersion" "1"
"win32"
{
"CRCFile" "bitmap_byteswap.vcxproj.vpc_crc"
"OutputFiles"
{
"0" "bitmap_byteswap.vcxproj"
"1" "bitmap_byteswap.vcxproj.filters"
}
}
}

2495
bitmap/colorconversion.cpp Normal file

File diff suppressed because it is too large Load Diff

1618
bitmap/floatbitmap.cpp Normal file

File diff suppressed because it is too large Load Diff

151
bitmap/floatbitmap2.cpp Normal file
View File

@@ -0,0 +1,151 @@
//===== Copyright <20> 1996-2006, Valve Corporation, All rights reserved. ======//
//
// Purpose:
//
//===========================================================================//
#include <tier0/platform.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include "bitmap/floatbitmap.h"
// NOTE: This has to be the last file included!
#include "tier0/memdbgon.h"
static float ScaleValue( float f, float overbright )
{
// map a value between 0..255 to the scale factor
int ival = ( int )f;
return ival * ( overbright / 255.0 );
}
static float IScaleValue( float f, float overbright )
{
f *= ( 1.0 / overbright );
int ival = ( int )MIN( 255, 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, 0 ), orig.Pixel( x, y, 0, 1 )), orig.Pixel( x, y, 0, 2 ));
if ( maxc == 0.0 )
{
// pixel is black. any scale value is fine.
newbm.Pixel( x, y, 0, 3 ) = newscale;
for( int c = 0;c < 3;c++ )
newbm.Pixel( x, y, 0, 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, 0, 3 ) = newscale;
for( int c = 0;c < 3;c++ )
newbm.Pixel( x, y, 0, c ) = orig.Pixel( x, y, 0, c ) / ( scale_we_will_get );
}
}
}
void FloatBitMap_t::Uncompress( float overbright )
{
for( int y = 0;y < NumRows();y++ )
for( int x = 0;x < NumCols();x++ )
{
int iactual_alpha_value = ( int )( 255.0 * Pixel( x, y, 0, 3 ) );
float actual_alpha_value = iactual_alpha_value * ( 1.0 / 255.0 );
for( int c = 0;c < 3;c++ )
{
int iactual_color_value = ( int )( 255.0 * Pixel( x, y, 0, c ) );
float actual_color_value = iactual_color_value * ( 1.0 / 255.0 );
Pixel( x, y, 0, 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( NumCols(), NumRows() );
// first, saturate to max overbright
for( int y = 0;y < NumRows();y++ )
for( int x = 0;x < NumCols();x++ )
for( int c = 0;c < 3;c++ )
Pixel( x, y, 0, c ) = MIN( overbright, Pixel( x, y, 0, c ));
// first pass - choose nominal scale values to convert to rgb,scale
for( int y = 0;y < NumRows();y++ )
for( int x = 0;x < NumCols();x++ )
{
// determine maximum component
float maxc = MAX( MAX( Pixel( x, y, 0, 0 ), Pixel( x, y, 0, 1 )), Pixel( x, y, 0, 2 ));
if ( maxc == 0 )
{
for( int c = 0;c < 4;c++ )
TmpFBM.Pixel( x, y, 0, 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, 0, 3 ) = closest_iscale;
for( int c = 0;c < 3;c++ )
TmpFBM.Pixel( x, y, 0, c ) = Pixel( x, y, 0, 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 < NumRows();y++ )
{
for( int x = 0;x < NumCols();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, NumCols() * NumRows() * 4 * sizeof( float ));
}
#endif
CopyAttrFrom( TmpFBM, FBM_ATTR_RED );
CopyAttrFrom( TmpFBM, FBM_ATTR_GREEN );
CopyAttrFrom( TmpFBM, FBM_ATTR_BLUE );
CopyAttrFrom( TmpFBM, FBM_ATTR_ALPHA );
// now, map scale to real value
for( int y = 0; y < NumRows(); y++ )
for( int x = 0; x < NumCols(); x++ )
Pixel( x, y, 0, 3 ) *= ( 1.0 / 255.0 );
}

118
bitmap/floatbitmap3.cpp Normal file
View File

@@ -0,0 +1,118 @@
//===== Copyright <20> 1996-2006, Valve Corporation, All rights reserved. ======//
//
// Purpose:
//
//===========================================================================//
#include <tier0/platform.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include "bitmap/floatbitmap.h"
#include "vstdlib/vstdlib.h"
#include "vstdlib/random.h"
#include "tier1/strtools.h"
// NOTE: This has to be the last file included!
#include "tier0/memdbgon.h"
void FloatBitMap_t::InitializeWithRandomPixelsFromAnotherFloatBM( FloatBitMap_t const & other )
{
for( int z = 0; z < NumSlices(); z++ )
{
for( int y = 0;y < NumRows();y++ )
{
for( int x = 0;x < NumCols();x++ )
{
int x1 = RandomInt( 0, other.NumCols() - 1 );
int y1 = RandomInt( 0, other.NumRows() - 1 );
int z1 = RandomInt( 0, other.NumSlices() - 1 );
for( int c = 0; c < 4; c++ )
{
Pixel( x, y, z, c ) = other.Pixel( x1, y1, z1, c );
}
}
}
}
}
void FloatBitMap_t::QuarterSizeWithGaussian( FloatBitMap_t *pBitmap )
{
// generate a new bitmap half on each axis, using a separable gaussian.
static float kernel[]={.05, .25, .4, .25, .05};
pBitmap->Init( NumCols() / 2, NumRows() / 2 );
for( int y = 0;y < NumRows() / 2;y++ )
for( int x = 0;x < NumCols() / 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( NumCols() - 1, x * 2 + xofs ));
for( int yofs =- 2;yofs <= 2;yofs++ )
{
int orig_y = MAX( 0, MIN( NumRows() - 1, y * 2 + yofs ));
float coeff = kernel[xofs + 2]* kernel[yofs + 2];
sum += Pixel( orig_x, orig_y, 0, c ) * coeff;
sumweights += coeff;
}
}
pBitmap->Pixel( x, y, 0, c ) = sum / sumweights;
}
}
}
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]-> NumCols() > 1 ) && ( m_pLevels[start_level]-> NumRows() > 1 ) )
{
if ( m_pLevels[start_level + 1] )
delete m_pLevels[start_level + 1];
m_pLevels[start_level + 1] = new FloatBitMap_t;
m_pLevels[start_level]->QuarterSizeWithGaussian( m_pLevels[start_level + 1] );
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, 0, 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];
}

354
bitmap/floatbitmap4.cpp Normal file
View File

@@ -0,0 +1,354 @@
//===== Copyright <20> 1996-2006, Valve Corporation, All rights reserved. ======//
//
// Purpose:
//
//===========================================================================//
#include <tier0/platform.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include "bitmap/floatbitmap.h"
#include "vstdlib/vstdlib.h"
#include "raytrace.h"
#include "mathlib/bumpvects.h"
#include "mathlib/halton.h"
#include "tier0/threadtools.h"
#include "tier0/progressbar.h"
// NOTE: This has to be the last file included!
#include "tier0/memdbgon.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 uintp SSBumpCalculationThreadFN( void * ctx1 )
{
SSBumpCalculationContext * ctx = ( SSBumpCalculationContext * ) ctx1;
RayStream ray_trace_stream_ctx;
RayTracingSingleResult * rslts = new
RayTracingSingleResult[ctx->ret_bm->NumCols() * 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->NumCols(); x++ )
{
Vector surf_pnt( x, y, ctx->bump_scale * ctx->src_bm->Pixel( x, y, 0, 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->NumCols(), ctx->src_bm->NumRows() );
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->NumCols(); 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->NumCols()] );
if ( ndotl < 0 )
ctx->ret_bm->Pixel( x, y, 0, 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, 0, c ) = ( ndotl * sum_dots ) / sum_possible_dots;
}
}
if ( ctx->m_nOptionFlags & SSBUMP_OPTION_NONDIRECTIONAL )
{
ctx->ret_bm->Pixel( x, y, 0, 1 ) = ctx->ret_bm->Pixel( x, y, 0, 0 ); // copy height
ctx->ret_bm->Pixel( x, y, 0, 2 ) = ctx->ret_bm->Pixel( x, y, 0, 0 ); // copy height
ctx->ret_bm->Pixel( x, y, 0, 3 ) = ctx->ret_bm->Pixel( x, y, 0, 0 ); // copy height
}
else
{
ctx->ret_bm->Pixel( x, y, 0, 3 ) = ctx->src_bm->Pixel( x, y, 0, 3 ); // copy height
}
}
}
delete[] rslts;
return 0;
}
void FloatBitMap_t::ComputeVertexPositionsAndNormals( float flHeightScale, Vector ** ppPosOut, Vector ** ppNormalOut ) const
{
Vector * verts = new Vector[NumCols() * NumRows()];
// first, calculate vertex positions
for( int y = 0; y < NumRows(); y++ )
for( int x = 0; x < NumCols(); x++ )
{
Vector * out = verts + x + y * NumCols();
out->x = x;
out->y = y;
out->z = flHeightScale * Pixel( x, y, 0, 3 );
}
Vector * normals = new Vector[NumCols() * NumRows()];
// now, calculate normals, smoothed
for( int y = 0; y < NumRows(); y++ )
for( int x = 0; x < NumCols(); 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 += NumCols();
int y0 = ( y + yofs );
if ( y0 < 0 )
y0 += NumRows();
x0 = x0 % NumCols();
y0 = y0 % NumRows();
int x1 = ( x0 + 1 ) % NumCols();
int y1 = ( y0 + 1 ) % NumRows();
// now, form the two triangles from this vertex
Vector p0 = verts[x0 + y0 * NumCols()];
Vector e1 = verts[x1 + y0 * NumCols()];
e1 -= p0;
Vector e2 = verts[x0 + y1 * NumCols()];
e2 -= p0;
Vector n1;
CrossProduct( e1, e2, n1 );
if ( n1.z < 0 )
n1.Negate();
e1 = verts[x + y1 * NumCols()];
e1 -= p0;
e2 = verts[x1 + y1 * NumCols()];
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 * NumCols()]= 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 * NumRows() * NumCols() );
// 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 = NumCols() - 1;
int min_y = 0;
int max_y = NumRows() - 1;
if ( tilex < 0 )
min_x = NumCols() / 2;
if ( tilex > 0 )
max_x = NumCols() / 2;
if ( tiley < 0 )
min_y = NumRows() / 2;
if ( tiley > 0 )
max_y = NumRows() / 2;
for( int y = min_y; y <= max_y; y++ )
for( int x = min_x; x <= max_x; x++ )
{
Vector ofs( tilex * NumCols(), tiley * NumRows(), 0 );
int x1 = ( x + 1 ) % NumCols();
int y1 = ( y + 1 ) % NumRows();
Vector v0 = verts[x + y * NumCols()];
Vector v1 = verts[x1 + y * NumCols()];
Vector v2 = verts[x1 + y1 * NumCols()];
Vector v3 = verts[x + y1 * NumCols()];
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( NumCols(), NumRows() );
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 = NumRows() - 1;
ctxs[0].m_nOptionFlags = nOptionFlags;
int nthreads = MIN( 32, GetCPUInformation().m_nPhysicalProcessors );
ThreadHandle_t waithandles[32];
int starty = 0;
int ystep = NumRows() / 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( NumRows() - 1, starty + ystep - 1 );
else
ctxs[t].max_y = NumRows() - 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 < NumRows(); nY++ )
for( int nX = 0; nX < NumCols(); nX++ )
{
float flScale = flOutputScale * ( 2.0 / 3.0 ) * ( Pixel( nX, nY, 0, 0 ) + Pixel( nX, nY, 0, 1 ) + Pixel( nX, nY, 0, 2 ) );
ret->Pixel( nX, nY, 0, 0 ) *= flScale;
ret->Pixel( nX, nY, 0, 1 ) *= flScale;
ret->Pixel( nX, nY, 0, 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 flBumpScale ) const
{
Vector * verts;
Vector * normals;
ComputeVertexPositionsAndNormals( flBumpScale, & verts, & normals );
FloatBitMap_t * ret = new FloatBitMap_t( NumCols(), NumRows() );
for( int y = 0; y < NumRows(); y++ )
for( int x = 0; x < NumCols(); x++ )
{
Vector const & N = normals[ x + y * NumCols() ];
ret->Pixel( x, y, 0, 0 ) = 0.5 + 0.5 * N.x;
ret->Pixel( x, y, 0, 1 ) = 0.5 + 0.5 * N.y;
ret->Pixel( x, y, 0, 2 ) = 0.5 + 0.5 * N.z;
ret->Pixel( x, y, 0, 3 ) = Pixel( x, y, 0, 3 );
}
return ret;
}

View File

@@ -0,0 +1,105 @@
//===== Copyright <20> 1996-2006, Valve Corporation, All rights reserved. ======//
//
// Purpose:
//
//===========================================================================//
#include <tier0/platform.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include "bitmap/floatbitmap.h"
#include <tier2/tier2.h>
#include "tier0/threadtools.h"
#include "tier0/progressbar.h"
// NOTE: This has to be the last file included!
#include "tier0/memdbgon.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 uintp 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->NumCols(); x++ )
{
for( int c = 0;c < 4;c++ )
{
float sum_weights = 0;
float filter_sum = 0;
float centerp = ctx->orig_bm->Pixel( x, y, 0, 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, 0, 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, 0, 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, GetCPUInformation().m_nPhysicalProcessors );
ThreadHandle_t waithandles[32];
int starty = 0;
int ystep = NumRows() / 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( NumRows() - 1, starty + ystep - 1 );
else
ctxs[t].max_y = NumRows() - 1;
waithandles[t]= CreateSimpleThread( TBFCalculationThreadFN, & ctxs[t] );
starty += ystep;
}
for( int t = 0;t < nthreads;t++ )
{
ThreadJoin( waithandles[t] );
}
}

179
bitmap/floatcubemap.cpp Normal file
View File

@@ -0,0 +1,179 @@
#include <tier0/platform.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include "bitmap/floatbitmap.h"
#include <filesystem.h>
#include <mathlib/vector.h>
#include "mathlib/spherical_geometry.h"
// NOTE: This has to be the last file included!
#include "tier0/memdbgon.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.NumCols() - 1 );
float yc = y * 1.0 / ( bm.NumRows() - 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].NumRows();dy++ )
for( int dx = 0;dx < out.face_maps[dface].NumCols();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].NumRows();sy++ )
for( int sx = 0;sx < face_maps[sface].NumCols();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, 0, c );
}
}
}
}
for( int c = 0;c < 3;c++ )
out.face_maps[dface].Pixel( dx, dy, 0, c ) = sum_rgb[c] * ( 1.0 / sum_weights );
}
}
}
void FloatCubeMap_t::CalculateSphericalHarmonicApproximation( int nOrder, Vector *flCoeffs )
{
for( int nL = 0; nL <= nOrder; nL++ )
{
for( int nM = - nL ; nM <= nL; nM++ )
{
Vector vecSum( 0, 0, 0 );
float flSumWeights = 0.;
for( int nFace = 0; nFace < 6; nFace++ )
for( int nY = 0; nY < face_maps[nFace].NumRows(); nY++ )
for( int nX = 0; nX < face_maps[nFace].NumCols(); nX++ )
{
// determine direction and area of sample. !!speed!! this could be incremental
Vector dir00 = PixelDirection( nFace, nX, nY );
Vector dir01 = PixelDirection( nFace, nX, nY + 1 );
Vector dir10 = PixelDirection( nFace, nX + 1 , nY );
Vector dir11 = PixelDirection( nFace, nX + 1, nY + 1 );
float flArea = UnitSphereTriangleArea( dir00, dir10, dir11 ) +
UnitSphereTriangleArea( dir00, dir01, dir11 );
float flHarmonic = SphericalHarmonic( nL, nM, dir00 );
flSumWeights += flArea;
for( int c = 0; c < 3; c++ )
vecSum[c] += flHarmonic * face_maps[nFace].Pixel( nX, nY, 0, c ) * flArea;
}
vecSum *= ( ( 4 * M_PI ) / flSumWeights );
*( flCoeffs++ ) = vecSum;
}
}
}
void FloatCubeMap_t::GenerateFromSphericalHarmonics( int nOrder, Vector const *flCoeffs )
{
for( int nFace = 0; nFace < 6; nFace++ )
face_maps[nFace].Clear( 0, 0, 0, 1 );
for( int nL = 0; nL <= nOrder; nL++ )
{
for( int nM = - nL ; nM <= nL; nM++ )
{
for( int nFace = 0; nFace < 6; nFace++ )
for( int nY = 0; nY < face_maps[nFace].NumRows(); nY++ )
for( int nX = 0; nX < face_maps[nFace].NumCols(); nX++ )
{
// determine direction and area of sample. !!speed!! this could be incremental
Vector dir00 = PixelDirection( nFace, nX, nY );
float flHarmonic = SphericalHarmonic( nL, nM, dir00 );
for( int c = 0; c < 3; c++ )
face_maps[nFace].Pixel( nX, nY, 0, c ) += ( *flCoeffs )[c] * flHarmonic;
}
flCoeffs++;
}
}
}

651
bitmap/imageformat.cpp Normal file
View File

@@ -0,0 +1,651 @@
//======= Copyright 1996-2005, Valve Corporation, All rights reserved. ======//
//
// Purpose:
//
//=============================================================================//
#if defined( _WIN32 ) && !defined( _X360 ) && !defined( DX_TO_GL_ABSTRACTION )
#include <windows.h>
#include "../dx9sdk/include/d3d9types.h"
#include "dx11sdk/d3d11.h"
#endif
#include "bitmap/imageformat.h"
#include "basetypes.h"
#include "tier0/dbg.h"
#ifndef _PS3
#include <malloc.h>
#include <memory.h>
#else
#include <stdlib.h>
#endif
#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 ImageFormatInfo_t g_ImageFormatInfo[] =
{
{ "UNKNOWN", 0, 0, 0, 0, 0, 0, 0, false, false, false }, // IMAGE_FORMAT_UNKNOWN,
{ "RGBA8888", 4, 8, 8, 8, 8, 0, 0, false, false, false }, // IMAGE_FORMAT_RGBA8888,
{ "ABGR8888", 4, 8, 8, 8, 8, 0, 0, false, false, false }, // IMAGE_FORMAT_ABGR8888,
{ "RGB888", 3, 8, 8, 8, 0, 0, 0, false, false, false }, // IMAGE_FORMAT_RGB888,
{ "BGR888", 3, 8, 8, 8, 0, 0, 0, false, false, false }, // IMAGE_FORMAT_BGR888,
{ "RGB565", 2, 5, 6, 5, 0, 0, 0, false, false, false }, // IMAGE_FORMAT_RGB565,
{ "I8", 1, 0, 0, 0, 0, 0, 0, false, false, false }, // IMAGE_FORMAT_I8,
{ "IA88", 2, 0, 0, 0, 8, 0, 0, false, false, false }, // IMAGE_FORMAT_IA88
{ "P8", 1, 0, 0, 0, 0, 0, 0, false, false, false }, // IMAGE_FORMAT_P8
{ "A8", 1, 0, 0, 0, 8, 0, 0, false, false, false }, // IMAGE_FORMAT_A8
{ "RGB888_BLUESCREEN", 3, 8, 8, 8, 0, 0, 0, false, false, false }, // IMAGE_FORMAT_RGB888_BLUESCREEN
{ "BGR888_BLUESCREEN", 3, 8, 8, 8, 0, 0, 0, false, false, false }, // IMAGE_FORMAT_BGR888_BLUESCREEN
{ "ARGB8888", 4, 8, 8, 8, 8, 0, 0, false, false, false }, // IMAGE_FORMAT_ARGB8888
{ "BGRA8888", 4, 8, 8, 8, 8, 0, 0, false, false, false }, // IMAGE_FORMAT_BGRA8888
{ "DXT1", 0, 0, 0, 0, 0, 0, 0, true, false, false }, // IMAGE_FORMAT_DXT1
{ "DXT3", 0, 0, 0, 0, 8, 0, 0, true, false, false }, // IMAGE_FORMAT_DXT3
{ "DXT5", 0, 0, 0, 0, 8, 0, 0, true, false, false }, // IMAGE_FORMAT_DXT5
{ "BGRX8888", 4, 8, 8, 8, 0, 0, 0, false, false, false }, // IMAGE_FORMAT_BGRX8888
{ "BGR565", 2, 5, 6, 5, 0, 0, 0, false, false, false }, // IMAGE_FORMAT_BGR565
{ "BGRX5551", 2, 5, 5, 5, 0, 0, 0, false, false, false }, // IMAGE_FORMAT_BGRX5551
{ "BGRA4444", 2, 4, 4, 4, 4, 0, 0, false, false, false }, // IMAGE_FORMAT_BGRA4444
{ "DXT1_ONEBITALPHA", 0, 0, 0, 0, 0, 0, 0, true, false, false }, // IMAGE_FORMAT_DXT1_ONEBITALPHA
{ "BGRA5551", 2, 5, 5, 5, 1, 0, 0, false, false, false }, // IMAGE_FORMAT_BGRA5551
{ "UV88", 2, 8, 8, 0, 0, 0, 0, false, false, false }, // IMAGE_FORMAT_UV88
{ "UVWQ8888", 4, 8, 8, 8, 8, 0, 0, false, false, false }, // IMAGE_FORMAT_UVWQ8888
{ "RGBA16161616F", 8, 16, 16, 16, 16, 0, 0, false, true, false }, // IMAGE_FORMAT_RGBA16161616F
{ "RGBA16161616", 8, 16, 16, 16, 16, 0, 0, false, false, false }, // IMAGE_FORMAT_RGBA16161616
{ "UVLX8888", 4, 8, 8, 8, 8, 0, 0, false, false, false }, // IMAGE_FORMAT_UVLX8888
{ "R32F", 4, 32, 0, 0, 0, 0, 0, false, true, false }, // IMAGE_FORMAT_R32F
{ "RGB323232F", 12, 32, 32, 32, 0, 0, 0, false, true, false }, // IMAGE_FORMAT_RGB323232F
{ "RGBA32323232F", 16, 32, 32, 32, 32, 0, 0, false, true, false }, // IMAGE_FORMAT_RGBA32323232F
{ "RG1616F", 4, 16, 16, 0, 0, 0, 0, false, true, false }, // IMAGE_FORMAT_RG1616F
{ "RG3232F", 8, 32, 32, 0, 0, 0, 0, false, true, false }, // IMAGE_FORMAT_RG3232F
{ "RGBX8888", 4, 8, 8, 8, 0, 0, 0, false, false, false }, // IMAGE_FORMAT_RGBX8888
{ "NV_NULL", 4, 8, 8, 8, 8, 0, 0, false, false, false }, // IMAGE_FORMAT_NV_NULL
// Vendor-dependent compressed formats typically used for normal map compression
{ "ATI1N", 0, 0, 0, 0, 0, 0, 0, true, false, false }, // IMAGE_FORMAT_ATI1N
{ "ATI2N", 0, 0, 0, 0, 0, 0, 0, true, false, false }, // IMAGE_FORMAT_ATI2N
{ "RGBA1010102", 4, 10, 10, 10, 2, 0, 0, false, false, false }, // IMAGE_FORMAT_RGBA1010102
{ "BGRA1010102", 4, 10, 10, 10, 2, 0, 0, false, false, false }, // IMAGE_FORMAT_BGRA1010102
{ "R16F", 2, 16, 0, 0, 0, 0, 0, false, false, false }, // IMAGE_FORMAT_R16F
// Vendor-dependent depth formats used for shadow depth mapping
{ "D16", 2, 0, 0, 0, 0, 16, 0, false, false, true }, // IMAGE_FORMAT_D16
{ "D15S1", 2, 0, 0, 0, 0, 15, 1, false, false, true }, // IMAGE_FORMAT_D15S1
{ "D32", 4, 0, 0, 0, 0, 32, 0, false, false, true }, // IMAGE_FORMAT_D32
{ "D24S8", 4, 0, 0, 0, 0, 24, 8, false, false, true }, // IMAGE_FORMAT_D24S8
{ "LINEAR_D24S8", 4, 0, 0, 0, 0, 24, 8, false, false, true }, // IMAGE_FORMAT_LINEAR_D24S8
{ "D24X8", 4, 0, 0, 0, 0, 24, 0, false, false, true }, // IMAGE_FORMAT_D24X8
{ "D24X4S4", 4, 0, 0, 0, 0, 24, 4, false, false, true }, // IMAGE_FORMAT_D24X4S4
{ "D24FS8", 4, 0, 0, 0, 0, 24, 8, false, false, true }, // IMAGE_FORMAT_D24FS8
{ "D16_SHADOW", 2, 0, 0, 0, 0, 16, 0, false, false, true }, // IMAGE_FORMAT_D16_SHADOW
{ "D24X8_SHADOW", 4, 0, 0, 0, 0, 24, 0, false, false, true }, // IMAGE_FORMAT_D24X8_SHADOW
{ "LINEAR_BGRX8888", 4, 8, 8, 8, 8, 0, 0, false, false, false }, // IMAGE_FORMAT_LINEAR_BGRX8888
{ "LINEAR_RGBA8888", 4, 8, 8, 8, 8, 0, 0, false, false, false }, // IMAGE_FORMAT_LINEAR_RGBA8888
{ "LINEAR_ABGR8888", 4, 8, 8, 8, 8, 0, 0, false, false, false }, // IMAGE_FORMAT_LINEAR_ABGR8888
{ "LINEAR_ARGB8888", 4, 8, 8, 8, 8, 0, 0, false, false, false }, // IMAGE_FORMAT_LINEAR_ARGB8888
{ "LINEAR_BGRA8888", 4, 8, 8, 8, 8, 0, 0, false, false, false }, // IMAGE_FORMAT_LINEAR_BGRA8888
{ "LINEAR_RGB888", 3, 8, 8, 8, 0, 0, 0, false, false, false }, // IMAGE_FORMAT_LINEAR_RGB888
{ "LINEAR_BGR888", 3, 8, 8, 8, 0, 0, 0, false, false, false }, // IMAGE_FORMAT_LINEAR_BGR888
{ "LINEAR_BGRX5551", 2, 5, 5, 5, 0, 0, 0, false, false, false }, // IMAGE_FORMAT_LINEAR_BGRX5551
{ "LINEAR_I8", 1, 0, 0, 0, 0, 0, 0, false, false, false }, // IMAGE_FORMAT_LINEAR_I8
{ "LINEAR_RGBA16161616", 8, 16, 16, 16, 16, 0, 0, false, false, false }, // IMAGE_FORMAT_LINEAR_RGBA16161616
{ "LINEAR_A8", 1, 0, 0, 0, 8, 0, 0, false, false, false }, // IMAGE_FORMAT_LINEAR_A8
{ "LINEAR_DXT1", 0, 0, 0, 0, 0, 0, 0, true, false, false }, // IMAGE_FORMAT_LINEAR_DXT1
{ "LINEAR_DXT3", 0, 0, 0, 0, 8, 0, 0, true, false, false }, // IMAGE_FORMAT_LINEAR_DXT3
{ "LINEAR_DXT5", 0, 0, 0, 0, 8, 0, 0, true, false, false }, // IMAGE_FORMAT_LINEAR_DXT5
{ "LE_BGRX8888", 4, 8, 8, 8, 8, 0, 0, false, false, false }, // IMAGE_FORMAT_LE_BGRX8888
{ "LE_BGRA8888", 4, 8, 8, 8, 8, 0, 0, false, false, false }, // IMAGE_FORMAT_LE_BGRA8888
{ "DXT1_RUNTIME", 0, 0, 0, 0, 0, 0, 0, true, false, false }, // IMAGE_FORMAT_DXT1_RUNTIME
{ "DXT5_RUNTIME", 0, 0, 0, 0, 8, 0, 0, true, false, false }, // IMAGE_FORMAT_DXT5_RUNTIME
// Vendor-dependent depth formats used for resolving
{ "INTZ", 4, 0, 0, 0, 0, 24, 8, false, false, true}, // IMAGE_FORMAT_INTZ
};
namespace ImageLoader
{
//-----------------------------------------------------------------------------
// Returns info about each image format
//-----------------------------------------------------------------------------
const ImageFormatInfo_t& ImageFormatInfo( ImageFormat fmt )
{
COMPILE_TIME_ASSERT( ( NUM_IMAGE_FORMATS + 1 ) == ARRAYSIZE( g_ImageFormatInfo ) );
Assert( unsigned( fmt + 1 ) <= ( NUM_IMAGE_FORMATS ) );
return g_ImageFormatInfo[ fmt + 1 ];
}
int GetMemRequired( int width, int height, int depth, int nMipmapCount, ImageFormat imageFormat, int *pAdjustedHeight )
{
depth = MAX( 1, depth );
int nRet = 0;
if ( nMipmapCount == 1 )
{
// Block compressed formats
const ImageFormatInfo_t &fmt = ImageFormatInfo( imageFormat );
if ( fmt.m_bIsCompressed )
{
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;
}
width >>= 2;
height >>= 2;
int numBlocks = width * height * depth;
switch ( imageFormat )
{
case IMAGE_FORMAT_DXT1:
case IMAGE_FORMAT_DXT1_RUNTIME:
case IMAGE_FORMAT_LINEAR_DXT1:
case IMAGE_FORMAT_ATI1N:
nRet = numBlocks * 8;
break;
case IMAGE_FORMAT_DXT3:
case IMAGE_FORMAT_DXT5:
case IMAGE_FORMAT_DXT5_RUNTIME:
case IMAGE_FORMAT_LINEAR_DXT3:
case IMAGE_FORMAT_LINEAR_DXT5:
case IMAGE_FORMAT_ATI2N:
nRet = numBlocks * 16;
break;
}
}
else
{
nRet = width * height * depth * fmt.m_nNumBytes;
}
if ( pAdjustedHeight )
{
*pAdjustedHeight = height;
}
return nRet;
}
// Mipmap version
int memSize = 0;
// Not sensical for mip chains
if ( pAdjustedHeight )
{
*pAdjustedHeight = 0;
}
while ( true )
{
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;
}
if ( nMipmapCount )
{
if ( --nMipmapCount == 0 )
break;
}
}
return memSize;
}
int GetMemRequired( int width, int height, int depth, ImageFormat imageFormat, bool mipmap, int *pAdjustedHeight )
{
return GetMemRequired( width, height, depth, mipmap ? 0 : 1, imageFormat, pAdjustedHeight );
}
int GetMipMapLevelByteOffset( int width, int height, ImageFormat imageFormat, int skipMipLevels, int nDepth )
{
int offset = 0;
while( skipMipLevels > 0 )
{
offset += GetMemRequired( width, height, nDepth, 1, imageFormat );
if( width == 1 && height == 1 && nDepth == 1 )
{
break;
}
width = MAX( 1, width >> 1 );
height = MAX( 1, height >> 1 );
nDepth = MAX( 1, nDepth >> 1 );
skipMipLevels--;
}
return offset;
}
//-----------------------------------------------------------------------------
// This version is for mipmaps which are stored smallest level to largest level in memory
//-----------------------------------------------------------------------------
int GetMipMapLevelByteOffsetReverse( int nWidth, int nHeight, int nDepth, int nTotalMipCount, ImageFormat imageFormat, int nMipLevel )
{
if ( nTotalMipCount == 1 )
return 0;
int nSkipSize = 0;
for ( int i = 0; i < nTotalMipCount; ++i )
{
int nMipSize = GetMemRequired( nWidth, nHeight, nDepth, 1, imageFormat );
if ( i > nMipLevel )
{
nSkipSize += nMipSize;
}
if( nWidth == 1 && nHeight == 1 && nDepth == 1 )
break;
nWidth = MAX( 1, nWidth >> 1 );
nHeight = MAX( 1, nHeight >> 1 );
nDepth = MAX( 1, nDepth >> 1 );
}
return nSkipSize;
}
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--;
}
}
void GetMipMapLevelDimensions( int &nWidth, int &nHeight, int &nDepth, int nMipLevel )
{
for( ; nMipLevel > 0; --nMipLevel )
{
if( nWidth <= 1 && nHeight <= 1 && nDepth <= 1 )
break;
nWidth >>= 1;
nHeight >>= 1;
nDepth >>= 1;
}
nWidth = MAX( nWidth, 1 );
nHeight = MAX( nHeight, 1 );
nDepth = MAX( nDepth, 1 );
}
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 POSIX
#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( PLATFORM_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( PLATFORM_X360 )
case D3DFMT_R8G8B8:
return IMAGE_FORMAT_BGR888;
#endif
#ifndef POSIX
case D3DFMT_A8B8G8R8:
return IMAGE_FORMAT_RGBA8888;
case D3DFMT_X8B8G8R8:
return IMAGE_FORMAT_RGBX8888;
#endif // !POSIX
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;
#if !defined( PLATFORM_X360 ) && !defined( POSIX )
case D3DFMT_P8:
return IMAGE_FORMAT_P8;
#endif
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;
case (D3DFORMAT)(MAKEFOURCC('N','U','L','L')):
return IMAGE_FORMAT_NULL;
case D3DFMT_D16:
return IMAGE_FORMAT_D16;
#ifndef POSIX
case D3DFMT_G16R16F:
return IMAGE_FORMAT_RG1616F;
case D3DFMT_G32R32F:
return IMAGE_FORMAT_RG3232F;
#endif // !POSIX
case D3DFMT_D24S8:
return IMAGE_FORMAT_D24S8;
case (D3DFORMAT)(MAKEFOURCC('A','T','I','1')):
return IMAGE_FORMAT_ATI1N;
case (D3DFORMAT)(MAKEFOURCC('A','T','I','2')):
return IMAGE_FORMAT_ATI2N;
#ifndef POSIX
case D3DFMT_A2B10G10R10:
return IMAGE_FORMAT_RGBA1010102;
case D3DFMT_A2R10G10B10:
return IMAGE_FORMAT_BGRA1010102;
case D3DFMT_R16F:
return IMAGE_FORMAT_R16F;
case D3DFMT_D32:
return IMAGE_FORMAT_D32;
#endif // !POSIX
case D3DFMT_D24X8:
return IMAGE_FORMAT_D24X8;
#ifndef PLATFORM_X360
case D3DFMT_D15S1:
return IMAGE_FORMAT_D15S1;
case D3DFMT_D24X4S4:
return IMAGE_FORMAT_D24X4S4;
#endif
case D3DFMT_UNKNOWN:
return IMAGE_FORMAT_UNKNOWN;
#ifdef PLATFORM_X360
case D3DFMT_LIN_A8R8G8B8:
return IMAGE_FORMAT_LINEAR_BGRA8888;
case D3DFMT_LIN_A8B8G8R8:
return IMAGE_FORMAT_LINEAR_RGBA8888;
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_LIN_D24S8:
return IMAGE_FORMAT_LINEAR_D24S8;
case D3DFMT_LIN_A8:
return IMAGE_FORMAT_LINEAR_A8;
case D3DFMT_LIN_DXT1:
return IMAGE_FORMAT_LINEAR_DXT1;
case D3DFMT_LIN_DXT3:
return IMAGE_FORMAT_LINEAR_DXT3;
case D3DFMT_LIN_DXT5:
return IMAGE_FORMAT_LINEAR_DXT5;
#endif
#if !defined( _PS3 )
case D3DFMT_D24FS8:
return IMAGE_FORMAT_D24FS8;
#endif // !_PS3
}
return IMAGE_FORMAT_UNKNOWN;
}
#ifdef _PS3
// Stub out some formats that don't have direct analgoues on PS3 or that we haven't yet mapped
#define D3DFMT_A8B8G8R8 D3DFMT_UNKNOWN
#define D3DFMT_P8 D3DFMT_UNKNOWN
#define D3DFMT_G16R16F D3DFMT_UNKNOWN
#define D3DFMT_G32R32F D3DFMT_UNKNOWN
#define D3DFMT_X8B8G8R8 D3DFMT_UNKNOWN
#define D3DFMT_A2B10G10R10 D3DFMT_UNKNOWN
#define D3DFMT_A2R10G10B10 D3DFMT_UNKNOWN
#define D3DFMT_R16F D3DFMT_UNKNOWN
#define D3DFMT_D32 D3DFMT_UNKNOWN
#define D3DFMT_D24FS8 D3DFMT_UNKNOWN
#endif // _PS3
// A format exists in here only if there is a direct mapping
static D3DFORMAT s_pD3DFormats[] =
{
D3DFMT_UNKNOWN, // IMAGE_FORMAT_UNKNOWN,
D3DFMT_A8B8G8R8, // IMAGE_FORMAT_RGBA8888,
D3DFMT_UNKNOWN, // IMAGE_FORMAT_ABGR8888,
D3DFMT_UNKNOWN, // IMAGE_FORMAT_RGB888,
#if !defined( PLATFORM_X360 )
D3DFMT_R8G8B8, // IMAGE_FORMAT_BGR888
#else
D3DFMT_UNKNOWN, // IMAGE_FORMAT_BGR888
#endif
D3DFMT_UNKNOWN, // IMAGE_FORMAT_RGB565,
D3DFMT_L8, // IMAGE_FORMAT_I8,
D3DFMT_A8L8, // IMAGE_FORMAT_IA88,
#ifndef PLATFORM_X360
D3DFMT_P8, // IMAGE_FORMAT_P8,
#else
D3DFMT_UNKNOWN, // IMAGE_FORMAT_P8,
#endif
D3DFMT_A8, // IMAGE_FORMAT_A8,
D3DFMT_UNKNOWN, // IMAGE_FORMAT_RGB888_BLUESCREEN,
D3DFMT_UNKNOWN, // IMAGE_FORMAT_BGR888_BLUESCREEN,
D3DFMT_UNKNOWN, // IMAGE_FORMAT_ARGB8888,
D3DFMT_A8R8G8B8, // IMAGE_FORMAT_BGRA8888,
D3DFMT_DXT1, // IMAGE_FORMAT_DXT1,
D3DFMT_DXT3, // IMAGE_FORMAT_DXT3,
D3DFMT_DXT5, // IMAGE_FORMAT_DXT5,
D3DFMT_X8R8G8B8, // IMAGE_FORMAT_BGRX8888,
D3DFMT_R5G6B5, // IMAGE_FORMAT_BGR565,
D3DFMT_X1R5G5B5, // IMAGE_FORMAT_BGRX5551,
D3DFMT_A4R4G4B4, // IMAGE_FORMAT_BGRA4444,
D3DFMT_UNKNOWN, // IMAGE_FORMAT_DXT1_ONEBITALPHA,
D3DFMT_A1R5G5B5, // IMAGE_FORMAT_BGRA5551,
D3DFMT_V8U8, // IMAGE_FORMAT_UV88,
D3DFMT_Q8W8V8U8, // IMAGE_FORMAT_UVWQ8888,
D3DFMT_A16B16G16R16F, // IMAGE_FORMAT_RGBA16161616F,
D3DFMT_A16B16G16R16, // IMAGE_FORMAT_RGBA16161616,
D3DFMT_X8L8V8U8, // IMAGE_FORMAT_UVLX8888,
D3DFMT_R32F, // IMAGE_FORMAT_R32F,
D3DFMT_UNKNOWN, // IMAGE_FORMAT_RGB323232F,
D3DFMT_A32B32G32R32F, // IMAGE_FORMAT_RGBA32323232F,
D3DFMT_G16R16F, // IMAGE_FORMAT_RG1616F,
D3DFMT_G32R32F, // IMAGE_FORMAT_RG3232F,
D3DFMT_X8B8G8R8, // IMAGE_FORMAT_RGBX8888,
(D3DFORMAT)(MAKEFOURCC('N','U','L','L')), // IMAGE_FORMAT_NULL,
(D3DFORMAT)(MAKEFOURCC('A','T','I','2')), // IMAGE_FORMAT_ATI2N,
(D3DFORMAT)(MAKEFOURCC('A','T','I','1')), // IMAGE_FORMAT_ATI1N,
D3DFMT_A2B10G10R10, // IMAGE_FORMAT_RGBA1010102,
D3DFMT_A2R10G10B10, // IMAGE_FORMAT_BGRA1010102,
D3DFMT_R16F, // IMAGE_FORMAT_R16F,
D3DFMT_D16, // IMAGE_FORMAT_D16,
#ifndef PLATFORM_X360
D3DFMT_D15S1, // IMAGE_FORMAT_D15S1,
D3DFMT_D32, // IMAGE_FORMAT_D32,
D3DFMT_D24S8, // IMAGE_FORMAT_D24S8,
D3DFMT_UNKNOWN, // IMAGE_FORMAT_LINEAR_D24S8,
D3DFMT_D24X8, // IMAGE_FORMAT_D24X8,
D3DFMT_D24X4S4, // IMAGE_FORMAT_D24X4S4,
D3DFMT_D24FS8, // IMAGE_FORMAT_D24FS8,
D3DFMT_UNKNOWN, // IMAGE_FORMAT_D16_SHADOW,
D3DFMT_UNKNOWN, // IMAGE_FORMAT_D24S8_SHADOW,
D3DFMT_UNKNOWN, // IMAGE_FORMAT_LINEAR_BGRX8888,
D3DFMT_UNKNOWN, // IMAGE_FORMAT_LINEAR_RGBA8888,
D3DFMT_UNKNOWN, // IMAGE_FORMAT_LINEAR_ABGR8888,
D3DFMT_UNKNOWN, // IMAGE_FORMAT_LINEAR_ARGB8888,
D3DFMT_UNKNOWN, // IMAGE_FORMAT_LINEAR_BGRA8888,
D3DFMT_UNKNOWN, // IMAGE_FORMAT_LINEAR_RGB888,
D3DFMT_UNKNOWN, // IMAGE_FORMAT_LINEAR_BGR888,
D3DFMT_UNKNOWN, // IMAGE_FORMAT_LINEAR_BGRX5551,
D3DFMT_UNKNOWN, // IMAGE_FORMAT_LINEAR_I8,
D3DFMT_UNKNOWN, // IMAGE_FORMAT_LINEAR_RGBA16161616,
D3DFMT_UNKNOWN, // IMAGE_FORMAT_LINEAR_A8,
D3DFMT_UNKNOWN, // IMAGE_FORMAT_LINEAR_DXT1,
D3DFMT_UNKNOWN, // IMAGE_FORMAT_LINEAR_DXT3,
D3DFMT_UNKNOWN, // IMAGE_FORMAT_LINEAR_DXT5,
D3DFMT_UNKNOWN, // IMAGE_FORMAT_LE_BGRX8888,
D3DFMT_UNKNOWN, // IMAGE_FORMAT_LE_BGRA8888,
D3DFMT_DXT1, // IMAGE_FORMAT_DXT5_RUNTIME,
D3DFMT_DXT5, // IMAGE_FORMAT_DXT5_RUNTIME,
(D3DFORMAT)(MAKEFOURCC('I','N','T','Z')), // IMAGE_FORMAT_INTZ,
#else
D3DFMT_UNKNOWN, // IMAGE_FORMAT_D15S1,
D3DFMT_D32, // IMAGE_FORMAT_D32,
D3DFMT_D24S8, // IMAGE_FORMAT_D24S8,
D3DFMT_LIN_D24S8, // IMAGE_FORMAT_LINEAR_D24S8,
D3DFMT_D24X8, // IMAGE_FORMAT_D24X8,
D3DFMT_UNKNOWN, // IMAGE_FORMAT_D24X4S4,
D3DFMT_D24FS8, // IMAGE_FORMAT_D24FS8,
D3DFMT_UNKNOWN, // IMAGE_FORMAT_D16_SHADOW,
D3DFMT_UNKNOWN, // IMAGE_FORMAT_D24S8_SHADOW,
D3DFMT_LIN_X8R8G8B8, // IMAGE_FORMAT_LINEAR_BGRX8888,
D3DFMT_LIN_A8B8G8R8, // IMAGE_FORMAT_LINEAR_RGBA8888,
D3DFMT_UNKNOWN, // IMAGE_FORMAT_LINEAR_ABGR8888,
D3DFMT_UNKNOWN, // IMAGE_FORMAT_LINEAR_ARGB8888,
D3DFMT_LIN_A8R8G8B8, // IMAGE_FORMAT_LINEAR_BGRA8888,
D3DFMT_UNKNOWN, // IMAGE_FORMAT_LINEAR_RGB888,
D3DFMT_UNKNOWN, // IMAGE_FORMAT_LINEAR_BGR888,
D3DFMT_LIN_X1R5G5B5, // IMAGE_FORMAT_LINEAR_BGRX5551,
D3DFMT_LIN_L8, // IMAGE_FORMAT_LINEAR_I8,
D3DFMT_LIN_A16B16G16R16, // IMAGE_FORMAT_LINEAR_RGBA16161616,
D3DFMT_LIN_A8, // IMAGE_FORMAT_LINEAR_A8
D3DFMT_LIN_DXT1, // IMAGE_FORMAT_LINEAR_DXT1,
D3DFMT_LIN_DXT3, // IMAGE_FORMAT_LINEAR_DXT3,
D3DFMT_LIN_DXT5, // IMAGE_FORMAT_LINEAR_DXT5,
D3DFMT_LE_X8R8G8B8, // IMAGE_FORMAT_LE_BGRX8888,
D3DFMT_LE_A8R8G8B8, // IMAGE_FORMAT_LE_BGRA8888,
D3DFMT_DXT1, // IMAGE_FORMAT_DXT5_RUNTIME,
D3DFMT_DXT5, // IMAGE_FORMAT_DXT5_RUNTIME,
#endif
};
D3DFORMAT ImageFormatToD3DFormat( ImageFormat format )
{
COMPILE_TIME_ASSERT( ARRAYSIZE( s_pD3DFormats ) == NUM_IMAGE_FORMATS + 1 );
return s_pD3DFormats[ format + 1 ];
}
#pragma warning (default:4063)
} // ImageLoader namespace ends

571
bitmap/psd.cpp Normal file
View File

@@ -0,0 +1,571 @@
//===== Copyright <20> 1996-2005, 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 *pDest = bitmap.GetBits();
for( int j=0; j < bitmap.Height(); ++j )
{
for ( int k = 0; k < bitmap.Width(); ++k, pDest += 4 )
{
pDest[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*)stackalloc( 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*)stackalloc( 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*)stackalloc( 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 ]
uint32 uSignature = BigLong( *( uint32* )( 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;
}

477
bitmap/psheet.cpp Normal file
View File

@@ -0,0 +1,477 @@
//===== Copyright <20> 1996-2006, Valve Corporation, All rights reserved. ======//
//
// Purpose: sheet code for particles and other sprite functions
//
//===========================================================================//
#include "bitmap/psheet.h"
#include "tier1/UtlStringMap.h"
#include "tier1/utlbuffer.h"
#include "tier2/fileutils.h"
// MOC_TODO: These probably shouldn't be here - maybe we should put CSheetExtended somewhere else, like materialsystem?
#include "materialsystem/imesh.h"
#include "materialsystem/imaterial.h"
#include "materialsystem/imaterialvar.h"
#include "materialsystem/itexture.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
CSheet::CSheet( void )
{
}
CSheet::CSheet( CUtlBuffer &buf )
{
// lets read a sheet
buf.ActivateByteSwappingIfBigEndian();
int nVersion = buf.GetInt(); // version#
int nNumCoordsPerFrame = (nVersion)?MAX_IMAGES_PER_FRAME_ON_DISK:1;
int nNumSequences = buf.GetInt();
int nNumToAllocate = nNumSequences;
// The old hardcoded arrays were 64.
// We will allocate 64 for now to handle content issues with
// rendering system trying to get at empty sequences.
if ( nNumToAllocate < 64 )
nNumToAllocate = 64;
m_SheetInfo.EnsureCapacity( nNumSequences );
for ( int i = 0; i < nNumToAllocate; ++i )
{
m_SheetInfo.AddToTail();
m_SheetInfo[i].m_pSamples = NULL;
m_SheetInfo[i].m_SeqFlags = 0;
m_SheetInfo[i].m_bSequenceIsCopyOfAnotherSequence = 0;
m_SheetInfo[i].m_nNumFrames = 0;
m_SheetInfo[i].m_flFrameSpan = 0.0f;
}
while ( nNumSequences-- )
{
int nSequenceIndex = buf.GetInt();
if ( nSequenceIndex < 0 )
{
Warning( "Invalid sequence number (%d)!!!\n", nSequenceIndex );
return;
}
else if ( nSequenceIndex >= m_SheetInfo.Count() )
{
// This can happen if users delete intermediate sequences.
// For example you can wind up with n sequences if you delete a sequence between 0 and n
// In this case we will pad out the vector.
int i = -1;
while ( i < nSequenceIndex )
{
i = m_SheetInfo.AddToTail();
m_SheetInfo[i].m_pSamples = NULL;
m_SheetInfo[i].m_SeqFlags = 0;
m_SheetInfo[i].m_bSequenceIsCopyOfAnotherSequence = 0;
m_SheetInfo[i].m_nNumFrames = 0;
m_SheetInfo[i].m_flFrameSpan = 0.0f;
}
}
m_SheetInfo[nSequenceIndex].m_SeqFlags = (uint8)(0xFF & buf.GetInt()); // reading an int, but not worth storing it all
int nFrameCount = buf.GetInt();
// Save off how many frames we have for this sequence
m_SheetInfo[nSequenceIndex].m_nNumFrames = nFrameCount;
Assert( nFrameCount >= 0 );
bool bSingleFrameSequence = ( nFrameCount == 1 );
int nTimeSamples = bSingleFrameSequence ? 1 : SEQUENCE_SAMPLE_COUNT;
if ( m_SheetInfo[nSequenceIndex].m_pSamples )
{
Warning( "Invalid particle sheet sequence index. There are more than one items with a sequence index of %d. We are only using the last one we found..\n", nSequenceIndex );
delete[] m_SheetInfo[nSequenceIndex].m_pSamples;
}
m_SheetInfo[nSequenceIndex].m_pSamples = new SheetSequenceSample_t[ nTimeSamples ];
int fTotalSequenceTime = ( int )buf.GetFloat();
float InterpKnot[SEQUENCE_SAMPLE_COUNT];
float InterpValue[SEQUENCE_SAMPLE_COUNT];
SheetSequenceSample_t Samples[SEQUENCE_SAMPLE_COUNT];
float fCurTime = 0.;
for( int nFrm = 0 ; nFrm < nFrameCount; nFrm++ )
{
float fThisDuration = buf.GetFloat();
InterpValue[ nFrm ] = nFrm;
InterpKnot [ nFrm ] = SEQUENCE_SAMPLE_COUNT*( fCurTime/ fTotalSequenceTime );
SheetSequenceSample_t &seq = Samples[ nFrm ];
seq.m_fBlendFactor = 0.0f;
for(int nImage = 0 ; nImage< nNumCoordsPerFrame; nImage++ )
{
SequenceSampleTextureCoords_t &s=seq.m_TextureCoordData[nImage];
s.m_fLeft_U0 = buf.GetFloat();
s.m_fTop_V0 = buf.GetFloat();
s.m_fRight_U0 = buf.GetFloat();
s.m_fBottom_V0 = buf.GetFloat();
}
if ( nNumCoordsPerFrame == 1 )
seq.CopyFirstFrameToOthers();
fCurTime += fThisDuration;
m_SheetInfo[nSequenceIndex].m_flFrameSpan = fCurTime;
}
// now, fill in the whole table
for( int nIdx = 0; nIdx < nTimeSamples; nIdx++ )
{
float flIdxA, flIdxB, flInterp;
GetInterpolationData( InterpKnot, InterpValue, nFrameCount,
SEQUENCE_SAMPLE_COUNT,
nIdx,
! ( m_SheetInfo[nSequenceIndex].m_SeqFlags & SEQ_FLAG_CLAMP ),
&flIdxA, &flIdxB, &flInterp );
SheetSequenceSample_t sA = Samples[(int) flIdxA];
SheetSequenceSample_t sB = Samples[(int) flIdxB];
SheetSequenceSample_t &oseq = m_SheetInfo[nSequenceIndex].m_pSamples[nIdx];
oseq.m_fBlendFactor = flInterp;
for(int nImage = 0 ; nImage< MAX_IMAGES_PER_FRAME_IN_MEMORY; nImage++ )
{
SequenceSampleTextureCoords_t &src0=sA.m_TextureCoordData[nImage];
SequenceSampleTextureCoords_t &src1=sB.m_TextureCoordData[nImage];
SequenceSampleTextureCoords_t &o=oseq.m_TextureCoordData[nImage];
o.m_fLeft_U0 = src0.m_fLeft_U0;
o.m_fTop_V0 = src0.m_fTop_V0;
o.m_fRight_U0 = src0.m_fRight_U0;
o.m_fBottom_V0 = src0.m_fBottom_V0;
o.m_fLeft_U1 = src1.m_fLeft_U0;
o.m_fTop_V1 = src1.m_fTop_V0;
o.m_fRight_U1 = src1.m_fRight_U0;
o.m_fBottom_V1 = src1.m_fBottom_V0;
}
}
}
// now, fill in all unseen sequences with copies of the first seen sequence to prevent crashes
// while editing
int nFirstSequence = -1;
for(int i= 0 ; i < m_SheetInfo.Count(); i++)
{
if ( m_SheetInfo[i].m_pSamples )
{
nFirstSequence = i;
break;
}
}
if ( nFirstSequence != -1 )
{
for(int i=0 ; i < m_SheetInfo.Count(); i++)
{
if ( m_SheetInfo[i].m_pSamples == NULL )
{
m_SheetInfo[i].m_pSamples = m_SheetInfo[nFirstSequence].m_pSamples;
m_SheetInfo[i].m_SeqFlags = m_SheetInfo[nFirstSequence].m_SeqFlags;
m_SheetInfo[i].m_nNumFrames = m_SheetInfo[nFirstSequence].m_nNumFrames;
Assert( m_SheetInfo[i].m_nNumFrames >= 1 );
m_SheetInfo[i].m_bSequenceIsCopyOfAnotherSequence = true;
}
}
}
}
CSheet::~CSheet( void )
{
for( int i = 0; i < m_SheetInfo.Count(); i++ )
{
if ( m_SheetInfo[i].m_pSamples && ( !m_SheetInfo[i].m_bSequenceIsCopyOfAnotherSequence ) )
{
delete[] m_SheetInfo[i].m_pSamples;
}
}
}
const SheetSequenceSample_t *CSheet::GetSampleForSequence( float flAge, float flAgeScale, int nSequence, bool bForceLoop )
{
if ( m_SheetInfo[nSequence].m_nNumFrames == 1 )
return (const SheetSequenceSample_t *) &m_SheetInfo[nSequence].m_pSamples[0];
flAge *= flAgeScale;
unsigned int nFrame = ( int )flAge;
if ( ( m_SheetInfo[nSequence].m_SeqFlags & SEQ_FLAG_CLAMP ) && !bForceLoop )
{
nFrame = MIN( nFrame, SEQUENCE_SAMPLE_COUNT-1 );
}
else
{
nFrame &= SEQUENCE_SAMPLE_COUNT-1;
}
return (const SheetSequenceSample_t *) &m_SheetInfo[nSequence].m_pSamples[nFrame];
}
//////////////////////////////////////////////////////////////////////////
CSheetExtended::CSheetExtended( IMaterial* pMaterial )
{
m_Material.Init( pMaterial );
m_pSheetData = NULL;
LoadFromMaterial(pMaterial);
}
CSheetExtended::~CSheetExtended()
{
delete m_pSheetData;
}
void CSheetExtended::LoadFromMaterial( IMaterial* pMaterial )
{
if ( pMaterial == NULL )
return;
bool bFound = false;
IMaterialVar *pVar = pMaterial->FindVar( "$basetexture", &bFound );
if ( !pVar || !bFound || !pVar->IsDefined() )
return;
ITexture *pTex = pVar->GetTextureValue();
if ( !pTex || pTex->IsError() )
return;
size_t nBytes;
void const *pSheetData = pTex->GetResourceData( VTF_RSRC_SHEET, &nBytes );
if ( pSheetData )
{
CUtlBuffer bufLoad( pSheetData, nBytes, CUtlBuffer::READ_ONLY );
LoadFromBuffer( bufLoad );
}
}
void CSheetExtended::LoadFromBuffer( CUtlBuffer& buf )
{
m_pSheetData = new CSheet(buf);
}
int CSheetExtended::GetSheetSequenceCount()
{
if ( m_pSheetData == NULL )
{
return 0;
}
int nUniqueSequences = 0;
for ( int i = 0; i < m_pSheetData->m_SheetInfo.Count(); ++i )
{
if ( !m_pSheetData->m_SheetInfo[i].m_bSequenceIsCopyOfAnotherSequence )
{
nUniqueSequences++;
}
}
return nUniqueSequences;
}
int CSheetExtended::GetNthSequenceIndex( int nSequenceNumber )
{
if ( m_pSheetData == NULL )
{
return 0;
}
int nCountValidSequences = 0;
for ( int i = 0; i < m_pSheetData->m_SheetInfo.Count(); ++i )
{
if ( !m_pSheetData->m_SheetInfo[i].m_bSequenceIsCopyOfAnotherSequence )
{
if ( nCountValidSequences == nSequenceNumber )
{
return i;
}
else
{
nCountValidSequences++;
}
}
}
return 0;
}
static SheetSequenceSample_t s_DefaultSheetSequence =
{
0.0f, 0.0f, 1.0f, 1.0f,
0.0f, 0.0f, 1.0f, 1.0f,
0.0f, 0.0f, 1.0f, 1.0f,
0.0f, 0.0f, 1.0f, 1.0f,
1.0f,
};
const SheetSequenceSample_t *CSheetExtended::GetSampleForSequence( float flAge, float flAgeScale, int nSequence, bool bForceLoop )
{
if ( m_pSheetData == NULL )
return &s_DefaultSheetSequence;
return m_pSheetData->GetSampleForSequence( flAge, flAgeScale, nSequence, bForceLoop );
}
float CSheetExtended::GetSequenceTimeSpan( int nSequenceIndex )
{
if ( m_pSheetData == NULL )
{
return 0.f;
}
return m_pSheetData->m_SheetInfo[nSequenceIndex].m_flFrameSpan;
}
bool CSheetExtended::ValidSheetData()
{
return (m_pSheetData != NULL);
}
bool CSheetExtended::SequenceHasAlphaData( int nSequenceIndex )
{
return !(m_pSheetData->m_SheetInfo[nSequenceIndex].m_SeqFlags & SEQ_FLAG_NO_ALPHA);
}
bool CSheetExtended::SequenceHasColorData( int nSequenceIndex )
{
return !(m_pSheetData->m_SheetInfo[nSequenceIndex].m_SeqFlags & SEQ_FLAG_NO_COLOR);
}
inline void TexCoords0( CMeshBuilder& meshBuilder, int nChannel, const SequenceSampleTextureCoords_t* pSample )
{
meshBuilder.TexCoord4f( nChannel, pSample->m_fLeft_U0, pSample->m_fTop_V0, pSample->m_fRight_U0, pSample->m_fBottom_V0 );
}
inline void TexCoords1( CMeshBuilder& meshBuilder, int nChannel, const SequenceSampleTextureCoords_t* pSample )
{
meshBuilder.TexCoord4f( nChannel, pSample->m_fLeft_U1, pSample->m_fTop_V1, pSample->m_fRight_U1, pSample->m_fBottom_V1 );
}
inline void SpriteCardVert( CMeshBuilder& meshBuilder,
const Vector &vCenter,
const float flRadius,
const SheetSequenceSample_t * pSample,
const SequenceSampleTextureCoords_t *pSample0,
const SequenceSampleTextureCoords_t *pSecondTexture0,
const SheetSequenceSample_t *pSample1Data,
const SequenceSampleTextureCoords_t *pSample1,
float flChannel3U, float flChannel3V )
{
meshBuilder.Position3fv( vCenter.Base() );
TexCoords0( meshBuilder, 0, pSample0 );
TexCoords1( meshBuilder, 1, pSample0 );
meshBuilder.TexCoord4f( 2, pSample->m_fBlendFactor, 0.0f, flRadius, 0.0f );
meshBuilder.TexCoord2f( 3, flChannel3U, flChannel3V );
TexCoords0( meshBuilder, 4, pSecondTexture0 );
if ( pSample1 )
{
TexCoords0( meshBuilder, 5, pSample1 );
TexCoords1( meshBuilder, 6, pSample1 );
meshBuilder.TexCoord4f( 7, pSample1Data->m_fBlendFactor, 0, 0, 0 );
}
}
void CSheetExtended::DrawSheet( IMesh *pMesh, const Vector &vCenter, float flRadius, int nSheetSequence, float flAge, float flSheetPreviewSpeed, bool bLoopSheetPreview, int nSecondarySequence, bool bOverrideSpriteCard )
{
// nSecondarySequence
bool bSpriteCardMaterial = false;
bSpriteCardMaterial = !bOverrideSpriteCard && m_Material && m_Material->IsSpriteCard();
const SheetSequenceSample_t *pSample = GetSampleForSequence( flAge, flSheetPreviewSpeed, nSheetSequence, bLoopSheetPreview );
const SequenceSampleTextureCoords_t *pSample0 = &(pSample->m_TextureCoordData[0]);
const SequenceSampleTextureCoords_t *pSecondTexture0 = &(pSample->m_TextureCoordData[1]);
const SheetSequenceSample_t *pSample1Data = NULL;
const SequenceSampleTextureCoords_t *pSample1 = NULL;
if ( (nSecondarySequence != -1) && IsMaterialDualSequence( m_Material ) )
{
const float SECONDARY_AGE_MULTIPLIER = 0.1f; // hardcoded 'best guess' at relative speed, since we don't want a whole UI for a second speed
float flSecondaryAge = flAge * SECONDARY_AGE_MULTIPLIER;
pSample1Data = GetSampleForSequence( flSecondaryAge, flSheetPreviewSpeed, nSecondarySequence, bLoopSheetPreview );
pSample1 = &(pSample1Data->m_TextureCoordData[0]);
}
CMeshBuilder meshBuilder;
meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 );
if ( bSpriteCardMaterial )
{
SpriteCardVert( meshBuilder, vCenter, flRadius, pSample, pSample0, pSecondTexture0, pSample1Data, pSample1, 0, 0 );
}
else
{
meshBuilder.Position3fv( (vCenter + Vector(-flRadius,-flRadius,0)).Base() );
meshBuilder.TexCoord2f( 0, pSample0->m_fLeft_U0, pSample0->m_fTop_V0 );
}
meshBuilder.Color4ub( 255, 255, 255, 255 );
meshBuilder.AdvanceVertex();
if ( bSpriteCardMaterial )
{
SpriteCardVert( meshBuilder, vCenter, flRadius, pSample, pSample0, pSecondTexture0, pSample1Data, pSample1, 1, 0 );
}
else
{
meshBuilder.Position3fv( (vCenter + Vector(+flRadius,-flRadius,0)).Base() );
meshBuilder.TexCoord2f( 0, pSample0->m_fRight_U0, pSample0->m_fTop_V0 );
}
meshBuilder.Color4ub( 255, 255, 255, 255 );
meshBuilder.AdvanceVertex();
if ( bSpriteCardMaterial )
{
SpriteCardVert( meshBuilder, vCenter, flRadius, pSample, pSample0, pSecondTexture0, pSample1Data, pSample1, 1, 1 );
}
else
{
meshBuilder.Position3fv( (vCenter + Vector(+flRadius,+flRadius,0)).Base() );
meshBuilder.TexCoord2f( 0, pSample0->m_fRight_U0, pSample0->m_fBottom_V0 );
}
meshBuilder.Color4ub( 255, 255, 255, 255 );
meshBuilder.AdvanceVertex();
if ( bSpriteCardMaterial )
{
SpriteCardVert( meshBuilder, vCenter, flRadius, pSample, pSample0, pSecondTexture0, pSample1Data, pSample1, 0, 1 );
}
else
{
meshBuilder.Position3fv( (vCenter + Vector(-flRadius,+flRadius,0)).Base() );
meshBuilder.TexCoord2f( 0, pSample0->m_fLeft_U0, pSample0->m_fBottom_V0 );
}
meshBuilder.Color4ub( 255, 255, 255, 255 );
meshBuilder.AdvanceVertex();
meshBuilder.End();
pMesh->Draw();
}
bool CSheetExtended::IsMaterialDualSequence( IMaterial* pMat )
{
if ( !pMat )
{
return false;
}
bool bFound = false;
IMaterialVar *pVar = pMat->FindVar( "$DUALSEQUENCE", &bFound );
return ( pVar && bFound && pVar->IsDefined() && pVar->GetIntValue() );
}
bool CSheetExtended::IsMaterialSeparateAlphaColorMaterial( IMaterial* pMat )
{
if ( !pMat || !IsMaterialDualSequence(pMat) )
{
return false;
}
bool bFound = false;
IMaterialVar *pVar = pMat->FindVar( "$SEQUENCE_BLEND_MODE", &bFound );
if ( !pVar || !bFound || !pVar->IsDefined() )
return false;
return (pVar->GetIntValue() == 1);
}

902
bitmap/resample.cpp Normal file
View File

@@ -0,0 +1,902 @@
//======= Copyright <20> 1996-2005, Valve Corporation, All rights reserved. ======//
//
// Purpose:
//
//=============================================================================//
#include "nvtc.h"
#include "bitmap/imageformat.h"
#include "basetypes.h"
#include "tier0/dbg.h"
#ifndef _PS3
#include <malloc.h>
#include <memory.h>
#else
#include <stdlib.h>
#endif
#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. 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)
int kernelWidth, kernelHeight, kernelDepth;
float sx, dx, sy, dy, sz, dz;
float flInvFactor = 1.0f;
kernelWidth = kernelHeight = kernelDepth = 1;
sx = dx = sy = dy = sz = dz = 0.0f;
if ( wratio > 1.0f )
{
kernelWidth = ( int )( kernelDiameter * wratio );
dx = 1.0f / (float)wratio;
sx = -((float)kernelDiameter - dx) * 0.5f;
flInvFactor *= wratio;
}
if ( hratio > 1.0f )
{
kernelHeight = ( int )( kernelDiameter * hratio );
dy = 1.0f / (float)hratio;
sy = -((float)kernelDiameter - dy) * 0.5f;
flInvFactor *= hratio;
}
if ( dratio > 1.0f )
{
kernelDepth = ( int )( kernelDiameter * dratio );
dz = 1.0f / (float)dratio;
sz = -((float)kernelDiameter - dz) * 0.5f;
flInvFactor *= dratio;
}
float z = sz;
int h, i, j;
float total = 0.0f;
for ( h = 0; h < kernelDepth; ++h )
{
float y = sy;
for ( i = 0; i < kernelHeight; ++i )
{
float x = sx;
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 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;
}
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
int i, k;
for ( k = 0; k < info.m_nSrcDepth; ++k )
{
for ( 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 ( 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;
}
}
}
}
}
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,
};
void ComputeNiceFilterKernel( float wratio, float hratio, float dratio, KernelInfo_t *pKernel )
{
// Kernel size is measured in dst pixels
pKernel->m_nDiameter = 6;
// Compute a nice kernel...
pKernel->m_nWidth = ( wratio > 1 ) ? ( int )( pKernel->m_nDiameter * wratio ) : 1;
pKernel->m_nHeight = ( hratio > 1 ) ? ( int )( pKernel->m_nDiameter * hratio ) : 1;
pKernel->m_nDepth = ( dratio > 1 ) ? ( int )( pKernel->m_nDiameter * dratio ) : 1;
// Cache the filter (2d kernels only)....
int power = -1;
if ( (wratio == hratio) && (dratio <= 1) && ( IsPowerOfTwo( pKernel->m_nWidth ) ) && ( IsPowerOfTwo( pKernel->m_nHeight ) ) )
{
power = 0;
int tempWidth = ( int )wratio;
while (tempWidth > 1)
{
++power;
tempWidth >>= 1;
}
// Don't cache anything bigger than 512x512
if (power >= 10)
{
power = -1;
}
}
static float* s_pKernelCache[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
static float* s_pInvKernelCache[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
if (power >= 0)
{
if (!s_pKernelCache[power])
{
s_pKernelCache[power] = new float[pKernel->m_nWidth * pKernel->m_nHeight];
s_pInvKernelCache[power] = new float[pKernel->m_nWidth * pKernel->m_nHeight];
GenerateNiceFilter( wratio, hratio, dratio, pKernel->m_nDiameter, s_pKernelCache[power], s_pInvKernelCache[power] );
}
pKernel->m_pKernel = s_pKernelCache[power];
pKernel->m_pInvKernel = s_pInvKernelCache[power];
}
else
{
// Don't cache non-square kernels, or 3d kernels
float *pTempMemory = new float[pKernel->m_nWidth * pKernel->m_nHeight * pKernel->m_nDepth];
float *pTempInvMemory = new float[pKernel->m_nWidth * pKernel->m_nHeight * pKernel->m_nDepth];
GenerateNiceFilter( wratio, hratio, dratio, pKernel->m_nDiameter, pTempMemory, pTempInvMemory );
pKernel->m_pKernel = pTempMemory;
pKernel->m_pInvKernel = pTempInvMemory;
}
}
void CleanupNiceFilterKernel( KernelInfo_t *pKernel )
{
if ( ( pKernel->m_nWidth != pKernel->m_nHeight ) || ( pKernel->m_nDepth > 1 ) || ( pKernel->m_nWidth > 512 ) ||
( !IsPowerOfTwo( pKernel->m_nWidth ) ) || ( !IsPowerOfTwo( pKernel->m_nHeight ) ) )
{
delete[] pKernel->m_pKernel;
delete[] pKernel->m_pInvKernel;
}
}
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;
memset( &kernel, 0, sizeof( KernelInfo_t ) );
float pKernelMem[1];
float pInvKernelMem[1];
if ( info.m_nFlags & RESAMPLE_NICE_FILTER )
{
ComputeNiceFilterKernel( wratio, hratio, dratio, &kernel );
}
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 );
CleanupNiceFilterKernel( &kernel );
}
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;
for( int y = 0; y < info.m_nDestHeight; y++ )
{
for( int x = 0; x < info.m_nDestWidth; x++ )
{
float accum[4];
accum[0] = accum[1] = accum[2] = accum[3] = 0;
for( int nSampleY = 0; nSampleY < nSampleHeight; nSampleY++ )
{
for( int 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];
}
}
for( int i = 0; i < 3; i++ )
{
accum[i] /= ( nSampleWidth * nSampleHeight );
pDst[(x+y*info.m_nDestWidth)*3+i] = accum[i];
}
}
}
return true;
}
bool ResampleRGBA32323232F( 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 don'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;
for( int y = 0; y < info.m_nDestHeight; y++ )
{
for( int x = 0; x < info.m_nDestWidth; x++ )
{
float accum[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
for( int nSampleY = 0; nSampleY < nSampleHeight; nSampleY++ )
{
for( int nSampleX = 0; nSampleX < nSampleWidth; nSampleX++ )
{
accum[0] += pSrc[((x*nSampleWidth+nSampleX)+(y*nSampleHeight+nSampleY)*info.m_nSrcWidth)*4+0];
accum[1] += pSrc[((x*nSampleWidth+nSampleX)+(y*nSampleHeight+nSampleY)*info.m_nSrcWidth)*4+1];
accum[2] += pSrc[((x*nSampleWidth+nSampleX)+(y*nSampleHeight+nSampleY)*info.m_nSrcWidth)*4+2];
accum[3] += pSrc[((x*nSampleWidth+nSampleX)+(y*nSampleHeight+nSampleY)*info.m_nSrcWidth)*4+3];
}
}
for( int i = 0; i < 4; i++ )
{
accum[i] /= ( nSampleWidth * nSampleHeight );
pDst[(x+y*info.m_nDestWidth)*4+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;
}
}
} // ImageLoader namespace ends

274
bitmap/texturepacker.cpp Normal file
View File

@@ -0,0 +1,274 @@
//========= Copyright <20> 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#if IS_WINDOWS_PC
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif
#include "bitmap/texturepacker.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CTexturePacker::CTexturePacker( int texWidth, int texHeight, int pixelGap )
{
m_PageWidth = texWidth;
m_PageHeight = texHeight;
m_PixelGap = pixelGap;
Clear();
}
//-----------------------------------------------------------------------------
// Purpose: Destructor
//-----------------------------------------------------------------------------
CTexturePacker::~CTexturePacker()
{
// remove all existing data
m_Tree.RemoveAll();
}
//-----------------------------------------------------------------------------
// Purpose: Resets the tree
//-----------------------------------------------------------------------------
void CTexturePacker::Clear()
{
// remove all existing data
m_Tree.RemoveAll();
// Add root item, everything else is procedurally generated
int rootIndex = m_Tree.InsertChildAfter( m_Tree.InvalidIndex(), m_Tree.InvalidIndex() );
TreeEntry_t &topNode = m_Tree[rootIndex];
topNode.rc.x = 0;
topNode.rc.y = 0;
topNode.rc.width = m_PageWidth;
topNode.rc.height = m_PageHeight;
topNode.bInUse = false;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CTexturePacker::IsLeaf( int nodeIndex )
{
int leftChildIndex = m_Tree.FirstChild( nodeIndex );
if ( leftChildIndex == m_Tree.InvalidIndex() )
{
return true;
}
else if ( m_Tree.NextSibling( leftChildIndex ) == m_Tree.InvalidIndex() )
{
return true;
}
return false;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CTexturePacker::IsLeftChild( int nodeIndexParent, int nodeIndexChild )
{
int leftChildIndex = m_Tree.FirstChild( nodeIndexParent );
if ( leftChildIndex == nodeIndexChild )
{
return true;
}
else
{
return false;
}
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CTexturePacker::IsRightChild( int nodeIndexParent, int nodeIndexChild )
{
return !IsLeftChild( nodeIndexParent, nodeIndexChild );
}
#ifdef _PS3
// Remove some annoying GCC warnings by specializing these template functions to remove a redundant (i>=0) clause
template <>
inline bool CUtlNTree<CTexturePacker::TreeEntry_t,short unsigned int>::IsValidIndex( short unsigned int i ) const
{
return (i < m_MaxElementIndex);
}
template <>
inline bool CUtlNTree<CTexturePacker::TreeEntry_t,short unsigned int>::IsInTree( short unsigned int i ) const
{
return (i < m_MaxElementIndex) && (InternalNode(i).m_PrevSibling != i);
}
#endif // _PS3
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
int CTexturePacker::InsertRect( const Rect_t& texRect, int nodeIndex )
{
if ( nodeIndex == -1 )
{
nodeIndex = m_Tree.Root();
Assert( nodeIndex != m_Tree.InvalidIndex() );
}
if ( !IsLeaf( nodeIndex) ) // not a leaf
{
// try inserting under left child
int leftChildIndex = m_Tree.FirstChild( nodeIndex );
int newIndex = InsertRect( texRect, leftChildIndex );
if ( m_Tree.IsValidIndex( newIndex ) )
{
return newIndex;
}
// no room, insert under right child
int rightChildIndex = m_Tree.NextSibling( leftChildIndex );
return InsertRect( texRect, rightChildIndex );
}
else
{
if ( m_Tree[nodeIndex].bInUse ) // there is already a glpyh here
{
return -1;
}
int cacheSlotWidth = m_Tree[nodeIndex].rc.width;
int cacheSlotHeight = m_Tree[nodeIndex].rc.height;
if ( ( texRect.width > cacheSlotWidth ) ||
( texRect.height > cacheSlotHeight ) )
{
// if this node's box is too small, return
return -1;
}
if ( ( texRect.width == cacheSlotWidth) &&
( texRect.height == cacheSlotHeight ) )
{
// if we're just right, accept
m_Tree[nodeIndex].bInUse = true;
return nodeIndex;
}
// otherwise, gotta split this node and create some kids
// decide which way to split
int dw = cacheSlotWidth - texRect.width;
int dh = cacheSlotHeight - texRect.height;
int leftChildIndex;
if ( dw > dh ) // split along x
{
leftChildIndex = m_Tree.InsertChildAfter( nodeIndex, m_Tree.InvalidIndex() );
Assert( IsLeftChild( nodeIndex, leftChildIndex ) );
TreeEntry_t &leftChild = m_Tree[leftChildIndex];
leftChild.rc.x = m_Tree[nodeIndex].rc.x;
leftChild.rc.y = m_Tree[nodeIndex].rc.y;
leftChild.rc.width = texRect.width;
leftChild.rc.height = cacheSlotHeight;
leftChild.bInUse = false;
int rightChildIndex = m_Tree.InsertChildAfter( nodeIndex, leftChildIndex );
Assert( IsRightChild( nodeIndex, rightChildIndex ) );
TreeEntry_t &rightChild = m_Tree[rightChildIndex];
rightChild.rc.x = m_Tree[nodeIndex].rc.x + texRect.width + m_PixelGap;
rightChild.rc.y = m_Tree[nodeIndex].rc.y;
rightChild.rc.width = dw - m_PixelGap;
rightChild.rc.height = cacheSlotHeight;
rightChild.bInUse = false;
Assert( m_Tree.Parent( leftChildIndex ) == m_Tree.Parent( rightChildIndex ) );
Assert( m_Tree.Parent( leftChildIndex ) == nodeIndex );
}
else // split along y
{
leftChildIndex = m_Tree.InsertChildAfter( nodeIndex, m_Tree.InvalidIndex() );
Assert( IsLeftChild( nodeIndex, leftChildIndex ) );
TreeEntry_t &leftChild = m_Tree[leftChildIndex];
leftChild.rc.x = m_Tree[nodeIndex].rc.x;
leftChild.rc.y = m_Tree[nodeIndex].rc.y;
leftChild.rc.width = cacheSlotWidth;
leftChild.rc.height = texRect.height;
leftChild.bInUse = false;
int rightChildIndex = m_Tree.InsertChildAfter( nodeIndex, leftChildIndex );
Assert( IsRightChild( nodeIndex, rightChildIndex ) );
TreeEntry_t &rightChild = m_Tree[rightChildIndex];
rightChild.rc.x = m_Tree[nodeIndex].rc.x;
rightChild.rc.y = m_Tree[nodeIndex].rc.y + texRect.height + m_PixelGap;
rightChild.rc.width = cacheSlotWidth;
rightChild.rc.height = dh - m_PixelGap;
rightChild.bInUse = false;
Assert( m_Tree.Parent( leftChildIndex ) == m_Tree.Parent( rightChildIndex ) );
Assert( m_Tree.Parent( leftChildIndex ) == nodeIndex );
}
// insert into first child we created
return InsertRect( texRect, leftChildIndex );
}
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CTexturePacker::RemoveRect( int nodeIndex )
{
if ( !m_Tree.IsInTree( nodeIndex ) )
{
return false;
}
m_Tree[nodeIndex].bInUse = false;
// If its a leaf, see if its peer is empty, if it is the split can go away.
if ( IsLeaf( nodeIndex) ) // a leaf
{
int parentIndex = m_Tree.Parent( nodeIndex );
// Is this node the left child?
if ( nodeIndex == m_Tree.FirstChild( parentIndex ) )
{
// Get the index of the right child
int peerIndex = m_Tree.NextSibling( nodeIndex );
if ( IsLeaf( peerIndex ) && !m_Tree[peerIndex].bInUse )
{
// Both children are leaves and neither is in use, remove the split here.
m_Tree.Remove( nodeIndex );
m_Tree.Remove( peerIndex );
}
}
else // Its the right child
{
// Get the index of the left child
int peerIndex = m_Tree.FirstChild( parentIndex );
Assert( nodeIndex == m_Tree.NextSibling( peerIndex ) );
if ( IsLeaf( peerIndex ) && !m_Tree[peerIndex].bInUse )
{
// Both children are leaves and neither is in use, remove the split here.
m_Tree.Remove( nodeIndex );
m_Tree.Remove( peerIndex );
}
}
}
return true;
}

1023
bitmap/tgaloader.cpp Normal file

File diff suppressed because it is too large Load Diff

496
bitmap/tgawriter.cpp Normal file
View File

@@ -0,0 +1,496 @@
//===== Copyright <20> 1996-2005, Valve Corporation, All rights reserved. ======//
//
// Purpose:
//
//===========================================================================//
#include <stdlib.h>
#include <stdio.h>
#include "tier0/dbg.h"
#ifndef _PS3
#include <malloc.h>
#else
#include <stdlib.h>
#endif
#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] = { 0x00, 0x00, 0x00, 0x00 }; // Valve Orange would be: { 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;
}
// This routine writes into a width x height subrect of an existing file at the offset ( nXOrigin, nYOrigin ) where the target image has a stride of nStride
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, nImageType, 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;
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;
}
// 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;
}
// Returns weight for Source pixel based on its position in the guardbanded source tile
static float GetSrcWeight( int nFileY, int nFileX, int nFileHeight, int nFileWidth,
int nRow, int nCol, int nHeight, int nWidth, int nGuardBandHeight, int nGuardBandWidth )
{
float flXWeight = 1.0f;
float flYWeight = 1.0f;
if ( nFileX < nGuardBandWidth ) // Left edge of file doesn't feather in X
{
flXWeight = 1.0f;
}
else if ( nFileX > ( nFileWidth - nGuardBandWidth ) ) // Right edge of file doesn't feather in X
{
flXWeight = 1.0f;
}
else if ( nCol < 2*nGuardBandWidth ) // Left guardband
{
flXWeight = nCol / ( (float) nGuardBandWidth * 2.0f );
}
else if ( nCol > nWidth ) // Right guardband
{
flXWeight = 1.0f - ( ( nCol - nWidth ) / ( (float) nGuardBandWidth * 2.0f ) );
}
if ( nFileY < nGuardBandHeight ) // Top edge of file doesn't feather in Y
{
flYWeight = 1.0f;
}
else if ( nFileY > ( nFileHeight - nGuardBandHeight ) ) // Bottom edge of file doesn't feather in Y
{
flYWeight = 1.0f;
}
else if ( nRow < 2*nGuardBandHeight ) // Top guardband
{
flYWeight = nRow / ( (float) nGuardBandHeight * 2.0f );
}
else if ( nRow > nHeight ) // Bottom guardband
{
flYWeight = 1.0f - ( ( nRow - nHeight ) / ( (float) nGuardBandHeight * 2.0f ) );
}
float flWeight = flXWeight * flYWeight;
Assert( ( flWeight >= 0 ) && ( flWeight <= 1.0f ) );
return flWeight;
}
// This flavor of this routine also writes a subrect into an existing file, but it feathers in a ( nGuardBandWidth, nGuardBandHeight ) border,
bool WriteRectNoAllocFeather( unsigned char *pImageData, const char *fileName, int nXOrigin, int nYOrigin, int width, int height,
int nGuardBandWidth, int nGuardBandHeight, 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, nImageType, 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;
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;
}
// 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;
}
// Run through each scanline of the incoming tile, including guardbands
for ( int row=0; row < ( height + 2*nGuardBandHeight ) ; row++ )
{
// Point to start of row
unsigned char *pSrc = pImageData + ( row * nBytesPerPixel * nStride );
unsigned char filePixel[4] = { 0, 0, 0, 0 };
// Run through each column of the incoming tile, including guardbands
for ( int col=0; col < ( width + 2*nGuardBandWidth ) ; col++ )
{
// Pixel location in the file
int fileX = nXOrigin - nGuardBandWidth + col;
int fileY = nYOrigin - nGuardBandHeight + row;
// If we're within the limits of the file's image
if ( ( fileX >= 0 ) && ( fileY >= 0 ) && ( fileX < tgaHeader.width ) && ( fileY < tgaHeader.height ) )
{
// Always just seek from the head of the file to the pixel in question and read it in
g_pFullFileSystem->Seek( fp, sizeof(tgaHeader) + nBytesPerPixel * ( tgaHeader.width * fileY + fileX ), FILESYSTEM_SEEK_HEAD );
g_pFullFileSystem->Read( filePixel, 3, fp ); // Just read B, G, R bytes
// Add weighted src pixel to the data from the file (which should be initialized to be black at start of day)
float flWeight = GetSrcWeight( fileY, fileX, tgaHeader.height, tgaHeader.width, row, col, height, width, nGuardBandHeight, nGuardBandWidth );
filePixel[0] = (int) ( flWeight * (float)pSrc[0] + (float)filePixel[0] );
filePixel[1] = (int) ( flWeight * (float)pSrc[1] + (float)filePixel[1] );
filePixel[2] = (int) ( flWeight * (float)pSrc[2] + (float)filePixel[2] );
// Jump back to start of current pixel and write over it
g_pFullFileSystem->Seek( fp, -3, FILESYSTEM_SEEK_CURRENT );
g_pFullFileSystem->Write( filePixel, nBytesPerPixel, fp );
}
// Advance src pointer to next column
pSrc += nBytesPerPixel;
}
}
g_pFullFileSystem->Close( fp );
return true;
}
} // end namespace TGAWriter

2
bitmap/vsi.nul Normal file
View File

@@ -0,0 +1,2 @@
SN Visual Studio Integration
IMPORTANT: Do not remove the custom build step for this file