gimp/plug-ins/common/psd.c

3023 lines
78 KiB
C

/*
* PSD Plugin version 3.0.14
* This GIMP plug-in is designed to load Adobe Photoshop(tm) files (.PSD)
*
* Adam D. Moss <adam@gimp.org> <adam@foxbox.org>
*
* If this plug-in fails to load a file which you think it should,
* please file a Bugzilla bug describing what seemed to go wrong,
* and anything you know about the image you tried to load. Attach a
* problematic PSD file to the bug report.
*
* Copyright (C) 1997-2004 Adam D. Moss
* Copyright (C) 1996 Torsten Martinsen
*
* 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 of the License, 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.
*/
/*
* Adobe and Adobe Photoshop are trademarks of Adobe Systems
* Incorporated that may be registered in certain jurisdictions.
*/
/*
* Revision history:
*
* 2004-02-12 / v3.0.14 / Adam D. Moss
* Fix a twisted utf8-obsessive bug diagnosed by
* Piotr Krysiuk <KrysiukP@prokom.pl>
*
* 2004-01-06 / v3.0.13 / Adam D. Moss
* Disable one of the PanoTools fixes by default, since it causes
* regressions in some ordinary PSD file loading.
*
* 2004-01-06 / v3.0.12 / Adam D. Moss
* Try to avoid 0-sized drawables (including channels) in all
* circumstances under GIMP 2.
*
* 2004-01-01 / v3.0.11 / Daniel Rogers <dsrogers@phaseveloctiy.org>
* GIMP crashes on 0x0 layers, so we skip them.
*
* 2003-11-27 / v3.0.10 / Adam D. Moss
* GIMP 1.3/2.0 needs its layer/channel names to be UTF8 or it
* fails wackily, so convert the strings from the PSD file to
* UTF8 instead of using them raw.
*
* 2003-10-05 / v3.0.9 / Morten Eriksen
* Fixed memory corruption bug: too little memory was allocated
* for the bitmap image buffer if (imgwidth % 8 != 0) for
* monocolor PSD images.
*
* 2003-08-31 / v3.0.8 / applied (modified) patch from Andy Wallis
* Fix for handling of layer masks. See bug #68538.
*
* 2003-06-16 / v3.0.7 / Adam D. Moss
* Avoid memory corruption when things get shot to hell in the
* image unpacking phase. Major version bumped to distinguish
* GIMP 1.3 development thread.
*
* 2000-08-23 / v2.0.6 / Adam D. Moss
* Eliminate more debugging output (don't people have more
* substantial problems to report? I'm poised at my keyboard).
*
* 1999-11-14 / v2.0.5 / Adam D. Moss
* Applied patch by Andy Hefner to load 1-bit images.
*
* 1999-08-13 / v2.0.4 / Adam D. Moss
* Allowed NULL layer names again, whee. (Also fixed the time machine.)
*
* 1999-08-20 / v2.0.3 / Adam D. Moss
* Ensure that NULL name does not get passed to gimp_layer_new(),
* or it will fail to create the layer and cause problems down
* the line (only since April 1999).
*
* 1999-01-18 / v2.0.2 / Adam D. Moss
* Better guess at how PSD files store Guide position precision.
*
* 1999-01-10 / v2.0.1 / Adam D. Moss
* Greatly reduced memory requirements for layered image loading -
* we now do just-in-time channel unpacking. Some little
* cleanups too.
*
* 1998-09-04 / v2.0.0 / Adam D. Moss
* Now recognises and loads the new Guides extensions written
* by Photoshop 4 and 5.
*
* 1998-07-31 / v1.9.9.9f / Adam D. Moss
* Use GIMP_OVERLAY_MODE if available.
*
* 1998-07-31 / v1.9.9.9e / Adam D. Moss
* Worked around some buggy PSD savers (suspect PS4 on Mac) - ugh.
* Fixed a bug when loading layer masks of certain dimensions.
*
* 1998-05-04 / v1.9.9.9b / Adam D. Moss
* Changed the Pascal-style string-reading stuff. That fixed
* some file-padding problems. Made all debugging output
* compile-time optional (please leave it enabled for now).
* Reduced memory requirements; still much room for improvement.
*
* 1998-04-28 / v1.9.9.9 / Adam D. Moss
* Fixed the correct channel interlacing of 'raw' flat images.
* Thanks to Christian Kirsch and Jay Cox for spotting this.
* Changed some of the I/O routines.
*
* 1998-04-26 / v1.9.9.8 / Adam D. Moss
* Implemented Aux-channels for layered files. Got rid
* of <endian.h> nonsense. Improved Layer Mask padding.
* Enforced num_layers/num_channels limit checks.
*
* 1998-04-23 / v1.9.9.5 / Adam D. Moss
* Got Layer Masks working, got Aux-channels working
* for unlayered files, fixed 'raw' channel loading, fixed
* some other mini-bugs, slightly better progress meters.
* Thanks to everyone who is helping with the testing!
*
* 1998-04-21 / v1.9.9.1 / Adam D. Moss
* A little cleanup. Implemented Layer Masks but disabled
* them again - PS masks can be a different size to their
* owning layer, unlike those in GIMP.
*
* 1998-04-19 / v1.9.9.0 / Adam D. Moss
* Much happier now.
*
* 1997-03-13 / v1.9.0 / Adam D. Moss
* Layers, channels and masks, oh my.
* + Bugfixes & rearchitecturing.
*
* 1997-01-30 / v1.0.12 / Torsten Martinsen
* Flat PSD image loading.
*/
/*
* TODO:
*
* Crush 16bpp channels *
* CMYK -> RGB *
* * I don't think these should be done lossily -- wait for
* GIMP to be able to support them natively.
*
* Read in the paths.
*
*/
/*
* BUGS:
*
* Sometimes creates a superfluous aux channel? Harmless.
*/
/* *** USER DEFINES *** */
#define LOAD_PROC "file-psd-load"
#define PLUG_IN_BINARY "psd"
/* set to TRUE if you want debugging, FALSE otherwise */
#define PSD_DEBUG FALSE
/* the max number of channels that this plugin should let a layer have */
#define MAX_CHANNELS 30
/* set to TRUE to allow a fix for transparency in PSD files
generated by PanoTools that unfortunately causes regressions
in some other ordinary files saved by Photoshop. */
#define PANOTOOLS_FIX FALSE
/* *** END OF USER DEFINES *** */
#define IFDBG if (PSD_DEBUG)
#include "config.h"
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <glib/gstdio.h>
#include <libgimp/gimp.h>
#include "libgimp/stdplugins-intl.h"
/* Local types etc
*/
typedef enum
{
PSD_UNKNOWN_IMAGE,
PSD_RGB_IMAGE,
PSD_RGBA_IMAGE,
PSD_GRAY_IMAGE,
PSD_GRAYA_IMAGE,
PSD_INDEXED_IMAGE,
PSD_INDEXEDA_IMAGE,
PSD_BITMAP_IMAGE
} psd_imagetype;
typedef struct PsdChannel
{
gchar *name;
guchar *data;
gint type;
guint32 compressedsize;
fpos_t fpos; /* Remember where the data is in the file, so we can
come back to it! */
/* We can't just assume that the channel's width and height are the
* same as those of the layer that owns the channel, since this
* channel may be a layer mask, which Photoshop allows to have a
* different size from the layer which it applies to.
*/
guint32 width;
guint32 height;
} PSDchannel;
typedef struct PsdGuide
{
gboolean horizontal; /* else vertical */
gint position;
} PSDguide;
typedef struct PsdLayer
{
gint num_channels;
PSDchannel *channel;
gint32 x;
gint32 y;
guint32 width;
guint32 height;
gchar blendkey[4];
guchar opacity;
gchar clipping;
gboolean protecttrans;
gboolean visible;
gchar *name;
gint32 lm_x;
gint32 lm_y;
gint32 lm_width;
gint32 lm_height;
} PSDlayer;
typedef gint32 Fixed;
typedef struct
{
Fixed hRes;
gint16 hRes_unit;
gint16 widthUnit;
Fixed vRes;
gint16 vRes_unit;
gint16 heightUnit;
/* Res_unit :
1 == Pixels per inch
2 == Pixels per cm
*/
} PSDresolution;
typedef struct PsdImage
{
gint num_layers;
PSDlayer *layer;
gboolean absolute_alpha;
gint type;
gulong colmaplen;
guchar *colmapdata;
guint num_aux_channels;
PSDchannel aux_channel[MAX_CHANNELS];
guint num_guides;
PSDguide *guides;
gchar *caption;
guint active_layer_num;
guint resolution_is_set;
PSDresolution resolution;
} PSDimage;
/* Declare some local functions.
*/
static void query (void);
static void run (const gchar *name,
gint nparams,
const GimpParam *param,
gint *nreturn_vals,
GimpParam **return_vals);
static GimpImageType psd_type_to_gimp_type (psd_imagetype psdtype);
static GimpImageBaseType psd_type_to_gimp_base_type (psd_imagetype psdtype);
static GimpLayerModeEffects psd_lmode_to_gimp_lmode (gchar modekey[4]);
static GimpUnit psd_unit_to_gimp_unit (gint psdunit);
static gint32 load_image (const gchar *filename);
/* Various local variables...
*/
GimpPlugInInfo PLUG_IN_INFO =
{
NULL, /* init_proc */
NULL, /* quit_proc */
query, /* query_proc */
run, /* run_proc */
};
static PSDimage psd_image;
static struct
{
gchar signature[4];
gushort version;
guchar reserved[6];
gushort channels;
gulong rows;
gulong columns;
gushort bpp;
gushort mode;
gulong imgreslen;
gulong miscsizelen;
gushort compression;
gushort *rowlength;
long imgdatalen;
} PSDheader;
static gchar * modename[] =
{
"Bitmap",
"Grayscale",
"Indexed Colour",
"RGB Colour",
"CMYK Colour",
"<invalid>",
"<invalid>",
"Multichannel",
"Duotone",
"Lab Colour",
"<invalid>"
};
static void unpack_pb_channel (FILE *fd,
guchar *dst,
gint32 unpackedlen,
guint32 *offset);
static void decode (long clen,
long uclen,
guchar *src,
guchar *dst,
int step);
static void packbitsdecode (long *clenp,
long uclen,
guchar *src,
guchar *dst,
int step);
static void cmyk2rgb (guchar *src,
guchar *destp,
long width,
long height,
int alpha);
static void cmykp2rgb (guchar *src,
guchar *destp,
long width,
long height,
int alpha);
static void bitmap2gray (guchar *src,
guchar *dest,
long w,
long h);
static guchar getguchar (FILE *fd,
gchar *why);
static gshort getgshort (FILE *fd,
gchar *why);
static glong getglong (FILE *fd,
gchar *why);
static void xfread (FILE *fd,
void *buf,
long len,
gchar *why);
static void xfread_interlaced (FILE *fd,
guchar *buf,
long len,
gchar *why,
gint step);
static void read_whole_file (FILE *fd);
static void reshuffle_cmap (guchar *map256);
static gchar *getpascalstring (FILE *fd,
gchar *why);
static gchar *getstring (size_t n,
FILE *fd,
gchar *why);
static void throwchunk (size_t n,
FILE *fd,
gchar *why);
static void dumpchunk (size_t n,
FILE *fd,
gchar *why);
static void seek_to_and_unpack_pixeldata (FILE *fd,
gint layeri,
gint channeli);
static void validate_aux_channel_name (gint aux_index);
MAIN ()
static void
query (void)
{
static GimpParamDef load_args[] =
{
{ GIMP_PDB_INT32, "run-mode", "Interactive, non-interactive" },
{ GIMP_PDB_STRING, "filename", "The name of the file to load" },
{ GIMP_PDB_STRING, "raw-filename", "The name of the file to load" }
};
static GimpParamDef load_return_vals[] =
{
{ GIMP_PDB_IMAGE, "image", "Output image" }
};
gimp_install_procedure (LOAD_PROC,
"loads files of the Photoshop(tm) PSD file format",
"This filter loads files of Adobe Photoshop(tm) "
"native PSD format. These files may be of any "
"image type supported by GIMP, with or without "
"layers, layer masks, aux channels and guides.",
"Adam D. Moss & Torsten Martinsen",
"Adam D. Moss & Torsten Martinsen",
"1996-1998",
"Photoshop image",
NULL,
GIMP_PLUGIN,
G_N_ELEMENTS (load_args),
G_N_ELEMENTS (load_return_vals),
load_args, load_return_vals);
gimp_register_file_handler_mime (LOAD_PROC, "image/x-psd");
gimp_register_magic_load_handler (LOAD_PROC,
"psd",
"",
"0,string,8BPS");
}
static void
run (const gchar *name,
gint nparams,
const GimpParam *param,
gint *nreturn_vals,
GimpParam **return_vals)
{
static GimpParam values[2];
GimpRunMode run_mode;
gint32 image_ID;
run_mode = param[0].data.d_int32;
*nreturn_vals = 1;
*return_vals = values;
values[0].type = GIMP_PDB_STATUS;
values[0].data.d_status = GIMP_PDB_CALLING_ERROR;
if (strcmp (name, LOAD_PROC) == 0)
{
image_ID = load_image (param[1].data.d_string);
if (image_ID != -1)
{
*nreturn_vals = 2;
values[0].data.d_status = GIMP_PDB_SUCCESS;
values[1].type = GIMP_PDB_IMAGE;
values[1].data.d_image = image_ID;
}
else
{
values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
}
}
}
static char *
sanitise_string (char *old_name)
{
if (old_name)
{
char *rtn = gimp_any_to_utf8 (old_name, -1,
_("Invalid UTF-8 string in PSD file"));
g_free(old_name);
return rtn;
}
return NULL;
}
static GimpImageType
psd_type_to_gimp_type (psd_imagetype psdtype)
{
switch(psdtype)
{
case PSD_RGBA_IMAGE: return GIMP_RGBA_IMAGE;
case PSD_RGB_IMAGE: return GIMP_RGB_IMAGE;
case PSD_GRAYA_IMAGE: return GIMP_GRAYA_IMAGE;
case PSD_GRAY_IMAGE: return GIMP_GRAY_IMAGE;
case PSD_INDEXEDA_IMAGE: return GIMP_INDEXEDA_IMAGE;
case PSD_INDEXED_IMAGE: return GIMP_INDEXED_IMAGE;
case PSD_BITMAP_IMAGE: return GIMP_GRAY_IMAGE;
default: return GIMP_RGB_IMAGE;
}
}
static GimpLayerModeEffects
psd_lmode_to_gimp_lmode (gchar modekey[4])
{
if (strncmp (modekey, "norm", 4) == 0) return GIMP_NORMAL_MODE;
if (strncmp (modekey, "dark", 4) == 0) return GIMP_DARKEN_ONLY_MODE;
if (strncmp (modekey, "lite", 4) == 0) return GIMP_LIGHTEN_ONLY_MODE;
if (strncmp (modekey, "hue ", 4) == 0) return GIMP_HUE_MODE;
if (strncmp (modekey, "sat ", 4) == 0) return GIMP_SATURATION_MODE;
if (strncmp (modekey, "colr", 4) == 0) return GIMP_COLOR_MODE;
if (strncmp (modekey, "mul ", 4) == 0) return GIMP_MULTIPLY_MODE;
if (strncmp (modekey, "scrn", 4) == 0) return GIMP_SCREEN_MODE;
if (strncmp (modekey, "diss", 4) == 0) return GIMP_DISSOLVE_MODE;
if (strncmp (modekey, "diff", 4) == 0) return GIMP_DIFFERENCE_MODE;
if (strncmp (modekey, "lum ", 4) == 0) return GIMP_VALUE_MODE;
if (strncmp (modekey, "hLit", 4) == 0) return GIMP_HARDLIGHT_MODE;
if (strncmp (modekey, "sLit", 4) == 0) return GIMP_SOFTLIGHT_MODE;
if (strncmp (modekey, "over", 4) == 0) return GIMP_OVERLAY_MODE;
g_printerr("PSD: Warning - UNKNOWN layer-blend mode, reverting to 'normal'\n");
return GIMP_NORMAL_MODE;
}
static GimpImageBaseType
psd_type_to_gimp_base_type (psd_imagetype psdtype)
{
switch(psdtype)
{
case PSD_RGBA_IMAGE:
case PSD_RGB_IMAGE: return GIMP_RGB;
case PSD_BITMAP_IMAGE:
case PSD_GRAYA_IMAGE:
case PSD_GRAY_IMAGE: return GIMP_GRAY;
case PSD_INDEXEDA_IMAGE:
case PSD_INDEXED_IMAGE: return GIMP_INDEXED;
default:
g_message ("Error: Can't convert PSD imagetype to GIMP imagetype");
gimp_quit();
return GIMP_RGB;
}
}
static GimpUnit
psd_unit_to_gimp_unit (int psdunit)
{
switch (psdunit)
{
case 1:
return GIMP_UNIT_INCH;
case 2: /* this means cm to PS, but MM is as close as we have */
return GIMP_UNIT_MM;
case 3:
return GIMP_UNIT_POINT;
case 4:
return GIMP_UNIT_PICA;
case 5: /* 5 == Columns, but what the heck is a column? */
default:
IFDBG printf("Warning: unable to convert psd unit %d to gimp unit\n", psdunit);
return GIMP_UNIT_PIXEL;
}
}
static GimpImageBaseType
psd_mode_to_gimp_base_type (gushort psdtype)
{
switch(psdtype)
{
case 1: return GIMP_GRAY;
case 2: return GIMP_INDEXED;
case 3: return GIMP_RGB;
default:
g_message ("Error: Can't convert PSD mode to GIMP base imagetype");
gimp_quit();
return GIMP_RGB;
}
}
static void
reshuffle_cmap (guchar *map256)
{
guchar *tmpmap;
gint i;
tmpmap = g_malloc (3 * 256);
for (i = 0; i < 256; i++)
{
tmpmap[i*3 ] = map256[i];
tmpmap[i*3+1] = map256[i+256];
tmpmap[i*3+2] = map256[i+512];
}
memcpy (map256, tmpmap, 3 * 256);
g_free (tmpmap);
}
static void
dispatch_resID (guint ID,
FILE *fd,
guint32 *offset,
guint32 Size)
{
if ( (ID < 0x0bb6) && (ID >0x07d0) )
{
IFDBG printf ("\t\tThe psd plugin does not currently support reading path data.\n");
throwchunk (Size, fd, "dispatch_res path throw");
(*offset) += Size;
}
else
switch (ID)
{
case 0x03ee:
{
gint32 remaining = Size;
IFDBG printf ("\t\tALPHA CHANNEL NAMES:\n");
if (Size > 0)
{
do
{
guchar slen;
slen = getguchar (fd, "alpha channel name length");
(*offset)++;
remaining--;
/* Check for (Mac?) Photoshop (4?) file-writing bug */
if (slen > remaining)
{
IFDBG {printf("\nYay, a file bug. "
"Yuck. Photoshop 4/Mac? "
"I'll work around you.\n");fflush(stdout);}
break;
}
if (slen)
{
guint32 alpha_name_len;
gchar* sname = getstring(slen, fd, "alpha channel name");
alpha_name_len = strlen (sname);
(*offset) += alpha_name_len;
remaining -= alpha_name_len;
sname = sanitise_string (sname);
psd_image.aux_channel[psd_image.num_aux_channels].name =
sname;
IFDBG printf("\t\t\tname: \"%s\"\n",
psd_image.aux_channel[psd_image.num_aux_channels].name);
}
else
{
psd_image.aux_channel[psd_image.num_aux_channels].name =
NULL;
IFDBG
{printf("\t\t\tNull channel name %d.\n",
psd_image.num_aux_channels);fflush(stdout);}
}
psd_image.num_aux_channels++;
if (psd_image.num_aux_channels > MAX_CHANNELS)
{
printf("\nPSD: Sorry - this image has too many "
"aux channels. Tell Adam!\n");
gimp_quit();
}
}
while (remaining > 0);
}
if (remaining)
{
IFDBG
dumpchunk (remaining, fd, "alphaname padding 0 throw");
else
throwchunk (remaining, fd, "alphaname padding 0 throw");
(*offset) += remaining;
remaining = 0;
}
}
break;
case 0x03ef:
IFDBG printf("\t\tDISPLAYINFO STRUCTURE: unhandled\n");
throwchunk (Size, fd, "dispatch_res");
(*offset) += Size;
break;
case 0x03f0: /* FIXME: untested */
{
psd_image.caption = getpascalstring(fd, "caption string");
(*offset)++;
if (psd_image.caption)
{
IFDBG printf("\t\t\tcontent: \"%s\"\n",psd_image.caption);
(*offset) += strlen(psd_image.caption);
}
}
break;
case 0x03f2:
IFDBG printf("\t\tBACKGROUND COLOR: unhandled\n");
throwchunk (Size, fd, "dispatch_res");
(*offset) += Size;
break;
case 0x03f4:
IFDBG printf("\t\tGREY/MULTICHANNEL HALFTONING INFO: unhandled\n");
throwchunk (Size, fd, "dispatch_res");
(*offset) += Size;
break;
case 0x03f5:
IFDBG printf("\t\tCOLOUR HALFTONING INFO: unhandled\n");
throwchunk (Size, fd, "dispatch_res");
(*offset) += Size;
break;
case 0x03f6:
IFDBG printf("\t\tDUOTONE HALFTONING INFO: unhandled\n");
throwchunk (Size, fd, "dispatch_res");
(*offset) += Size;
break;
case 0x03f7:
IFDBG printf("\t\tGREYSCALE/MULTICHANNEL TRANSFER FUNCTION: unhandled\n");
throwchunk (Size, fd, "dispatch_res");
(*offset) += Size;
break;
case 0x03f8:
IFDBG printf("\t\tCOLOUR TRANSFER FUNCTION: unhandled\n");
throwchunk (Size, fd, "dispatch_res");
(*offset) += Size;
break;
case 0x03f9:
IFDBG printf("\t\tDUOTONE TRANSFER FUNCTION: unhandled\n");
throwchunk (Size, fd, "dispatch_res");
(*offset) += Size;
break;
case 0x03fa:
IFDBG printf("\t\tDUOTONE IMAGE INFO: unhandled\n");
throwchunk (Size, fd, "dispatch_res");
(*offset) += Size;
break;
case 0x03fb:
IFDBG printf("\t\tEFFECTIVE BLACK/WHITE VALUES: unhandled\n");
throwchunk (Size, fd, "dispatch_res");
(*offset) += Size;
break;
case 0x03fe:
IFDBG printf("\t\tQUICK MASK INFO: unhandled\n");
throwchunk (Size, fd, "dispatch_res");
(*offset) += Size;
break;
case 0x0400:
{
IFDBG printf("\t\tLAYER STATE INFO:\n");
psd_image.active_layer_num = getgshort(fd, "ID target_layer_num");
IFDBG printf("\t\t\ttarget: %d\n",(gint)psd_image.active_layer_num);
(*offset) += 2;
}
break;
case 0x0402:
IFDBG printf("\t\tLAYER GROUP INFO: unhandled\n");
IFDBG printf("\t\t\t(Inferred number of layers: %d)\n",(gint)(Size/2));
throwchunk (Size, fd, "dispatch_res");
(*offset) += Size;
break;
case 0x0405:
IFDBG printf ("\t\tIMAGE MODE FOR RAW FORMAT: unhandled\n");
throwchunk (Size, fd, "dispatch_res");
(*offset) += Size;
break;
case 0x0408:
{
gint32 remaining = Size;
int i;
IFDBG printf ("\t\tGUIDE INFORMATION:\n");
if (Size > 0)
{
gshort magic1, magic2, magic3, magic4, magic5, magic6;
glong num_guides;
PSDguide *guide;
magic1 = getgshort(fd, "guide"); (*offset) += 2;
magic2 = getgshort(fd, "guide"); (*offset) += 2;
magic3 = getgshort(fd, "guide"); (*offset) += 2;
magic4 = getgshort(fd, "guide"); (*offset) += 2;
magic5 = getgshort(fd, "guide"); (*offset) += 2;
magic6 = getgshort(fd, "guide"); (*offset) += 2;
remaining -= 12;
IFDBG printf("\t\t\tSize: %d\n", Size);
IFDBG printf("\t\t\tMagic: %d %d %d %d %d %d\n",
magic1, magic2, magic3, magic4, magic5, magic6);
IFDBG printf("\t\t\tMagic: 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x\n",
magic1, magic2, magic3, magic4, magic5, magic6);
num_guides = getglong(fd, "guide");
(*offset) += 4; remaining -= 4;
if (remaining != num_guides*5)
{
IFDBG printf ("** FUNNY AMOUNT OF GUIDE DATA (%d)\n",
remaining);
goto funny_amount_of_guide_data;
}
IFDBG printf("\t\t\tNumber of guides is %ld\n", num_guides);
psd_image.num_guides = num_guides;
psd_image.guides = g_new(PSDguide, num_guides);
guide = psd_image.guides;
for (i = 0; i < num_guides; i++, guide++)
{
guide->position = getglong(fd, "guide");
IFDBG printf ("Position: %d %x\n", guide->position,
guide->position);
guide->horizontal = (1 == getguchar(fd, "guide"));
(*offset) += 5; remaining -= 5;
if (guide->horizontal)
{
guide->position =
RINT((double)(guide->position * (magic4>>8))
/(double)(magic4&255));
}
else
{
guide->position =
RINT((double)(guide->position * (magic6>>8))
/(double)(magic6&255));
}
IFDBG printf("\t\t\tGuide %d at %d, %s\n", i+1,
guide->position,
guide->horizontal ? "horizontal" : "vertical");
}
}
funny_amount_of_guide_data:
if (remaining)
{
IFDBG
{
printf ("** GUIDE INFORMATION DROSS: ");
dumpchunk(remaining, fd, "dispatch_res");
}
else
{
throwchunk (remaining, fd, "dispatch_res");
}
(*offset) += remaining;
}
}
break;
case 0x03ed:
{
IFDBG printf ("\t\tResolution Info:\n");
psd_image.resolution_is_set = 1;
psd_image.resolution.hRes = getglong(fd, "hRes");
psd_image.resolution.hRes_unit = getgshort(fd, "hRes_unit");
psd_image.resolution.widthUnit = getgshort(fd, "WidthUnit");
psd_image.resolution.vRes = getglong(fd, "vRes");
psd_image.resolution.vRes_unit = getgshort(fd, "vRes_unit");
psd_image.resolution.heightUnit = getgshort(fd, "HeightUnit");
(*offset) += Size;
IFDBG printf("\t\t\tres = %f, %f\n",
psd_image.resolution.hRes / 65536.0,
psd_image.resolution.vRes / 65536.0);
} break;
case 0x0409:
/* DATA LAYOUT for thumbail resource */
/* 4 bytes format (1 = jfif, 0 = raw) */
/* 4 bytes width width of thumbnail */
/* 4 bytes height height of thumbnail */
/* 4 bytes widthbytes for validation only?*/
/* 4 bytes size for validation only?*/
/* 4 bytes compressed size for validation only?*/
/* 2 bytes bits per pixel Always 24? */
/* 2 bytes planes Always 1? */
/* size bytes data JFIF (or raw) data */
IFDBG printf("\t\t<Photoshop 4.0 style thumbnail (BGR)> unhandled\n");
/* for resource 0x0409 we have to swap the r and b channels
after decoding */
throwchunk (Size, fd, "dispatch_res");
(*offset) += Size;
break;
case 0x040C:
/* See above */
IFDBG printf("\t\t<Photoshop 5.0 style thumbnail (RGB)> unhandled\n");
throwchunk (Size, fd, "dispatch_res");
(*offset) += Size;
break;
case 0x03e9:
case 0x03f1:
case 0x03f3:
case 0x03fd:
case 0x0401:
case 0x0404:
case 0x0406:
case 0x0bb7:
case 0x2710:
IFDBG printf ("\t\t<Field is irrelevant to GIMP at this time.>\n");
throwchunk (Size, fd, "dispatch_res");
(*offset) += Size;
break;
case 0x03e8:
case 0x03eb:
IFDBG printf ("\t\t<Obsolete Photoshop 2.0 field.>\n");
throwchunk (Size, fd, "dispatch_res");
(*offset) += Size;
break;
case 0x03fc:
case 0x03ff:
case 0x0403:
IFDBG printf ("\t\t<Obsolete field.>\n");
throwchunk (Size, fd, "dispatch_res");
(*offset) += Size;
break;
default:
IFDBG
{
printf ("\t\t<Undocumented field.>\n");
dumpchunk(Size, fd, "dispatch_res");
}
else
throwchunk (Size, fd, "dispatch_res");
(*offset) += Size;
break;
}
}
static void
do_layer_record (FILE *fd,
guint32 *offset,
gint layernum)
{
PSDlayer *layer;
gint32 top, left, bottom, right;
guint32 extradatasize, layermaskdatasize, layerrangesdatasize;
guint32 totaloff;
gchar sig[4];
guchar flags;
gint i;
IFDBG printf("\t\t\tLAYER RECORD (layer %d)\n", (int)layernum);
layer = psd_image.layer + layernum;
/* table 11-12 */
top = getglong (fd, "layer top");
(*offset)+=4;
left = getglong (fd, "layer left");
(*offset)+=4;
bottom = getglong (fd, "layer bottom");
(*offset)+=4;
right = getglong (fd, "layer right");
(*offset)+=4;
layer->x = left;
layer->y = top;
layer->width = right - left;
layer->height = bottom - top;
IFDBG printf("\t\t\t\tLayer extents: (%d,%d) -> (%d,%d)\n",
left,top,right,bottom);
layer->num_channels = getgshort (fd, "layer num_channels");
(*offset)+=2;
IFDBG printf("\t\t\t\tNumber of channels: %d\n",
(int)layer->num_channels);
if (layer->num_channels)
{
layer->channel = g_new(PSDchannel, layer->num_channels);
for (i = 0; i < layer->num_channels; i++)
{
PSDchannel *channel = layer->channel + i;
/* table 11-13 */
IFDBG printf("\t\t\t\tCHANNEL LENGTH INFO (%d)\n", i);
channel->type = getgshort(fd, "channel id");
(*offset)+=2;
IFDBG printf("\t\t\t\t\tChannel TYPE: %d\n", channel->type);
channel->compressedsize = getglong(fd, "channeldatalength");
(*offset)+=4;
IFDBG printf("\t\t\t\t\tChannel Data Length: %d\n",
channel->compressedsize);
}
} else {
IFDBG printf("\t\t\t\tOo-er, layer has no channels. Hmm.\n");
}
xfread(fd, sig, 4, "layer blend sig");
(*offset)+=4;
if (strncmp(sig, "8BIM", 4)!=0)
{
IFDBG printf("\t\t\t(layer blend signature '%c%c%c%c' is incorrect: quitting)\n", sig[0], sig[1], sig[2], sig[3]);
g_message ("Error: layer blend signature is incorrect. :-(");
gimp_quit();
}
xfread(fd, layer->blendkey, 4, "layer blend key");
(*offset)+=4;
IFDBG printf("\t\t\t\tBlend type: PSD(\"%c%c%c%c\") = GIMP(%d)\n",
layer->blendkey[0],
layer->blendkey[1],
layer->blendkey[2],
layer->blendkey[3],
psd_lmode_to_gimp_lmode(layer->blendkey));
layer->opacity = getguchar(fd, "layer opacity");
(*offset)++;
IFDBG printf("\t\t\t\tLayer Opacity: %d\n", layer->opacity);
layer->clipping = getguchar(fd, "layer clipping");
(*offset)++;
IFDBG printf("\t\t\t\tLayer Clipping: %d (%s)\n",
layer->clipping,
layer->clipping == 0 ? "base" : "non-base");
flags = getguchar(fd, "layer flags");
(*offset)++;
IFDBG printf("\t\t\t\tLayer Flags: %d (%s, %s)\n", flags,
flags&1?"lock alpha":"don't lock alpha",
flags&2?"visible":"not visible");
layer->protecttrans = (flags & 1) ? TRUE : FALSE;
layer->visible = (flags & 2) ? FALSE : TRUE;
getguchar(fd, "layer record filler");
(*offset)++;
extradatasize = getglong(fd, "layer extra data size");
(*offset)+=4;
IFDBG printf("\t\t\t\tEXTRA DATA SIZE: %d\n",extradatasize);
/* FIXME: should do something with this data */
/*throwchunk (extradatasize, fd, "layer extradata throw");
(*offset) += extradatasize;*/
totaloff = (*offset) + extradatasize;
/* table 11-14 */
layermaskdatasize = getglong(fd, "layer mask data size");
(*offset)+=4;
IFDBG printf("\t\t\t\t\tLAYER MASK DATA SIZE: %d\n", layermaskdatasize);
if (layermaskdatasize)
{
guchar color;
guchar flags;
int o = 0;
top = getglong(fd, "lmask top");
o += 4;
left = getglong(fd, "lmask left");
o += 4;
bottom = getglong(fd, "lmask bottom");
o += 4;
right = getglong(fd, "lmask right");
o += 4;
layer->lm_x = left;
layer->lm_y = top;
layer->lm_width = right - left;
layer->lm_height = bottom - top;
color = getguchar(fd, "lmask color");
flags = getguchar(fd, "lmask flags");
o += 2;
IFDBG printf ("\t\t\t\t\t\ttop: %d\n", top);
IFDBG printf ("\t\t\t\t\t\tleft: %d\n", left);
IFDBG printf ("\t\t\t\t\t\tbottom: %d\n", bottom);
IFDBG printf ("\t\t\t\t\t\tright: %d\n", right);
IFDBG printf ("\t\t\t\t\t\tcolor: %d\n", color);
IFDBG printf ("\t\t\t\t\t\tflags: %X\n", flags);
IFDBG printf ("\t\t\t\t\t\t\trelative: %d\n", flags & 0x1);
IFDBG printf ("\t\t\t\t\t\t\tvisible: %d\n", ((flags & 0x2) >> 1));
IFDBG printf ("\t\t\t\t\t\t\tinvert: %d\n", ((flags & 0x4) >> 2));
throwchunk (layermaskdatasize - o, fd, "extra layer mask data");
(*offset) += layermaskdatasize;
}
layerrangesdatasize = getglong (fd, "layer ranges data size");
(*offset) += 4;
IFDBG printf("\t\t\t\t\t\tLAYER RANGES DATA SIZE: %d\n", layermaskdatasize);
if (layerrangesdatasize)
{
throwchunk (layerrangesdatasize, fd, "layer ranges data throw");
(*offset) += layerrangesdatasize;
}
layer->name = getpascalstring (fd, "layer name");
(*offset)++;
if (layer->name)
{
(*offset) += strlen (layer->name);
IFDBG printf("\t\t\t\t\t\tLAYER NAME: '%s'\n", layer->name);
layer->name = sanitise_string (layer->name);
}
else
{
IFDBG printf ("\t\t\t\t\t\tNULL LAYER NAME\n");
}
/* If no layermask data - set offset and size from layer data */
if (! layermaskdatasize)
{
IFDBG g_printerr ("Setting layer mask data layer\n");
psd_image.layer[layernum].lm_x = psd_image.layer[layernum].x;
psd_image.layer[layernum].lm_y = psd_image.layer[layernum].y;
psd_image.layer[layernum].lm_width = psd_image.layer[layernum].width;
psd_image.layer[layernum].lm_height = psd_image.layer[layernum].height;
}
if (totaloff-(*offset) > 0)
{
IFDBG
{
printf ("Warning: layer record dross: ");
dumpchunk (totaloff-(*offset), fd, "layer record dross throw");
}
else
{
throwchunk (totaloff-(*offset), fd, "layer record dross throw");
}
(*offset) = totaloff;
}
}
static void
do_layer_struct (FILE *fd,
guint32 *offset)
{
gint i;
IFDBG printf ("\t\tLAYER STRUCTURE SECTION\n");
psd_image.num_layers = getgshort (fd, "layer struct numlayers");
(*offset)+=2;
IFDBG printf("\t\t\tCanonical number of layers: %d%s\n",
psd_image.num_layers>0?
(int)psd_image.num_layers:abs(psd_image.num_layers),
psd_image.num_layers>0?"":" (absolute/alpha)");
if (psd_image.num_layers < 0)
{
psd_image.num_layers = -psd_image.num_layers;
psd_image.absolute_alpha = TRUE;
}
else
{
psd_image.absolute_alpha = FALSE;
}
psd_image.layer = g_new0 (PSDlayer, psd_image.num_layers);
for (i = 0; i < psd_image.num_layers; i++)
{
do_layer_record (fd, offset, i);
}
}
static void
do_layer_pixeldata (FILE *fd,
guint32 *offset)
{
gint layeri, channeli;
for (layeri = 0; layeri < psd_image.num_layers; layeri++)
{
PSDlayer *layer = psd_image.layer + layeri;
for (channeli = 0; channeli < layer->num_channels; channeli++)
{
PSDchannel *channel = layer->channel + channeli;
if (channel->type == -2)
{
channel->width = layer->lm_width;
channel->height = layer->lm_height;
}
else
{
channel->width = layer->width;
channel->height = layer->height;
}
fgetpos (fd, &channel->fpos);
throwchunk (channel->compressedsize, fd, "channel data skip");
(*offset) += channel->compressedsize;
}
}
}
static void
seek_to_and_unpack_pixeldata (FILE *fd,
gint layeri,
gint channeli)
{
int width, height;
guchar *tmpline;
gint compression;
guint32 offset = 0;
PSDchannel *channel = &psd_image.layer[layeri].channel[channeli];
fsetpos(fd, &channel->fpos);
compression = getgshort(fd, "layer channel compression type");
offset+=2;
width = channel->width;
height = channel->height;
IFDBG
{
printf ("\t\t\tLayer (%d) Channel (%d:%d) Compression: %d (%s)\n",
layeri,
channeli,
channel->type,
compression,
compression==0?"raw":(compression==1?"RLE":"*UNKNOWN!*"));
fflush (stdout);
}
channel->data = g_malloc (width * height);
tmpline = g_malloc (width + 1);
switch (compression)
{
case 0: /* raw data */
{
gint linei;
for (linei = 0; linei < height; linei++)
{
xfread (fd, channel->data + linei * width, width,
"raw channel line");
offset += width;
}
#if 0
/* Pad raw data to multiple of 2? */
if ((height * width) & 1)
{
getguchar (fd, "raw channel padding");
offset++;
}
#endif
}
break;
case 1: /* RLE, one row at a time, padded to an even width */
{
gint linei;
gint blockread;
/* we throw this away because in theory we can trust the
data to unpack to the right length... hmm... */
throwchunk (height * 2, fd, "widthlist");
offset += height * 2;
blockread = offset;
/*IFDBG {printf("\nHere comes the guitar solo...\n");
fflush(stdout);}*/
for (linei = 0; linei < height; linei++)
{
/*printf(" %d ", *offset);*/
unpack_pb_channel (fd, tmpline,
width
/*+ (width&1)*/,
&offset);
memcpy (channel->data + linei * width, tmpline, width);
}
IFDBG {printf("\t\t\t\t\tActual compressed size was %d bytes\n",
offset-blockread);fflush(stdout);}
}
break;
default: /* *unknown* */
IFDBG {printf("\nEEP!\n");fflush(stdout);}
g_message ("*** Unknown compression type in channel.");
gimp_quit();
break;
}
g_free (tmpline);
}
static void
do_layers (FILE *fd,
guint32 *offset)
{
guint32 section_length;
section_length = getglong (fd, "layerinfo sectionlength");
(*offset)+=4;
IFDBG printf ("\tLAYER INFO SECTION\n");
IFDBG printf ("\t\tSECTION LENGTH: %u\n", section_length);
do_layer_struct (fd, offset);
do_layer_pixeldata (fd, offset);
}
static void
do_layer_and_mask (FILE *fd)
{
guint32 offset = 0;
guint32 Size = PSDheader.miscsizelen;
guint32 offset_now = ftell(fd);
IFDBG printf ("LAYER AND MASK INFO\n");
IFDBG printf ("\tSECTION LENGTH: %u\n", Size);
if (Size == 0)
return;
do_layers (fd, &offset);
IFDBG {printf("And...?\n");fflush(stdout);}
if (offset < Size)
{
IFDBG
{
printf ("PSD: Supposedly there are %d bytes of mask info left.\n",
Size-offset);
if ((Size-offset == 4) || (Size-offset == 24))
printf(" That sounds good to me.\n");
else
printf(" That sounds strange to me.\n");
}
/* if ((getguchar(fd, "mask info throw")!=0) ||
(getguchar(fd, "mask info throw")!=0) ||
(getguchar(fd, "mask info throw")!=0) ||
(getguchar(fd, "mask info throw")!=0))
{
printf("*** This mask info block looks pretty bogus.\n");
}*/
}
else
printf("PSD: Stern warning - no mask info.\n");
/* If 'offset' wasn't being buggily updated, we wouldn't need this. (!?) */
fseek(fd, Size+offset_now, SEEK_SET);
}
static void
do_image_resources (FILE *fd)
{
guint16 ID;
gchar *Name;
guint32 Size;
guint32 offset = 0;
IFDBG printf ("IMAGE RESOURCE BLOCK:\n");
psd_image.resolution_is_set = 0;
/* FIXME: too trusting that the file isn't corrupt */
while (offset < PSDheader.imgreslen-1)
{
gchar sig[4];
xfread (fd, sig, 4, "imageresources signature");
offset += 4;
/* generic information about a block ID */
ID = getgshort (fd, "ID num");
offset += 2;
IFDBG printf ("\tID: 0x%04x / ",ID);
Name = getpascalstring (fd, "ID name");
offset++;
if (Name)
{
IFDBG printf ("\"%s\" ", Name);
offset += strlen (Name);
if (!(strlen (Name) & 1))
{
throwchunk (1, fd, "ID name throw");
offset ++;
}
g_free (Name);
}
else
{
throwchunk (1, fd, "ID name throw2");
offset++;
}
Size = getglong (fd, "ID Size");
offset += 4;
IFDBG printf ("Size: %d\n", Size);
if (strncmp (sig, "8BIM", 4) == 0)
dispatch_resID (ID, fd, &offset, Size);
else
{
printf ("PSD: Warning, unknown resource signature \"%.4s\" at or before offset %d ::: skipping\n", sig, offset - 8);
throwchunk (Size, fd, "Skipping Unknown Resource");
offset += Size;
}
if (Size&1)
{
IFDBG printf ("+1");
throwchunk (1, fd, "ID content throw");
offset ++;
}
}
/* if (offset != PSDheader.imgreslen)
{
printf("\tSucking imageres byte...\n");
throwchunk (1, fd, "imageres suck");
offset ++;
}*/
}
#if PANOTOOLS_FIX
/* Convert RGB data to RGBA data */
static guchar *
RGB_to_RGBA (const guchar *rgb_data,
gint numpix)
{
guchar *rtn;
int i,j;
if (!rgb_data)
{
printf ("NULL rgb data - eep!");
return NULL;
}
rtn = g_malloc (numpix * 4);
j = 0;
for (i = 0; i < numpix; i++)
{
rtn[j++] = rgb_data[i*3];
rtn[j++] = rgb_data[i*3+1];
rtn[j++] = rgb_data[i*3+2];
rtn[j++] = 255;
}
return rtn;
}
#endif /* PANOTOOLS_FIX */
static guchar *
chans_to_GRAYA (const guchar *grey,
const guchar *alpha,
gint numpix)
{
guchar *rtn;
int i;
if (!grey || !alpha)
{
printf ("NULL channel - eep!");
return NULL;
}
rtn = g_malloc (numpix * 2);
for (i = 0; i < numpix; i++)
{
rtn[i*2 ] = grey[i];
rtn[i*2+1] = alpha[i];
}
return rtn;
}
static guchar *
chans_to_RGB (const guchar *red,
const guchar *green,
const guchar *blue,
gint numpix)
{
guchar *rtn;
int i;
if (!red || !green || !blue)
{
printf ("NULL channel - eep!");
return NULL;
}
rtn = g_malloc (numpix * 3);
for (i = 0; i < numpix; i++)
{
rtn[i*3 ] = red[i];
rtn[i*3+1] = green[i];
rtn[i*3+2] = blue[i];
}
return rtn;
}
static guchar *
chans_to_RGBA (const guchar *red,
const guchar *green,
const guchar *blue,
const guchar *alpha,
gint numpix)
{
guchar *rtn;
int i;
gboolean careful = FALSE;
if (!red || !green || !blue || !alpha)
{
printf ("chans_to_RGBA : NULL channel - eep!");
careful = TRUE;
}
rtn = g_malloc (numpix * 4);
if (!careful)
{
for (i = 0; i < numpix; i++)
{
rtn[i*4 ] = red[i];
rtn[i*4+1] = green[i];
rtn[i*4+2] = blue[i];
rtn[i*4+3] = alpha[i];
}
}
else
{
for (i = 0; i < numpix; i++)
{
rtn[i*4 ] = (red == NULL) ? 0 : red[i];
rtn[i*4+1] = (green == NULL) ? 0 : green[i];
rtn[i*4+2] = (blue == NULL) ? 0 : blue[i];
rtn[i*4+3] = (alpha == NULL) ? 0 : alpha[i];
}
}
return rtn;
}
static gboolean
psd_layer_has_alpha (PSDlayer *layer)
{
int i;
for (i = 0; i < layer->num_channels; i++)
{
if (layer->channel[i].type == -1)
{
return TRUE;
}
}
return FALSE;
}
static void
validate_aux_channel_name (gint aux_index)
{
if (psd_image.aux_channel[aux_index].name == NULL)
{
if (aux_index == 0)
psd_image.aux_channel[aux_index].name = g_strdup ("Aux channel");
else
psd_image.aux_channel[aux_index].name =
g_strdup_printf ("Aux channel #%d", aux_index);
}
}
static void
extract_data_and_channels (guchar *src,
gint gimpstep,
gint psstep,
gint32 image_ID,
GimpDrawable *drawable,
gint width,
gint height)
{
guchar *primary_data;
guchar *aux_data;
GimpPixelRgn pixel_rgn;
IFDBG printf ("Extracting primary channel data (%d channels)\n"
"\tand %d auxiliary channels.\n", gimpstep, psstep-gimpstep);
/* gimp doesn't like 0 width/height drawables. */
if ((width == 0) || (height == 0))
{
IFDBG printf("(bad channel dimensions -- skipping)");
return;
}
primary_data = g_malloc (width * height * gimpstep);
{
int pix, chan;
for (pix = 0; pix < width * height; pix++)
{
for (chan = 0; chan < gimpstep; chan++)
{
primary_data[pix * gimpstep + chan] = src[pix * psstep + chan];
}
}
gimp_pixel_rgn_init (&pixel_rgn, drawable,
0, 0, drawable->width, drawable->height,
TRUE, FALSE);
gimp_pixel_rgn_set_rect (&pixel_rgn, primary_data,
0, 0, drawable->width, drawable->height);
gimp_drawable_flush (drawable);
gimp_drawable_detach (drawable);
}
g_free (primary_data);
aux_data = g_malloc (width * height);
{
int pix, chan, aux_index;
gint32 channel_ID;
GimpDrawable *chdrawable;
GimpRGB colour;
gimp_rgb_set (&colour, 0.0, 0.0, 0.0);
for (chan = gimpstep; chan < psstep; chan++)
{
for (pix = 0; pix < width * height; pix++)
{
aux_data [pix] = src [pix * psstep + chan];
}
aux_index = chan - gimpstep;
validate_aux_channel_name (aux_index);
channel_ID = gimp_channel_new (image_ID,
psd_image.aux_channel[aux_index].name,
width, height,
100.0, &colour);
gimp_image_add_channel (image_ID, channel_ID, 0);
gimp_drawable_set_visible (channel_ID, FALSE);
chdrawable = gimp_drawable_get (channel_ID);
gimp_pixel_rgn_init (&pixel_rgn, chdrawable,
0, 0, chdrawable->width, chdrawable->height,
TRUE, FALSE);
gimp_pixel_rgn_set_rect (&pixel_rgn, aux_data,
0, 0, chdrawable->width, chdrawable->height);
gimp_drawable_flush (chdrawable);
gimp_drawable_detach (chdrawable);
}
}
g_free (aux_data);
IFDBG printf ("Done with that.\n\n");
}
static void
extract_channels (guchar *src,
gint num_wanted,
gint psstep,
gint32 image_ID,
gint width,
gint height)
{
guchar *aux_data;
GimpPixelRgn pixel_rgn;
IFDBG printf ("Extracting %d/%d auxiliary channels.\n", num_wanted, psstep);
/* gimp doesn't like 0 width/height drawables. */
if ((width == 0) || (height == 0))
{
IFDBG printf("(bad channel dimensions -- skipping)");
return;
}
aux_data = g_malloc (width * height);
{
int pix, chan, aux_index;
gint32 channel_ID;
GimpDrawable *chdrawable;
GimpRGB colour;
gimp_rgb_set (&colour, 0.0, 0.0, 0.0);
for (chan = psstep - num_wanted; chan < psstep; chan++)
{
for (pix = 0; pix < width * height; pix++)
{
aux_data [pix] = src [pix * psstep + chan];
}
aux_index = chan - (psstep - num_wanted);
validate_aux_channel_name (aux_index);
channel_ID = gimp_channel_new (image_ID,
psd_image.aux_channel[aux_index].name,
width, height,
100.0, &colour);
gimp_image_add_channel (image_ID, channel_ID, 0);
gimp_drawable_set_visible (channel_ID, FALSE);
chdrawable = gimp_drawable_get (channel_ID);
gimp_pixel_rgn_init (&pixel_rgn, chdrawable,
0, 0, chdrawable->width, chdrawable->height,
TRUE, FALSE);
gimp_pixel_rgn_set_rect (&pixel_rgn, aux_data,
0, 0, chdrawable->width, chdrawable->height);
gimp_drawable_flush (chdrawable);
gimp_drawable_detach (chdrawable);
}
}
g_free(aux_data);
IFDBG printf ("Done with that.\n\n");
}
static void
resize_mask (guchar *src,
guchar *dest,
gint32 src_x,
gint32 src_y,
gint32 src_w,
gint32 src_h,
gint32 dest_w,
gint32 dest_h)
{
gint x, y;
IFDBG printf ("--> %p %p : %d %d . %d %d . %d %d\n",
src, dest,
src_x, src_y,
src_w, src_h,
dest_w, dest_h);
for (y = 0; y < dest_h; y++)
{
for (x = 0; x < dest_w; x++)
{
/* Avoid a 1-pixel border top-left */
if ((x >= src_x) && (x < src_x + src_w) &&
(y >= src_y) && (y < src_y + src_h))
{
dest[dest_w * y + x] =
src[src_w * (y - src_y) + (x - src_x)];
}
else
{
dest[dest_w * y + x] = 255;
}
}
}
}
static gint32
load_image (const gchar *name)
{
FILE *fd;
gboolean want_aux;
guchar *cmykbuf;
guchar *dest = NULL, *temp;
long channels, nguchars;
psd_imagetype imagetype;
gboolean cmyk = FALSE;
gint step = 1;
gint32 image_ID = -1;
gint32 layer_ID = -1;
GimpDrawable *drawable = NULL;
GimpPixelRgn pixel_rgn;
gint32 iter;
fpos_t tmpfpos;
int red_chan, grn_chan, blu_chan, alpha_chan, ichan;
IFDBG printf ("------- %s ---------------------------------\n",name);
fd = g_fopen (name, "rb");
if (! fd)
{
g_message (_("Could not open '%s' for reading: %s"),
gimp_filename_to_utf8 (name), g_strerror (errno));
return -1;
}
gimp_progress_init_printf (_("Opening '%s'"),
gimp_filename_to_utf8 (name));
read_whole_file (fd);
if (psd_image.num_layers > 0) /* PS3-style */
{
int lnum;
GimpImageBaseType gimagetype;
gimagetype = psd_mode_to_gimp_base_type (PSDheader.mode);
image_ID =
gimp_image_new (PSDheader.columns, PSDheader.rows, gimagetype);
gimp_image_set_filename (image_ID, name);
if (psd_image.resolution_is_set)
{
if (psd_image.resolution.widthUnit == 2) /* px/cm */
{
gdouble factor = gimp_unit_get_factor (GIMP_UNIT_MM) / 10.0;
psd_image.resolution.hRes *= factor;
psd_image.resolution.vRes *= factor;
}
gimp_image_set_resolution (image_ID,
psd_image.resolution.hRes / 65536.0,
psd_image.resolution.vRes / 65536.0);
/* currently can only set one unit for the image so we use the
horizontal unit from the psd image */
gimp_image_set_unit (image_ID,
psd_unit_to_gimp_unit (psd_image.resolution.widthUnit));
}
fgetpos (fd, &tmpfpos);
for (lnum = 0; lnum < psd_image.num_layers; lnum++)
{
gint numc;
guchar *merged_data = NULL;
PSDlayer *layer = psd_image.layer + lnum;
gboolean empty = FALSE;
/*
* Since PS supports sloppy bounding boxes it is possible to
* have a 0x0 or Xx0 or 0xY layer. Gimp doesn't support a
* 0x0 layer so we insert an empty layer of image size
* instead.
*/
if ((layer->width == 0) || (layer->height == 0))
{
empty = TRUE;
layer->x = 0;
layer->y = 0;
layer->width = gimp_image_width (image_ID);
layer->height = gimp_image_height (image_ID);
}
numc = layer->num_channels;
IFDBG printf ("Hey, it's a LAYER with %d channels!\n", numc);
switch (gimagetype)
{
case GIMP_GRAY:
{
IFDBG printf("It's GRAY.\n");
if (! empty)
{
if (!psd_layer_has_alpha (layer))
{
merged_data = g_malloc (layer->width * layer->height);
seek_to_and_unpack_pixeldata (fd, lnum, 0);
memcpy (merged_data, layer->channel[0].data,
layer->width * layer->height);
g_free (layer->channel[0].data);
}
else
{
seek_to_and_unpack_pixeldata (fd, lnum, 0);
seek_to_and_unpack_pixeldata (fd, lnum, 1);
merged_data = chans_to_GRAYA (layer->channel[1].data,
layer->channel[0].data,
layer->width *
layer->height);
g_free (layer->channel[0].data);
g_free (layer->channel[1].data);
}
}
layer_ID = gimp_layer_new (image_ID,
layer->name,
layer->width,
layer->height,
(numc == 1) ?
GIMP_GRAY_IMAGE : GIMP_GRAYA_IMAGE,
(100.0 * layer->opacity) / 255.0,
psd_lmode_to_gimp_lmode (layer->blendkey));
}
break;
case GIMP_RGB:
{
IFDBG printf ("It's RGB, %dx%d.\n", layer->width,
layer->height);
if (! empty)
{
if (!psd_layer_has_alpha (layer))
{
seek_to_and_unpack_pixeldata (fd, lnum, 0);
seek_to_and_unpack_pixeldata (fd, lnum, 1);
seek_to_and_unpack_pixeldata (fd, lnum, 2);
merged_data = chans_to_RGB (layer->channel[0].data,
layer->channel[1].data,
layer->channel[2].data,
layer->width *
layer->height);
g_free (layer->channel[0].data);
g_free (layer->channel[1].data);
g_free (layer->channel[2].data);
IFDBG fprintf (stderr, "YAH0a\n");
}
else
{
seek_to_and_unpack_pixeldata (fd, lnum, 0);
seek_to_and_unpack_pixeldata (fd, lnum, 1);
seek_to_and_unpack_pixeldata (fd, lnum, 2);
seek_to_and_unpack_pixeldata (fd, lnum, 3);
/* Fix for unexpected layer data order for files
* from PS files created by PanoTools. Rather
* than assuming an order, we find the actual order.
*/
red_chan = grn_chan = blu_chan = alpha_chan = -1;
for (ichan = 0; ichan < numc; ichan++)
{
switch (psd_image.layer[lnum].channel[ichan].type)
{
case 0: red_chan = ichan; break;
case 1: grn_chan = ichan; break;
case 2: blu_chan = ichan; break;
case -1: alpha_chan = ichan; break;
}
}
if ((red_chan < 0) ||
(grn_chan < 0) ||
(blu_chan < 0) ||
(alpha_chan < 0))
{
g_message ("Error: Cannot identify required RGBA channels");
gimp_quit ();
break;
}
merged_data =
chans_to_RGBA (psd_image.layer[lnum].channel[red_chan].data,
psd_image.layer[lnum].channel[grn_chan].data,
psd_image.layer[lnum].channel[blu_chan].data,
psd_image.layer[lnum].channel[alpha_chan].data,
psd_image.layer[lnum].width *
psd_image.layer[lnum].height);
g_free (layer->channel[0].data);
g_free (layer->channel[1].data);
g_free (layer->channel[2].data);
g_free (layer->channel[3].data);
IFDBG fprintf (stderr, "YAH0b\n");
}
}
IFDBG fprintf (stderr, "YAH1\n");
layer_ID = gimp_layer_new (image_ID,
psd_image.layer[lnum].name,
psd_image.layer[lnum].width,
psd_image.layer[lnum].height,
psd_layer_has_alpha (&psd_image.layer[lnum]) ?
GIMP_RGBA_IMAGE : GIMP_RGB_IMAGE,
(100.0 * psd_image.layer[lnum].opacity) / 255.0,
psd_lmode_to_gimp_lmode(psd_image.layer[lnum].blendkey));
IFDBG fprintf(stderr, "YAH2\n");
}
break;
default:
g_message ("Error: Sorry, can't deal with a layered image of this type.\n");
gimp_quit();
break;
}
gimp_image_add_layer (image_ID, layer_ID, 0);
IFDBG fprintf (stderr, "YAH3\n");
/* Do a layer mask if it exists */
for (iter = 0; !empty && iter < layer->num_channels; iter++)
{
if (layer->channel[iter].type == -2) /* is mask */
{
gint32 mask_id;
guchar *lm_data;
IFDBG fprintf (stderr, "Unpacking a layer mask!\n");
lm_data = g_malloc (layer->width * layer->height);
{
#if PANOTOOLS_FIX
guchar *tmp;
#endif
seek_to_and_unpack_pixeldata (fd, lnum, iter);
/* PS layer masks can be a different size to
their owning layer, so we have to resize them. */
resize_mask (layer->channel[iter].data,
lm_data,
layer->lm_x - layer->x,
layer->lm_y - layer->y,
layer->lm_width, layer->lm_height,
layer->width, layer->height);
/* won't be needing the original data any more */
g_free (layer->channel[iter].data);
/* give it to GIMP */
mask_id = gimp_layer_create_mask (layer_ID, 0);
#if PANOTOOLS_FIX
/* Convert the layer RGB data (not the mask) to RGBA */
tmp = merged_data;
merged_data = RGB_to_RGBA (tmp,
psd_image.layer[lnum].width *
psd_image.layer[lnum].height);
g_free (tmp);
/* Add alpha - otherwise cannot add layer mask */
gimp_layer_add_alpha (layer_ID);
/* Add layer mask */
#endif /* PANOTOOLS_FIX */
IFDBG printf ("Adding layer mask %d to layer %d\n",
mask_id, layer_ID);
gimp_layer_add_mask (layer_ID, mask_id);
drawable = gimp_drawable_get (mask_id);
gimp_pixel_rgn_init (&pixel_rgn, drawable, 0, 0,
layer->width, layer->height,
TRUE, FALSE);
gimp_pixel_rgn_set_rect (&pixel_rgn, lm_data, 0, 0,
layer->width, layer->height);
}
g_free (lm_data);
gimp_drawable_flush (drawable);
gimp_drawable_detach (drawable);
}
}
IFDBG fprintf(stderr, "YAH4\n");
gimp_layer_translate (layer_ID, layer->x, layer->y);
gimp_layer_set_lock_alpha (layer_ID, layer->protecttrans);
gimp_drawable_set_visible (layer_ID, layer->visible);
drawable = gimp_drawable_get (layer_ID);
IFDBG fprintf (stderr, "YAH5 - merged_data=%p, drawable=%p, drawdim=%dx%dx%d\n",
merged_data,
drawable,
drawable->width,
drawable->height,
drawable->bpp);
if (empty)
{
gimp_drawable_fill (layer_ID, GIMP_TRANSPARENT_FILL);
}
else
{
gimp_pixel_rgn_init (&pixel_rgn, drawable, 0, 0,
layer->width, layer->height,
TRUE, FALSE);
gimp_pixel_rgn_set_rect (&pixel_rgn, merged_data, 0, 0,
layer->width, layer->height);
IFDBG fprintf(stderr, "YAH6\n");
}
gimp_drawable_flush (drawable);
gimp_drawable_detach (drawable);
drawable = NULL;
g_free (merged_data);
gimp_progress_update ((double) (lnum+1.0) /
(double) psd_image.num_layers);
}
fsetpos (fd, &tmpfpos);
}
if ((psd_image.num_aux_channels > 0) &&
(psd_image.num_layers > 0))
{
want_aux = TRUE;
IFDBG printf ("::::::::::: WANT AUX :::::::::::::::::::::::::::::::::::::::\n");
}
else
{
want_aux = FALSE;
}
if (want_aux || (psd_image.num_layers == 0)) /* Photoshop2-style: NO LAYERS. */
{
IFDBG printf ("Image data %ld chars\n", PSDheader.imgdatalen);
step = PSDheader.channels;
imagetype = PSD_UNKNOWN_IMAGE;
switch (PSDheader.mode)
{
case 0: /* Bitmap */
imagetype = PSD_BITMAP_IMAGE;
break;
case 1: /* Grayscale */
imagetype = PSD_GRAY_IMAGE;
break;
case 2: /* Indexed Colour */
imagetype = PSD_INDEXED_IMAGE;
break;
case 3: /* RGB Colour */
imagetype = PSD_RGB_IMAGE;
break;
case 4: /* CMYK Colour */
cmyk = TRUE;
switch (PSDheader.channels)
{
case 4:
imagetype = PSD_RGB_IMAGE;
break;
case 5:
imagetype = PSD_RGBA_IMAGE;
break;
default:
printf ("PSD: cannot handle CMYK with more than 5 channels\n");
return -1;
break;
}
break;
case 7: /* Multichannel (?) */
case 8: /* Duotone */
case 9: /* Lab Colour */
default:
break;
}
if (imagetype == PSD_UNKNOWN_IMAGE)
{
printf("PSD: Image type %d (%s) is not supported in this data format\n",
PSDheader.mode, (PSDheader.mode > 10) ?
"<out of range>" : modename[PSDheader.mode]);
return -1;
}
if ((PSDheader.bpp != 8) && (PSDheader.bpp != 1))
{
printf("PSD: The GIMP only supports 8-bit or 1-bit deep PSD images "
"at this time.\n");
return -1;
}
IFDBG printf ("psd:%d gimp:%d gimpbase:%d\n",
imagetype,
psd_type_to_gimp_type(imagetype),
psd_type_to_gimp_base_type(imagetype));
if (!want_aux)
{
/* gimp doesn't like 0 width/height drawables. */
if ((PSDheader.columns == 0) || (PSDheader.rows == 0))
{
IFDBG printf ("(bad psd2-style image dimensions -- skipping)");
image_ID = -1;
goto finish_up;
}
image_ID = gimp_image_new (PSDheader.columns, PSDheader.rows,
psd_type_to_gimp_base_type(imagetype));
gimp_image_set_filename (image_ID, name);
if (psd_type_to_gimp_base_type (imagetype) == GIMP_INDEXED)
{
if ((psd_image.colmaplen % 3) != 0)
printf("PSD: Colourmap looks screwed! Aiee!\n");
if (psd_image.colmaplen == 0)
printf("PSD: Indexed image has no colourmap!\n");
if (psd_image.colmaplen != 768)
printf("PSD: Warning: Indexed image is %ld!=256 colours.\n",
psd_image.colmaplen / 3);
if (psd_image.colmaplen == 768)
{
reshuffle_cmap (psd_image.colmapdata);
gimp_image_set_colormap (image_ID,
psd_image.colmapdata,
256);
}
}
layer_ID = gimp_layer_new (image_ID, _("Background"),
PSDheader.columns, PSDheader.rows,
psd_type_to_gimp_type(imagetype),
100, GIMP_NORMAL_MODE);
gimp_image_add_layer (image_ID, layer_ID, 0);
drawable = gimp_drawable_get (layer_ID);
}
if (want_aux)
{
switch (PSDheader.mode)
{
case 1: /* Grayscale */
channels = 1;
break;
case 2: /* Indexed Colour */
channels = 1;
break;
case 3: /* RGB Colour */
channels = 3;
break;
default:
printf ("aux? Aieeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee!!!!!!!!!\n");
channels = 1;
break;
}
channels = PSDheader.channels - channels;
if (psd_image.absolute_alpha)
{
channels--;
}
}
else
{
channels = gimp_drawable_bpp (drawable->drawable_id);
}
dest = g_malloc (step * PSDheader.columns * PSDheader.rows);
if (PSDheader.compression == 1)
{
nguchars = PSDheader.columns * PSDheader.rows;
temp = g_malloc (PSDheader.imgdatalen);
xfread (fd, temp, PSDheader.imgdatalen, "image data");
if (!cmyk)
{
gimp_progress_update (1.0);
if (imagetype == PSD_BITMAP_IMAGE) /* convert bitmap to grayscale */
{
guchar *monobuf;
monobuf =
g_malloc (((PSDheader.columns + 7) >> 3) * PSDheader.rows);
decode (PSDheader.imgdatalen,
nguchars >> 3, temp,monobuf, step);
bitmap2gray (monobuf, dest,
PSDheader.columns, PSDheader.rows);
g_free (monobuf);
}
else
{
decode (PSDheader.imgdatalen, nguchars, temp, dest, step);
}
}
else
{
gimp_progress_update (1.0);
cmykbuf = g_malloc (step * nguchars);
decode (PSDheader.imgdatalen, nguchars, temp, cmykbuf, step);
cmyk2rgb (cmykbuf, dest, PSDheader.columns, PSDheader.rows,
step > 4);
g_free (cmykbuf);
}
g_free (temp);
}
else
{
if (!cmyk)
{
gimp_progress_update (1.0);
xfread_interlaced (fd, dest, PSDheader.imgdatalen,
"raw image data", step);
}
else
{
gimp_progress_update (10);
cmykbuf = g_malloc (PSDheader.imgdatalen);
xfread_interlaced (fd, cmykbuf, PSDheader.imgdatalen,
"raw cmyk image data", step);
cmykp2rgb (cmykbuf, dest,
PSDheader.columns, PSDheader.rows, step > 4);
g_free (cmykbuf);
}
}
if (want_aux) /* want_aux */
{
extract_channels (dest, channels, step,
image_ID,
PSDheader.columns, PSDheader.rows);
goto finish_up; /* Haha! Look! A goto! */
}
else
{
gimp_progress_update (1.0);
if (channels == step) /* gimp bpp == psd bpp */
{
if (psd_type_to_gimp_type (imagetype) == GIMP_INDEXEDA_IMAGE)
{
printf ("@@@@ Didn't know that this could happen...\n");
for (iter = 0; iter < drawable->width * drawable->height; iter++)
{
dest[iter * 2 + 1] = 255;
}
}
gimp_pixel_rgn_init (&pixel_rgn, drawable,
0, 0, drawable->width, drawable->height,
TRUE, FALSE);
gimp_pixel_rgn_set_rect (&pixel_rgn, dest,
0, 0, drawable->width, drawable->height);
gimp_drawable_flush (drawable);
gimp_drawable_detach (drawable);
}
else
{
IFDBG printf ("Uhhh... uhm... extra channels... heavy...\n");
extract_data_and_channels (dest, channels, step,
image_ID, drawable,
drawable->width, drawable->height);
}
}
finish_up:
g_free (dest);
if (psd_image.colmaplen > 0)
g_free(psd_image.colmapdata);
}
if (psd_image.num_guides > 0)
{
PSDguide *guide = psd_image.guides;
int i;
IFDBG printf ("--- Adding %d Guides\n", psd_image.num_guides);
for (i = 0; i < psd_image.num_guides; i++, guide++)
{
if (guide->horizontal)
gimp_image_add_hguide (image_ID, guide->position);
else
gimp_image_add_vguide (image_ID, guide->position);
}
}
gimp_displays_flush ();
IFDBG printf ("--- %d layers : pos %ld : a-alph %d ---\n",
psd_image.num_layers, (long int)ftell(fd),
psd_image.absolute_alpha);
return image_ID;
}
static void
decode (long clen,
long uclen,
guchar *src,
guchar *dst,
int step)
{
gint i, j;
gint32 l;
gushort *w;
l = clen;
for (i = 0; i < PSDheader.rows*PSDheader.channels; ++i)
{
l -= PSDheader.rowlength[i];
}
if (l)
{
g_warning("decode: %ld should be zero\n", (long)l);
}
w = PSDheader.rowlength;
packbitsdecode (&clen, uclen, src, dst++, step);
for (j = 0; j < step-1; ++j)
{
for (i = 0; i < PSDheader.rows; ++i)
{
src += *w++;
}
packbitsdecode (&clen, uclen, src, dst++, step);
}
IFDBG printf ("clen %ld\n", clen);
}
/*
* Decode a PackBits data stream.
*/
static void
packbitsdecode (long *clenp,
long uclen,
guchar *src,
guchar *dst,
int step)
{
gint n, b;
gint32 clen = *clenp;
while ((clen > 0) && (uclen > 0))
{
n = (int) *src++;
if (n >= 128)
n -= 256;
if (n < 0)
{
/* replicate next guchar -n+1 times */
clen -= 2;
if (n == -128) /* nop */
continue;
n = -n + 1;
uclen -= n;
for (b = *src++; n > 0; --n)
{
*dst = b;
dst += step;
}
}
else
{
/* copy next n+1 guchars literally */
for (b = ++n; b > 0; --b)
{
*dst = *src++;
dst += step;
}
uclen -= n;
clen -= n+1;
}
}
if (uclen > 0)
{
printf ("PSD: unexpected EOF while reading image data\n");
gimp_quit ();
}
*clenp = clen;
}
/*
* Decode a PackBits channel from file.
*/
static void
unpack_pb_channel (FILE *fd,
guchar *dst,
gint32 unpackedlen,
guint32 *offset)
{
int n, b;
gint32 upremain = unpackedlen;
while (upremain > 0)
{
n = (int) getguchar(fd, "packbits1");
(*offset)++;
if (n >= 128)
n -= 256;
if (n < 0)
{
/* replicate next guchar -n+1 times */
if (n == -128) /* nop */
continue;
n = -n + 1;
/* upremain -= n;*/
b = getguchar(fd, "packbits2");
(*offset)++;
for (; n > 0; --n)
{
if (upremain >= 0)
{
*dst = b;
dst ++;
}
upremain--;
}
}
else
{
/* copy next n+1 guchars literally */
for (b = ++n; b > 0; --b)
{
const guchar c = getguchar (fd, "packbits3");
if (upremain >= 0)
{
*dst = c;
dst ++;
}
(*offset)++;
upremain--;
}
/* upremain -= n;*/
}
}
if (upremain < 0)
{
printf("*** Unpacking overshot destination (%d) buffer by %d bytes!\n",
unpackedlen,
-upremain);
}
}
static void
cmyk2rgb (unsigned char *src,
unsigned char *dst,
long width,
long height,
int alpha)
{
int r, g, b, k;
int i, j;
for (i = 0; i < height; i++)
{
for (j = 0; j < width; j++)
{
r = *src++;
g = *src++;
b = *src++;
k = *src++;
gimp_cmyk_to_rgb_int (&r, &g, &b, &k);
*dst++ = r;
*dst++ = g;
*dst++ = b;
if (alpha)
*dst++ = *src++;
}
if ((i % 5) == 0)
gimp_progress_update ((2.0 * height) / ((double) height + (double) i));
}
}
/*
* Decode planar CMYK(A) to RGB(A).
*/
static void
cmykp2rgb (unsigned char *src,
unsigned char *dst,
long width,
long height,
int alpha)
{
int r, g, b, k;
int i, j;
long n;
guchar *rp, *gp, *bp, *kp, *ap;
n = width * height;
rp = src;
gp = rp + n;
bp = gp + n;
kp = bp + n;
ap = kp + n;
for (i = 0; i < height; i++)
{
for (j = 0; j < width; j++)
{
r = *rp++;
g = *gp++;
b = *bp++;
k = *kp++;
gimp_cmyk_to_rgb_int (&r, &g, &b, &k);
*dst++ = r;
*dst++ = g;
*dst++ = b;
if (alpha)
*dst++ = *ap++;
}
if ((i % 5) == 0)
gimp_progress_update ((2.0 * height) / ((double) height + (double) i));
}
}
static void
bitmap2gray (guchar *src,
guchar *dest,
long w,
long h)
{
int i,j;
for(i = 0; i < h; i++)
{
int mask = 0x80;
for(j = 0; j < w; j++)
{
*dest++ = (*src&mask) ? 0 : 255;
mask >>= 1;
if(!mask)
{
src++;
mask = 0x80;
}
}
if (mask != 0x80) src++;
}
}
static void
dumpchunk (size_t n,
FILE *fd,
gchar *why)
{
guint32 i;
printf("\n");
for (i = 0; i < n; i++)
{
printf ("%02x ", (int) getguchar (fd, why));
}
printf ("\n");
}
static void
throwchunk (size_t n,
FILE *fd,
gchar *why)
{
#if 0
guchar *tmpchunk;
if (n == 0)
return;
tmpchunk = g_malloc (n);
xfread (fd, tmpchunk, n, why);
g_free (tmpchunk);
#else
if (n == 0)
return;
if (fseek (fd, n, SEEK_CUR) != 0)
{
printf ("PSD: unable to seek forward while reading '%s' chunk\n", why);
gimp_quit();
}
#endif
}
static gchar *
getstring (size_t n,
FILE *fd,
gchar *why)
{
gchar *tmpchunk;
tmpchunk = g_malloc (n + 1);
xfread (fd, tmpchunk, n, why);
tmpchunk[n] = 0;
return tmpchunk; /* caller should free memory */
}
static gchar *
getpascalstring (FILE *fd,
gchar *why)
{
guchar *tmpchunk;
guchar len;
xfread (fd, &len, 1, why);
if (len == 0)
return NULL;
tmpchunk = g_malloc(len+1);
xfread(fd, tmpchunk, len, why);
tmpchunk[len]=0;
return (gchar *) tmpchunk; /* caller should free memory */
}
static guchar
getguchar (FILE *fd,
gchar *why)
{
gint tmp;
tmp = fgetc (fd);
if (tmp == EOF)
{
printf ("PSD: unexpected EOF while reading '%s' chunk\n", why);
gimp_quit ();
}
return tmp;
}
static gshort
getgshort (FILE *fd,
gchar *why)
{
guchar b1, b2;
b1 = getguchar (fd, why);
b2 = getguchar (fd, why);
return (gshort) ((b1 * 256) + b2);
}
static glong
getglong (FILE *fd,
gchar *why)
{
guchar s1, s2, s3, s4;
s1 = getguchar (fd, why);
s2 = getguchar (fd, why);
s3 = getguchar (fd, why);
s4 = getguchar (fd, why);
return (glong) ((s1 * 256 * 256 * 256) +
(s2 * 256 * 256) +
(s3 * 256) +
s4);
}
static void
xfread (FILE *fd,
void *buf,
long len,
gchar *why)
{
if (fread (buf, len, 1, fd) == 0)
{
printf ("PSD: unexpected EOF while reading '%s' chunk\n", why);
gimp_quit();
}
}
static void
xfread_interlaced (FILE *fd,
guchar *buf,
long len,
gchar *why,
gint step)
{
guchar *dest;
gint pix, pos, bpplane;
bpplane = len / step;
if (len % step != 0)
{
printf ("PSD: Stern warning: data size is not a factor of step size!\n");
}
for (pix = 0; pix < step; pix++)
{
dest = buf + pix;
for (pos = 0; pos < bpplane; pos++)
{
*dest = getguchar (fd, why);
dest += step;
}
}
}
static void
read_whole_file (FILE *fd)
{
guint16 w;
gint32 pos;
gchar dummy[6];
gint i;
xfread (fd, &PSDheader.signature, 4, "signature");
PSDheader.version = getgshort (fd, "version");
xfread (fd, &dummy, 6, "reserved");
PSDheader.channels = getgshort (fd, "channels");
PSDheader.rows = getglong (fd, "rows");
PSDheader.columns = getglong (fd, "columns");
PSDheader.bpp = getgshort (fd, "depth");
PSDheader.mode = getgshort (fd, "mode");
psd_image.num_layers = 0;
psd_image.type = PSDheader.mode;
psd_image.colmaplen = 0;
psd_image.num_aux_channels = 0;
psd_image.num_guides = 0;
psd_image.colmaplen = getglong (fd, "color data length");
if (psd_image.colmaplen > 0)
{
psd_image.colmapdata = g_malloc (psd_image.colmaplen);
xfread (fd, psd_image.colmapdata, psd_image.colmaplen, "colormap");
}
PSDheader.imgreslen = getglong (fd, "image resource length");
if (PSDheader.imgreslen > 0)
{
do_image_resources (fd);
}
PSDheader.miscsizelen = getglong (fd, "misc size data length");
if (PSDheader.miscsizelen > 0)
{
do_layer_and_mask (fd);
}
PSDheader.compression = getgshort (fd, "compression");
IFDBG printf("<<compr:%d>>", (int) PSDheader.compression);
if (PSDheader.compression == 1) /* RLE */
{
PSDheader.rowlength = g_malloc (PSDheader.rows *
PSDheader.channels * sizeof(gushort));
for (i = 0; i < PSDheader.rows * PSDheader.channels; ++i)
PSDheader.rowlength[i] = getgshort (fd, "x");
}
pos = ftell (fd);
fseek (fd, 0, SEEK_END);
PSDheader.imgdatalen = ftell (fd) - pos;
fseek (fd, pos, SEEK_SET);
if (strncmp (PSDheader.signature, "8BPS", 4) != 0)
{
printf ("PSD: not an Adobe Photoshop PSD file\n");
gimp_quit ();
}
if (PSDheader.version != 1)
{
printf ("PSD: bad version number '%d', not 1\n", PSDheader.version);
gimp_quit ();
}
w = PSDheader.mode;
IFDBG printf ("HEAD:\n"
"\tChannels %d\n\tRows %ld\n\tColumns %ld\n\tDepth %d\n\tMode %d (%s)\n"
"\tColour data %ld guchars\n",
PSDheader.channels, PSDheader.rows,
PSDheader.columns, PSDheader.bpp,
w, modename[w < 10 ? w : 10],
psd_image.colmaplen);
/* printf("\tImage resource length: %lu\n", PSDheader.imgreslen);*/
IFDBG printf ("\tLayer/Mask Data length: %lu\n", PSDheader.miscsizelen);
w = PSDheader.compression;
IFDBG printf ("\tCompression %d (%s)\n", w, w ? "RLE" : "raw");
}