initial
This commit is contained in:
277
bitmap/ImageByteSwap.cpp
Normal file
277
bitmap/ImageByteSwap.cpp
Normal 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
|
||||
}
|
||||
}
|
||||
100
bitmap/_vpc_/manifest_bitmap/win32/manifest.txt
Normal file
100
bitmap/_vpc_/manifest_bitmap/win32/manifest.txt
Normal 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:
|
||||
|
||||
16
bitmap/_vpc_/manifest_bitmap_byteswap/win32/manifest.txt
Normal file
16
bitmap/_vpc_/manifest_bitmap_byteswap/win32/manifest.txt
Normal 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
778
bitmap/bitmap.cpp
Normal file
@@ -0,0 +1,778 @@
|
||||
//======= Copyright 1996-2005, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "bitmap/bitmap.h"
|
||||
#include "dbg.h"
|
||||
#include "utlbuffer.h"
|
||||
#include "bitmap/psd.h"
|
||||
#include "bitmap/tgaloader.h"
|
||||
|
||||
// Should be last include
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
bool Bitmap_t::IsValid() const
|
||||
{
|
||||
if ( m_nWidth <= 0 || m_nHeight <= 0 || m_pBits == NULL )
|
||||
{
|
||||
Assert( m_nWidth == 0 );
|
||||
Assert( m_nHeight == 0 );
|
||||
Assert( m_pBits == NULL );
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Bitmap_t::Clear()
|
||||
{
|
||||
if ( m_pBits && m_bOwnsBuffer )
|
||||
{
|
||||
free( m_pBits );
|
||||
}
|
||||
Reset();
|
||||
}
|
||||
|
||||
void Bitmap_t::Init( int xs, int ys, ImageFormat imageFormat, int nStride )
|
||||
{
|
||||
|
||||
// Check for bogus allocation sizes
|
||||
if (xs <= 0 || ys <= 0 )
|
||||
{
|
||||
Assert( xs == 0 );
|
||||
Assert( ys == 0 );
|
||||
Clear();
|
||||
return;
|
||||
}
|
||||
|
||||
int nPixSize = ImageLoader::SizeInBytes( imageFormat );
|
||||
|
||||
// Auto detect stride
|
||||
if ( nStride == 0 )
|
||||
{
|
||||
nStride = nPixSize * xs;
|
||||
}
|
||||
|
||||
// Check for NOP
|
||||
if (
|
||||
m_pBits
|
||||
&& m_bOwnsBuffer
|
||||
&& m_nWidth == xs
|
||||
&& m_nHeight == ys
|
||||
&& nStride == m_nStride
|
||||
&& nPixSize == m_nPixelSize )
|
||||
{
|
||||
// We're already got a buffer of the right size.
|
||||
// The only thing that might be wrong is the pixel format.
|
||||
m_ImageFormat = imageFormat;
|
||||
return;
|
||||
}
|
||||
|
||||
// Free up anything already allocated
|
||||
Clear();
|
||||
|
||||
// Remember dimensions and pixel format
|
||||
m_nWidth = xs;
|
||||
m_nHeight = ys;
|
||||
m_ImageFormat = imageFormat;
|
||||
m_nPixelSize = nPixSize;
|
||||
m_nStride = nStride;
|
||||
|
||||
// Allocate buffer. Because this is a PC game,
|
||||
// failure is impossible....right?
|
||||
m_pBits = (byte *)malloc( ys * m_nStride );
|
||||
|
||||
// Assume ownership
|
||||
m_bOwnsBuffer = true;
|
||||
}
|
||||
|
||||
void Bitmap_t::SetBuffer( int nWidth, int nHeight, ImageFormat imageFormat, unsigned char *pBits, bool bAssumeOwnership, int nStride )
|
||||
{
|
||||
Assert( pBits );
|
||||
Assert( nWidth > 0 );
|
||||
Assert( nHeight > 0 );
|
||||
|
||||
// Free up anything already allocated
|
||||
Clear();
|
||||
|
||||
// Remember dimensions and pixel format
|
||||
m_nWidth = nWidth;
|
||||
m_nHeight = nHeight;
|
||||
m_ImageFormat = imageFormat;
|
||||
m_nPixelSize = ImageLoader::SizeInBytes( imageFormat );
|
||||
if ( nStride == 0 )
|
||||
{
|
||||
m_nStride = m_nPixelSize * nWidth;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_nStride = nStride;
|
||||
}
|
||||
|
||||
// Set our buffer pointer
|
||||
m_pBits = pBits;
|
||||
|
||||
// Assume ownership of the buffer, if requested
|
||||
m_bOwnsBuffer = bAssumeOwnership;
|
||||
|
||||
// We should be good to go
|
||||
Assert( IsValid() );
|
||||
}
|
||||
|
||||
Color Bitmap_t::GetColor( int x, int y ) const
|
||||
{
|
||||
Assert( x >= 0 && x < m_nWidth );
|
||||
Assert( y >= 0 && y < m_nHeight );
|
||||
Assert( m_pBits );
|
||||
|
||||
// Get pointer to pixel data
|
||||
byte *ptr = m_pBits + (y*m_nStride) + x* m_nPixelSize;
|
||||
|
||||
// Check supported image formats
|
||||
switch ( m_ImageFormat )
|
||||
{
|
||||
case IMAGE_FORMAT_RGBA8888:
|
||||
return Color( ptr[0], ptr[1], ptr[2], ptr[3] );
|
||||
|
||||
case IMAGE_FORMAT_ABGR8888:
|
||||
return Color( ptr[3], ptr[2], ptr[1], ptr[0] );
|
||||
|
||||
default:
|
||||
Assert( !"Unsupport image format!");
|
||||
return Color( 255,0,255,255 );
|
||||
}
|
||||
}
|
||||
|
||||
void Bitmap_t::SetColor( int x, int y, Color c )
|
||||
{
|
||||
Assert( x >= 0 && x < m_nWidth );
|
||||
Assert( y >= 0 && y < m_nHeight );
|
||||
Assert( m_pBits );
|
||||
|
||||
// Get pointer to pixel data
|
||||
byte *ptr = m_pBits + (y*m_nStride) + x* m_nPixelSize;
|
||||
|
||||
// Check supported image formats
|
||||
switch ( m_ImageFormat )
|
||||
{
|
||||
case IMAGE_FORMAT_RGBA8888:
|
||||
ptr[0] = c.r();
|
||||
ptr[1] = c.g();
|
||||
ptr[2] = c.b();
|
||||
ptr[3] = c.a();
|
||||
break;
|
||||
|
||||
case IMAGE_FORMAT_ABGR8888:
|
||||
ptr[0] = c.a();
|
||||
ptr[1] = c.b();
|
||||
ptr[2] = c.g();
|
||||
ptr[3] = c.r();
|
||||
break;
|
||||
|
||||
default:
|
||||
Assert( !"Unsupport image format!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//bool LoadVTF( const char *pszFilename )
|
||||
//{
|
||||
//
|
||||
// // Load the raw file data
|
||||
// CUtlBuffer fileData;
|
||||
// if ( !filesystem->ReadFile( pszFilename, "game", fileData ) )
|
||||
// {
|
||||
// Warning( "Failed to load %s\n", pszFilename);
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// return LoadVTFFromBuffer( fileData, pszFilename );
|
||||
//}
|
||||
//
|
||||
//bool LoadVTFFromBuffer( CUtlBuffer fileData, const char *pszDebugName = "buffer" )
|
||||
//{
|
||||
//
|
||||
// // Parse it into VTF object
|
||||
// IVTFTexture *pVTFTexture( CreateVTFTexture() );
|
||||
// if ( !pVTFTexture->Unserialize( fileData ) )
|
||||
// {
|
||||
// DestroyVTFTexture( pVTFTexture );
|
||||
// Warning( "Failed to deserialize VTF %s\n", pszDebugName);
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// // We are re-reading our own files, so they should be 8888's
|
||||
// if ( pVTFTexture->Format() != IMAGE_FORMAT_RGBA8888 )
|
||||
// {
|
||||
// DestroyVTFTexture( pVTFTexture );
|
||||
// Warning( "%s isn't RGBA8888\n", pszDebugName);
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// // Copy the image data
|
||||
// Allocate( pVTFTexture->Width(), pVTFTexture->Height() );
|
||||
// for ( int y = 0 ; y < m_nHeight ; ++y )
|
||||
// {
|
||||
// memcpy( PixPtr(0, y), pVTFTexture->ImageData(0, 0, 0, 0, y), m_nWidth*4 );
|
||||
// }
|
||||
//
|
||||
// // Clean up
|
||||
// DestroyVTFTexture( pVTFTexture );
|
||||
// return true;
|
||||
//}
|
||||
//
|
||||
//bool SaveVTF( CUtlBuffer &outBuffer )
|
||||
//{
|
||||
// // Create the VTF to write into
|
||||
// IVTFTexture *pVTFTexture( CreateVTFTexture() );
|
||||
// const int nFlags = TEXTUREFLAGS_NOMIP | TEXTUREFLAGS_NOLOD | TEXTUREFLAGS_SRGB;
|
||||
// if ( !pVTFTexture->Init( m_nWidth, m_nHeight, 1, IMAGE_FORMAT_RGBA8888, nFlags, 1, 1 ) )
|
||||
// {
|
||||
// DestroyVTFTexture( pVTFTexture );
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// // write the rgba image to the vtf texture using the pixel writer
|
||||
// CPixelWriter pixelWriter;
|
||||
// pixelWriter.SetPixelMemory( pVTFTexture->Format(), pVTFTexture->ImageData(), pVTFTexture->RowSizeInBytes( 0 ) );
|
||||
//
|
||||
// for (int y = 0; y < m_nHeight; ++y)
|
||||
// {
|
||||
// pixelWriter.Seek( 0, y );
|
||||
// for (int x = 0; x < m_nWidth; ++x)
|
||||
// {
|
||||
// Color c = GetPix( x, y );
|
||||
// pixelWriter.WritePixel( c.r(), c.g(), c.b(), c.a() );
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // Serialize to the buffer
|
||||
// if ( !pVTFTexture->Serialize( outBuffer ) )
|
||||
// {
|
||||
// DestroyVTFTexture( pVTFTexture );
|
||||
// return false;
|
||||
// }
|
||||
// DestroyVTFTexture( pVTFTexture );
|
||||
// return true;
|
||||
//}
|
||||
|
||||
//void Resize( int nNewSizeX, int nNewSizeY, const Image *pImgSrc = NULL )
|
||||
//{
|
||||
// if ( pImgSrc == NULL )
|
||||
// {
|
||||
// pImgSrc = this;
|
||||
// }
|
||||
//
|
||||
// if ( nNewSizeX == m_nWidth && nNewSizeY == m_nHeight && pImgSrc == this )
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// byte *pNewData = (byte *)malloc( nNewSizeX * nNewSizeY * 4 );
|
||||
// ImgUtl_StretchRGBAImage( pImgSrc->m_pBits, pImgSrc->m_nWidth, pImgSrc->m_nHeight, pNewData, nNewSizeX, nNewSizeY );
|
||||
// Clear();
|
||||
// m_pBits = pNewData;
|
||||
// m_nWidth = nNewSizeX;
|
||||
// m_nHeight = nNewSizeY;
|
||||
//}
|
||||
//
|
||||
//void Crop( int x0, int y0, int nNewSizeX, int nNewSizeY, const Image *pImgSrc )
|
||||
//{
|
||||
// if ( pImgSrc == NULL )
|
||||
// {
|
||||
// pImgSrc = this;
|
||||
// }
|
||||
//
|
||||
// if ( nNewSizeX == m_nWidth && nNewSizeY == m_nHeight && pImgSrc == this )
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// Assert( x0 >= 0 );
|
||||
// Assert( y0 >= 0 );
|
||||
// Assert( x0 + nNewSizeX <= pImgSrc->m_nWidth );
|
||||
// Assert( y0 + nNewSizeY <= pImgSrc->m_nHeight );
|
||||
//
|
||||
// // Allocate new buffer
|
||||
// int nRowSize = nNewSizeX * 4;
|
||||
// byte *pNewData = (byte *)malloc( nNewSizeY * nRowSize );
|
||||
//
|
||||
// // Copy data, one row at a time
|
||||
// for ( int y = 0 ; y < nNewSizeY ; ++y )
|
||||
// {
|
||||
// memcpy( pNewData + y*nRowSize, pImgSrc->PixPtr(x0, y0+y), nRowSize );
|
||||
// }
|
||||
//
|
||||
// // Replace current buffer with the new one
|
||||
// Clear();
|
||||
// m_pBits = pNewData;
|
||||
// m_nWidth = nNewSizeX;
|
||||
// m_nHeight = nNewSizeY;
|
||||
//}
|
||||
|
||||
void Bitmap_t::MakeLogicalCopyOf( Bitmap_t &src, bool bTransferBufferOwnership )
|
||||
{
|
||||
// What does it mean to make a logical copy of an
|
||||
// invalid bitmap? I'll tell you what it means: you have a bug.
|
||||
Assert( src.IsValid() );
|
||||
|
||||
// Free up anything we already own
|
||||
Clear();
|
||||
|
||||
// Copy all of the member variables so we are
|
||||
// a logical copy of the source bitmap
|
||||
m_nWidth = src.m_nWidth;
|
||||
m_nHeight = src.m_nHeight;
|
||||
m_nPixelSize = src.m_nPixelSize;
|
||||
m_nStride = src.m_nStride;
|
||||
m_ImageFormat = src.m_ImageFormat;
|
||||
m_pBits = src.m_pBits;
|
||||
Assert( !m_bOwnsBuffer );
|
||||
|
||||
// Check for assuming ownership of the buffer
|
||||
if ( bTransferBufferOwnership )
|
||||
{
|
||||
if ( src.m_bOwnsBuffer )
|
||||
{
|
||||
m_bOwnsBuffer = true;
|
||||
src.m_bOwnsBuffer = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// They don't own the buffer? Then who does?
|
||||
// Maybe nobody, and it would safe to assume
|
||||
// ownership. But more than likely, this is a
|
||||
// bug.
|
||||
Assert( src.m_bOwnsBuffer );
|
||||
|
||||
// And a leak is better than a double-free.
|
||||
// Don't assume ownership of the buffer.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Bitmap_t::Crop( int x0, int y0, int nWidth, int nHeight, const Bitmap_t *pImgSource )
|
||||
{
|
||||
// Check for cropping in place, then save off our data to a temp
|
||||
Bitmap_t temp;
|
||||
if ( pImgSource == this || !pImgSource )
|
||||
{
|
||||
temp.MakeLogicalCopyOf( *this, m_bOwnsBuffer );
|
||||
pImgSource = &temp;
|
||||
}
|
||||
|
||||
// No source image?
|
||||
if ( !pImgSource->IsValid() )
|
||||
{
|
||||
Assert( pImgSource->IsValid() );
|
||||
return;
|
||||
}
|
||||
|
||||
// Sanity check crop rectangle
|
||||
Assert( x0 >= 0 );
|
||||
Assert( y0 >= 0 );
|
||||
Assert( x0 + nWidth <= pImgSource->Width() );
|
||||
Assert( y0 + nHeight <= pImgSource->Height() );
|
||||
|
||||
// Allocate buffer
|
||||
Init( nWidth, nHeight, pImgSource->Format() );
|
||||
|
||||
// Something wrong?
|
||||
if ( !IsValid() )
|
||||
{
|
||||
Assert( IsValid() );
|
||||
return;
|
||||
}
|
||||
|
||||
// Copy the data a row at a time
|
||||
int nRowSize = m_nWidth * m_nPixelSize;
|
||||
for ( int y = 0 ; y < m_nHeight ; ++y )
|
||||
{
|
||||
memcpy( GetPixel(y,0), pImgSource->GetPixel( x0, y + y0 ), nRowSize );
|
||||
}
|
||||
}
|
||||
|
||||
void Bitmap_t::SetPixelData( const Bitmap_t &src, int nSrcX1, int nSrcY1, int nCopySizeX, int nCopySizeY, int nDestX1, int nDestY1 )
|
||||
{
|
||||
// Safety
|
||||
if ( !src.IsValid() )
|
||||
{
|
||||
Assert( src.IsValid() );
|
||||
return;
|
||||
}
|
||||
if ( !IsValid() )
|
||||
{
|
||||
Assert( IsValid() );
|
||||
return;
|
||||
}
|
||||
|
||||
// You need to specify a valid source rectangle, we cannot clip that for you
|
||||
if ( nSrcX1 < 0 || nSrcY1 < 0 || nSrcX1 + nCopySizeX > src.Width() || nSrcY1 + nCopySizeY > src.Height() )
|
||||
{
|
||||
Assert( nSrcX1 >= 0 );
|
||||
Assert( nSrcY1 >= 0 );
|
||||
Assert( nSrcX1 + nCopySizeX <= src.Width() );
|
||||
Assert( nSrcY1 + nCopySizeY <= src.Height() );
|
||||
return;
|
||||
}
|
||||
|
||||
// But we can clip the rectangle if it extends outside the destination image in a perfectly
|
||||
// reasonable way
|
||||
if ( nDestX1 < 0 )
|
||||
{
|
||||
nCopySizeX += nDestX1;
|
||||
nDestX1 = 0;
|
||||
}
|
||||
if ( nDestX1 + nCopySizeX > Width() )
|
||||
{
|
||||
nCopySizeX = Width() - nDestX1;
|
||||
}
|
||||
if ( nDestY1 < 0 )
|
||||
{
|
||||
nCopySizeY += nDestY1;
|
||||
nDestY1 = 0;
|
||||
}
|
||||
if ( nDestY1 + nCopySizeY > Height() )
|
||||
{
|
||||
nCopySizeY = Height() - nDestY1;
|
||||
}
|
||||
if ( nCopySizeX <= 0 || nCopySizeY <= 0 )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Copy the pixel data
|
||||
for ( int y = 0 ; y < nCopySizeY ; ++y )
|
||||
{
|
||||
// Wow, this could be a lot faster in the common case
|
||||
// that the pixe formats are the same. But...this code
|
||||
// is simple and works, and is NOT the root of all evil.
|
||||
for ( int x = 0 ; x < nCopySizeX ; ++x )
|
||||
{
|
||||
Color c = src.GetColor( nSrcX1 + x, nSrcY1 + y );
|
||||
SetColor( nDestX1 + x, nDestY1 + y, c );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Bitmap_t::SetPixelData( const Bitmap_t &src, int nDestX1, int nDestY1 )
|
||||
{
|
||||
SetPixelData( src, 0, 0, src.Width(), src.Height(), nDestX1, nDestY1 );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Returns true if it's a PFM file
|
||||
//-----------------------------------------------------------------------------
|
||||
bool IsPFMFile( CUtlBuffer &buf )
|
||||
{
|
||||
int nGet = buf.TellGet();
|
||||
char c0 = buf.GetChar();
|
||||
char c1 = buf.GetChar();
|
||||
char c2 = buf.GetChar();
|
||||
buf.SeekGet( CUtlBuffer::SEEK_HEAD, nGet );
|
||||
|
||||
return ( c0 == 'P' && ( c1 == 'F' || c1 == 'f' ) && c2 == 0xA );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// This reads an integer from a binary CUtlBuffer.
|
||||
//-----------------------------------------------------------------------------
|
||||
static int ReadIntFromUtlBuffer( CUtlBuffer &buf )
|
||||
{
|
||||
int val = 0;
|
||||
int c;
|
||||
while( buf.IsValid() )
|
||||
{
|
||||
c = buf.GetChar();
|
||||
if( c >= '0' && c <= '9' )
|
||||
{
|
||||
val = val * 10 + ( c - '0' );
|
||||
}
|
||||
else
|
||||
{
|
||||
buf.SeekGet( CUtlBuffer::SEEK_CURRENT, -1 );
|
||||
break;
|
||||
}
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline bool IsWhitespace( char c )
|
||||
{
|
||||
return c == ' ' || c == '\t' || c == 10;
|
||||
}
|
||||
|
||||
static void EatWhiteSpace( CUtlBuffer &buf )
|
||||
{
|
||||
while( buf.IsValid() )
|
||||
{
|
||||
int c = buf.GetChar();
|
||||
if( !IsWhitespace( c ) )
|
||||
{
|
||||
buf.SeekGet( CUtlBuffer::SEEK_CURRENT, -1 );
|
||||
return;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Reads PFM info + advances to the texture bits
|
||||
//-----------------------------------------------------------------------------
|
||||
bool PFMGetInfo_AndAdvanceToTextureBits( CUtlBuffer &pfmBuffer, int &nWidth, int &nHeight, ImageFormat &imageFormat )
|
||||
{
|
||||
pfmBuffer.SeekGet( CUtlBuffer::SEEK_HEAD, 0 );
|
||||
if( pfmBuffer.GetChar() != 'P' )
|
||||
{
|
||||
Assert( 0 );
|
||||
return false;
|
||||
}
|
||||
char c = pfmBuffer.GetChar();
|
||||
if ( c == 'F' )
|
||||
{
|
||||
imageFormat = IMAGE_FORMAT_RGB323232F;
|
||||
}
|
||||
else if ( c == 'f' )
|
||||
{
|
||||
imageFormat = IMAGE_FORMAT_R32F;
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert( 0 );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( pfmBuffer.GetChar() != 0xa )
|
||||
{
|
||||
Assert( 0 );
|
||||
return false;
|
||||
}
|
||||
nWidth = ReadIntFromUtlBuffer( pfmBuffer );
|
||||
EatWhiteSpace( pfmBuffer );
|
||||
nHeight = ReadIntFromUtlBuffer( pfmBuffer );
|
||||
|
||||
// eat crap until the next newline
|
||||
while( pfmBuffer.IsValid() && pfmBuffer.GetChar() != 0xa )
|
||||
{
|
||||
}
|
||||
|
||||
int nScale = ReadIntFromUtlBuffer( pfmBuffer );
|
||||
|
||||
// eat crap until the next newline
|
||||
while( pfmBuffer.IsValid() && pfmBuffer.GetChar() != 0xa )
|
||||
{
|
||||
}
|
||||
|
||||
if ( nScale > 0 )
|
||||
{
|
||||
pfmBuffer.SetBigEndian( true );
|
||||
}
|
||||
|
||||
// Here, the buffer should be at the start of the texture data
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Loads a PFM file into a Bitmap_t as RGBA32323232F data
|
||||
// PFM source data doesn't have alpha, so we put 1.0f into alpha
|
||||
//-----------------------------------------------------------------------------
|
||||
bool PFMReadFileRGBA32323232F( CUtlBuffer &fileBuffer, Bitmap_t &bitmap, float pfmScale )
|
||||
{
|
||||
int nWidth = 0, nHeight = 0;
|
||||
ImageFormat imageFormat = IMAGE_FORMAT_UNKNOWN;
|
||||
PFMGetInfo_AndAdvanceToTextureBits( fileBuffer, nWidth, nHeight, imageFormat ); // Read the header (advances us to the texture bits)
|
||||
Assert( imageFormat == IMAGE_FORMAT_RGB323232F );
|
||||
|
||||
bitmap.Init( nWidth, nHeight, IMAGE_FORMAT_RGBA32323232F ); // Init the bitmap
|
||||
|
||||
// NOTE: PFMs are displayed *UPSIDE-DOWN* in Photoshop! (as of CS2... HDRShop gets it right)
|
||||
// Reading rows in reverse order here is the correct thing to do:
|
||||
for( int y = ( nHeight - 1 ); y >= 0; y-- )
|
||||
{
|
||||
Assert( fileBuffer.IsValid() );
|
||||
if ( !fileBuffer.IsValid() )
|
||||
return false;
|
||||
|
||||
float *pOut = ( (float *)bitmap.GetBits() ) + y*nWidth*4; // Point to output row
|
||||
|
||||
for ( int x = 0; x < nWidth; x++ ) // For every RGB input color
|
||||
{
|
||||
fileBuffer.Get( pOut, sizeof( float ) ); // Get red
|
||||
*pOut *= pfmScale; // Scale into output
|
||||
pOut++;
|
||||
fileBuffer.Get( pOut, sizeof( float ) ); // Get green
|
||||
*pOut *= pfmScale; // Scale into output
|
||||
pOut++;
|
||||
fileBuffer.Get( pOut, sizeof( float ) ); // Get blue
|
||||
*pOut *= pfmScale; // Scale into output
|
||||
pOut++;
|
||||
*pOut = 1.0f; // 1.0f into alpha
|
||||
pOut++;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Loads a PFM file into a Bitmap_t as RGB323232F data
|
||||
//-----------------------------------------------------------------------------
|
||||
bool PFMReadFileRGB323232F( CUtlBuffer &fileBuffer, Bitmap_t &bitmap, float pfmScale )
|
||||
{
|
||||
// Read the header (advances us to the texture bits)
|
||||
int nWidth = 0, nHeight = 0;
|
||||
ImageFormat imageFormat = IMAGE_FORMAT_UNKNOWN;
|
||||
PFMGetInfo_AndAdvanceToTextureBits( fileBuffer, nWidth, nHeight, imageFormat );
|
||||
Assert( imageFormat == IMAGE_FORMAT_RGB323232F );
|
||||
|
||||
// Init the bitmap
|
||||
bitmap.Init( nWidth, nHeight, IMAGE_FORMAT_RGB323232F );
|
||||
|
||||
// Read the texels
|
||||
for( int y = ( nHeight - 1 ); y >= 0; y-- )
|
||||
{
|
||||
Assert( fileBuffer.IsValid() );
|
||||
if ( !fileBuffer.IsValid() )
|
||||
return false;
|
||||
|
||||
// NOTE: PFMs are displayed *UPSIDE-DOWN* in Photoshop! (as of CS2... HDRShop gets it right)
|
||||
// Reading rows in reverse order here is the correct thing to do:
|
||||
float *pRow = ( (float *)bitmap.GetBits() ) + y*nWidth*3;
|
||||
|
||||
fileBuffer.Get( pRow, nWidth*3*sizeof( float ) );
|
||||
for ( int x = 0; x < nWidth*3; x++ )
|
||||
{
|
||||
pRow[ x ] *= pfmScale;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Loads the x channel of a PFM file into a Bitmap_t as R32F data
|
||||
//-----------------------------------------------------------------------------
|
||||
bool PFMReadFileR32F( CUtlBuffer &fileBuffer, Bitmap_t &bitmap, float pfmScale )
|
||||
{
|
||||
// Read the header (advances us to the texture bits)
|
||||
int nWidth, nHeight;
|
||||
ImageFormat fileImageFormat; // Format of data in file
|
||||
PFMGetInfo_AndAdvanceToTextureBits( fileBuffer, nWidth, nHeight, fileImageFormat );
|
||||
Assert( fileImageFormat == IMAGE_FORMAT_RGB323232F );
|
||||
|
||||
// Init the bitmap
|
||||
bitmap.Init( nWidth, nHeight, IMAGE_FORMAT_R32F );
|
||||
|
||||
float flMin = FLOAT32_MAX;
|
||||
float flMax = FLOAT32_MIN;
|
||||
|
||||
// Read the texels, snarfing out just the x component
|
||||
for( int y = ( nHeight - 1 ); y >= 0; y-- )
|
||||
{
|
||||
Assert( fileBuffer.IsValid() );
|
||||
if ( !fileBuffer.IsValid() )
|
||||
return false;
|
||||
|
||||
// NOTE: PFMs are displayed *UPSIDE-DOWN* in Photoshop! (as of CS2... HDRShop gets it right)
|
||||
// Reading rows in reverse order here is the correct thing to do:
|
||||
float *pRow = ( (float *)bitmap.GetBits() ) + y*nWidth;
|
||||
|
||||
for ( int x = 0; x < nWidth; x++ )
|
||||
{
|
||||
fileBuffer.Get( pRow+x, sizeof( float ) ); // Grab x component and scale
|
||||
pRow[x] *= pfmScale;
|
||||
|
||||
flMin = MIN( pRow[x], flMin );
|
||||
flMax = MAX( pRow[x], flMax );
|
||||
|
||||
float flDummy[2];
|
||||
fileBuffer.Get( &flDummy, 2*sizeof( float ) ); // Jump past y and z components in file
|
||||
}
|
||||
}
|
||||
|
||||
printf( "Displacement Range: (%g, %g)\n", flMin, flMax );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Loads a PFM file into a Bitmap_t
|
||||
//-----------------------------------------------------------------------------
|
||||
bool PFMReadFile( CUtlBuffer &buf, Bitmap_t *pBitmap )
|
||||
{
|
||||
// Read the header (advances us to the texture bits)
|
||||
int nWidth = 0, nHeight = 0;
|
||||
ImageFormat fmt = IMAGE_FORMAT_UNKNOWN;
|
||||
PFMGetInfo_AndAdvanceToTextureBits( buf, nWidth, nHeight, fmt );
|
||||
|
||||
// Init the bitmap
|
||||
pBitmap->Init( nWidth, nHeight, fmt );
|
||||
|
||||
int nRowBytes = ImageLoader::GetMemRequired( nWidth, 1, 1, 1, fmt );
|
||||
|
||||
// Read the texels
|
||||
for( int y = ( nHeight - 1 ); y >= 0; y-- )
|
||||
{
|
||||
Assert( buf.IsValid() );
|
||||
if ( !buf.IsValid() )
|
||||
return false;
|
||||
|
||||
// NOTE: PFMs are displayed *UPSIDE-DOWN* in Photoshop! (as of CS2... HDRShop gets it right)
|
||||
// Reading rows in reverse order here is the correct thing to do:
|
||||
void *pDest = pBitmap->GetBits() + y * nRowBytes;
|
||||
buf.Get( pDest, nRowBytes );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Loads a bitmap from an arbitrary file: could be a TGA, PSD, or PFM.
|
||||
// LoadBitmap autodetects which type
|
||||
//-----------------------------------------------------------------------------
|
||||
BitmapFileType_t LoadBitmapFile( CUtlBuffer &buf, Bitmap_t *pBitmap )
|
||||
{
|
||||
if ( IsPSDFile( buf ) )
|
||||
{
|
||||
// FIXME: Make it actually load the true format
|
||||
if ( !PSDReadFileRGBA8888( buf, *pBitmap ) )
|
||||
return BITMAP_FILE_TYPE_UNKNOWN;
|
||||
return BITMAP_FILE_TYPE_PSD;
|
||||
}
|
||||
|
||||
if ( IsPFMFile( buf ) )
|
||||
{
|
||||
if ( !PFMReadFile( buf, pBitmap ) )
|
||||
return BITMAP_FILE_TYPE_UNKNOWN;
|
||||
return BITMAP_FILE_TYPE_PFM;
|
||||
}
|
||||
|
||||
// It's not really possible to detect TGAness.. there's no identifier
|
||||
// Assume TGA file here
|
||||
int nWidth, nHeight;
|
||||
ImageFormat fmt;
|
||||
float flGamma;
|
||||
int nGet = buf.TellGet();
|
||||
bool bOk = TGALoader::GetInfo( buf, &nWidth, &nHeight, &fmt, &flGamma );
|
||||
buf.SeekGet( CUtlBuffer::SEEK_HEAD, nGet );
|
||||
if ( !bOk )
|
||||
return BITMAP_FILE_TYPE_UNKNOWN;
|
||||
|
||||
// FIXME: TGALoader is incredibly inefficient when trying to just get
|
||||
// the bits as-is as it loads into RGBA8888 and then color converts out
|
||||
pBitmap->Init( nWidth, nHeight, fmt );
|
||||
if ( !TGALoader::Load( pBitmap->GetBits(), buf, nWidth, nHeight, fmt, flGamma, false ) )
|
||||
return BITMAP_FILE_TYPE_UNKNOWN;
|
||||
|
||||
return BITMAP_FILE_TYPE_TGA;
|
||||
}
|
||||
|
||||
63
bitmap/bitmap.vpc
Normal file
63
bitmap/bitmap.vpc
Normal 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"
|
||||
}
|
||||
}
|
||||
13
bitmap/bitmap.vpc.vpc_cache
Normal file
13
bitmap/bitmap.vpc.vpc_cache
Normal file
@@ -0,0 +1,13 @@
|
||||
"vpc_cache"
|
||||
{
|
||||
"CacheVersion" "1"
|
||||
"win32"
|
||||
{
|
||||
"CRCFile" "bitmap.vcxproj.vpc_crc"
|
||||
"OutputFiles"
|
||||
{
|
||||
"0" "bitmap.vcxproj"
|
||||
"1" "bitmap.vcxproj.filters"
|
||||
}
|
||||
}
|
||||
}
|
||||
47
bitmap/bitmap_byteswap.vpc
Normal file
47
bitmap/bitmap_byteswap.vpc
Normal 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"
|
||||
}
|
||||
}
|
||||
13
bitmap/bitmap_byteswap.vpc.vpc_cache
Normal file
13
bitmap/bitmap_byteswap.vpc.vpc_cache
Normal 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
2495
bitmap/colorconversion.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1618
bitmap/floatbitmap.cpp
Normal file
1618
bitmap/floatbitmap.cpp
Normal file
File diff suppressed because it is too large
Load Diff
151
bitmap/floatbitmap2.cpp
Normal file
151
bitmap/floatbitmap2.cpp
Normal 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
118
bitmap/floatbitmap3.cpp
Normal 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
354
bitmap/floatbitmap4.cpp
Normal 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;
|
||||
}
|
||||
|
||||
105
bitmap/floatbitmap_bilateralfilter.cpp
Normal file
105
bitmap/floatbitmap_bilateralfilter.cpp
Normal 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
179
bitmap/floatcubemap.cpp
Normal 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
651
bitmap/imageformat.cpp
Normal 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
571
bitmap/psd.cpp
Normal 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
477
bitmap/psheet.cpp
Normal 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
902
bitmap/resample.cpp
Normal 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
274
bitmap/texturepacker.cpp
Normal 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
1023
bitmap/tgaloader.cpp
Normal file
File diff suppressed because it is too large
Load Diff
496
bitmap/tgawriter.cpp
Normal file
496
bitmap/tgawriter.cpp
Normal 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
2
bitmap/vsi.nul
Normal file
@@ -0,0 +1,2 @@
|
||||
SN Visual Studio Integration
|
||||
IMPORTANT: Do not remove the custom build step for this file
|
||||
Reference in New Issue
Block a user