Copy link to clipboard
Copied
I'm extending the Outbound sample to have each layer exported as a PNG file. I got it done for RGB layers, but it does not work for Text Layers.
For text layers I got the rectangle where the text should be drawn, but no text. That's fine. The pExportRecord->pReadLayerDesc->name brings the Text value, but I could not find where to find information about font size, font style, font name, font color, and so on.
Could someone help me on that?
Thanks
Copy link to clipboard
Copied
I am working on exporting different layers also, how did you get image data for each specific layers?
-Thanks
Copy link to clipboard
Copied
I have changed the Outbound sample shipped with the Adobe Photoshop CS4 SDK.
Following is the code I have developed for test purposes. Please, have in mind that this code is a draft test and need to be rewritten following good practices in order to become a “good code”. Anyway, I hope you can have your question answered.
Best regards.
// ADOBE SYSTEMS INCORPORATED
// Copyright 1993 - 2002 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this
// file in accordance with the terms of the Adobe license agreement
// accompanying it. If you have received this file from a source
// other than Adobe, then your use, modification, or distribution
// of it requires the prior written permission of Adobe.
//-------------------------------------------------------------------
//-------------------------------------------------------------------------------
//
// File:
// Outbound.cpp
//
// Copyright 1990-1992, Thomas Knoll.
// All Rights Reserved.
//
// Description:
// This file contains the functions and source
// for the Export module Outbound, a module that
// creates a file and stores raw pixel data in it.
//
// Use:
// This module shows how to export raw data to a file.
// It uses a simple "FileUtilities" library that comes
// with the SDK. You use it via File>>Export>>Outbound.
//
//-------------------------------------------------------------------------------
//-------------------------------------------------------------------------------
// Includes
//-------------------------------------------------------------------------------
// Modify the following defines if you have to target a platform prior to the ones specified below.
// Refer to MSDN for the latest info on corresponding values for different platforms.
#include <afxwin.h>
#include <stdio.h>
#include <tchar.h>
#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // some CString constructors will be explicit
#include <atlbase.h>
#include <atlstr.h>
#include <atlimage.h>
#include <stdio.h>
#define USING_MFC
#include "Outbound.h"
//-------------------------------------------------------------------------------
// Prototypes.
//-------------------------------------------------------------------------------
void InitGlobals (Ptr globalPtr); // Initialize globals.
void DoPrepare (GPtr globals);
void DoStart (GPtr globals); // Main routine.
void DoContinue (GPtr globals);
void DoFinish (GPtr globals);
void DoInitialRect (GPtr globals);
Boolean DoNextRect (GPtr globals);
void DoExportRect (GPtr globals);
void SaveImage( ReadChannelDesc *pReadChannelDesc, int imageIndex, ReadChannelDesc *pTransparency );
//-------------------------------------------------------------------------------
// Globals -- Define global variables for plug-in scope.
//-------------------------------------------------------------------------------
SPBasicSuite * sSPBasic = NULL;
//-------------------------------------------------------------------------------
//
// PluginMain / main
//
// All calls to the plug-in module come through this routine.
// It must be placed first in the resource. To achieve this,
// most development systems require this be the first routine
// in the source.
//
// The entrypoint will be "pascal void" for Macintosh,
// "void" for Windows.
//
// Inputs:
// const int16 selector Host provides selector indicating
// what command to do.
//
// ExportRecordPtr exportParamBlock Host provides pointer to parameter
// block containing pertinent data
// and callbacks from the host.
// See PIExport.h.
//
// Outputs:
// ExportRecordPtr exportParamBlock Host provides pointer to parameter
// block containing pertinent data
// and callbacks from the host.
// See PIExport.h.
//
// intptr_t *data Use this to store a handle or pointer to our
// global parameters structure, which
// is maintained by the host between
// calls to the plug-in.
//
// int16 *result Return error result or noErr. Some
// errors are handled by the host, some
// are silent, and some you must handle.
// See PIGeneral.h.
//
//-------------------------------------------------------------------------------
ExportRecordPtr gExportRecord;
DLLExport MACPASCAL void PluginMain (const int16 selector,
ExportRecordPtr exportParamBlock,
intptr_t *data,
int16 *result)
{
//---------------------------------------------------------------------------
// (1) Check for about box request.
//
// The about box is a special request; the parameter block is not filled
// out, none of the callbacks or standard data is available. Instead,
// the parameter block points to an AboutRecord, which is used
// on Windows.
//---------------------------------------------------------------------------
gExportRecord = exportParamBlock;
if (selector == exportSelectorAbout)
{
sSPBasic = ((AboutRecord*)exportParamBlock)->sSPBasic;
DoAbout((AboutRecordPtr)exportParamBlock);
}
else
{ // do the rest of the process as normal:
sSPBasic = ((ExportRecordPtr)exportParamBlock)->sSPBasic;
Ptr globalPtr = NULL; // Pointer for global structure
GPtr globals = NULL; // actual globals
//-----------------------------------------------------------------------
// (2) Allocate and initalize globals.
//
// AllocateGlobals requires the pointer to result, the pointer to the
// parameter block, a pointer to the handle procs, the size of our local
// "Globals" structure, a pointer to the long *data, a Function
// Proc (FProcP) to the InitGlobals routine. It automatically sets-up,
// initializes the globals (if necessary), results result to 0, and
// returns with a valid pointer to the locked globals handle or NULL.
//-----------------------------------------------------------------------
globalPtr = AllocateGlobals (result,
exportParamBlock,
exportParamBlock->handleProcs,
sizeof(Globals),
data,
InitGlobals);
if (globalPtr == NULL)
{ // Something bad happened if we couldn't allocate our pointer.
// Fortunately, everything's already been cleaned up,
// so all we have to do is report an error.
*result = memFullErr;
return;
}
// Get our "globals" variable assigned as a Global Pointer struct with the
// data we've returned:
globals = (GPtr)globalPtr;
//-----------------------------------------------------------------------
// (3) Dispatch selector.
//-----------------------------------------------------------------------
switch (selector)
{
case exportSelectorPrepare:
DoPrepare(globals);
break;
case exportSelectorStart:
DoStart(globals);
break;
case exportSelectorContinue:
DoContinue(globals);
break;
case exportSelectorFinish:
DoFinish(globals);
break;
}
//-----------------------------------------------------------------------
// (4) Unlock data, and exit resource.
//
// Result is automatically returned in *result, which is
// pointed to by gResult.
//-----------------------------------------------------------------------
// unlock handle pointing to parameter block and data so it can move
// if memory gets shuffled:
if ((Handle)*data != NULL)
PIUnlockHandle((Handle)*data);
} // about selector special
} // end PluginMain
//-------------------------------------------------------------------------------
//
// InitGlobals
//
// Initalize any global values here. Called only once when global
// space is reserved for the first time.
//
// Inputs:
// Ptr globalPtr Standard pointer to a global structure.
//
// Outputs:
// Initializes any global values with their defaults.
//
//-------------------------------------------------------------------------------
void InitGlobals (Ptr globalPtr)
{
// create "globals" as a our struct global pointer so that any
// macros work:
GPtr globals = (GPtr)globalPtr;
} // end InitGlobals
//-------------------------------------------------------------------------------
//
// DoPrepare
//
// Initialize parameters to default values.
//
// Inputs:
//
// Outputs:
/*****************************************************************************/
/* Prepare to export an image. If the plug-in module needs only a limited
amount of memory, it can lower the value of the 'maxData' field. */
void DoPrepare (GPtr globals)
{
}
/*****************************************************************************/
/* Requests pointer to the first part of the image to be filtered. */
void DoStart (GPtr globals)
{
/* check with the scripting system whether to pop our dialog */
gQueryForParameters = ReadScriptParams (globals);
if (!DoUI (globals))
return;
FILE *pfTemp = fopen( (LPSTR)gExportRecord->filename, "w" );
if( pfTemp )
{
fprintf( pfTemp, "imageSize.h: %d\r\n", gExportRecord->imageSize.h );
fprintf( pfTemp, "imageSize.v: %d\r\n", gExportRecord->imageSize.v );
fprintf( pfTemp, "imageSize32.h: %d\r\n", gExportRecord->imageSize32.h );
fprintf( pfTemp, "imageSize32.v: %d\r\n", gExportRecord->imageSize32.v );
fprintf( pfTemp, "depth: %d\r\n", gExportRecord->depth );
fprintf( pfTemp, "imageHRes: %d\r\n", gExportRecord->imageHRes );
fprintf( pfTemp, "imageVRes: %d\r\n", gExportRecord->imageVRes );
fprintf( pfTemp, "mode: %d\r\n", gExportRecord->imageMode );
fprintf( pfTemp, "planes: %d\r\n", gExportRecord->planes );
ReadImageDocumentDesc *pReadImageDocumentDesc = gExportRecord->documentInfo;
fprintf( pfTemp, "\r\nReadImageDocumentDesc\r\n" );
int imageIndex = 0;
while( pReadImageDocumentDesc )
{
fprintf( pfTemp, "layerCount: %d\r\n", pReadImageDocumentDesc->layerCount );
fprintf( pfTemp, "alphaChannelCount: %d\r\n", pReadImageDocumentDesc->alphaChannelCount );
fprintf( pfTemp, "bounds.bottom: %d\r\n", pReadImageDocumentDesc->bounds.bottom );
fprintf( pfTemp, "bounds.left: %d\r\n", pReadImageDocumentDesc->bounds.left );
fprintf( pfTemp, "bounds.right: %d\r\n", pReadImageDocumentDesc->bounds.right );
fprintf( pfTemp, "bounds.top: %d\r\n", pReadImageDocumentDesc->bounds.top );
fprintf( pfTemp, "depth: %d\r\n", pReadImageDocumentDesc->depth );
fprintf( pfTemp, "documentType: %d\r\n", pReadImageDocumentDesc->documentType );
fprintf( pfTemp, "imageMode: %d\r\n", pReadImageDocumentDesc->imageMode );
ReadLayerDesc *pReadLayerDesc = pReadImageDocumentDesc->layersDescriptor;
fprintf( pfTemp, "\r\nReadLayerDesc\r\n" );
while( pReadLayerDesc )
{
fprintf( pfTemp, "layer index: %d\r\n", imageIndex );
fprintf( pfTemp, "layer name: %s\r\n", pReadLayerDesc->name );
fprintf( pfTemp, "isVisible: %s\r\n", pReadLayerDesc->isVisible? "true":"false" );
fprintf( pfTemp, "isPixelBased: %s\r\n", pReadLayerDesc->isPixelBased? "true":"false" );
if( pReadLayerDesc->layerMask )
{
fprintf( pfTemp, "layerMask->bounds.bottom: %d\r\n", pReadLayerDesc->layerMask->bounds.bottom );
fprintf( pfTemp, "layerMask->bounds.left: %d\r\n", pReadLayerDesc->layerMask->bounds.left );
fprintf( pfTemp, "layerMask->bounds.right: %d\r\n", pReadLayerDesc->layerMask->bounds.right );
fprintf( pfTemp, "layerMask->bounds.top: %d\r\n", pReadLayerDesc->layerMask->bounds.top );
}
if( pReadLayerDesc->compositeChannelsList )
{
fprintf( pfTemp, "compositeChannelsList->bounds.bottom: %d\r\n", pReadLayerDesc->compositeChannelsList->bounds.bottom );
fprintf( pfTemp, "compositeChannelsList->bounds.left: %d\r\n", pReadLayerDesc->compositeChannelsList->bounds.left );
fprintf( pfTemp, "compositeChannelsList->bounds.right: %d\r\n", pReadLayerDesc->compositeChannelsList->bounds.right );
fprintf( pfTemp, "compositeChannelsList->bounds.top: %d\r\n", pReadLayerDesc->compositeChannelsList->bounds.top );
SaveImage( pReadLayerDesc->compositeChannelsList, imageIndex, pReadLayerDesc->transparency );
}
if( pReadLayerDesc->smartFilterMask )
{
fprintf( pfTemp, "smartFilterMask->bounds.bottom: %d\r\n", pReadLayerDesc->smartFilterMask->bounds.bottom );
fprintf( pfTemp, "smartFilterMask->bounds.left: %d\r\n", pReadLayerDesc->smartFilterMask->bounds.left );
fprintf( pfTemp, "smartFilterMask->bounds.right: %d\r\n", pReadLayerDesc->smartFilterMask->bounds.right );
fprintf( pfTemp, "smartFilterMask->bounds.top: %d\r\n", pReadLayerDesc->smartFilterMask->bounds.top );
}
if( pReadLayerDesc->vectorMask )
{
fprintf( pfTemp, "vectorMask->bounds.bottom: %d\r\n", pReadLayerDesc->vectorMask->bounds.bottom );
fprintf( pfTemp, "vectorMask->bounds.left: %d\r\n", pReadLayerDesc->vectorMask->bounds.left );
fprintf( pfTemp, "vectorMask->bounds.right: %d\r\n", pReadLayerDesc->vectorMask->bounds.right );
fprintf( pfTemp, "vectorMask->bounds.top: %d\r\n", pReadLayerDesc->vectorMask->bounds.top );
}
fprintf( pfTemp, "----\r\n" );
pReadLayerDesc = pReadLayerDesc->next;
imageIndex++;
}
fprintf( pfTemp, "----\r\n" );
pReadImageDocumentDesc = gExportRecord->documentInfo->next;
}
fclose( pfTemp );
}
}
/*****************************************************************************/
/* Filters the area and requests the next area. */
void DoContinue (GPtr globals)
{
/* We shouldn't get here because we did all of the work during the
start phase, but we add some code just in case. */
MarkExportFinished (gStuff);
gResult = userCanceledErr;
}
/*****************************************************************************/
/* This routine will always be called if DoStart does not return an error
(even if DoContinue returns an error or the user aborts the operation).
This allows the module to perform any needed cleanup. None is required
in this example. */
void DoFinish (GPtr globals)
{
WriteScriptParams (globals);
}
/*****************************************************************************/
#define RANGE_ITER(lower,upper,first,last,step) \
for (lower = (first); \
(upper = (((lower) + (short)(step) < (last)) ? (lower) + (short)(step) : (last))), \
lower < (last); \
lower = upper)
/*****************************************************************************/
Boolean WriteExportFile (GPtr globals)
{
/* We write out the file as an interleaved raw file. */
/* We need to figure out how many rows to write at one time. */
long chunk = gStuff->maxData / gStuff->imageSize.h / gStuff->planes;
ExportRegion region;
region.rect.left = 0;
region.rect.right = gStuff->imageSize.h;
region.loPlane = 0;
region.hiPlane = gStuff->planes - 1;
RANGE_ITER (region.rect.top, region.rect.bottom,
0, gStuff->imageSize.v, chunk)
{
int16 row;
long rowCount = gStuff->imageSize.h * (long) gStuff->planes;
void *data = 0;
int32 rowBytes = 0;
unsigned8 *rowData;
if (!TSC (TestAbort ())) return FALSE;
if (!TSR (FetchData (gStuff, ®ion, &data, &rowBytes))) return FALSE;
for (row = region.rect.top, rowData = (unsigned8 *) data;
row < region.rect.bottom;
++row, rowData += rowBytes)
{
long count = rowCount;
PIUpdateProgress (row, gStuff->imageSize.v);
if (!TSC (TestAbort ())) return FALSE;
if (!TSR (FSWrite (gFRefNum, &count, rowData))) return FALSE;
}
}
return TRUE;
}
/*****************************************************************************/
OSErr FetchData (ExportRecord *stuff,
ExportRegion *region,
void **data,
int32 *rowBytes)
{
OSErr result;
if (!WarnHostAdvanceStateAvailable (stuff->advanceState))
return userCanceledErr;
stuff->theRect = region->rect;
stuff->loPlane = region->loPlane;
stuff->hiPlane = region->hiPlane;
result = (*(stuff->advanceState)) ();
if (result != noErr)
{
*data = NULL;
*rowBytes = 0;
}
else
{
*data = stuff->data;
*rowBytes = stuff->rowBytes;
}
return result;
}
/*****************************************************************************/
void MarkExportFinished (ExportRecord *stuff)
{
PISetRect (&stuff->theRect, 0, 0, 0, 0);
}
//-------------------------------------------------------------------------------
// end Outbound.cpp
const unsigned short CHANNEL_RED = 1;
const unsigned short CHANNEL_GREEN = 2;
const unsigned short CHANNEL_BLUE = 3;
const unsigned short CHANNEL_ALFA = 17; //=ctLayerMask
void SaveImage( ReadChannelDesc *pChannelDesc, int imageIndex, ReadChannelDesc *pTransparency )
{
ReadPixelsProc myReadProc = gExportRecord->channelPortProcs->readPixelsProc;
int width;
int height;
if( pTransparency )
{
width = pTransparency->limitBounds.right - pTransparency->limitBounds.left;
height = pTransparency->limitBounds.bottom - pTransparency->limitBounds.top;
}
else
{
width = pChannelDesc->limitBounds.right - pChannelDesc->limitBounds.left;
height = pChannelDesc->limitBounds.bottom - pChannelDesc->limitBounds.top;
}
int bufferSize = width * height;
RGBQUAD *quadBytes = (RGBQUAD *)malloc( bufferSize *sizeof(RGBQUAD) );
memset( quadBytes, 0, sizeof(RGBQUAD)*bufferSize );
VRect *outVRect = new VRect();
while( pChannelDesc )
{
LPBYTE buffer = new BYTE[bufferSize];
memset( buffer, 0, sizeof(BYTE)*bufferSize );
PSScaling pssc;
if( pTransparency )
{
pssc.sourceRect = pssc.destinationRect = pTransparency->limitBounds;
}
else
{
pssc.sourceRect = pssc.destinationRect = pChannelDesc->limitBounds;
}
VRect *theRect;
if( pTransparency )
{
theRect = new VRect(pTransparency->limitBounds);
}
else
{
theRect = new VRect(pChannelDesc->limitBounds);
}
PixelMemoryDesc pixelMemoryDesc;
pixelMemoryDesc.bitOffset = 0;
pixelMemoryDesc.data = buffer;
pixelMemoryDesc.colBits = pChannelDesc->depth;
pixelMemoryDesc.depth = pChannelDesc->depth;
pixelMemoryDesc.rowBits = width * pChannelDesc->depth;
OSErr readResult = myReadProc( pChannelDesc->port, &pssc, theRect, &pixelMemoryDesc, outVRect );
if( pChannelDesc->channelType == CHANNEL_RED )
{
for( int i =0; i < bufferSize; i++ )
{
quadBytes[i].rgbRed = buffer[i];
}
}
else if( pChannelDesc->channelType == CHANNEL_GREEN )
{
for( int i =0; i < bufferSize; i++ )
{
quadBytes[i].rgbGreen = buffer[i];
}
}
else if( pChannelDesc->channelType == CHANNEL_BLUE )
{
for( int i =0; i < bufferSize; i++ )
{
quadBytes[i].rgbBlue = buffer[i];
}
}
else if( pChannelDesc->channelType == CHANNEL_ALFA )
{
for( int i =0; i < bufferSize; i++ )
{
quadBytes[i].rgbReserved = buffer[i];
}
}
pChannelDesc = pChannelDesc->next;
delete theRect;
}
CBitmap bmp;
if( bmp.CreateBitmap( width, height, 1, 32, quadBytes ) )
{
CImage img;
img.Attach( bmp );
char filename[100];
sprintf( filename, "c:\\temp\\img%d.png", imageIndex);
img.Save( filename, Gdiplus::ImageFormatPNG );
}
delete outVRect;
}
Copy link to clipboard
Copied
I have changed the Outbound sample shipped with the Adobe Photoshop CS4 SDK.
Attached is the code I have developed for test purposes. Please, have in mind that this code is a draft test and need to be rewritten following good practices in order to become a “good code”. Anyway, I hope you can have your question answered.
Best regards.
Copy link to clipboard
Copied
I have changed the Outbound sample shipped with the Adobe Photoshop CS4 SDK.
Following is the code I have developed for test purposes. Please, have in mind that this code is a draft test and need to be rewritten following good practices in order to become a “good code”. Anyway, I hope you can have your question answered.
Best regards.
// ADOBE SYSTEMS INCORPORATED
// Copyright 1993 - 2002 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this
// file in accordance with the terms of the Adobe license agreement
// accompanying it. If you have received this file from a source
// other than Adobe, then your use, modification, or distribution
// of it requires the prior written permission of Adobe.
//-------------------------------------------------------------------
//-------------------------------------------------------------------------------
//
// File:
// Outbound.cpp
//
// Copyright 1990-1992, Thomas Knoll.
// All Rights Reserved.
//
// Description:
// This file contains the functions and source
// for the Export module Outbound, a module that
// creates a file and stores raw pixel data in it.
//
// Use:
// This module shows how to export raw data to a file.
// It uses a simple "FileUtilities" library that comes
// with the SDK. You use it via File>>Export>>Outbound.
//
//-------------------------------------------------------------------------------
//-------------------------------------------------------------------------------
// Includes
//-------------------------------------------------------------------------------
// Modify the following defines if you have to target a platform prior to the ones specified below.
// Refer to MSDN for the latest info on corresponding values for different platforms.
#include <afxwin.h>
#include <stdio.h>
#include <tchar.h>
#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // some CString constructors will be explicit
#include <atlbase.h>
#include <atlstr.h>
#include <atlimage.h>
#include <stdio.h>
#define USING_MFC
#include "Outbound.h"
//-------------------------------------------------------------------------------
// Prototypes.
//-------------------------------------------------------------------------------
void InitGlobals (Ptr globalPtr); // Initialize globals.
void DoPrepare (GPtr globals);
void DoStart (GPtr globals); // Main routine.
void DoContinue (GPtr globals);
void DoFinish (GPtr globals);
void DoInitialRect (GPtr globals);
Boolean DoNextRect (GPtr globals);
void DoExportRect (GPtr globals);
void SaveImage( ReadChannelDesc *pReadChannelDesc, int imageIndex, ReadChannelDesc *pTransparency );
//-------------------------------------------------------------------------------
// Globals -- Define global variables for plug-in scope.
//-------------------------------------------------------------------------------
SPBasicSuite * sSPBasic = NULL;
//-------------------------------------------------------------------------------
//
// PluginMain / main
//
// All calls to the plug-in module come through this routine.
// It must be placed first in the resource. To achieve this,
// most development systems require this be the first routine
// in the source.
//
// The entrypoint will be "pascal void" for Macintosh,
// "void" for Windows.
//
// Inputs:
// const int16 selector Host provides selector indicating
// what command to do.
//
// ExportRecordPtr exportParamBlock Host provides pointer to parameter
// block containing pertinent data
// and callbacks from the host.
// See PIExport.h.
//
// Outputs:
// ExportRecordPtr exportParamBlock Host provides pointer to parameter
// block containing pertinent data
// and callbacks from the host.
// See PIExport.h.
//
// intptr_t *data Use this to store a handle or pointer to our
// global parameters structure, which
// is maintained by the host between
// calls to the plug-in.
//
// int16 *result Return error result or noErr. Some
// errors are handled by the host, some
// are silent, and some you must handle.
// See PIGeneral.h.
//
//-------------------------------------------------------------------------------
ExportRecordPtr gExportRecord;
DLLExport MACPASCAL void PluginMain (const int16 selector,
ExportRecordPtr exportParamBlock,
intptr_t *data,
int16 *result)
{
//---------------------------------------------------------------------------
// (1) Check for about box request.
//
// The about box is a special request; the parameter block is not filled
// out, none of the callbacks or standard data is available. Instead,
// the parameter block points to an AboutRecord, which is used
// on Windows.
//---------------------------------------------------------------------------
gExportRecord = exportParamBlock;
if (selector == exportSelectorAbout)
{
sSPBasic = ((AboutRecord*)exportParamBlock)->sSPBasic;
DoAbout((AboutRecordPtr)exportParamBlock);
}
else
{ // do the rest of the process as normal:
sSPBasic = ((ExportRecordPtr)exportParamBlock)->sSPBasic;
Ptr globalPtr = NULL; // Pointer for global structure
GPtr globals = NULL; // actual globals
//-----------------------------------------------------------------------
// (2) Allocate and initalize globals.
//
// AllocateGlobals requires the pointer to result, the pointer to the
// parameter block, a pointer to the handle procs, the size of our local
// "Globals" structure, a pointer to the long *data, a Function
// Proc (FProcP) to the InitGlobals routine. It automatically sets-up,
// initializes the globals (if necessary), results result to 0, and
// returns with a valid pointer to the locked globals handle or NULL.
//-----------------------------------------------------------------------
globalPtr = AllocateGlobals (result,
exportParamBlock,
exportParamBlock->handleProcs,
sizeof(Globals),
data,
InitGlobals);
if (globalPtr == NULL)
{ // Something bad happened if we couldn't allocate our pointer.
// Fortunately, everything's already been cleaned up,
// so all we have to do is report an error.
*result = memFullErr;
return;
}
// Get our "globals" variable assigned as a Global Pointer struct with the
// data we've returned:
globals = (GPtr)globalPtr;
//-----------------------------------------------------------------------
// (3) Dispatch selector.
//-----------------------------------------------------------------------
switch (selector)
{
case exportSelectorPrepare:
DoPrepare(globals);
break;
case exportSelectorStart:
DoStart(globals);
break;
case exportSelectorContinue:
DoContinue(globals);
break;
case exportSelectorFinish:
DoFinish(globals);
break;
}
//-----------------------------------------------------------------------
// (4) Unlock data, and exit resource.
//
// Result is automatically returned in *result, which is
// pointed to by gResult.
//-----------------------------------------------------------------------
// unlock handle pointing to parameter block and data so it can move
// if memory gets shuffled:
if ((Handle)*data != NULL)
PIUnlockHandle((Handle)*data);
} // about selector special
} // end PluginMain
//-------------------------------------------------------------------------------
//
// InitGlobals
//
// Initalize any global values here. Called only once when global
// space is reserved for the first time.
//
// Inputs:
// Ptr globalPtr Standard pointer to a global structure.
//
// Outputs:
// Initializes any global values with their defaults.
//
//-------------------------------------------------------------------------------
void InitGlobals (Ptr globalPtr)
{
// create "globals" as a our struct global pointer so that any
// macros work:
GPtr globals = (GPtr)globalPtr;
} // end InitGlobals
//-------------------------------------------------------------------------------
//
// DoPrepare
//
// Initialize parameters to default values.
//
// Inputs:
//
// Outputs:
/*****************************************************************************/
/* Prepare to export an image. If the plug-in module needs only a limited
amount of memory, it can lower the value of the 'maxData' field. */
void DoPrepare (GPtr globals)
{
}
/*****************************************************************************/
/* Requests pointer to the first part of the image to be filtered. */
void DoStart (GPtr globals)
{
/* check with the scripting system whether to pop our dialog */
gQueryForParameters = ReadScriptParams (globals);
if (!DoUI (globals))
return;
FILE *pfTemp = fopen( (LPSTR)gExportRecord->filename, "w" );
if( pfTemp )
{
fprintf( pfTemp, "imageSize.h: %d\r\n", gExportRecord->imageSize.h );
fprintf( pfTemp, "imageSize.v: %d\r\n", gExportRecord->imageSize.v );
fprintf( pfTemp, "imageSize32.h: %d\r\n", gExportRecord->imageSize32.h );
fprintf( pfTemp, "imageSize32.v: %d\r\n", gExportRecord->imageSize32.v );
fprintf( pfTemp, "depth: %d\r\n", gExportRecord->depth );
fprintf( pfTemp, "imageHRes: %d\r\n", gExportRecord->imageHRes );
fprintf( pfTemp, "imageVRes: %d\r\n", gExportRecord->imageVRes );
fprintf( pfTemp, "mode: %d\r\n", gExportRecord->imageMode );
fprintf( pfTemp, "planes: %d\r\n", gExportRecord->planes );
ReadImageDocumentDesc *pReadImageDocumentDesc = gExportRecord->documentInfo;
fprintf( pfTemp, "\r\nReadImageDocumentDesc\r\n" );
int imageIndex = 0;
while( pReadImageDocumentDesc )
{
fprintf( pfTemp, "layerCount: %d\r\n", pReadImageDocumentDesc->layerCount );
fprintf( pfTemp, "alphaChannelCount: %d\r\n", pReadImageDocumentDesc->alphaChannelCount );
fprintf( pfTemp, "bounds.bottom: %d\r\n", pReadImageDocumentDesc->bounds.bottom );
fprintf( pfTemp, "bounds.left: %d\r\n", pReadImageDocumentDesc->bounds.left );
fprintf( pfTemp, "bounds.right: %d\r\n", pReadImageDocumentDesc->bounds.right );
fprintf( pfTemp, "bounds.top: %d\r\n", pReadImageDocumentDesc->bounds.top );
fprintf( pfTemp, "depth: %d\r\n", pReadImageDocumentDesc->depth );
fprintf( pfTemp, "documentType: %d\r\n", pReadImageDocumentDesc->documentType );
fprintf( pfTemp, "imageMode: %d\r\n", pReadImageDocumentDesc->imageMode );
ReadLayerDesc *pReadLayerDesc = pReadImageDocumentDesc->layersDescriptor;
fprintf( pfTemp, "\r\nReadLayerDesc\r\n" );
while( pReadLayerDesc )
{
fprintf( pfTemp, "layer index: %d\r\n", imageIndex );
fprintf( pfTemp, "layer name: %s\r\n", pReadLayerDesc->name );
fprintf( pfTemp, "isVisible: %s\r\n", pReadLayerDesc->isVisible? "true":"false" );
fprintf( pfTemp, "isPixelBased: %s\r\n", pReadLayerDesc->isPixelBased? "true":"false" );
if( pReadLayerDesc->layerMask )
{
fprintf( pfTemp, "layerMask->bounds.bottom: %d\r\n", pReadLayerDesc->layerMask->bounds.bottom );
fprintf( pfTemp, "layerMask->bounds.left: %d\r\n", pReadLayerDesc->layerMask->bounds.left );
fprintf( pfTemp, "layerMask->bounds.right: %d\r\n", pReadLayerDesc->layerMask->bounds.right );
fprintf( pfTemp, "layerMask->bounds.top: %d\r\n", pReadLayerDesc->layerMask->bounds.top );
}
if( pReadLayerDesc->compositeChannelsList )
{
fprintf( pfTemp, "compositeChannelsList->bounds.bottom: %d\r\n", pReadLayerDesc->compositeChannelsList->bounds.bottom );
fprintf( pfTemp, "compositeChannelsList->bounds.left: %d\r\n", pReadLayerDesc->compositeChannelsList->bounds.left );
fprintf( pfTemp, "compositeChannelsList->bounds.right: %d\r\n", pReadLayerDesc->compositeChannelsList->bounds.right );
fprintf( pfTemp, "compositeChannelsList->bounds.top: %d\r\n", pReadLayerDesc->compositeChannelsList->bounds.top );
SaveImage( pReadLayerDesc->compositeChannelsList, imageIndex, pReadLayerDesc->transparency );
}
if( pReadLayerDesc->smartFilterMask )
{
fprintf( pfTemp, "smartFilterMask->bounds.bottom: %d\r\n", pReadLayerDesc->smartFilterMask->bounds.bottom );
fprintf( pfTemp, "smartFilterMask->bounds.left: %d\r\n", pReadLayerDesc->smartFilterMask->bounds.left );
fprintf( pfTemp, "smartFilterMask->bounds.right: %d\r\n", pReadLayerDesc->smartFilterMask->bounds.right );
fprintf( pfTemp, "smartFilterMask->bounds.top: %d\r\n", pReadLayerDesc->smartFilterMask->bounds.top );
}
if( pReadLayerDesc->vectorMask )
{
fprintf( pfTemp, "vectorMask->bounds.bottom: %d\r\n", pReadLayerDesc->vectorMask->bounds.bottom );
fprintf( pfTemp, "vectorMask->bounds.left: %d\r\n", pReadLayerDesc->vectorMask->bounds.left );
fprintf( pfTemp, "vectorMask->bounds.right: %d\r\n", pReadLayerDesc->vectorMask->bounds.right );
fprintf( pfTemp, "vectorMask->bounds.top: %d\r\n", pReadLayerDesc->vectorMask->bounds.top );
}
fprintf( pfTemp, "----\r\n" );
pReadLayerDesc = pReadLayerDesc->next;
imageIndex++;
}
fprintf( pfTemp, "----\r\n" );
pReadImageDocumentDesc = gExportRecord->documentInfo->next;
}
fclose( pfTemp );
}
}
/*****************************************************************************/
/* Filters the area and requests the next area. */
void DoContinue (GPtr globals)
{
/* We shouldn't get here because we did all of the work during the
start phase, but we add some code just in case. */
MarkExportFinished (gStuff);
gResult = userCanceledErr;
}
/*****************************************************************************/
/* This routine will always be called if DoStart does not return an error
(even if DoContinue returns an error or the user aborts the operation).
This allows the module to perform any needed cleanup. None is required
in this example. */
void DoFinish (GPtr globals)
{
WriteScriptParams (globals);
}
/*****************************************************************************/
#define RANGE_ITER(lower,upper,first,last,step) \
for (lower = (first); \
(upper = (((lower) + (short)(step) < (last)) ? (lower) + (short)(step) : (last))), \
lower < (last); \
lower = upper)
/*****************************************************************************/
Boolean WriteExportFile (GPtr globals)
{
/* We write out the file as an interleaved raw file. */
/* We need to figure out how many rows to write at one time. */
long chunk = gStuff->maxData / gStuff->imageSize.h / gStuff->planes;
ExportRegion region;
region.rect.left = 0;
region.rect.right = gStuff->imageSize.h;
region.loPlane = 0;
region.hiPlane = gStuff->planes - 1;
RANGE_ITER (region.rect.top, region.rect.bottom,
0, gStuff->imageSize.v, chunk)
{
int16 row;
long rowCount = gStuff->imageSize.h * (long) gStuff->planes;
void *data = 0;
int32 rowBytes = 0;
unsigned8 *rowData;
if (!TSC (TestAbort ())) return FALSE;
if (!TSR (FetchData (gStuff, ®ion, &data, &rowBytes))) return FALSE;
for (row = region.rect.top, rowData = (unsigned8 *) data;
row < region.rect.bottom;
++row, rowData += rowBytes)
{
long count = rowCount;
PIUpdateProgress (row, gStuff->imageSize.v);
if (!TSC (TestAbort ())) return FALSE;
if (!TSR (FSWrite (gFRefNum, &count, rowData))) return FALSE;
}
}
return TRUE;
}
/*****************************************************************************/
OSErr FetchData (ExportRecord *stuff,
ExportRegion *region,
void **data,
int32 *rowBytes)
{
OSErr result;
if (!WarnHostAdvanceStateAvailable (stuff->advanceState))
return userCanceledErr;
stuff->theRect = region->rect;
stuff->loPlane = region->loPlane;
stuff->hiPlane = region->hiPlane;
result = (*(stuff->advanceState)) ();
if (result != noErr)
{
*data = NULL;
*rowBytes = 0;
}
else
{
*data = stuff->data;
*rowBytes = stuff->rowBytes;
}
return result;
}
/*****************************************************************************/
void MarkExportFinished (ExportRecord *stuff)
{
PISetRect (&stuff->theRect, 0, 0, 0, 0);
}
//-------------------------------------------------------------------------------
// end Outbound.cpp
const unsigned short CHANNEL_RED = 1;
const unsigned short CHANNEL_GREEN = 2;
const unsigned short CHANNEL_BLUE = 3;
const unsigned short CHANNEL_ALFA = 17; //=ctLayerMask
void SaveImage( ReadChannelDesc *pChannelDesc, int imageIndex, ReadChannelDesc *pTransparency )
{
ReadPixelsProc myReadProc = gExportRecord->channelPortProcs->readPixelsProc;
int width;
int height;
if( pTransparency )
{
width = pTransparency->limitBounds.right - pTransparency->limitBounds.left;
height = pTransparency->limitBounds.bottom - pTransparency->limitBounds.top;
}
else
{
width = pChannelDesc->limitBounds.right - pChannelDesc->limitBounds.left;
height = pChannelDesc->limitBounds.bottom - pChannelDesc->limitBounds.top;
}
int bufferSize = width * height;
RGBQUAD *quadBytes = (RGBQUAD *)malloc( bufferSize *sizeof(RGBQUAD) );
memset( quadBytes, 0, sizeof(RGBQUAD)*bufferSize );
VRect *outVRect = new VRect();
while( pChannelDesc )
{
LPBYTE buffer = new BYTE[bufferSize];
memset( buffer, 0, sizeof(BYTE)*bufferSize );
PSScaling pssc;
if( pTransparency )
{
pssc.sourceRect = pssc.destinationRect = pTransparency->limitBounds;
}
else
{
pssc.sourceRect = pssc.destinationRect = pChannelDesc->limitBounds;
}
VRect *theRect;
if( pTransparency )
{
theRect = new VRect(pTransparency->limitBounds);
}
else
{
theRect = new VRect(pChannelDesc->limitBounds);
}
PixelMemoryDesc pixelMemoryDesc;
pixelMemoryDesc.bitOffset = 0;
pixelMemoryDesc.data = buffer;
pixelMemoryDesc.colBits = pChannelDesc->depth;
pixelMemoryDesc.depth = pChannelDesc->depth;
pixelMemoryDesc.rowBits = width * pChannelDesc->depth;
OSErr readResult = myReadProc( pChannelDesc->port, &pssc, theRect, &pixelMemoryDesc, outVRect );
if( pChannelDesc->channelType == CHANNEL_RED )
{
for( int i =0; i < bufferSize; i++ )
{
quadBytes[i].rgbRed = buffer[i];
}
}
else if( pChannelDesc->channelType == CHANNEL_GREEN )
{
for( int i =0; i < bufferSize; i++ )
{
quadBytes[i].rgbGreen = buffer[i];
}
}
else if( pChannelDesc->channelType == CHANNEL_BLUE )
{
for( int i =0; i < bufferSize; i++ )
{
quadBytes[i].rgbBlue = buffer[i];
}
}
else if( pChannelDesc->channelType == CHANNEL_ALFA )
{
for( int i =0; i < bufferSize; i++ )
{
quadBytes[i].rgbReserved = buffer[i];
}
}
pChannelDesc = pChannelDesc->next;
delete theRect;
}
CBitmap bmp;
if( bmp.CreateBitmap( width, height, 1, 32, quadBytes ) )
{
CImage img;
img.Attach( bmp );
char filename[100];
sprintf( filename, "c:\\temp\\img%d.png", imageIndex);
img.Save( filename, Gdiplus::ImageFormatPNG );
}
delete outVRect;
}
Copy link to clipboard
Copied
This is great, thanks for the help.
Copy link to clipboard
Copied
I need to do something similar to this in an export plugin. But when I run the outbound plugin with your modifications I get no output. The Channel and Transparency limitBounds are such that the width and height are always zero. It does appear to have values for right and left, but the top and bottom values are always zero.
I just created a new layer within Photoshop Elements 6 and drew with a brush to attempt to write out the seperate PNG files.
Thanks
Donevan
Copy link to clipboard
Copied
That code was written to handle RGB images.
Copy link to clipboard
Copied
I have tested this code with RGB, but i'm able to export only two layers for rest the width and height size comes to zero.
can you explain the reason.
Copy link to clipboard
Copied
I have observe the same results PS 5.1.
But PS latest versions gives the width and height properly.
Guess: The issue you have mentioned, might be a core API issue in a older version of PS.
Copy link to clipboard
Copied
If it helps, you can use my standalone tool psdparse to export PNG files for every layer in the PSD. It also exports XML of metadata including the internal descriptor data of type tool objects (fwiw).
http://telegraphics.com.au/sw/#psdparse