www.pudn.com > PtOpenGuiSourceCode.zip > file.c
/* Panorama_Tools - Generate, Edit and Convert Panoramic Images
Copyright (C) 1998,1999 - Helmut Dersch der@fh-furtwangen.de
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
/*------------------------------------------------------------*/
// Functions to read and write Photoshop, and write tiffs
#include "filter.h"
#define WRITEUCHAR( theChar ) ch = theChar; count = 1; mywrite(fnum,count,&ch);
#define WRITESHORT( theShort ) svar = theShort; d = data; SHORTNUMBER( svar, d ); \
count = 2; mywrite (fnum,count,data);
#define WRITELONG( theLong ) var = theLong; d = data; LONGNUMBER( var, d ); \
count = 4; mywrite (fnum,count,data);
#define READLONG( theLong ) count = 4; myread(src,count,data); \
d = data; NUMBERLONG( var, d ); \
theLong = var;
#define READSHORT( theShort ) count = 2; myread(src,count,data); \
d = data; NUMBERSHORT( svar, d ); \
theShort = svar;
#define READUCHAR( theChar ) count = 1; myread(src,count,&ch); theChar = ch;
// local functions
static int writeImageDataPlanar ( Image *im, file_spec fnum );
static int readImageDataPlanar (Image *im, file_spec fnum ) ;
static int ParsePSDHeader ( char *header, Image *im );
static int writeChannelData ( Image *im, file_spec fnum, int channel, PTRect *r );
static int writeLayerAndMask ( Image *im, file_spec fnum );
static void getImageRectangle ( Image *im, PTRect *r );
static int fileCopy ( file_spec src, file_spec dest, int numBytes, unsigned char *buf);
static void orAlpha ( unsigned char* alpha, unsigned char *buf, Image *im, PTRect *r );
static void writeWhiteBackground ( int width, int height, file_spec fnum );
static int addLayer ( Image *im, file_spec src, file_spec fnum , stBuf *sB);
#define PSDHLENGTH 26
// Save image as single layer PSD file
// Image is background
int writePSD(Image *im, fullPath *sfile )
{
file_spec fnum;
char data[12], *d;
short svar;
long count;
unsigned long var;
unsigned char ch;
int channels, BitsPerChannel;
GetChannels( im, channels );
GetBitsPerChannel( im, BitsPerChannel );
if( myopen( sfile, write_bin, fnum ) )
{
PrintError("Error Writing Image File");
return -1;
}
// Write PSD Header
WRITELONG( '8BPS' );
WRITESHORT( 1 );
WRITELONG( 0 ); WRITESHORT( 0 );
WRITEUCHAR( 0 );
WRITEUCHAR( channels );// No of channels
WRITELONG( im->height );
WRITELONG( im->width );
WRITESHORT( BitsPerChannel ); // BitsPerChannel
switch( im->dataformat )
{
case _Lab: WRITESHORT( 9 );
break;
case _RGB: WRITESHORT( 3 );
break;
default: WRITESHORT( 3 );
}
WRITELONG( 0 ); // Color Mode
WRITELONG( 0 ); // Image Resources
WRITELONG( 0 ); // Layer & Mask
// Image data
writeImageDataPlanar( im, fnum );
myclose (fnum );
return 0;
}
// Save image as single layer PSD file
// Image is layer in front of white background
int writePSDwithLayer(Image *im, fullPath *sfile )
{
file_spec fnum;
char data[12], *d;
long count;
unsigned long var;
unsigned char ch;
short svar;
int BitsPerChannel;
TwoToOneByte( im ); // Multilayer image format doesn't support 16bit channels
GetBitsPerChannel( im, BitsPerChannel );
if( myopen( sfile, write_bin, fnum ) )
{
PrintError("Error Writing Image File");
return -1;
}
// Write PSD Header
WRITELONG( '8BPS' );
WRITESHORT( 1 );
WRITELONG( 0 ); WRITESHORT( 0 );
WRITEUCHAR( 0 );
WRITEUCHAR( 3 ); // No of channels; Background always white, 3 channels
WRITELONG( im->height );
WRITELONG( im->width );
WRITESHORT( BitsPerChannel ); // BitsPerChannel
switch( im->dataformat )
{
case _Lab: WRITESHORT( 9 );
break;
case _RGB: WRITESHORT( 3 );
break;
default: WRITESHORT( 3 );
}
WRITELONG( 0 ); // Color Mode
WRITELONG( 0 ); // Image Resources
writeLayerAndMask( im, fnum );
writeWhiteBackground( im->width * BitsPerChannel/8, im->height, fnum );
myclose (fnum );
return 0;
}
// Add image as additional layer into PSD-file
int addLayerToFile( Image *im, fullPath* sfile, fullPath* dfile, stBuf *sB)
{
file_spec src;
file_spec fnum;
Image sim; // background image
char header[128], *h;
long count, len, i, srcCount = 0, result = 0;
unsigned char **buf;
unsigned long var;
char data[12], *d;
int BitsPerChannel;
TwoToOneByte( im ); // Multilayer image format doesn't support 16bit channels
GetBitsPerChannel( im, BitsPerChannel );
if( myopen( sfile,read_bin, src ) )
{
PrintError("Error Opening Image File");
return -1;
}
// Read psd header
h = header;
count = PSDHLENGTH;
myread( src,count,h); srcCount += count;
if( count != PSDHLENGTH )
{
PrintError("Error Reading Image File");
myclose( src );
return -1;
}
if( ParsePSDHeader( header, &sim ) != 0 )
{
PrintError("Wrong File Format");
myclose( src );
return -1;
}
// Check if image can be inserted
if( sim.width != im->width || sim.height != im->height )
{
PrintError("Can't add layer: Images have different size");
return -1;
}
// Read (and ignore) Color mode data
READLONG( len ); srcCount += (4 + len);
count = 1;
for( i=0; ibitsPerPixel / 8;
// Write Compression info
WRITESHORT( 0 ); // Raw data
// Buffer to hold data in one channel
count = im->width * im->height * BitsPerChannel / 8;
channel = (unsigned char**)mymalloc( count );
if( channel == NULL )
{
PrintError("Not Enough Memory");
return -1;
}
if( BitsPerChannel == 8 )
{
for( color = 0; color<3; color++)
{
ch = *channel; idata = &(*im->data)[color + channels - 3];
for(y=0; yheight;y++)
{
idy = y * im->bytesPerLine;
for(x=0; xwidth;x++)
{
*ch++ = idata [ idy + x * bpp ];
}
}
mywrite( fnum, count, *channel );
}
}
else // 16
{
for( color = 0; color<3; color++)
{
ch = *channel; idata = &(*im->data)[2*(color + channels - 3)];
for(y=0; yheight;y++)
{
idy = y * im->bytesPerLine;
for(x=0; xwidth;x++)
{
*ch++ = idata [ idy + x * bpp ];
*ch++ = idata [ idy + x * bpp + 1];
}
}
mywrite( fnum, count, *channel );
}
}
if( im->bitsPerPixel == 32 )
{
// Write 1byte alpha channel
ch = *channel; idata = &(*im->data)[0];
for(y=0; yheight;y++)
{
idy = y * im->bytesPerLine;
for(x=0; xwidth;x++)
{
*ch++ = idata [ idy + x * bpp ];
}
}
mywrite( fnum, count, *channel );
}
else if( im->bitsPerPixel == 64 )
{
// Write 2byte alpha channel
ch = *channel; idata = &(*im->data)[0];
for(y=0; yheight;y++)
{
idy = y * im->bytesPerLine;
for(x=0; xwidth;x++)
{
*ch++ = idata [ idy + x * bpp ];
*ch++ = idata [ idy + x * bpp + 1];
}
}
mywrite( fnum, count, *channel );
}
myfree( (void**)channel );
return 0;
}
// Write white background, RLE-compressed
static void writeWhiteBackground( int width, int height, file_spec fnum )
{
short svar;
long count, w8, w;
char data[12], *d, scanline[256];
int numChannels = 3, i, bytecount, dim = height*numChannels;
WRITESHORT( 1 ); // RLE compressed
w8 = width;
d = scanline;
// Set up scanline
for(w=w8; w>128; w-=128)
{
*d++ = -127; *d++ = (char)255;
}
switch(w)
{
case 0: break;
case 1: *d++=0; *d++ = (char)255;
break;
default: *d++=1-w; *d++ = (char)255;
break;
}
bytecount = d - scanline;
// Scanline counts (rows*channels)
for(i=0; i < dim; i++)
{
WRITESHORT( bytecount );
}
// RLE compressed data
count = bytecount;
for(i=0; i < dim; i++)
{
mywrite( fnum, count, scanline );
}
}
// image is allocated, but not image data
// mode = 0: only load image struct
// mode = 1: also allocate and load data
int readPSD(Image *im, fullPath *sfile, int mode)
{
file_spec src;
char header[128], *h;
long count, len, i;
unsigned long var;
char data[12], *d;
if( myopen( sfile, read_bin, src ) )
{
PrintError("Error Opening Image File");
return -1;
}
// Read psd header
h = header;
count = PSDHLENGTH;
myread( src,count,h);
if( count != PSDHLENGTH )
{
PrintError("Error Reading Image File");
myclose( src );
return -1;
}
if( ParsePSDHeader( header, im ) != 0 )
{
PrintError("Wrong File Format");
myclose( src );
return -1;
}
if( mode == 0 )
{
myclose( src );
return 0;
}
im->data = (unsigned char**) mymalloc( im->dataSize );
if( im->data == NULL )
{
PrintError("Not enough memory to read image");
myclose( src );
return -1;
}
// Read (and ingnore) Color mode data
READLONG( len );
count = 1;
for( i=0; i 4 ) channels = 4;
NUMBERLONG( im->height, h );
NUMBERLONG( im->width, h );
NUMBERSHORT( s, h );
if( s!= 8 && s!= 16)
{
PrintError( "Depth must be 8 or 16 Bits per Channel" );
return -1;
}
im->bitsPerPixel = s * channels;
NUMBERSHORT( s, h );
switch( s )
{
case 3: im->dataformat = _RGB; break;
case 9: im->dataformat = _Lab; break;
default: PrintError( "Color mode must be RGB or Lab" );return -1;
}
im->bytesPerLine = im->width * im->bitsPerPixel/8;
im->dataSize = im->height * im->bytesPerLine;
return 0;
}
static int readImageDataPlanar(Image *im, file_spec src )
{
register int x,y,idy, bpp;
unsigned char **channel = NULL;
register unsigned char *h, *idata;
int result = 0, i, chnum,BitsPerChannel, channels;
long count;
short svar;
char data[12], *d ;
GetBitsPerChannel( im, BitsPerChannel );
GetChannels( im, channels );
bpp = im->bitsPerPixel / 8;
// Read Compression info
READSHORT( svar );
if( svar!= 0 )
{
PrintError("Image data must not be compressed");
return -1;
}
// Allocate memory for one channel
count = im->width * im->height * BitsPerChannel/8 ;
channel = (unsigned char**)mymalloc( count );
if( channel == NULL )
{
PrintError("Not Enough Memory");
return -1;
}
for(i = 0; i < channels; i++) // Read each channel
{
chnum = i + channels - 3;
if(chnum == 4) chnum = 0; // Order: r g b (alpha)
myread(src,count,*channel);
if( count != im->width * im->height * BitsPerChannel/8)
{
PrintError("Error Reading Image Data");
result = -1;
goto readImageDataPlanar_exit;
}
h = *channel;
if( BitsPerChannel == 8 )
{
idata = &(*im->data)[chnum];
for(y=0; yheight;y++)
{
idy = y * im->bytesPerLine;
for(x=0; xwidth;x++)
{
idata [ idy + x * bpp ] = *h++;
}
}
}
else // 16
{
idata = &(*im->data)[chnum*2];
for(y=0; yheight;y++)
{
idy = y * im->bytesPerLine;
for(x=0; xwidth;x++)
{
idata [ idy + x * bpp ] = *h++;
idata [ idy + x * bpp + 1] = *h++;
}
}
}
}
readImageDataPlanar_exit:
if( channel != NULL )
myfree( (void**)channel );
return result;
}
// Write image as separate first layer
static int writeLayerAndMask( Image *im, file_spec fnum )
{
unsigned long var;
PTRect theRect;
int channelLength;
long count;
short svar;
char data[12], *d;
unsigned char ch;
int i, chnum, lenLayerInfo, numLayers,BitsPerChannel,channels;
int oddSized = 0;
GetBitsPerChannel( im, BitsPerChannel );
GetChannels( im, channels );
getImageRectangle( im, &theRect );
numLayers = 1;
channelLength = (theRect.right-theRect.left) * (theRect.bottom-theRect.top) * BitsPerChannel/8 + 2;
lenLayerInfo = 2 + numLayers * (4*4 + 2 + channels * 6 + 4 + 4 + 4 * 1 + 4 + 12 + channels * channelLength);
WRITELONG( lenLayerInfo + 4 + 4 );
// Layer info. See table 2Ð13.
if( lenLayerInfo/2 != (lenLayerInfo+1)/2 ) // odd
{
lenLayerInfo += 1; oddSized = 1;
}
// Length of the layers info section, rounded up to a multiple of 2.
WRITELONG( lenLayerInfo );
// 2 bytes Count Number of layers. If <0, then number of layers is absolute value,
// and the first alpha channel contains the transparency data for
// the merged result.
WRITESHORT( numLayers );
// ********** Layer Structure ********************************* //
WRITELONG( theRect.top ); // Layer top
WRITELONG( theRect.left ); // Layer left
WRITELONG( theRect.bottom ) ; // Layer bottom
WRITELONG( theRect.right ) ; // Layer right
WRITESHORT( channels ); // The number of channels in the layer.
// ********** Channel information ***************************** //
WRITESHORT( 0 ); // red
WRITELONG( channelLength ) // Length of following channel data.
WRITESHORT( 1 ); // green
WRITELONG( channelLength ) // Length of following channel data.
WRITESHORT( 2 ); // blue
WRITELONG( channelLength ) // Length of following channel data.
if( channels == 4 ) // alpha channel
{
WRITESHORT( -1 ); //-2 ); // transparency mask
WRITELONG( channelLength ) // Length of following channel data.
}
// ********** End Channel information ***************************** //
WRITELONG( '8BIM'); // Blend mode signature Always 8BIM.
WRITELONG( 'norm'); // Blend mode key
WRITEUCHAR(255); // 1 byte Opacity 0 = transparent ... 255 = opaque
WRITEUCHAR( 0 ); // 1 byte Clipping 0 = base, 1 = nonÐbase
WRITEUCHAR( 1 ); // 1 byte Flags bit 0 = transparency protected bit 1 = visible
WRITEUCHAR( 0 ); // 1 byte (filler) (zero)
WRITELONG( 12); // Extra data size Length of the extra data field.
WRITELONG( 0 ); // Layer Mask data
WRITELONG( 0 ); // Layer blending ranges
WRITEUCHAR( 3 ); WRITEUCHAR( 'L' );WRITEUCHAR( '0' );WRITEUCHAR( '1' );// Layer name
// ************* End Layer Structure ******************************* //
// ************* Write Channel Image data ************************** //
for(i = 0; i < channels; i++)
{
chnum = i + channels - 3;
if(chnum == 4) chnum = 0; // Order: r g b (alpha)
if( writeChannelData( im, fnum, chnum, &theRect ) )
return -1;
}
if( oddSized ) // pad byte
{
WRITEUCHAR( 0 );
}
// ************* End Write Channel Image data ************************** //
// ************* Global layer mask info ******************************** //
WRITELONG( 0 ); // Length of global layer mask info section.
// WRITESHORT( 0 ); // 2 bytes Overlay color space
// WRITESHORT( 0 ); // 4 * 2 byte color components
// WRITESHORT( 0 );
// WRITESHORT( 0 );
// WRITESHORT( 0 );
// WRITESHORT( 0 ); // 2 bytes Opacity 0 = transparent, 100 = opaque.
// WRITEUCHAR( 128 ); // 1 byte Kind 0=Color selectedÑi.e. inverted;
// 1=Color protected;128=use value stored per layer.
// This value is preferred. The others are for back-ward
// compatibility with beta versions.
// WRITEUCHAR( 0 );
return 0;
}
static int writeChannelData( Image *im, file_spec fnum, int channel, PTRect *theRect )
{
register int x,y,idy, bpp,BitsPerChannel,channels;
unsigned char **ch;
register unsigned char *c, *idata;
long count;
short svar;
char data[12], *d;
GetBitsPerChannel( im, BitsPerChannel );
GetChannels( im, channels );
// Write Compression info
WRITESHORT( 0 ); // Raw data
bpp = im->bitsPerPixel/8;
count = (theRect->right - theRect->left) * (theRect->bottom - theRect->top) * BitsPerChannel/8;
ch = (unsigned char**)mymalloc( count );
if( ch == NULL )
{
PrintError("Not Enough Memory");
return -1;
}
c = *ch; idata = &((*(im->data))[channel*BitsPerChannel/8]);
if(BitsPerChannel == 8)
{
for(y=theRect->top; ybottom;y++)
{
idy = y * im->bytesPerLine;
for(x=theRect->left; xright;x++)
{
*c++ = idata [ idy + x * bpp ];
}
}
}
else // 16
{
for(y=theRect->top; ybottom;y++)
{
idy = y * im->bytesPerLine;
for(x=theRect->left; xright;x++)
{
*c++ = idata [ idy + x * bpp ];
*c++ = idata [ idy + x * bpp + 1 ];
}
}
}
mywrite( fnum, count, *ch );
myfree( (void**)ch );
return 0;
}
// Return the smallest rectangle enclosing the image;
// Use alpha channel and rgb data
static void getImageRectangle( Image *im, PTRect *theRect )
{
register unsigned char *alpha, *data;
register int cx,x,y,cy,bpp,channels;
int BitsPerChannel;
GetChannels( im, channels );
GetBitsPerChannel( im, BitsPerChannel );
bpp = im->bitsPerPixel/8;
theRect->top = 0;
theRect->left = 0;
theRect->bottom = im->height;
theRect->right = im->width;
if( channels == 4 ){ // alpha channel present
if( BitsPerChannel == 8 ){
for(y=0; yheight; y++){
for(x=0, alpha = *(im->data) + y * im->bytesPerLine;
xwidth;
x++, alpha += 4){
if( * (int*) alpha ){
theRect->top = y;
goto _get_bottom;
}
}
}
}else{ // 16
for(y=0; yheight; y++){
for(x=0, alpha = *(im->data) + y * im->bytesPerLine;
xwidth;
x++, alpha += 8){
if( * (int*) alpha || * (int*) (alpha+4) ){
theRect->top = y;
goto _get_bottom;
}
}
}
}
_get_bottom:
if( BitsPerChannel == 8 ){
for(y=im->height-1; y>=0; y--){
for(x=0, alpha = *(im->data) + y * im->bytesPerLine;
xwidth;
x++, alpha += 4){
if( * (int*) alpha ){
theRect->bottom = y+1;
goto _get_left;
}
}
}
}else{ // 16
for(y=im->height-1; y>=0; y--){
for(x=0, alpha = *(im->data) + y * im->bytesPerLine;
xwidth;
x++, alpha += 8){
if( * (int*) alpha || * (int*) (alpha+4) ){
theRect->bottom = y+1;
goto _get_left;
}
}
}
}
_get_left:
if( BitsPerChannel == 8 ){
for(x=0; xwidth; x++){
for(y=0, alpha = *(im->data) + x*bpp;
yheight;
y++, alpha += im->bytesPerLine){
if( * (int*) alpha ){
theRect->left = x;
goto _get_right;
}
}
}
}else{ // 16
for(x=0; xwidth; x++){
for(y=0, alpha = *(im->data) + x*bpp;
yheight;
y++, alpha += im->bytesPerLine){
if( * (int*) alpha || * (int*) (alpha+4) ){
theRect->left = x;
goto _get_right;
}
}
}
}
_get_right:
if( BitsPerChannel == 8 ){
for(x=im->width-1; x>=0; x--){
for(y=0, alpha = *(im->data) + x*bpp;
yheight;
y++, alpha += im->bytesPerLine){
if( * (int*) alpha ){
theRect->right = x + 1;
goto _get_exit;
}
}
}
}else{ // 16
for(x=im->width-1; x>=0; x--){
for(y=0, alpha = *(im->data) + x*bpp;
yheight;
y++, alpha += im->bytesPerLine){
if( * (int*) alpha || * (int*) (alpha+4) ){
theRect->right = x + 1;
goto _get_exit;
}
}
}
}
}
else // no alpha channel: Use non black rectangle
{
alpha = *(im->data);
if( BitsPerChannel == 8 )
{
for(y=0; yheight; y++)
{
cy = y * im->bytesPerLine;
for(x=0; xwidth; x++)
{
data = alpha + cy + bpp*x;
if( *data || *(data+1) || *(data+2) )
{
theRect->top = y;
goto _get_bottom_noalpha;
}
}
}
}
else // 16
{
for(y=0; yheight; y++)
{
cy = y * im->bytesPerLine;
for(x=0; xwidth; x++)
{
data = alpha + cy + bpp*x;
if( *((USHORT*)data) || *(((USHORT*)data)+1) || *(((USHORT*)data)+2) )
{
theRect->top = y;
goto _get_bottom_noalpha;
}
}
}
}
_get_bottom_noalpha:
if( BitsPerChannel == 8 )
{
for(y=im->height-1; y>=0; y--)
{
cy = y * im->bytesPerLine;
for(x=0; xwidth; x++)
{
data = alpha + cy + bpp*x;
if( *data || *(data+1) || *(data+2) )
{
theRect->bottom = y+1;
goto _get_left_noalpha;
}
}
}
}
else // 16
{
for(y=im->height-1; y>=0; y--)
{
cy = y * im->bytesPerLine;
for(x=0; xwidth; x++)
{
data = alpha + cy + bpp*x;
if( *((USHORT*)data) || *(((USHORT*)data)+1) || *(((USHORT*)data)+2) )
{
theRect->bottom = y+1;
goto _get_left_noalpha;
}
}
}
}
_get_left_noalpha:
if( BitsPerChannel == 8 )
{
for(x=0; xwidth; x++)
{
cx = bpp * x;
for(y=0; yheight; y++)
{
data = alpha + y * im->bytesPerLine + cx;
if( *data || *(data+1) || *(data+2) )
{
theRect->left = x;
goto _get_right_noalpha;
}
}
}
}
else // 16
{
for(x=0; xwidth; x++)
{
cx = bpp * x;
for(y=0; yheight; y++)
{
data = alpha + y * im->bytesPerLine + cx;
if( *((USHORT*)data) || *(((USHORT*)data)+1) || *(((USHORT*)data)+2) )
{
theRect->left = x;
goto _get_right_noalpha;
}
}
}
}
_get_right_noalpha:
if( BitsPerChannel == 8 )
{
for(x=im->width-1; x>=0; x--)
{
cx = bpp * x;
for(y=0; yheight; y++)
{
data = alpha + y * im->bytesPerLine + cx;
if( *data || *(data+1) || *(data+2) )
{
theRect->right = x + 1;
goto _get_exit;
}
}
}
}
else // 16
{
for(x=im->width-1; x>=0; x--)
{
cx = bpp * x;
for(y=0; yheight; y++)
{
data = alpha + y * im->bytesPerLine + cx;
if( *((USHORT*)data) || *(((USHORT*)data)+1) || *(((USHORT*)data)+2) )
{
theRect->right = x + 1;
goto _get_exit;
}
}
}
}
}
_get_exit: ;
}
// Image is added to layer structure of file.
// There must be one valid layer structure, and the
// filepointer is at the beginning of it in both src and dest
static int addLayer( Image *im, file_spec src, file_spec fnum, stBuf *sB )
{
unsigned long var;
PTRect theRect, *nRect = NULL;
int channelLength, bpp, oddSized = 0;
int result = 0;
long count;
short svar;
char data[12], *d;
unsigned char ch;
int i, k, lenLayerInfo, numLayers,channels,BitsPerChannel;
int *chlength = NULL, *nchannel = NULL,
*cnames = NULL, *nodd = NULL;
unsigned char **alpha = NULL, **buf = NULL;
GetChannels( im, channels );
GetBitsPerChannel( im, BitsPerChannel );
bpp = im->bitsPerPixel/8;
if( channels == 4 ) // Alpha channel present
{
if( sB->seam == _middle ) // we have to stitch
alpha = (unsigned char**) mymalloc( im->width * im->height * BitsPerChannel/8);
}
if( alpha != NULL )
{
memset( *alpha, 0 , im->width * im->height * BitsPerChannel/8);
}
getImageRectangle( im, &theRect );
channelLength = (theRect.right-theRect.left) * (theRect.bottom-theRect.top) + 2;
// Read layerinfo up to channeldata
READLONG( var ); // Length of the miscellaneous information section (ignored)
READLONG( var ); // Length of the layers info section, rounded up to a multiple of 2(ignored)
READSHORT( numLayers ); // Number of layers
chlength = (int*) malloc( numLayers * sizeof( int ));
nchannel = (int*) malloc( numLayers * sizeof( int ));
cnames = (int*) malloc( numLayers * sizeof( int ));
nodd = (int*) malloc( numLayers * sizeof( int ));
nRect = (PTRect*) malloc( numLayers * sizeof( PTRect ));
if( chlength == NULL || nchannel== NULL || nRect == NULL ||
cnames == NULL || nodd == NULL)
{
PrintError("Not enough memory 1");
result = -1;
goto _addLayer_exit;
}
lenLayerInfo = 2;
for(i=0; i 3 ) // 1st alpha channel
{
READSHORT( svar ); // transparency mask
READLONG( chlength[i]); // Length of following channel data.
}
if( nchannel[i] > 4 ) // 2nd alpha channel
{
READSHORT( svar ); // transparency mask
READLONG( chlength[i]); // Length of following channel data.
}
// ********** End Channel information ***************************** //
READLONG( var ); // Blend mode signature Always 8BIM.
READLONG( var ); // Blend mode key
READLONG( var ); // Four flag bytes
READLONG( var ); // Extra data size Length of the extra data field. This is the
READLONG( var ); // Layer Mask data
READLONG( var ); // Layer blending ranges
READLONG( cnames[i] ); // Layer name
var = 4*4 + 2 + nchannel[i] * 6 + 4 + 4 + 4 * 1 + 4 + 12 + nchannel[i] * chlength[i]; // length
if( var/2 != (var+1)/2 ) // odd
{
nodd[i] = 1; var++;
//READUCHAR( ch );
}
else
{
nodd[i] = 0;
}
lenLayerInfo += var;
}
// length of new channel
if(0)//alpha != NULL)
var = 4*4 + 2 + (channels+1) * 6 + 4 + 4 + 4 * 1 + 4 + 12 + (channels+1) * channelLength;
else
var = 4*4 + 2 + channels * 6 + 4 + 4 + 4 * 1 + 4 + 12 + channels * channelLength;
if( var/2 != (var+1)/2 ) // odd
{
oddSized = 1; var++;
// PrintError("odd");
}
lenLayerInfo += var;
//printf("Writing Length of Layerinfo: %d\n", lenLayerInfo);
if( lenLayerInfo/2 != (lenLayerInfo+1)/2 ) // odd, should never happen
{
//PrintError("odd");
lenLayerInfo += 1;
}
WRITELONG( lenLayerInfo + 4 + 4 );
// Length of the layers info section, rounded up to a multiple of 2.
WRITELONG( lenLayerInfo ); ;
//printf("Corrected to: %d\n", lenLayerInfo);
// 2 bytes Count Number of layers. If <0, then number of layers is absolute value,
// and the first alpha channel contains the transparency data for
// the merged result.
WRITESHORT( numLayers + 1);
// Write previous layers, read channel data
for(i=0; i 3 ) // alpha channel
{
WRITESHORT( -2); // transparency mask
WRITELONG( chlength[i] ); // Length of following channel data.
}
// ********** End Channel information ***************************** //
WRITELONG( '8BIM'); // Blend mode signature Always 8BIM.
WRITELONG( 'norm'); // Blend mode key
WRITEUCHAR(255); // 1 byte Opacity 0 = transparent ... 255 = opaque
WRITEUCHAR( 0 ); // 1 byte Clipping 0 = base, 1 = nonÐbase
WRITEUCHAR( 1 ); // 1 byte Flags bit 0 = transparency protected bit 1 = visible
WRITEUCHAR( 0 ); // 1 byte (filler) (zero)
WRITELONG( 12); // Extra data size Length of the extra data field. This is the
WRITELONG( 0 ); // Layer Mask data
WRITELONG( 0 ); // Layer blending ranges
WRITELONG( cnames[i] ); // Layer name
//printf("Writing layer %d: top %d, left %d, bottom %d, right %d, size %d\n", i,
//nRect[i].top, nRect[i].left, nRect[i].bottom, nRect[i].right, chlength[i]);
}
// ************** The New Layer ************************************ //
// ********** Layer Structure ********************************* //
//printf("Adding layer : top %d, left %d, bottom %d, right %d, size %d\n",
//theRect.top, theRect.left, theRect.bottom, theRect.right, channelLength);
WRITELONG( theRect.top ); // Layer top
WRITELONG( theRect.left ); // Layer left
WRITELONG( theRect.bottom ) ; // Layer bottom
WRITELONG( theRect.right ) ; // Layer right
if( alpha!= NULL )
{
WRITESHORT( channels);//+1 ); // The number of channels in the layer.
}
else
{
WRITESHORT( channels ); // The number of channels in the layer.
}
// ********** Channel information ***************************** //
WRITESHORT( 0 ); // red
WRITELONG( channelLength ) // Length of following channel data.
WRITESHORT( 1 ); // green
WRITELONG( channelLength ) // Length of following channel data.
WRITESHORT( 2 ); // blue
WRITELONG( channelLength ) // Length of following channel data.
if( bpp == 4 ) // alpha channel
{
WRITESHORT( -2 ); //2 ); // transparency mask
WRITELONG( channelLength ) // Length of following channel data.
}
// ********** End Channel information ***************************** //
WRITELONG( '8BIM'); // Blend mode signature Always 8BIM.
WRITELONG( 'norm'); // Blend mode key
WRITEUCHAR(255); // 1 byte Opacity 0 = transparent ... 255 = opaque
WRITEUCHAR( 0 ); // 1 byte Clipping 0 = base, 1 = nonÐbase
WRITEUCHAR( 1 ); // 1 byte Flags bit 0 = transparency protected bit 1 = visible
WRITEUCHAR( 0 ); // 1 byte (filler) (zero)
WRITELONG( 12); // Extra data size Length of the extra data field. This is the
WRITELONG( 0 ); // Layer Mask data
WRITELONG( 0 ); // Layer blending ranges
sprintf(&(data[1]), "L%02d", numLayers+1 ); data[0] = 3;
count = 4;
mywrite( fnum, count, data ); // Layer Name
// ************* End Layer Structure ******************************* //
// ************* Write Channel Image data ************************** //
for( i=0; i< numLayers; i++)
{
buf = (unsigned char**) mymalloc( chlength[i] );
if( buf == NULL )
{
PrintError("Not enough memory, %d bytes needed", (int)chlength[i]);
result = -1;
goto _addLayer_exit;
}
for( k=0; k< nchannel[i]; k++ )
{
fileCopy( src, fnum, chlength[i], *buf );
//printf("Compression Layer %d Channel %d: %d\n", i,k,(int)*((short*)*buf));
if( k == 3) // found an alpha channel
{
if( alpha!= NULL )
{
orAlpha( *alpha, &((*buf)[2]), im, &(nRect[i]) );
}
}
}
myfree( (void**)buf );
if( nodd[i] ) // pad byte
{
READUCHAR( ch );
WRITEUCHAR( 0 );
}
}
// Write color channels
for( i=0; i<3; i++)
{
if( writeChannelData( im, fnum, i + channels - 3, &theRect ) )
{
result = -1;
goto _addLayer_exit;
}
}
if( channels > 3 ) // Alpha channel present
{
#if 0
if( writeChannelData( im, fnum, 0, &theRect ) )
{
result = -1;
goto _addLayer_exit;
}
#endif
if( sB->seam == _middle && alpha != NULL ) // Create stitching mask
{
mergeAlpha( im, *alpha, sB->feather, &theRect );
#if 0
{
static nim = 0;
fullPath fp;
makePathToHost( &fp );
sprintf( (char*)fp.name, "Test.%d", nim++);
c2pstr((char*)fp.name);
mycreate( &fp, '8BIM', '8BPS' );
writePSD(im, &fp );
}
#endif
}
if( writeChannelData( im, fnum, 0, &theRect ) )
{
result = -1;
goto _addLayer_exit;
}
}
if( oddSized ) // pad byte
{
WRITEUCHAR( 0 );
}
// ************* End Write Channel Image data ************************** //
// ************* Global layer mask info ******************************** //
READLONG( var ); WRITELONG( 0 ); // Length of global layer mask info section.
//READSHORT( svar ); WRITESHORT( 0 ); // 2 bytes Overlay color space
//READSHORT( svar ); WRITESHORT( 0 ); // 4 * 2 byte color components
//READSHORT( svar ); WRITESHORT( 0 );
//READSHORT( svar ); WRITESHORT( 0 );
//READSHORT( svar ); WRITESHORT( 0 );
//READSHORT( svar ); WRITESHORT( 0 ); // 2 bytes Opacity 0 = transparent, 100 = opaque.
//READSHORT( svar ); WRITEUCHAR( 128 ); // 1 byte Kind 0=Color selectedÑi.e. inverted;
// 1=Color protected;128=use value stored per layer.
// This value is preferred. The others are for back-ward
// compatibility with beta versions.
//WRITEUCHAR( 0 );
_addLayer_exit:
if( alpha != NULL ) myfree( (void**)alpha );
if( chlength != NULL ) free(chlength);
if( nchannel != NULL ) free(nchannel);
if( nRect != NULL ) free(nRect);
if( cnames != NULL ) free(cnames);
if( nodd != NULL ) free(nodd);
return result;
}
static int fileCopy( file_spec src, file_spec dest, int numBytes, unsigned char *buf )
{
long count = numBytes;
myread( src, count, buf );
mywrite( dest, count, buf );
return 0;
}
// Or two alpha channels: one in alpha (same size as image im)
// one in buf comprising the rectangle top,bottom,left,right
// store result in alpha
static void orAlpha( unsigned char* alpha, unsigned char *buf, Image *im, PTRect *theRect )
{
register int x,y,ay,by,w;
int BitsPerChannel;
GetBitsPerChannel( im, BitsPerChannel );
if( im->bitsPerPixel != 32 && im->bitsPerPixel != 64) return;
w = (theRect->right - theRect->left);
if( BitsPerChannel == 8 )
{
for(y=theRect->top; ybottom; y++)
{
ay = y * im->width;
by = (y - theRect->top) * w;
{
for(x=theRect->left; xright; x++)
{
if( buf[ by + x - theRect->left ] )
alpha[ ay + x ] = 255U;
}
}
}
}
else // 16
{
for(y=theRect->top; ybottom; y++)
{
ay = y * im->width * 2;
by = (y - theRect->top) * w * 2;
{
for(x=theRect->left; xright; x++)
{
if( *((USHORT*)(buf+ by + 2*(x - theRect->left))) )
*((USHORT*)(alpha + ay + 2*x) ) = 65535U;
}
}
}
}
}
void FindScript( aPrefs *thePrefs )
{
FindFile( &(thePrefs->scriptFile) );
}
int LoadOptions( cPrefs * thePrefs )
{
fullPath path;
file_spec fnum;
struct correct_Prefs loadPrefs;
long count;
int result;
if( FindFile( &path ) )
{
return -1;
}
if( myopen( &path, read_bin, fnum ))
{
PrintError("Could not open file");
return -1;
}
count = sizeof( struct correct_Prefs);
myread( fnum, count, &loadPrefs );
if( count != sizeof( struct correct_Prefs) || loadPrefs.magic != 20 )
{
PrintError( "Wrong format!");
result = -1;
}
else
{
memcpy((char*) thePrefs, (char*)&loadPrefs, sizeof(struct correct_Prefs));
result = 0;
}
myclose(fnum);
return result;
}
char* LoadScript( fullPath* scriptFile )
{
fullPath sfile;
int result = FALSE, i;
file_spec fnum;
long count;
char *script = NULL, ch;
memset( &sfile, 0, sizeof( fullPath ) );
if( memcmp( scriptFile, &sfile, sizeof( fullPath ) ) == 0 )
{
PrintError("No Scriptfile selected");
goto _loadError;
}
if( myopen( scriptFile, read_text, fnum ))
{
PrintError("Error Opening Scriptfile");
goto _loadError;
}
count = 1; i=0; // Get file length
while( count == 1 )
{
myread( fnum, count, &ch );
if(count==1) i++;
}
myclose(fnum);
count = i;
script = (char*)malloc( count+1 );
if( script == NULL )
{
PrintError("Not enough memory to load scriptfile");
goto _loadError;
}
if( myopen( scriptFile, read_text, fnum ))
{
PrintError("Error Opening Scriptfile");
goto _loadError;
}
myread(fnum,count,script);
script[count] = 0;
myclose(fnum);
return script;
_loadError:
return (char*)NULL;
}
int WriteScript( char* res, fullPath* scriptFile, int launch )
{
fullPath sfile;
int result = FALSE;
file_spec fnum;
long count;
memset( &sfile, 0, sizeof( fullPath ) );
if( memcmp( scriptFile, &sfile, sizeof( fullPath ) ) == 0 )
{
PrintError("No Scriptfile selected");
goto _writeError;
}
memcpy( &sfile, scriptFile, sizeof (fullPath) );
mydelete( &sfile );
mycreate(&sfile,'ttxt','TEXT');
if( myopen( &sfile, write_text, fnum ) )
{
PrintError("Error Opening Scriptfile");
goto _writeError;
}
count = strlen( res );
mywrite( fnum, count, res );
myclose (fnum );
if( launch == 1 )
{
showScript( &sfile);
}
return 0;
_writeError:
return -1;
}
void SaveOptions( cPrefs * thePrefs )
{
fullPath path;
file_spec fspec;
long count;
memset( &path, 0, sizeof( fullPath ));
if( SaveFileAs( &path, "Save Settings as..", "Params" ) )
return;
mycreate (&path,'GKON','TEXT');
if( myopen( &path, write_bin, fspec ) )
return;
count = sizeof( cPrefs );
mywrite( fspec, count, thePrefs );
myclose( fspec );
}
// Temporarily save a 32bit image (including Alpha-channel) using name fname
int SaveBufImage( Image *image, char *fname )
{
fullPath fspec;
MakeTempName( &fspec, fname );
mydelete( &fspec );
mycreate( &fspec, '8BIM', '8BPS');
return writePSD(image, &fspec);
}
// Load a saved 32bit image (including Alpha-channel) using name fname
// image must be allocated (not data)
// mode = 0: only load image struct
// mode = 1: also allocate and load data
int LoadBufImage( Image *image, char *fname, int mode)
{
fullPath fspec;
MakeTempName( &fspec, fname );
return readPSD(image, &fspec, mode);
}
// Read Photoshop Multilayer-image
int readPSDMultiLayerImage( MultiLayerImage *mim, fullPath* sfile){
file_spec src;
char header[128], *h;
long count,chlength;
unsigned long var;
char data[12], *d;
short svar;
int i, k, result = 0,nchannel, *nodd = NULL;
unsigned char **buf = NULL, ch;
Image im;
int BitsPerSample = 8;
SetImageDefaults( &im );
if( myopen( sfile,read_bin, src ) ){
PrintError("Error Opening Image File");
return -1;
}
// Read psd header
h = header;
count = PSDHLENGTH;
myread( src,count,h);
if( count != PSDHLENGTH ){
PrintError("Error Reading Image Header");
myclose( src );
return -1;
}
if( ParsePSDHeader( header, &im ) != 0 ){
PrintError("Wrong File Format");
myclose( src );
return -1;
}
// Read (and ignore) Color mode data
READLONG( var );
count = 1;
for( i=0; inumLayers ); // Number of layers
mim->Layer = (Image*) malloc( mim->numLayers * sizeof( Image ) );
nodd = (int*) malloc( mim->numLayers * sizeof( int ));
if( mim->Layer == NULL || nodd == NULL)
{
PrintError("Not enough memory");
result = -1;
goto readPSDMultiLayerImage_exit;
}
for(i=0; inumLayers; i++)
{
SetImageDefaults( &mim->Layer[i] );
mim->Layer[i].width = im.width;
mim->Layer[i].height = im.height;
READLONG( mim->Layer[i].selection.top ) ; // Layer top
READLONG( mim->Layer[i].selection.left ); // Layer left
READLONG( mim->Layer[i].selection.bottom); // Layer bottom
READLONG( mim->Layer[i].selection.right ); // Layer right
READSHORT( nchannel ); // The number of channels in the layer.
mim->Layer[i].bitsPerPixel = nchannel * BitsPerSample;
mim->Layer[i].bytesPerLine = (mim->Layer[i].selection.right - mim->Layer[i].selection.left) *
mim->Layer[i].bitsPerPixel/8;
mim->Layer[i].dataSize = mim->Layer[i].bytesPerLine *
(mim->Layer[i].selection.bottom - mim->Layer[i].selection.top);
mim->Layer[i].data = (unsigned char**) mymalloc(mim->Layer[i].dataSize);
if( mim->Layer[i].data == NULL )
{
PrintError("Not enough memory");
result = -1;
goto readPSDMultiLayerImage_exit;
}
// ********** Channel information ***************************** //
READSHORT( svar ); // red
READLONG(chlength);// Length of following channel data.
READSHORT( svar ); // green
READLONG(var); // Length of following channel data.
READSHORT( svar ); // blue
READLONG(var); // Length of following channel data.
if( mim->Layer[i].bitsPerPixel == 32 || mim->Layer[i].bitsPerPixel == 64) // alpha channel
{
READSHORT( svar ); // transparency mask
READLONG( var); // Length of following channel data.
}
// ********** End Channel information ***************************** //
READLONG( var ); // Blend mode signature Always 8BIM.
READLONG( var ); // Blend mode key
READLONG( var ); // Four flag bytes
READLONG( var ); // Extra data size Length of the extra data field. This is the
READLONG( var ); // Layer Mask data
READLONG( var ); // Layer blending ranges
READLONG( var ); // Layer name
var = 4*4 + 2 + nchannel * 6 + 4 + 4 + 4 * 1 + 4 + 12 + nchannel * chlength; // length
if( var/2 != (var+1)/2 ) // odd
{
nodd[i] = 1;
//READUCHAR( ch );
}
else
{
nodd[i] = 0;
}
}
// ************* End Layer Structure ******************************* //
// ************* Read Channel Image data ************************** //
for( i=0; i< mim->numLayers; i++)
{
nchannel = mim->Layer[i].bitsPerPixel/BitsPerSample;
chlength = mim->Layer[i].dataSize/ nchannel;
buf = (unsigned char**) mymalloc( chlength );
if( buf == NULL )
{
PrintError("Not enough memory");
result = -1;
goto readPSDMultiLayerImage_exit;
}
for( k=0; k< nchannel; k++ )
{
READSHORT( svar );
if( svar != 0 )
{
PrintError("File format error");
result = -1;
goto readPSDMultiLayerImage_exit;
}
count = chlength;
myread( src, count, *buf );
{
register int x,y,cy,by;
register unsigned char* theData = *(mim->Layer[i].data);
int offset,bpp;
offset = (mim->Layer[i].bitsPerPixel == 32?1:0) + k;
if( k==3 ) offset = 0;
bpp = mim->Layer[i].bitsPerPixel/8;
for(y=0; yLayer[i].height; y++)
{
cy = y*mim->Layer[i].bytesPerLine + offset;
by = y*mim->Layer[i].width;
for(x=0; xLayer[i].width; x++)
{
theData[cy + bpp*x] = (*buf)[by + x];
}
}
}
}
myfree( (void**)buf );
if( nodd[i] ) // pad byte
{
READUCHAR( ch );
}
}
readPSDMultiLayerImage_exit:
if( nodd != NULL ) free( nodd );
myclose( src );
return result;
}