Created
December 11, 2014 02:26
-
-
Save lardratboy/51a45a83545bf9992011 to your computer and use it in GitHub Desktop.
This is an expanded version of the my PSD reader - handles layers, named alphas it is a bit messy but what living code isn't...
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Adobe Photoshop .PSD bitmap file loader | |
// | |
// Copyright (c) 2002, Brad P. Taylor, LLC | |
// | |
// All rights reserved, unauthorized reproduction prohibited | |
// | |
// -- FILE NOTES -- | |
// | |
////////////////////////////////////////////////////////////////////// | |
#if !defined(AFX_TPSDFILECODE_H__9D2B4C1A_1D42_4364_A93E_65D59EF0F741__INCLUDED_) | |
#define AFX_TPSDFILECODE_H__9D2B4C1A_1D42_4364_A93E_65D59EF0F741__INCLUDED_ | |
#if _MSC_VER > 1000 | |
#pragma once | |
#endif // _MSC_VER > 1000 | |
// ---------------------------------------------------------------------------- | |
#include <list> | |
#include <vector> | |
#include "BPTLLC_Memory.h" | |
// ---------------------------------------------------------------------------- | |
//#define BPT_PSD_LAYER_NAME_DIAGNOSTIC_TRACE | |
// ---------------------------------------------------------------------------- | |
//#define BPT_PSD_DBG_BLOCK_POLICY | |
#if defined(BPT_PSD_DBG_BLOCK_POLICY) | |
#define BPT_PSD_DBG_BLOCK_POLICY_( XXX ) XXX | |
#undef BPT_PSD_DBG_BLOCK_POLICY | |
#else | |
#define BPT_PSD_DBG_BLOCK_POLICY_( XXX ) | |
#endif | |
#define BPT_PSD_DBG_BLOCK_POLICY(STUFF) BLOCK_POLICY_POLICY_(BPT_PSD_DBG_BLOCK_POLICY_)(STUFF) | |
// ---------------------------------------------------------------------------- | |
//#define BPT_PSD_DECODE_BLOCK_POLICY | |
#if defined(BPT_PSD_DECODE_BLOCK_POLICY) | |
#define BPT_PSD_DECODE_BLOCK_POLICY_( XXX ) XXX | |
#undef BPT_PSD_DECODE_BLOCK_POLICY | |
#else | |
#define BPT_PSD_DECODE_BLOCK_POLICY_( XXX ) | |
#endif | |
#define BPT_PSD_DECODE_BLOCK_POLICY(STUFF) BLOCK_POLICY_POLICY_(BPT_PSD_DECODE_BLOCK_POLICY_)(STUFF) | |
// ---------------------------------------------------------------------------- | |
//#define BPT_PSD_CHANNEL_BLOCK_POLICY | |
#if defined(BPT_PSD_CHANNEL_BLOCK_POLICY) | |
#define BPT_PSD_CHANNEL_BLOCK_POLICY_( XXX ) XXX | |
#undef BPT_PSD_CHANNEL_BLOCK_POLICY | |
#else | |
#define BPT_PSD_CHANNEL_BLOCK_POLICY_( XXX ) | |
#endif | |
#define BPT_PSD_CHANNEL_BLOCK_POLICY(STUFF) BLOCK_POLICY_POLICY_(BPT_PSD_CHANNEL_BLOCK_POLICY_)(STUFF) | |
// ---------------------------------------------------------------------------- | |
//#define BPT_PSD_FILE_BLOCK_POLICY | |
#if defined(BPT_PSD_FILE_BLOCK_POLICY) | |
#define BPT_PSD_FILE_BLOCK_POLICY_( XXX ) XXX | |
#undef BPT_PSD_FILE_BLOCK_POLICY | |
#else | |
#define BPT_PSD_FILE_BLOCK_POLICY_( XXX ) | |
#endif | |
#define BPT_PSD_FILE_BLOCK_POLICY(STUFF) BLOCK_POLICY_POLICY_(BPT_PSD_FILE_BLOCK_POLICY_)(STUFF) | |
// ---------------------------------------------------------------------------- | |
//#define BPT_PSD_ALLOC_BLOCK_POLICY | |
#if defined(BPT_PSD_ALLOC_BLOCK_POLICY) | |
#define BPT_PSD_ALLOC_BLOCK_POLICY_( XXX ) XXX | |
#undef BPT_PSD_ALLOC_BLOCK_POLICY | |
#else | |
#define BPT_PSD_ALLOC_BLOCK_POLICY_( XXX ) | |
#endif | |
#define BPT_PSD_ALLOC_BLOCK_POLICY(STUFF) BLOCK_POLICY_POLICY_(BPT_PSD_ALLOC_BLOCK_POLICY_)(STUFF) | |
// ---------------------------------------------------------------------------- | |
//#define BPT_PSD_FREE_BLOCK_POLICY | |
#if defined(BPT_PSD_FREE_BLOCK_POLICY) | |
#define BPT_PSD_FREE_BLOCK_POLICY_( XXX ) XXX | |
#undef BPT_PSD_FREE_BLOCK_POLICY | |
#else | |
#define BPT_PSD_FREE_BLOCK_POLICY_( XXX ) | |
#endif | |
#define BPT_PSD_FREE_BLOCK_POLICY(STUFF) BLOCK_POLICY_POLICY_(BPT_PSD_FREE_BLOCK_POLICY_)(STUFF) | |
// ---------------------------------------------------------------------------- | |
namespace BPT { | |
// ------------------------------------------------------------------------ | |
// | |
// SPSD_BPTMemoryPolicy | |
// | |
template< class T = void * > | |
struct SPSD_BPTMemoryPolicy { | |
typedef T pointer_type; | |
static pointer_type Allocate( const size_t nBytes ) { | |
BPT_PSD_ALLOC_BLOCK_POLICY( TRACE( "PSD::Alloc( %d )\n", nBytes ) ); | |
return reinterpret_cast<pointer_type>( bptllc_MemAlloc( nBytes, "SPSD_BPTMemoryPolicy" ) ); | |
} | |
static void Free( pointer_type ptr ) { | |
BPT_PSD_FREE_BLOCK_POLICY( TRACE( "PSD::Free( %p )\n", ptr ) ); | |
bptllc_MemFree( reinterpret_cast<void *>( ptr ), "SPSD_BPTMemoryPolicy" ); | |
} | |
}; /* struct SPSD_BPTMemoryPolicy */ | |
// | |
// SPSD_New | |
// | |
template< class T = void * > | |
struct SPSD_New { | |
typedef T pointer_type; | |
static pointer_type Allocate( const size_t nBytes ) { | |
BPT_PSD_ALLOC_BLOCK_POLICY( TRACE( "PSD::Alloc( %d )\n", nBytes ) ); | |
return reinterpret_cast<pointer_type>( new BYTE [ nBytes ] ); | |
} | |
static void Free( pointer_type ptr ) { | |
BPT_PSD_FREE_BLOCK_POLICY( TRACE( "PSD::Free( %p )\n", ptr ) ); | |
if ( ptr ) delete [] ptr; | |
} | |
}; /* struct SPSD_New */ | |
// ------------------------------------------------------------------------ | |
// | |
// TFileIO<> | |
// | |
template< class MEM = SPSD_BPTMemoryPolicy<> > | |
class TFileIO { | |
public: | |
typedef MEM memory_policy_type; | |
private: | |
MEM m_MemoryPolicy; | |
FILE * m_Handle; | |
public: | |
TFileIO() : m_Handle( 0 ) { /* Empty */ } | |
TFileIO( MEM memPolicy ) : | |
m_MemoryPolicy( memPolicy), m_Handle( 0 ) { /* Empty */ } | |
~TFileIO() { | |
if ( m_Handle ) { | |
Close(); | |
} | |
} | |
bool Open( const char * filename, const char * access ) { | |
BPT_PSD_FILE_BLOCK_POLICY( TRACE( "PSD::File::Open( \"%s\", \"%s\" )\n", filename, access ) ); | |
m_Handle = fopen( filename, access ); | |
return (0 != m_Handle); | |
} | |
void Close() { | |
if ( m_Handle ) { | |
BPT_PSD_FILE_BLOCK_POLICY( TRACE( "PSD::File::Close( %p )\n", m_Handle ) ); | |
fclose( m_Handle ); | |
m_Handle = 0; | |
} | |
} | |
int FTell() { | |
if ( m_Handle ) { | |
return ftell( m_Handle ); | |
} | |
return 0; | |
} | |
void Seek( const int pos, const int mode ) { | |
if ( m_Handle ) { | |
fseek( m_Handle, pos, mode ); | |
} | |
} | |
int Read_Byte() { | |
if ( m_Handle ) { | |
return static_cast<unsigned>(fgetc( m_Handle )) & 255; | |
} | |
return 0; | |
} | |
int Read_m16() { | |
int hi = Read_Byte(); | |
int lo = Read_Byte(); | |
return ((hi << 8) | lo); | |
} | |
int Read_m32() { | |
int a = Read_Byte(); | |
int b = Read_Byte(); | |
int c = Read_Byte(); | |
int d = Read_Byte(); | |
return (int)((a << 24) | (b << 16) | (c << 8) | d); | |
} | |
BYTE * LoadData( const int nBytes ) { | |
BPT_PSD_FILE_BLOCK_POLICY( TRACE( "PSD::LoadData( %d )\n", nBytes ) ); | |
if ( m_Handle ) { | |
typename MEM::pointer_type pMemory = m_MemoryPolicy.Allocate( nBytes ); | |
if ( 0 == pMemory ) { | |
return 0; | |
} | |
fread( reinterpret_cast<void *>( pMemory ), 1, nBytes, m_Handle ); | |
return reinterpret_cast<BYTE *>( pMemory ); | |
} | |
return static_cast<BYTE *>( 0 ); | |
} | |
void UnloadData( BYTE * ptr ) { | |
// BPT_PSD_FILE_BLOCK_POLICY( TRACE( "PSD::UnloadData( %p )\n", ptr ) ); | |
if ( ptr ) { | |
m_MemoryPolicy.Free( reinterpret_cast<typename MEM::pointer_type>( ptr ) ); | |
} | |
} | |
}; /* template TFileIO<> */ | |
// (PSD-CHANNEL-READ-HELPERS) --------------------------------------------- | |
// | |
// CPSDScanlineUnpack8BitChannel | |
// | |
class CPSDScanlineUnpack8BitChannel { | |
private: | |
// -------------------------------------------------------------------- | |
typedef const BYTE * data_iterator; | |
BYTE Read_Byte( data_iterator & it ) { | |
return *it++; | |
} | |
WORD Read_m16( data_iterator & it ) { | |
int hi = Read_Byte( it ); | |
int lo = Read_Byte( it ); | |
return (WORD)((hi << 8) | lo); | |
} | |
public: | |
CPSDScanlineUnpack8BitChannel() { /* Empty */ } | |
template< class SURF > | |
bool operator()( | |
SURF * pSurface | |
,const BYTE * pScanlineSizes | |
,const BYTE * pChannelDataStart | |
) { | |
BPT_PSD_DECODE_BLOCK_POLICY( | |
TRACE( "PSD::Decode Scanline Unpack %d x %d from (sizes %p, data %p)\n", | |
pSurface->Width(), pSurface->Height(), pScanlineSizes, pChannelDataStart ) | |
); | |
data_iterator dataIt = pChannelDataStart; | |
data_iterator sizesIt = pScanlineSizes; | |
int h = pSurface->Height(); | |
int w = pSurface->Width(); | |
for ( int y = 0; y < h; y++ ) { | |
data_iterator readIt = dataIt; | |
dataIt += Read_m16( sizesIt ); | |
typename SURF::pixel_iterator it = pSurface->Iterator( 0, y ); | |
for ( int pixelsToProcess = w; 0 < pixelsToProcess; ) { | |
int len = Read_Byte( readIt ); | |
if ( 128 > len ) { // Literal | |
++len; | |
pixelsToProcess -= len; | |
if ( 0 > pixelsToProcess ) { | |
len += pixelsToProcess; | |
pixelsToProcess = 0; | |
} | |
while ( 0 < len-- ) { | |
*it++ = Read_Byte( readIt ); | |
} | |
} else if ( 128 < len ) { // Run | |
len = (len ^ 0xff) + 2; | |
pixelsToProcess -= len; | |
if ( 0 > pixelsToProcess ) { | |
len += pixelsToProcess; | |
pixelsToProcess = 0; | |
} | |
int value = Read_Byte( readIt ); | |
while ( 0 < len-- ) { | |
*it++ = value; | |
} | |
} | |
} | |
} | |
return true; | |
} | |
}; /* class CPSDScanlineUnpack8BitChannel */ | |
// | |
// CPSDUnpack8BitChannel | |
// | |
class CPSDUnpack8BitChannel { | |
public: | |
CPSDUnpack8BitChannel() { /* Empty */ } | |
template< class SURF > | |
bool operator()( SURF * surface, const BYTE * compressedChannelDataPtr ) { | |
BPT_PSD_DECODE_BLOCK_POLICY( | |
TRACE( "PSD::Decode Unpack %d x %d from %p\n", | |
surface->Width(), surface->Height(), compressedChannelDataPtr ) | |
); | |
int pixelsToProcess = (surface->Width() * surface->Height()); | |
int resetCount = surface->Width(); | |
int pixelsLeft = resetCount; | |
int y = 0; | |
typename SURF::pixel_iterator it = surface->Iterator( 0, y++ ); | |
while ( 0 < pixelsToProcess ) { | |
int len = static_cast<int>( *compressedChannelDataPtr++ ); | |
if ( 128 > len ) { // Literal | |
++len; | |
pixelsToProcess -= len; | |
if ( 0 > pixelsToProcess ) { | |
len += pixelsToProcess; | |
pixelsToProcess = 0; | |
} | |
while ( 0 < len ) { | |
// Process as many pixels in this span as possible | |
// ------------------------------------------------ | |
int count = min( pixelsLeft, len ); | |
pixelsLeft -= count; | |
len -= count; | |
while ( 0 <= --count ) { | |
*it++ = *compressedChannelDataPtr++; | |
} | |
// If we're at the end of the span move | |
// ------------------------------------------------ | |
if ( (0 == pixelsLeft) && pixelsToProcess ) { | |
it = surface->Iterator( 0, y++ ); | |
pixelsLeft = resetCount; | |
} | |
} | |
} else if ( 128 < len ) { // Run | |
len = (len ^ 0xff) + 2; | |
pixelsToProcess -= len; | |
if ( 0 > pixelsToProcess ) { | |
len += pixelsToProcess; | |
pixelsToProcess = 0; | |
} | |
int value = static_cast<int>( *compressedChannelDataPtr++ ); | |
while ( 0 < len ) { | |
// Process as many pixels in this span as possible | |
// ------------------------------------------------ | |
int count = min( pixelsLeft, len ); | |
pixelsLeft -= count; | |
len -= count; | |
while ( 0 <= --count ) { | |
*it++ = value; | |
} | |
// If we're at the end of the span move | |
// ------------------------------------------------ | |
if ( (0 == pixelsLeft) && pixelsToProcess ) { | |
it = surface->Iterator( 0, y++ ); | |
pixelsLeft = resetCount; | |
} | |
} | |
} | |
} | |
return true; | |
} | |
}; /* class CPSDUnpack8BitChannel */ | |
// | |
// CPSDReadRaw8BitChannel | |
// | |
class CPSDReadRaw8BitChannel { | |
public: | |
CPSDReadRaw8BitChannel() { /* EMPTY */ } | |
template< class SURF > | |
bool operator()( SURF * surface, const BYTE * channelDataPtr ) { | |
BPT_PSD_DECODE_BLOCK_POLICY( | |
TRACE( "PSD::Decode Raw %d x %d from %p\n", | |
surface->Width(), surface->Height(), channelDataPtr ) | |
); | |
int cx = surface->Width(); | |
int cy = surface->Height(); | |
for ( int y = 0; y < cy; y++ ) { | |
SURF::pixel_iterator it = surface->Iterator( 0, y ); | |
for ( int x = 0; x < cx; x++ ) { | |
*it++ = *channelDataPtr++; | |
} | |
} | |
return true; | |
} | |
}; /* class CPSDReadRaw8BitChannel */ | |
// (LAYER-STORAGE-CLASS) -------------------------------------------------- | |
// | |
// TPSDLayer | |
// | |
template< | |
class SURF | |
,class CCOL = std::list< SURF * > | |
,class MEM = SPSD_BPTMemoryPolicy<> /* SPSD_New<> */ | |
> | |
class TPSDLayer { | |
public: // traits | |
typedef CCOL channel_collection_type; | |
typedef SURF channel_surface_type; | |
typedef MEM memory_policy_type; | |
typedef TPSDLayer< SURF, CCOL, MEM > this_type; | |
// | |
// SLayerInfo | |
// | |
struct SLayerInfo { | |
int left; | |
int top; | |
int right; | |
int bottom; | |
DWORD blendModeSignature; | |
DWORD blendModeKey; | |
BYTE opacity; | |
BYTE clipping; | |
BYTE flags; | |
BYTE filter; | |
DWORD extraDataSize; | |
char szLayerName[ 257 ]; | |
void Clear() { | |
left = 0; | |
top = 0; | |
right = 0; | |
bottom = 0; | |
blendModeSignature = 0; | |
blendModeKey = 0; | |
opacity = 0; | |
clipping = 0; | |
flags = 0; | |
filter = 0; | |
extraDataSize = 0; | |
szLayerName[ 0 ] = '\0'; | |
} | |
SLayerInfo() { | |
Clear(); | |
} | |
}; /* struct SLayerInfo */ | |
private: // data | |
CCOL m_Channels; | |
MEM m_MemoryPolicy; | |
typename MEM::pointer_type m_pExtraData; | |
public: // data | |
SLayerInfo info; | |
public: // interface | |
TPSDLayer() : m_pExtraData(0) { info.Clear(); } | |
~TPSDLayer() { | |
Release(); | |
} | |
void Release() { | |
// Empty out the extra data | |
// ---------------------------------------------------------------- | |
if ( m_pExtraData ) { | |
m_MemoryPolicy.Free( m_pExtraData ); | |
m_pExtraData = 0; | |
} | |
// Empty out the channel collection | |
// ---------------------------------------------------------------- | |
CCOL::iterator it; | |
for ( it = m_Channels.begin(); it != m_Channels.end(); it++ ) { | |
delete (*it); | |
} | |
m_Channels.clear(); | |
// Clear all the 'fields' | |
// ---------------------------------------------------------------- | |
info.Clear(); | |
} | |
// -------------------------------------------------------------------- | |
bool Create( const int w, const int h, const int channels ) { | |
BPT_PSD_CHANNEL_BLOCK_POLICY( TRACE( "PSD::Layer::Create %d x %d channels %d\n", w, h, channels ) ); | |
for ( int i = 0; i < channels; i++ ) { | |
SURF * pSurface = new SURF; | |
if( !pSurface ) { | |
Release(); | |
return false; | |
} | |
m_Channels.push_back( pSurface ); | |
if ( !pSurface->Create( w, h ) ) { | |
Release(); | |
return false; | |
} | |
} | |
return true; | |
} | |
// Set the extra data | |
// -------------------------------------------------------------------- | |
bool SetExtraData( const BYTE * pExtraData, const int nBytes ) { | |
if ( m_pExtraData ) { | |
m_MemoryPolicy.Free( m_pExtraData ); | |
m_pExtraData = 0; | |
} | |
if ( 0 < nBytes ) { | |
m_pExtraData = m_MemoryPolicy.Allocate( nBytes ); | |
if ( m_pExtraData ) { | |
memcpy( m_pExtraData, pExtraData, nBytes ); | |
} | |
} | |
return true; | |
} | |
// Specific channel surface request (R,G,B,A etc...) | |
// -------------------------------------------------------------------- | |
SURF * GetChannel( const int nChannel ) { | |
BPT_PSD_CHANNEL_BLOCK_POLICY( TRACE( "PSD::Layer::GetChannel %d\n", nChannel ) ); | |
CCOL::iterator it; | |
int channel = 0; | |
for ( it = m_Channels.begin(); it != m_Channels.end(); it++ ) { | |
if ( channel == nChannel ) { | |
return *it; | |
} | |
++channel; | |
} | |
return 0; | |
} | |
CCOL * GetChannelCollection() { | |
BPT_PSD_CHANNEL_BLOCK_POLICY( TRACE( "PSD::Layer::GetChannel collection\n" ) ); | |
return &m_Channels; | |
} | |
int Width() { | |
return info.right - info.left; | |
} | |
int Height() { | |
return info.bottom - info.top; | |
} | |
}; /* class TPSDLayer */ | |
// (LAYER-LOAD-HELPER-TYPE) ----------------------------------------------------- | |
// | |
// PSDLHelp | |
// | |
template< class T > | |
struct PSDLHelp { | |
T * pLayerInfo; | |
int channelID; | |
DWORD channelDataSize; | |
PSDLHelp( | |
T * _pLayerInfo | |
,const int _channelID | |
,const DWORD _channelDataSize | |
) : pLayerInfo( _pLayerInfo ) | |
,channelID( _channelID ) | |
,channelDataSize( _channelDataSize ) { | |
/* empty */ | |
} | |
}; /* struct PSDLHelp */ | |
// | |
// SMemoryReadHelper | |
// | |
struct SMemoryReadHelper { | |
typedef const BYTE * data_iterator; | |
static void Skip_N( data_iterator & it, const int N ) { | |
it += N; | |
} | |
static BYTE Read_Byte( data_iterator & it ) { | |
return *it++; | |
} | |
static WORD Read_m16( data_iterator & it ) { | |
int hi = Read_Byte( it ); | |
int lo = Read_Byte( it ); | |
return (WORD)((hi << 8) | lo); | |
} | |
static DWORD Read_m32( data_iterator & it ) { | |
int a = Read_Byte( it ); | |
int b = Read_Byte( it ); | |
int c = Read_Byte( it ); | |
int d = Read_Byte( it ); | |
return (DWORD)((a << 24) | (b << 16) | (c << 8) | d); | |
} | |
// | |
// Read_NativeStruct() | |
// | |
template<class T> static T Read_NativeStruct( data_iterator & it ) { | |
T value = *(reinterpret_cast<const T *>( it )); | |
it += sizeof(T); | |
return value; | |
} | |
}; /* struct SMemoryReadHelper */ | |
// (PSD-FILE-LOAD-FUNCTIONS) ---------------------------------------------- | |
// | |
// TPSDFileLoader<> | |
// | |
template< | |
class SURF | |
,class REZCB = void * | |
,class CCOL = std::list< SURF * > | |
,class LAYER = TPSDLayer<SURF,CCOL> | |
,class LCOL = std::list< LAYER * > | |
,class INPOLICY = TFileIO<> | |
,class INTVEC = std::vector<int> | |
,class HELPCOL = std::vector< PSDLHelp<LAYER> > | |
> | |
class TPSDFileLoader { | |
public: | |
// -------------------------------------------------------------------- | |
typedef TPSDFileLoader< | |
SURF | |
,REZCB | |
,CCOL | |
,LAYER | |
,LCOL | |
,INPOLICY | |
,INTVEC | |
,HELPCOL | |
> this_type; | |
typedef SURF channel_surface_type; | |
typedef CCOL channel_collection_type; | |
typedef LCOL layer_collection_type; | |
typedef INPOLICY input_policy_type; | |
typedef REZCB image_resource_callback_type; | |
typedef LAYER layer_type; | |
private: | |
// -------------------------------------------------------------------- | |
enum { | |
PHOTOSHOP_MAGIC_NUMBER = 0x38425053 | |
,RGB_COLOR_MODE = 3 | |
,VALID_VERSION_NUMBER = 1 | |
,VALID_CHANNEL_BIT_SIZE = 8 | |
,COMPRESSION_NONE = 0 | |
,COMPRESSION_PACKBITS = 1 | |
}; | |
// -------------------------------------------------------------------- | |
template< class DECODER > | |
bool LoadChannelsHandler( | |
INPOLICY & input, DECODER & decode, INTVEC & dataSizeTable, | |
CCOL * pChannelCollection, int loadChannelCount | |
) { | |
// Prepare for channel data load loop | |
// ---------------------------------------------------------------- | |
CCOL::iterator channelIT = pChannelCollection->begin(); | |
INTVEC::iterator sizeIT = dataSizeTable.begin(); | |
// Read each channel | |
// ------------------------------------------------------------- | |
for ( int channel = 0; channel < loadChannelCount; channel++ ) { | |
// read the entire channel into ram | |
// -------------------------------------------------------- | |
int channelDataSize = *sizeIT++; | |
BYTE * data = input.LoadData( channelDataSize ); | |
if ( !data ) { | |
return false; | |
} | |
// Determine which surface & channel to write data into | |
// -------------------------------------------------------- | |
decode( *channelIT++, data ); | |
// Unload the loaded data | |
// -------------------------------------------------------- | |
input.UnloadData( data ); | |
} | |
return true; | |
} | |
// -------------------------------------------------------------------- | |
template< class T > class THandleCallback { | |
private: | |
T m_Callback; | |
public: | |
THandleCallback( T callback ) : m_Callback( callback ) { /* empty */ } | |
enum { is_not_void = 1 }; | |
bool operator()( | |
const BYTE * pResourceData | |
,const char * pszResourceName | |
,const DWORD type | |
,const WORD id | |
,const int dataSize | |
) { | |
return m_Callback( | |
pResourceData, pszResourceName, type, id, dataSize | |
); | |
} | |
}; | |
template<> class THandleCallback<void *> { | |
public: | |
THandleCallback<void *>( void * ) { /* empty */ } | |
enum { is_not_void = 0 }; | |
bool operator()( | |
const BYTE * pResourceData | |
,const char * pszResourceName | |
,const DWORD type | |
,const WORD id | |
,const int dataSize | |
) { | |
return true; | |
} | |
}; | |
// -------------------------------------------------------------------- | |
template< class CALLBACK_HANDLER > | |
bool ProcessImageResourceBlock( | |
INPOLICY & input, const int blockSize, CALLBACK_HANDLER & callback | |
) { | |
int firstOffset = input.FTell(); | |
int resourceOffset = firstOffset; | |
char szName[ 257 ]; | |
do { | |
resourceOffset = input.FTell(); | |
// read the type and id | |
// ------------------------------------------------------------ | |
DWORD type = input.Read_m32(); | |
WORD id = input.Read_m16(); | |
// read the pascal string | |
// ------------------------------------------------------------ | |
BYTE stringLength = input.Read_Byte(); | |
int pascalStringDataSize = (1 + stringLength); | |
for ( int i = 0; i < stringLength; i++ ) { | |
szName[ i ] = input.Read_Byte(); | |
} | |
szName[ stringLength ] = 0; | |
if ( pascalStringDataSize & 1 ) { | |
++pascalStringDataSize; | |
input.Read_Byte(); | |
} | |
// Load the resource data | |
// ------------------------------------------------------------ | |
int logicalDataSize = input.Read_m32(); | |
BYTE * pData = input.LoadData( logicalDataSize ); | |
if ( !callback( pData, szName, type, id, logicalDataSize ) ) { | |
return false; | |
} | |
// If there was data then Unload it. | |
// ------------------------------------------------------------ | |
if ( pData ) { | |
input.UnloadData( pData ); | |
} | |
// Calculate the whole resource size to move to next position | |
// ------------------------------------------------------------ | |
int physicalDataSize = logicalDataSize + (logicalDataSize & 1); | |
int wholeSubResourceSize = | |
pascalStringDataSize + | |
sizeof(DWORD) + | |
sizeof(WORD) + | |
sizeof(DWORD) + | |
physicalDataSize; | |
resourceOffset += wholeSubResourceSize; | |
input.Seek( resourceOffset, SEEK_SET ); | |
} while ( blockSize > (resourceOffset - firstOffset) ); | |
return true; | |
} | |
// -------------------------------------------------------------------- | |
bool ProcessLayers( | |
LCOL * pLayersCollection, INPOLICY & input, const int blockSize | |
) { | |
int firstOffset = input.FTell(); | |
int resourceOffset = firstOffset; | |
int blockCounter = 0; | |
do { | |
resourceOffset = input.FTell(); | |
// Load the resource data | |
// ------------------------------------------------------------ | |
int logicalDataSize = (signed int)input.Read_m32(); | |
if ( 0 >= logicalDataSize ) { | |
break; | |
} | |
// process the layers block | |
// ------------------------------------------------------------ | |
if ( 1 == ++blockCounter ) { | |
BYTE * pData = input.LoadData( logicalDataSize ); | |
if ( !pData ) { | |
return false; | |
} | |
// setup our read iterator | |
// ---------------------------------------------------------------- | |
SMemoryReadHelper::data_iterator readIt = | |
static_cast<SMemoryReadHelper::data_iterator>( pData ); | |
int layerCount = (signed short)SMemoryReadHelper::Read_m16( readIt ); | |
bool bUsesFirstAlphaChannel = (0 > layerCount); | |
if ( 0 > layerCount ) layerCount = -layerCount; | |
HELPCOL channelDataLoadInfo; | |
for ( int layer = 0; layer < layerCount; layer++ ) { | |
// start by creating a new layer info instance | |
// ------------------------------------------------------------ | |
LAYER * pLayer = new LAYER; | |
if ( !pLayer ) { | |
input.UnloadData( pData ); | |
return false; | |
} | |
// Read layer position rect | |
// ------------------------------------------------------------ | |
RECT layerRect; | |
layerRect.top = SMemoryReadHelper::Read_m32( readIt ); | |
layerRect.left = SMemoryReadHelper::Read_m32( readIt ); | |
layerRect.bottom = SMemoryReadHelper::Read_m32( readIt ); | |
layerRect.right = SMemoryReadHelper::Read_m32( readIt ); | |
// give dimension to our new layer info instance and add it | |
// to the layers collection. | |
// ------------------------------------------------------------ | |
int layerChannels = SMemoryReadHelper::Read_m16( readIt ); | |
pLayersCollection->push_back( pLayer ); | |
if ( !pLayer->Create( | |
(layerRect.right - layerRect.left) | |
,(layerRect.bottom - layerRect.top) | |
,layerChannels | |
) ) { | |
input.UnloadData( pData ); | |
return false; | |
} | |
// Read each channel data type and length info | |
// ------------------------------------------------------------ | |
for ( int channel = 0; channel < layerChannels; channel++ ) { | |
int channelID = (signed short)SMemoryReadHelper::Read_m16( readIt ); | |
DWORD channelDataLength = SMemoryReadHelper::Read_m32( readIt ); | |
channelDataLoadInfo.push_back( | |
HELPCOL::value_type( pLayer, channelID, channelDataLength ) | |
); | |
} | |
// Read the blend and other misc info | |
// ------------------------------------------------------------ | |
DWORD blendModeSignature = SMemoryReadHelper::Read_m32( readIt ); | |
DWORD blendModeKey = SMemoryReadHelper::Read_m32( readIt ); | |
BYTE opacity = SMemoryReadHelper::Read_Byte( readIt ); | |
BYTE clipping = SMemoryReadHelper::Read_Byte( readIt ); | |
BYTE flags = SMemoryReadHelper::Read_Byte( readIt ); | |
BYTE filter = SMemoryReadHelper::Read_Byte( readIt ); | |
DWORD extraDataSize = SMemoryReadHelper::Read_m32( readIt ); | |
// Fill in the layer information here | |
// ------------------------------------------------------------ | |
pLayer->info.left = layerRect.left; | |
pLayer->info.top = layerRect.top; | |
pLayer->info.right = layerRect.right; | |
pLayer->info.bottom = layerRect.bottom; | |
pLayer->info.blendModeSignature = blendModeSignature; | |
pLayer->info.blendModeKey = blendModeKey; | |
pLayer->info.opacity = opacity; | |
pLayer->info.clipping = clipping; | |
pLayer->info.flags = flags; | |
pLayer->info.filter = filter; | |
pLayer->info.extraDataSize = extraDataSize; | |
// Handle the 'extra' data | |
// ------------------------------------------------------------ | |
if ( !pLayer->SetExtraData( readIt, extraDataSize ) ) { | |
input.UnloadData( pData ); | |
return false; | |
} else { | |
SMemoryReadHelper::data_iterator parseIt = readIt; | |
// skip the pad | |
SMemoryReadHelper::Skip_N( parseIt, 4 ); | |
// skip the layer blending ranges | |
DWORD layerBlendingRangesSize = SMemoryReadHelper::Read_m32( parseIt ); | |
SMemoryReadHelper::Skip_N( parseIt, layerBlendingRangesSize ); | |
// okay the iterator should be at a pascal string for the layer name | |
BYTE layerNameLength = SMemoryReadHelper::Read_Byte( parseIt ); | |
for ( int i = 0; i < layerNameLength; i++ ) { | |
pLayer->info.szLayerName[ i ] = SMemoryReadHelper::Read_Byte( parseIt ); | |
} | |
pLayer->info.szLayerName[ layerNameLength ] = '\0'; | |
// ============================================================================ | |
// Now check for a longer unicode string name | |
// FIX!!! should make sure it's inside the read block!!! | |
// ============================================================================ | |
#if 1 // 06/08/06 BPT | |
// from looking at the data it looks like it's always | |
// padded so that the 8BIM is on some sort of 16 alignment | |
// it's probably descibed in newer versions of the Photoshop SDK??? | |
int pad = (((layerNameLength + 15) & (~0xf)) - layerNameLength) - 1; | |
if ( 0 < pad ) { | |
SMemoryReadHelper::Skip_N( parseIt, pad ); | |
} | |
DWORD id_8BIM = SMemoryReadHelper::Read_m32( parseIt ); | |
if ( 0x3842494d == id_8BIM ) { | |
DWORD id_luni = SMemoryReadHelper::Read_m32( parseIt ); | |
if ( 0x6c756e69 == id_luni ) { | |
DWORD id_luni_blockLen = SMemoryReadHelper::Read_m32( parseIt ); | |
DWORD id_luni_stringLen = SMemoryReadHelper::Read_m32( parseIt ); | |
// This really should use some unicode conversion function! | |
char szLongerLayerName[ 257 ]; | |
int limitedLen = min( 255, (int)id_luni_stringLen ); | |
for ( int i = 0; i < limitedLen; ++i ) { | |
if ( 0 == SMemoryReadHelper::Read_Byte( parseIt ) ) { | |
szLongerLayerName[ i ] = SMemoryReadHelper::Read_Byte( parseIt ); | |
} else { | |
szLongerLayerName[ i ] = ' '; | |
} | |
} | |
szLongerLayerName[ limitedLen ] = '\0'; | |
// only replace the string if the smaller | |
// string is a subset of the larger string! | |
if ( szLongerLayerName == strstr( szLongerLayerName, pLayer->info.szLayerName ) ) { | |
#if defined(BPT_PSD_LAYER_NAME_DIAGNOSTIC_TRACE) | |
if ( strlen(szLongerLayerName) != strlen(pLayer->info.szLayerName) ) | |
{ | |
TRACE( | |
"Replacing short \"%s\" with Long \"%s\"\n" | |
, pLayer->info.szLayerName | |
, szLongerLayerName | |
); | |
} | |
#endif | |
strcpy( pLayer->info.szLayerName, szLongerLayerName ); | |
} | |
} | |
} | |
#endif // // 06/08/06 BPT | |
// ============================================================================ | |
// ============================================================================ | |
} | |
// readIt += extraDataSize; | |
SMemoryReadHelper::Skip_N( readIt, extraDataSize ); | |
} | |
// Now read all the layer channels | |
// ---------------------------------------------------------------- | |
HELPCOL::iterator it = channelDataLoadInfo.begin(); | |
for ( ; it != channelDataLoadInfo.end(); it++ ) { | |
SMemoryReadHelper::data_iterator channelReader = readIt; | |
readIt += it->channelDataSize; | |
// match the channel id type to the layer channel | |
// ------------------------------------------------------------ | |
// <<< NEEDS MORE DESIGN >>> | |
// -- what if there are more than the 5 channels handled? | |
// -- also what if there is only an alpha channel? | |
int nChannel = it->channelID; | |
if ( -1 == nChannel ) { | |
nChannel = 3; | |
} else if ( -2 == nChannel ) { | |
nChannel = 4; | |
} | |
SURF * pSurface = it->pLayerInfo->GetChannel( nChannel ); | |
// now read the channel data into the channel surface | |
// ------------------------------------------------------------ | |
if ( pSurface ) { | |
int type = SMemoryReadHelper::Read_m16( channelReader ); | |
if ( 0 == type ) { | |
CPSDReadRaw8BitChannel rawDecoder; | |
if ( !rawDecoder( pSurface, channelReader ) ) { | |
input.UnloadData( pData ); | |
return false; | |
} | |
} else { | |
CPSDScanlineUnpack8BitChannel compressedDecoder; | |
if ( !compressedDecoder( | |
pSurface | |
,channelReader | |
,channelReader + (pSurface->Height() * 2) | |
) ) { | |
input.UnloadData( pData ); | |
return false; | |
} | |
} | |
} | |
} | |
// If there was data then Unload it. | |
// ------------------------------------------------------------ | |
if ( pData ) { | |
input.UnloadData( pData ); | |
} | |
} else { | |
// Ignore global mask data | |
} | |
// Move to the next resource | |
// ------------------------------------------------------------ | |
resourceOffset += logicalDataSize; | |
input.Seek( resourceOffset, SEEK_SET ); | |
} while ( blockSize > (resourceOffset - firstOffset) ); | |
return true; | |
} | |
// -------------------------------------------------------------------- | |
public: | |
// -------------------------------------------------------------------- | |
void Unload( LCOL * pLoadedData ) { | |
if ( !pLoadedData ) { | |
return; | |
} | |
LCOL::iterator it; | |
for ( it = pLoadedData->begin(); it != pLoadedData->end(); it++ ) { | |
delete (*it); | |
} | |
delete pLoadedData; | |
} | |
// -------------------------------------------------------------------- | |
LCOL * LoadFrom( | |
INPOLICY & input | |
, SIZE * pCanvasSize = 0 | |
, REZCB resourceCallback = REZCB(0) | |
, const bool bSkipAlphaChannels = true | |
) { | |
// Read & Validate the header | |
// ---------------------------------------------------------------- | |
if ( PHOTOSHOP_MAGIC_NUMBER != input.Read_m32() ) { | |
return 0; | |
} | |
if ( VALID_VERSION_NUMBER != input.Read_m16() ) { | |
return 0; | |
} | |
// Skip 6 bytes of pad | |
// ---------------------------------------------------------------- | |
input.Read_m32(); // Pad4 (should be zero) | |
input.Read_m16(); // Pad2 (should be zero) | |
// Read the 'channel' count | |
// ---------------------------------------------------------------- | |
int channelCount = input.Read_m16(); | |
// Get the bitmap dimensions | |
// ---------------------------------------------------------------- | |
SIZE imageSize; | |
imageSize.cy = input.Read_m32(); | |
imageSize.cx = input.Read_m32(); | |
if ( pCanvasSize ) { | |
pCanvasSize->cx = imageSize.cx; | |
pCanvasSize->cy = imageSize.cy; | |
} | |
// Read the size of the channel data | |
// ---------------------------------------------------------------- | |
if ( VALID_CHANNEL_BIT_SIZE != input.Read_m16() ) { | |
return 0; | |
} | |
if ( RGB_COLOR_MODE != input.Read_m16() ) { | |
return 0; | |
} | |
// Skip unprocessed color model data block | |
// ---------------------------------------------------------------- | |
int colorModelDataSize = input.Read_m32(); | |
if ( colorModelDataSize ) { | |
input.Seek( colorModelDataSize, SEEK_CUR ); | |
} | |
// Process Image resource data block | |
// ---------------------------------------------------------------- | |
{ | |
int imageResourceDataSize = input.Read_m32(); | |
if ( imageResourceDataSize ) { | |
int pastBlockOffset = input.FTell() + imageResourceDataSize; | |
typedef THandleCallback<REZCB> resource_handler_type; | |
resource_handler_type resourceCallbackHandler( resourceCallback ); | |
if ( resource_handler_type::is_not_void ) { | |
if ( ProcessImageResourceBlock( | |
input, imageResourceDataSize, resourceCallbackHandler ) ) { | |
return 0; | |
} | |
} | |
input.Seek( pastBlockOffset, SEEK_SET ); | |
} | |
} | |
// Create the layer collection | |
// ---------------------------------------------------------------- | |
LCOL * pLayerCollection = new LCOL; | |
if ( !pLayerCollection ) { | |
return 0; | |
} | |
// layer and mask data block | |
// ---------------------------------------------------------------- | |
{ | |
int layerAndMaskInfoDataSize = input.Read_m32(); | |
if ( layerAndMaskInfoDataSize ) { | |
int pastBlockOffset = input.FTell() + layerAndMaskInfoDataSize; | |
if ( !ProcessLayers( | |
pLayerCollection, input, layerAndMaskInfoDataSize ) ) { | |
if ( pLayerCollection ) { | |
Unload( pLayerCollection ); | |
} | |
return 0; | |
} | |
// process layers here... | |
// -------------------------------------------------------- | |
input.Seek( pastBlockOffset, SEEK_SET ); | |
} | |
} | |
// Create the composite 'layer' also houses alpha channels... | |
// ---------------------------------------------------------------- | |
if ( !bSkipAlphaChannels ) { | |
LAYER * pLayer = new LAYER; | |
if ( !pLayer ) { | |
goto HANDLE_ERROR; | |
} | |
if ( !pLayer->Create( imageSize.cx, imageSize.cy, channelCount ) ) { | |
delete pLayer; | |
goto HANDLE_ERROR; | |
} | |
// Fill in the layer info and add to our collection. | |
// ---------------------------------------------------------------- | |
pLayer->info.left = 0; | |
pLayer->info.top = 0; | |
pLayer->info.right = imageSize.cx; | |
pLayer->info.bottom = imageSize.cy; | |
pLayerCollection->push_back( pLayer ); | |
// Get down to business and read the composite/alpha channels | |
// ---------------------------------------------------------------- | |
{ | |
CCOL * pChannelCollection = pLayer->GetChannelCollection(); | |
int compressionType = input.Read_m16(); | |
if ( COMPRESSION_NONE == compressionType ) { | |
// Build the dataSizeTable | |
// ------------------------------------------------------------ | |
INTVEC dataSizeTable; | |
int channelDataSize = imageSize.cx * imageSize.cy; | |
for ( int i = 0; i < channelCount; i++ ) { | |
dataSizeTable.push_back( channelDataSize ); | |
} | |
// Call the handler | |
// ------------------------------------------------------------ | |
CPSDReadRaw8BitChannel raw; | |
if ( !LoadChannelsHandler( | |
input, raw, dataSizeTable, | |
pChannelCollection, channelCount ) ) { | |
goto HANDLE_ERROR; | |
} | |
} else if ( COMPRESSION_PACKBITS == compressionType ) { | |
// Read in the compressed channel data size table | |
// Assume reading entire compressed channel as one big chunk | |
// ------------------------------------------------------------- | |
INTVEC dataSizeTable; | |
for ( int i = 0; i < channelCount; i++ ) { | |
int channelDataSize = 0; | |
for ( int y = 0; y < imageSize.cy; y++ ) { | |
channelDataSize += input.Read_m16(); | |
} | |
dataSizeTable.push_back( channelDataSize ); | |
} | |
// Call the handler | |
// ------------------------------------------------------------ | |
CPSDUnpack8BitChannel unpacker; | |
if ( !LoadChannelsHandler( | |
input, unpacker, dataSizeTable, | |
pChannelCollection, channelCount ) ) { | |
goto HANDLE_ERROR; | |
} | |
} else { | |
goto HANDLE_ERROR; | |
} | |
} | |
} | |
// Finally return our loaded data | |
// ---------------------------------------------------------------- | |
return pLayerCollection; | |
// ---------------------------------------------------------------- | |
HANDLE_ERROR: | |
if ( pLayerCollection ) { | |
Unload( pLayerCollection ); | |
} | |
return 0; | |
} | |
// -------------------------------------------------------------------- | |
LCOL * | |
Load( | |
const char * pInputName | |
, SIZE * pCanvasSize = 0 | |
, REZCB resourceCallback = REZCB(0) | |
, const bool bSkipAlphaChannels = true | |
) { | |
// Open the file return error if not opened. | |
// ---------------------------------------------------------------- | |
INPOLICY input; | |
if ( !input.Open( pInputName, "rb" ) ) { | |
return 0; | |
} | |
LCOL * pLayers = LoadFrom( | |
input | |
, pCanvasSize | |
, resourceCallback | |
, bSkipAlphaChannels | |
); | |
input.Close(); | |
return pLayers; | |
} | |
}; /* template TPSDFileLoader<> */ | |
// ------------------------------------------------------------------------ | |
// | |
// SAlphaNames | |
// | |
struct SAlphaNames { | |
typedef std::string string_type; | |
typedef std::list< string_type * > name_collection_type; | |
name_collection_type m_Names; | |
void EmptyCollection() { | |
name_collection_type::iterator it; | |
for ( it = m_Names.begin(); it != m_Names.end(); it++ ) { | |
delete (*it); | |
} | |
m_Names.clear(); | |
} | |
~SAlphaNames() { | |
EmptyCollection(); | |
} | |
SAlphaNames( const int zero = 0 ) { /* empty */ } | |
bool operator()( | |
const BYTE * pResourceData | |
,const char * pszResourceName | |
,const DWORD type | |
,const WORD id | |
,const int dataSize | |
) { | |
if ( !pResourceData ) { | |
return false; | |
} | |
const BYTE * pResourceEnd = pResourceData + dataSize; | |
if ( 1006 == id ) { | |
while ( pResourceEnd != pResourceData ) { | |
int len = *pResourceData++; | |
if ( len ) { | |
const char * pChars = reinterpret_cast<const char *>( pResourceData ); | |
string_type * pName = new string_type( pChars, pChars + len ); | |
if ( !pName ) { | |
return false; | |
} | |
m_Names.push_back( pName ); | |
pResourceData += len; | |
} | |
} | |
} | |
return true; | |
} | |
}; /* struct SAlphaNames */ | |
// ------------------------------------------------------------------------ | |
}; // namespace BPT | |
#endif // !defined(AFX_TPSDFILECODE_H__9D2B4C1A_1D42_4364_A93E_65D59EF0F741__INCLUDED_) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment