Skip to content

Instantly share code, notes, and snippets.

@lardratboy
Created December 11, 2014 02:26
Show Gist options
  • Save lardratboy/51a45a83545bf9992011 to your computer and use it in GitHub Desktop.
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...
// 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