Greatly reduced memory requirements for layered image loading - we now do

Sun Jan 10 20:13:13 GMT 1999 Adam D. Moss <adam@gimp.org>

	* plug-ins/psd/psd.c:
        Greatly reduced memory requirements for layered image loading -
        we now do just-in-time channel unpacking.  Some little
        cleanups too.
This commit is contained in:
GMT 1999 Adam D. Moss 1999-01-10 20:15:45 +00:00 committed by Adam D. Moss
parent f0558a7ea2
commit 1fa91b23aa
3 changed files with 481 additions and 360 deletions

View File

@ -1,3 +1,10 @@
Sun Jan 10 20:13:13 GMT 1999 Adam D. Moss <adam@gimp.org>
* plug-ins/psd/psd.c:
Greatly reduced memory requirements for layered image loading -
we now do just-in-time channel unpacking. Some little
cleanups too.
Sat Jan 9 21:24:20 PST 1999 Manish Singh <yosh@gimp.org> Sat Jan 9 21:24:20 PST 1999 Manish Singh <yosh@gimp.org>
* autogen.sh: rework to do gcg by ourselves * autogen.sh: rework to do gcg by ourselves

View File

@ -1,5 +1,5 @@
/* /*
* PSD Plugin version 2.0.0 * PSD Plugin version 2.0.1
* This GIMP plug-in is designed to load Adobe Photoshop(tm) files (.PSD) * This GIMP plug-in is designed to load Adobe Photoshop(tm) files (.PSD)
* *
* Adam D. Moss <adam@gimp.org> <adam@foxbox.org> * Adam D. Moss <adam@gimp.org> <adam@foxbox.org>
@ -35,67 +35,68 @@
/* /*
* Revision history: * Revision history:
* *
* 98.09.04 / v2.0.0 / Adam D. Moss * 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 * Now recognises and loads the new Guides extensions written
* by Photoshop 4 and 5. * by Photoshop 4 and 5.
* *
* 98.07.31 / v1.9.9.9f / Adam D. Moss * 1998.07.31 / v1.9.9.9f / Adam D. Moss
* Use OVERLAY_MODE if available. * Use OVERLAY_MODE if available.
* *
* 98.07.31 / v1.9.9.9e / Adam D. Moss * 1998.07.31 / v1.9.9.9e / Adam D. Moss
* Worked around some buggy PSD savers (suspect PS4 on Mac) - ugh. * Worked around some buggy PSD savers (suspect PS4 on Mac) - ugh.
* Fixed a bug when loading layer masks of certain dimensions. * Fixed a bug when loading layer masks of certain dimensions.
* *
* 98.05.04 / v1.9.9.9b / Adam D. Moss * 1998.05.04 / v1.9.9.9b / Adam D. Moss
* Changed the Pascal-style string-reading stuff. That fixed * Changed the Pascal-style string-reading stuff. That fixed
* some file-padding problems. Made all debugging output * some file-padding problems. Made all debugging output
* compile-time optional (please leave it enabled for now). * compile-time optional (please leave it enabled for now).
* Reduced memory requirements; still much room for improvement. * Reduced memory requirements; still much room for improvement.
* *
* 98.04.28 / v1.9.9.9 / Adam D. Moss * 1998.04.28 / v1.9.9.9 / Adam D. Moss
* Fixed the correct channel interlacing of 'raw' flat images. * Fixed the correct channel interlacing of 'raw' flat images.
* Thanks to Christian Kirsch and Jay Cox for spotting this. * Thanks to Christian Kirsch and Jay Cox for spotting this.
* Changed some of the I/O routines. * Changed some of the I/O routines.
* *
* 98.04.26 / v1.9.9.8 / Adam D. Moss * 1998.04.26 / v1.9.9.8 / Adam D. Moss
* Implemented Aux-channels for layered files. Got rid * Implemented Aux-channels for layered files. Got rid
* of <endian.h> nonsense. Improved Layer Mask padding. * of <endian.h> nonsense. Improved Layer Mask padding.
* Enforced num_layers/num_channels limit checks. * Enforced num_layers/num_channels limit checks.
* *
* 98.04.23 / v1.9.9.5 / Adam D. Moss * 1998.04.23 / v1.9.9.5 / Adam D. Moss
* Got Layer Masks working, got Aux-channels working * Got Layer Masks working, got Aux-channels working
* for unlayered files, fixed 'raw' channel loading, fixed * for unlayered files, fixed 'raw' channel loading, fixed
* some other mini-bugs, slightly better progress meters. * some other mini-bugs, slightly better progress meters.
* Thanks to everyone who is helping with the testing! * Thanks to everyone who is helping with the testing!
* *
* 98.04.21 / v1.9.9.1 / Adam D. Moss * 1998.04.21 / v1.9.9.1 / Adam D. Moss
* A little cleanup. Implemented Layer Masks but disabled * A little cleanup. Implemented Layer Masks but disabled
* them again - PS masks can be a different size to their * them again - PS masks can be a different size to their
* owning layer, unlike those in GIMP. * owning layer, unlike those in GIMP.
* *
* 98.04.19 / v1.9.9.0 / Adam D. Moss * 1998.04.19 / v1.9.9.0 / Adam D. Moss
* Much happier now. * Much happier now.
* *
* 97.03.13 / v1.9.0 / Adam D. Moss * 1997.03.13 / v1.9.0 / Adam D. Moss
* Layers, channels and masks, oh my. * Layers, channels and masks, oh my.
* + Bugfixes & rearchitecturing. * + Bugfixes & rearchitecturing.
* *
* 97.01.30 / v1.0.12 / Torsten Martinsen * 1997.01.30 / v1.0.12 / Torsten Martinsen
* Flat PSD image loading. * Flat PSD image loading.
*/ */
/* /*
* TODO: * TODO:
* *
* Crush 16bpp channels * Crush 16bpp channels (Wait until GIMP 2.0, probably)
* CMYK -> RGB * CMYK -> RGB
* Load BITMAP mode * Load BITMAP mode
* *
* File saving. (I am unlikely to be able to do this for * File saving
* practical reasons. Suggest someone works on it as a
* separate plugin - please let me know.)
*
* Reduce memory requirements!
*/ */
/* /*
@ -106,7 +107,7 @@
/* *** DEFINES *** */ /* *** USER DEFINES *** */
/* set to TRUE if you want debugging, FALSE otherwise */ /* set to TRUE if you want debugging, FALSE otherwise */
#define PSD_DEBUG TRUE #define PSD_DEBUG TRUE
@ -120,7 +121,7 @@
/* the max number of guides that this plugin should let an image have */ /* the max number of guides that this plugin should let an image have */
#define MAX_GUIDES 200 #define MAX_GUIDES 200
/* *** END OF DEFINES *** */ /* *** END OF USER DEFINES *** */
@ -149,35 +150,6 @@ typedef enum
} psd_imagetype; } psd_imagetype;
/* Declare some local functions.
*/
static void query (void);
static void run (char *name,
int nparams,
GParam *param,
int *nreturn_vals,
GParam **return_vals);
GDrawableType psd_type_to_gimp_type
(psd_imagetype psdtype);
GImageType psd_type_to_gimp_base_type
(psd_imagetype psdtype);
GLayerMode psd_lmode_to_gimp_lmode
(gchar modekey[4]);
static gint32 load_image (char *filename);
GPlugInInfo PLUG_IN_INFO =
{
NULL, /* init_proc */
NULL, /* quit_proc */
query, /* query_proc */
run, /* run_proc */
};
typedef struct PsdChannel typedef struct PsdChannel
{ {
gchar *name; gchar *name;
@ -186,8 +158,18 @@ typedef struct PsdChannel
guint32 compressedsize; guint32 compressedsize;
} PSDchannel; 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 typedef struct PsdGuide
@ -197,7 +179,6 @@ typedef struct PsdGuide
} PSDguide; } PSDguide;
typedef struct PsdLayer typedef struct PsdLayer
{ {
gint num_channels; gint num_channels;
@ -225,7 +206,6 @@ typedef struct PsdLayer
} PSDlayer; } PSDlayer;
typedef struct PsdImage typedef struct PsdImage
{ {
gint num_layers; gint num_layers;
@ -247,15 +227,42 @@ typedef struct PsdImage
gchar *caption; gchar *caption;
guint active_layer_num; guint active_layer_num;
} PSDimage; } PSDimage;
static PSDimage psd_image; /* Declare some local functions.
*/
static void query (void);
static void run (char *name,
int nparams,
GParam *param,
int *nreturn_vals,
GParam **return_vals);
static GDrawableType psd_type_to_gimp_type
(psd_imagetype psdtype);
static GImageType psd_type_to_gimp_base_type
(psd_imagetype psdtype);
static GLayerMode psd_lmode_to_gimp_lmode
(gchar modekey[4]);
static gint32 load_image (char *filename);
/* Various local variables...
*/
GPlugInInfo PLUG_IN_INFO =
{
NULL, /* init_proc */
NULL, /* quit_proc */
query, /* query_proc */
run, /* run_proc */
};
static PSDimage psd_image;
static struct { static struct {
gchar signature[4]; gchar signature[4];
gushort version; gushort version;
@ -312,8 +319,9 @@ static void read_whole_file(FILE *fd);
static void reshuffle_cmap(guchar *map256); static void reshuffle_cmap(guchar *map256);
static guchar* getpascalstring(FILE *fd, gchar *why); static guchar* getpascalstring(FILE *fd, gchar *why);
static gchar* getstring(size_t n, FILE * fd, gchar *why); static gchar* getstring(size_t n, FILE * fd, gchar *why);
void throwchunk(size_t n, FILE * fd, gchar *why); static void throwchunk(size_t n, FILE * fd, gchar *why);
void dumpchunk(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);
MAIN() MAIN()
@ -390,7 +398,7 @@ run (char *name,
} }
GDrawableType static GDrawableType
psd_type_to_gimp_type (psd_imagetype psdtype) psd_type_to_gimp_type (psd_imagetype psdtype)
{ {
switch(psdtype) switch(psdtype)
@ -407,7 +415,7 @@ psd_type_to_gimp_type (psd_imagetype psdtype)
GLayerMode static GLayerMode
psd_lmode_to_gimp_lmode (gchar modekey[4]) psd_lmode_to_gimp_lmode (gchar modekey[4])
{ {
if (strncmp(modekey, "norm", 4)==0) return(NORMAL_MODE); if (strncmp(modekey, "norm", 4)==0) return(NORMAL_MODE);
@ -444,7 +452,7 @@ psd_lmode_to_gimp_lmode (gchar modekey[4])
GImageType static GImageType
psd_type_to_gimp_base_type (psd_imagetype psdtype) psd_type_to_gimp_base_type (psd_imagetype psdtype)
{ {
switch(psdtype) switch(psdtype)
@ -787,7 +795,10 @@ dispatch_resID(guint ID, FILE *fd, guint32 *offset, guint32 Size)
default: default:
IFDBG printf ("\t\t<Undocumented field.>\n"); IFDBG printf ("\t\t<Undocumented field.>\n");
IFDBG
dumpchunk(Size, fd, "dispatch_res"); dumpchunk(Size, fd, "dispatch_res");
else
throwchunk(Size, fd, "dispatch_res");
(*offset) += Size; (*offset) += Size;
break; break;
} }
@ -1013,9 +1024,7 @@ do_layer_struct(FILE *fd, guint32 *offset)
static void static void
do_layer_pixeldata(FILE *fd, guint32 *offset) do_layer_pixeldata(FILE *fd, guint32 *offset)
{ {
guint16 compression;
gint layeri, channeli; gint layeri, channeli;
guchar *tmpline;
for (layeri=0; layeri<psd_image.num_layers; layeri++) for (layeri=0; layeri<psd_image.num_layers; layeri++)
{ {
@ -1036,10 +1045,34 @@ do_layer_pixeldata(FILE *fd, guint32 *offset)
height = psd_image.layer[layeri].height; height = psd_image.layer[layeri].height;
} }
tmpline = xmalloc(width + 1); fgetpos(fd, &psd_image.layer[layeri].channel[channeli].fpos);
psd_image.layer[layeri].channel[channeli].width = width;
psd_image.layer[layeri].channel[channeli].height = height;
throwchunk(psd_image.layer[layeri].channel[channeli].compressedsize,
fd, "channel data skip");
(*offset) +=
psd_image.layer[layeri].channel[channeli].compressedsize;
}
}
}
static void
seek_to_and_unpack_pixeldata(FILE* fd, gint layeri, gint channeli)
{
int width, height;
guchar *tmpline;
gint compression;
guint32 offset = 0;
fsetpos(fd, &psd_image.layer[layeri].channel[channeli].fpos);
compression = getgshort(fd, "layer channel compression type"); compression = getgshort(fd, "layer channel compression type");
(*offset)+=2; offset+=2;
width = psd_image.layer[layeri].channel[channeli].width;
height = psd_image.layer[layeri].channel[channeli].height;
IFDBG IFDBG
{ {
@ -1048,41 +1081,38 @@ do_layer_pixeldata(FILE *fd, guint32 *offset)
channeli, channeli,
psd_image.layer[layeri].channel[channeli].type, psd_image.layer[layeri].channel[channeli].type,
compression, compression,
compression==0?"raw":(compression==1?"RLE":"UNKNOWN!")); compression==0?"raw":(compression==1?"RLE":"*UNKNOWN!*"));
fflush(stdout); fflush(stdout);
} }
IFDBG {printf("\t\t\t\tLoading channel data (%d bytes)...\n",
width*height);fflush(stdout);}
psd_image.layer[layeri].channel[channeli].data = psd_image.layer[layeri].channel[channeli].data =
xmalloc (width * height); xmalloc (width * height);
tmpline = xmalloc(width + 1);
switch (compression) switch (compression)
{ {
case 0: /* raw data */ case 0: /* raw data */
{ {
gint linei; gint linei;
for (linei=0; for (linei=0; linei < height; linei++)
linei<height;
linei++)
{ {
xfread(fd, xfread(fd,
(psd_image.layer[layeri].channel[channeli].data+ (psd_image.layer[layeri].channel[channeli].data+
linei * width), linei * width),
width, width,
"raw channel line"); "raw channel line");
(*offset) += width; offset += width;
} }
/* Pad raw data to multiple of 2? */
#if 0 #if 0
if ((height * /* Pad raw data to multiple of 2? */
width) & 1) if ((height * width) & 1)
{ {
getguchar(fd, "raw channel padding"); getguchar(fd, "raw channel padding");
(*offset)++; offset++;
} }
#endif #endif
} }
@ -1096,9 +1126,9 @@ do_layer_pixeldata(FILE *fd, guint32 *offset)
data to unpack to the right length... hmm... */ data to unpack to the right length... hmm... */
throwchunk(height * 2, throwchunk(height * 2,
fd, "widthlist"); fd, "widthlist");
(*offset) += height * 2; offset += height * 2;
blockread = (*offset); blockread = offset;
/*IFDBG {printf("\nHere comes the guitar solo...\n"); /*IFDBG {printf("\nHere comes the guitar solo...\n");
fflush(stdout);}*/ fflush(stdout);}*/
@ -1111,7 +1141,7 @@ do_layer_pixeldata(FILE *fd, guint32 *offset)
unpack_pb_channel(fd, tmpline, unpack_pb_channel(fd, tmpline,
width width
/*+ (width&1)*/, /*+ (width&1)*/,
offset); &offset);
memcpy((psd_image.layer[layeri].channel[channeli].data+ memcpy((psd_image.layer[layeri].channel[channeli].data+
linei * width), linei * width),
tmpline, tmpline,
@ -1119,7 +1149,7 @@ do_layer_pixeldata(FILE *fd, guint32 *offset)
} }
IFDBG {printf("\t\t\t\t\tActual compressed size was %d bytes\n" IFDBG {printf("\t\t\t\t\tActual compressed size was %d bytes\n"
, (*offset)-blockread);fflush(stdout);} , offset-blockread);fflush(stdout);}
} }
break; break;
default: /* *unknown* */ default: /* *unknown* */
@ -1134,11 +1164,6 @@ do_layer_pixeldata(FILE *fd, guint32 *offset)
else else
IFDBG {printf("\nTRIED TO FREE NULL!");fflush(stdout);} IFDBG {printf("\nTRIED TO FREE NULL!");fflush(stdout);}
} }
}
/* printf("\n[[%ld]]\n", getglong(fd, "uhhhh"));
(*offset)+=4;*/
}
static void static void
@ -1158,7 +1183,6 @@ do_layers(FILE *fd, guint32 *offset)
} }
static void static void
do_layer_and_mask(FILE *fd) do_layer_and_mask(FILE *fd)
{ {
@ -1203,12 +1227,11 @@ do_layer_and_mask(FILE *fd)
printf("PSD: Stern warning - no mask info.\n"); printf("PSD: Stern warning - no mask info.\n");
/* If 'offset' wasn't being buggily updated, we wouldn't need this. */ /* If 'offset' wasn't being buggily updated, we wouldn't need this. (!?) */
fseek(fd, Size+offset_now, SEEK_SET); fseek(fd, Size+offset_now, SEEK_SET);
} }
static void static void
do_image_resources(FILE *fd) do_image_resources(FILE *fd)
{ {
@ -1574,6 +1597,7 @@ load_image(char *name)
GDrawable *drawable = NULL; GDrawable *drawable = NULL;
GPixelRgn pixel_rgn; GPixelRgn pixel_rgn;
gint32 iter; gint32 iter;
fpos_t tmpfpos;
IFDBG printf("------- %s ---------------------------------\n",name); IFDBG printf("------- %s ---------------------------------\n",name);
@ -1615,6 +1639,8 @@ load_image(char *name)
gimp_image_new (PSDheader.columns, PSDheader.rows, gimagetype); gimp_image_new (PSDheader.columns, PSDheader.rows, gimagetype);
gimp_image_set_filename (image_ID, name); gimp_image_set_filename (image_ID, name);
fgetpos (fd, &tmpfpos);
for (lnum=0; lnum<psd_image.num_layers; lnum++) for (lnum=0; lnum<psd_image.num_layers; lnum++)
{ {
gint numc; gint numc;
@ -1633,6 +1659,7 @@ load_image(char *name)
{ {
merged_data = xmalloc(psd_image.layer[lnum].width * merged_data = xmalloc(psd_image.layer[lnum].width *
psd_image.layer[lnum].height); psd_image.layer[lnum].height);
seek_to_and_unpack_pixeldata(fd, lnum, 0);
memcpy(merged_data, psd_image.layer[lnum].channel[0].data, memcpy(merged_data, psd_image.layer[lnum].channel[0].data,
psd_image.layer[lnum].width * psd_image.layer[lnum].width *
psd_image.layer[lnum].height); psd_image.layer[lnum].height);
@ -1642,6 +1669,8 @@ load_image(char *name)
} }
else else
{ {
seek_to_and_unpack_pixeldata(fd, lnum, 0);
seek_to_and_unpack_pixeldata(fd, lnum, 1);
merged_data = merged_data =
chans_to_GRAYA (psd_image.layer[lnum].channel[1].data, chans_to_GRAYA (psd_image.layer[lnum].channel[1].data,
psd_image.layer[lnum].channel[0].data, psd_image.layer[lnum].channel[0].data,
@ -1668,6 +1697,9 @@ load_image(char *name)
IFDBG printf("It's RGB.\n"); IFDBG printf("It's RGB.\n");
if (!psd_layer_has_alpha(&psd_image.layer[lnum])) if (!psd_layer_has_alpha(&psd_image.layer[lnum]))
{ {
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 = merged_data =
chans_to_RGB (psd_image.layer[lnum].channel[0].data, chans_to_RGB (psd_image.layer[lnum].channel[0].data,
psd_image.layer[lnum].channel[1].data, psd_image.layer[lnum].channel[1].data,
@ -1684,6 +1716,10 @@ load_image(char *name)
} }
else 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);
merged_data = merged_data =
chans_to_RGBA (psd_image.layer[lnum].channel[1].data, chans_to_RGBA (psd_image.layer[lnum].channel[1].data,
psd_image.layer[lnum].channel[2].data, psd_image.layer[lnum].channel[2].data,
@ -1733,6 +1769,7 @@ load_image(char *name)
lm_data = xmalloc(psd_image.layer[lnum].width * lm_data = xmalloc(psd_image.layer[lnum].width *
psd_image.layer[lnum].height); psd_image.layer[lnum].height);
{ {
seek_to_and_unpack_pixeldata(fd, lnum, iter);
/* PS layer masks can be a different size to /* PS layer masks can be a different size to
their owning layer, so we have to resize them. */ their owning layer, so we have to resize them. */
resize_mask(psd_image.layer[lnum].channel[iter].data, resize_mask(psd_image.layer[lnum].channel[iter].data,
@ -1804,8 +1841,9 @@ load_image(char *name)
gimp_progress_update ((double)(lnum+1.0) / gimp_progress_update ((double)(lnum+1.0) /
(double)psd_image.num_layers); (double)psd_image.num_layers);
} }
}
fsetpos (fd, &tmpfpos);
}
if ((psd_image.num_aux_channels > 0) && if ((psd_image.num_aux_channels > 0) &&
@ -1821,7 +1859,7 @@ load_image(char *name)
if (want_aux || (psd_image.num_layers==0)) /* PS2-style - NO LAYERS. */ if (want_aux || (psd_image.num_layers==0)) /* Photoshop2-style: NO LAYERS. */
{ {
IFDBG printf("Image data %ld chars\n", PSDheader.imgdatalen); IFDBG printf("Image data %ld chars\n", PSDheader.imgdatalen);
@ -1879,7 +1917,8 @@ load_image(char *name)
if (PSDheader.bpp != 8) if (PSDheader.bpp != 8)
{ {
printf("%s: The GIMP only supports 8-bit deep PSD images\n", printf("%s: The GIMP only supports 8-bit deep PSD images "
"at this time.\n",
prog_name); prog_name);
return(-1); return(-1);
} }
@ -1963,9 +2002,6 @@ load_image(char *name)
dest = xmalloc( step * PSDheader.columns * PSDheader.rows ); dest = xmalloc( step * PSDheader.columns * PSDheader.rows );
gimp_progress_update ((double)0.0);
if (PSDheader.compression == 1) if (PSDheader.compression == 1)
{ {
nguchars = PSDheader.columns * PSDheader.rows; nguchars = PSDheader.columns * PSDheader.rows;
@ -1973,16 +2009,15 @@ load_image(char *name)
xfread(fd, temp, PSDheader.imgdatalen, "image data"); xfread(fd, temp, PSDheader.imgdatalen, "image data");
if (!cmyk) if (!cmyk)
{ {
gimp_progress_update ((double)0.25); gimp_progress_update ((double)1.00);
decode(PSDheader.imgdatalen, nguchars, temp, dest, step); decode(PSDheader.imgdatalen, nguchars, temp, dest, step);
} }
else else
{ {
gimp_progress_update ((double)0.25); gimp_progress_update ((double)1.00);
cmykbuf = xmalloc(step * nguchars); cmykbuf = xmalloc(step * nguchars);
decode(PSDheader.imgdatalen, nguchars, temp, cmykbuf, step); decode(PSDheader.imgdatalen, nguchars, temp, cmykbuf, step);
gimp_progress_update ((double)0.50);
cmyk2rgb(cmykbuf, dest, PSDheader.columns, PSDheader.rows, cmyk2rgb(cmykbuf, dest, PSDheader.columns, PSDheader.rows,
step > 4); step > 4);
g_free(cmykbuf); g_free(cmykbuf);
@ -1993,18 +2028,17 @@ load_image(char *name)
{ {
if (!cmyk) if (!cmyk)
{ {
gimp_progress_update ((double)0.50); gimp_progress_update ((double)1.00);
xfread_interlaced(fd, dest, PSDheader.imgdatalen, xfread_interlaced(fd, dest, PSDheader.imgdatalen,
"raw image data", step); "raw image data", step);
} }
else else
{ {
gimp_progress_update ((double)0.25); gimp_progress_update ((double)1.00);
cmykbuf = xmalloc(PSDheader.imgdatalen); cmykbuf = xmalloc(PSDheader.imgdatalen);
xfread_interlaced(fd, cmykbuf, PSDheader.imgdatalen, xfread_interlaced(fd, cmykbuf, PSDheader.imgdatalen,
"raw cmyk image data", step); "raw cmyk image data", step);
gimp_progress_update ((double)0.50);
cmykp2rgb(cmykbuf, dest, cmykp2rgb(cmykbuf, dest,
PSDheader.columns, PSDheader.rows, step > 4); PSDheader.columns, PSDheader.rows, step > 4);
g_free(cmykbuf); g_free(cmykbuf);
@ -2022,7 +2056,7 @@ load_image(char *name)
} }
else else
{ {
gimp_progress_update ((double)0.96); gimp_progress_update ((double)1.00);
if (channels == step) /* gimp bpp == psd bpp */ if (channels == step) /* gimp bpp == psd bpp */
{ {
@ -2100,18 +2134,27 @@ decode(long clen, long uclen, char * src, char * dst, int step)
l = clen; l = clen;
for (i = 0; i < PSDheader.rows*PSDheader.channels; ++i) for (i = 0; i < PSDheader.rows*PSDheader.channels; ++i)
{
l -= PSDheader.rowlength[i]; l -= PSDheader.rowlength[i];
}
if (l) if (l)
{
g_warning("decode: %ld should be zero\n", (long)l); g_warning("decode: %ld should be zero\n", (long)l);
}
w = PSDheader.rowlength; w = PSDheader.rowlength;
packbitsdecode(&clen, uclen, src, dst++, step); packbitsdecode(&clen, uclen, src, dst++, step);
for (j = 0; j < step-1; ++j) {
for (j = 0; j < step-1; ++j)
{
for (i = 0; i < PSDheader.rows; ++i) for (i = 0; i < PSDheader.rows; ++i)
{
src += *w++; src += *w++;
}
packbitsdecode(&clen, uclen, src, dst++, step); packbitsdecode(&clen, uclen, src, dst++, step);
} }
IFDBG printf("clen %ld\n", clen); IFDBG printf("clen %ld\n", clen);
} }
@ -2308,7 +2351,7 @@ cmyk_to_rgb(gint *c, gint *m, gint *y, gint *k)
} }
void static void
dumpchunk(size_t n, FILE * fd, gchar *why) dumpchunk(size_t n, FILE * fd, gchar *why)
{ {
guint32 i; guint32 i;
@ -2324,9 +2367,10 @@ dumpchunk(size_t n, FILE * fd, gchar *why)
} }
void static void
throwchunk(size_t n, FILE * fd, gchar *why) throwchunk(size_t n, FILE * fd, gchar *why)
{ {
#if 0
guchar *tmpchunk; guchar *tmpchunk;
if (n==0) if (n==0)
@ -2337,6 +2381,19 @@ throwchunk(size_t n, FILE * fd, gchar *why)
tmpchunk = xmalloc(n); tmpchunk = xmalloc(n);
xfread(fd, tmpchunk, n, why); xfread(fd, tmpchunk, n, why);
g_free(tmpchunk); g_free(tmpchunk);
#else
if (n==0)
{
return;
}
if (fseek(fd, n, SEEK_CUR) != 0)
{
printf("%s: unable to seek forward while reading '%s' chunk\n",
prog_name, why);
gimp_quit();
}
#endif
} }

View File

@ -1,5 +1,5 @@
/* /*
* PSD Plugin version 2.0.0 * PSD Plugin version 2.0.1
* This GIMP plug-in is designed to load Adobe Photoshop(tm) files (.PSD) * This GIMP plug-in is designed to load Adobe Photoshop(tm) files (.PSD)
* *
* Adam D. Moss <adam@gimp.org> <adam@foxbox.org> * Adam D. Moss <adam@gimp.org> <adam@foxbox.org>
@ -35,67 +35,68 @@
/* /*
* Revision history: * Revision history:
* *
* 98.09.04 / v2.0.0 / Adam D. Moss * 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 * Now recognises and loads the new Guides extensions written
* by Photoshop 4 and 5. * by Photoshop 4 and 5.
* *
* 98.07.31 / v1.9.9.9f / Adam D. Moss * 1998.07.31 / v1.9.9.9f / Adam D. Moss
* Use OVERLAY_MODE if available. * Use OVERLAY_MODE if available.
* *
* 98.07.31 / v1.9.9.9e / Adam D. Moss * 1998.07.31 / v1.9.9.9e / Adam D. Moss
* Worked around some buggy PSD savers (suspect PS4 on Mac) - ugh. * Worked around some buggy PSD savers (suspect PS4 on Mac) - ugh.
* Fixed a bug when loading layer masks of certain dimensions. * Fixed a bug when loading layer masks of certain dimensions.
* *
* 98.05.04 / v1.9.9.9b / Adam D. Moss * 1998.05.04 / v1.9.9.9b / Adam D. Moss
* Changed the Pascal-style string-reading stuff. That fixed * Changed the Pascal-style string-reading stuff. That fixed
* some file-padding problems. Made all debugging output * some file-padding problems. Made all debugging output
* compile-time optional (please leave it enabled for now). * compile-time optional (please leave it enabled for now).
* Reduced memory requirements; still much room for improvement. * Reduced memory requirements; still much room for improvement.
* *
* 98.04.28 / v1.9.9.9 / Adam D. Moss * 1998.04.28 / v1.9.9.9 / Adam D. Moss
* Fixed the correct channel interlacing of 'raw' flat images. * Fixed the correct channel interlacing of 'raw' flat images.
* Thanks to Christian Kirsch and Jay Cox for spotting this. * Thanks to Christian Kirsch and Jay Cox for spotting this.
* Changed some of the I/O routines. * Changed some of the I/O routines.
* *
* 98.04.26 / v1.9.9.8 / Adam D. Moss * 1998.04.26 / v1.9.9.8 / Adam D. Moss
* Implemented Aux-channels for layered files. Got rid * Implemented Aux-channels for layered files. Got rid
* of <endian.h> nonsense. Improved Layer Mask padding. * of <endian.h> nonsense. Improved Layer Mask padding.
* Enforced num_layers/num_channels limit checks. * Enforced num_layers/num_channels limit checks.
* *
* 98.04.23 / v1.9.9.5 / Adam D. Moss * 1998.04.23 / v1.9.9.5 / Adam D. Moss
* Got Layer Masks working, got Aux-channels working * Got Layer Masks working, got Aux-channels working
* for unlayered files, fixed 'raw' channel loading, fixed * for unlayered files, fixed 'raw' channel loading, fixed
* some other mini-bugs, slightly better progress meters. * some other mini-bugs, slightly better progress meters.
* Thanks to everyone who is helping with the testing! * Thanks to everyone who is helping with the testing!
* *
* 98.04.21 / v1.9.9.1 / Adam D. Moss * 1998.04.21 / v1.9.9.1 / Adam D. Moss
* A little cleanup. Implemented Layer Masks but disabled * A little cleanup. Implemented Layer Masks but disabled
* them again - PS masks can be a different size to their * them again - PS masks can be a different size to their
* owning layer, unlike those in GIMP. * owning layer, unlike those in GIMP.
* *
* 98.04.19 / v1.9.9.0 / Adam D. Moss * 1998.04.19 / v1.9.9.0 / Adam D. Moss
* Much happier now. * Much happier now.
* *
* 97.03.13 / v1.9.0 / Adam D. Moss * 1997.03.13 / v1.9.0 / Adam D. Moss
* Layers, channels and masks, oh my. * Layers, channels and masks, oh my.
* + Bugfixes & rearchitecturing. * + Bugfixes & rearchitecturing.
* *
* 97.01.30 / v1.0.12 / Torsten Martinsen * 1997.01.30 / v1.0.12 / Torsten Martinsen
* Flat PSD image loading. * Flat PSD image loading.
*/ */
/* /*
* TODO: * TODO:
* *
* Crush 16bpp channels * Crush 16bpp channels (Wait until GIMP 2.0, probably)
* CMYK -> RGB * CMYK -> RGB
* Load BITMAP mode * Load BITMAP mode
* *
* File saving. (I am unlikely to be able to do this for * File saving
* practical reasons. Suggest someone works on it as a
* separate plugin - please let me know.)
*
* Reduce memory requirements!
*/ */
/* /*
@ -106,7 +107,7 @@
/* *** DEFINES *** */ /* *** USER DEFINES *** */
/* set to TRUE if you want debugging, FALSE otherwise */ /* set to TRUE if you want debugging, FALSE otherwise */
#define PSD_DEBUG TRUE #define PSD_DEBUG TRUE
@ -120,7 +121,7 @@
/* the max number of guides that this plugin should let an image have */ /* the max number of guides that this plugin should let an image have */
#define MAX_GUIDES 200 #define MAX_GUIDES 200
/* *** END OF DEFINES *** */ /* *** END OF USER DEFINES *** */
@ -149,35 +150,6 @@ typedef enum
} psd_imagetype; } psd_imagetype;
/* Declare some local functions.
*/
static void query (void);
static void run (char *name,
int nparams,
GParam *param,
int *nreturn_vals,
GParam **return_vals);
GDrawableType psd_type_to_gimp_type
(psd_imagetype psdtype);
GImageType psd_type_to_gimp_base_type
(psd_imagetype psdtype);
GLayerMode psd_lmode_to_gimp_lmode
(gchar modekey[4]);
static gint32 load_image (char *filename);
GPlugInInfo PLUG_IN_INFO =
{
NULL, /* init_proc */
NULL, /* quit_proc */
query, /* query_proc */
run, /* run_proc */
};
typedef struct PsdChannel typedef struct PsdChannel
{ {
gchar *name; gchar *name;
@ -186,8 +158,18 @@ typedef struct PsdChannel
guint32 compressedsize; guint32 compressedsize;
} PSDchannel; 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 typedef struct PsdGuide
@ -197,7 +179,6 @@ typedef struct PsdGuide
} PSDguide; } PSDguide;
typedef struct PsdLayer typedef struct PsdLayer
{ {
gint num_channels; gint num_channels;
@ -225,7 +206,6 @@ typedef struct PsdLayer
} PSDlayer; } PSDlayer;
typedef struct PsdImage typedef struct PsdImage
{ {
gint num_layers; gint num_layers;
@ -247,15 +227,42 @@ typedef struct PsdImage
gchar *caption; gchar *caption;
guint active_layer_num; guint active_layer_num;
} PSDimage; } PSDimage;
static PSDimage psd_image; /* Declare some local functions.
*/
static void query (void);
static void run (char *name,
int nparams,
GParam *param,
int *nreturn_vals,
GParam **return_vals);
static GDrawableType psd_type_to_gimp_type
(psd_imagetype psdtype);
static GImageType psd_type_to_gimp_base_type
(psd_imagetype psdtype);
static GLayerMode psd_lmode_to_gimp_lmode
(gchar modekey[4]);
static gint32 load_image (char *filename);
/* Various local variables...
*/
GPlugInInfo PLUG_IN_INFO =
{
NULL, /* init_proc */
NULL, /* quit_proc */
query, /* query_proc */
run, /* run_proc */
};
static PSDimage psd_image;
static struct { static struct {
gchar signature[4]; gchar signature[4];
gushort version; gushort version;
@ -312,8 +319,9 @@ static void read_whole_file(FILE *fd);
static void reshuffle_cmap(guchar *map256); static void reshuffle_cmap(guchar *map256);
static guchar* getpascalstring(FILE *fd, gchar *why); static guchar* getpascalstring(FILE *fd, gchar *why);
static gchar* getstring(size_t n, FILE * fd, gchar *why); static gchar* getstring(size_t n, FILE * fd, gchar *why);
void throwchunk(size_t n, FILE * fd, gchar *why); static void throwchunk(size_t n, FILE * fd, gchar *why);
void dumpchunk(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);
MAIN() MAIN()
@ -390,7 +398,7 @@ run (char *name,
} }
GDrawableType static GDrawableType
psd_type_to_gimp_type (psd_imagetype psdtype) psd_type_to_gimp_type (psd_imagetype psdtype)
{ {
switch(psdtype) switch(psdtype)
@ -407,7 +415,7 @@ psd_type_to_gimp_type (psd_imagetype psdtype)
GLayerMode static GLayerMode
psd_lmode_to_gimp_lmode (gchar modekey[4]) psd_lmode_to_gimp_lmode (gchar modekey[4])
{ {
if (strncmp(modekey, "norm", 4)==0) return(NORMAL_MODE); if (strncmp(modekey, "norm", 4)==0) return(NORMAL_MODE);
@ -444,7 +452,7 @@ psd_lmode_to_gimp_lmode (gchar modekey[4])
GImageType static GImageType
psd_type_to_gimp_base_type (psd_imagetype psdtype) psd_type_to_gimp_base_type (psd_imagetype psdtype)
{ {
switch(psdtype) switch(psdtype)
@ -787,7 +795,10 @@ dispatch_resID(guint ID, FILE *fd, guint32 *offset, guint32 Size)
default: default:
IFDBG printf ("\t\t<Undocumented field.>\n"); IFDBG printf ("\t\t<Undocumented field.>\n");
IFDBG
dumpchunk(Size, fd, "dispatch_res"); dumpchunk(Size, fd, "dispatch_res");
else
throwchunk(Size, fd, "dispatch_res");
(*offset) += Size; (*offset) += Size;
break; break;
} }
@ -1013,9 +1024,7 @@ do_layer_struct(FILE *fd, guint32 *offset)
static void static void
do_layer_pixeldata(FILE *fd, guint32 *offset) do_layer_pixeldata(FILE *fd, guint32 *offset)
{ {
guint16 compression;
gint layeri, channeli; gint layeri, channeli;
guchar *tmpline;
for (layeri=0; layeri<psd_image.num_layers; layeri++) for (layeri=0; layeri<psd_image.num_layers; layeri++)
{ {
@ -1036,10 +1045,34 @@ do_layer_pixeldata(FILE *fd, guint32 *offset)
height = psd_image.layer[layeri].height; height = psd_image.layer[layeri].height;
} }
tmpline = xmalloc(width + 1); fgetpos(fd, &psd_image.layer[layeri].channel[channeli].fpos);
psd_image.layer[layeri].channel[channeli].width = width;
psd_image.layer[layeri].channel[channeli].height = height;
throwchunk(psd_image.layer[layeri].channel[channeli].compressedsize,
fd, "channel data skip");
(*offset) +=
psd_image.layer[layeri].channel[channeli].compressedsize;
}
}
}
static void
seek_to_and_unpack_pixeldata(FILE* fd, gint layeri, gint channeli)
{
int width, height;
guchar *tmpline;
gint compression;
guint32 offset = 0;
fsetpos(fd, &psd_image.layer[layeri].channel[channeli].fpos);
compression = getgshort(fd, "layer channel compression type"); compression = getgshort(fd, "layer channel compression type");
(*offset)+=2; offset+=2;
width = psd_image.layer[layeri].channel[channeli].width;
height = psd_image.layer[layeri].channel[channeli].height;
IFDBG IFDBG
{ {
@ -1048,41 +1081,38 @@ do_layer_pixeldata(FILE *fd, guint32 *offset)
channeli, channeli,
psd_image.layer[layeri].channel[channeli].type, psd_image.layer[layeri].channel[channeli].type,
compression, compression,
compression==0?"raw":(compression==1?"RLE":"UNKNOWN!")); compression==0?"raw":(compression==1?"RLE":"*UNKNOWN!*"));
fflush(stdout); fflush(stdout);
} }
IFDBG {printf("\t\t\t\tLoading channel data (%d bytes)...\n",
width*height);fflush(stdout);}
psd_image.layer[layeri].channel[channeli].data = psd_image.layer[layeri].channel[channeli].data =
xmalloc (width * height); xmalloc (width * height);
tmpline = xmalloc(width + 1);
switch (compression) switch (compression)
{ {
case 0: /* raw data */ case 0: /* raw data */
{ {
gint linei; gint linei;
for (linei=0; for (linei=0; linei < height; linei++)
linei<height;
linei++)
{ {
xfread(fd, xfread(fd,
(psd_image.layer[layeri].channel[channeli].data+ (psd_image.layer[layeri].channel[channeli].data+
linei * width), linei * width),
width, width,
"raw channel line"); "raw channel line");
(*offset) += width; offset += width;
} }
/* Pad raw data to multiple of 2? */
#if 0 #if 0
if ((height * /* Pad raw data to multiple of 2? */
width) & 1) if ((height * width) & 1)
{ {
getguchar(fd, "raw channel padding"); getguchar(fd, "raw channel padding");
(*offset)++; offset++;
} }
#endif #endif
} }
@ -1096,9 +1126,9 @@ do_layer_pixeldata(FILE *fd, guint32 *offset)
data to unpack to the right length... hmm... */ data to unpack to the right length... hmm... */
throwchunk(height * 2, throwchunk(height * 2,
fd, "widthlist"); fd, "widthlist");
(*offset) += height * 2; offset += height * 2;
blockread = (*offset); blockread = offset;
/*IFDBG {printf("\nHere comes the guitar solo...\n"); /*IFDBG {printf("\nHere comes the guitar solo...\n");
fflush(stdout);}*/ fflush(stdout);}*/
@ -1111,7 +1141,7 @@ do_layer_pixeldata(FILE *fd, guint32 *offset)
unpack_pb_channel(fd, tmpline, unpack_pb_channel(fd, tmpline,
width width
/*+ (width&1)*/, /*+ (width&1)*/,
offset); &offset);
memcpy((psd_image.layer[layeri].channel[channeli].data+ memcpy((psd_image.layer[layeri].channel[channeli].data+
linei * width), linei * width),
tmpline, tmpline,
@ -1119,7 +1149,7 @@ do_layer_pixeldata(FILE *fd, guint32 *offset)
} }
IFDBG {printf("\t\t\t\t\tActual compressed size was %d bytes\n" IFDBG {printf("\t\t\t\t\tActual compressed size was %d bytes\n"
, (*offset)-blockread);fflush(stdout);} , offset-blockread);fflush(stdout);}
} }
break; break;
default: /* *unknown* */ default: /* *unknown* */
@ -1134,11 +1164,6 @@ do_layer_pixeldata(FILE *fd, guint32 *offset)
else else
IFDBG {printf("\nTRIED TO FREE NULL!");fflush(stdout);} IFDBG {printf("\nTRIED TO FREE NULL!");fflush(stdout);}
} }
}
/* printf("\n[[%ld]]\n", getglong(fd, "uhhhh"));
(*offset)+=4;*/
}
static void static void
@ -1158,7 +1183,6 @@ do_layers(FILE *fd, guint32 *offset)
} }
static void static void
do_layer_and_mask(FILE *fd) do_layer_and_mask(FILE *fd)
{ {
@ -1203,12 +1227,11 @@ do_layer_and_mask(FILE *fd)
printf("PSD: Stern warning - no mask info.\n"); printf("PSD: Stern warning - no mask info.\n");
/* If 'offset' wasn't being buggily updated, we wouldn't need this. */ /* If 'offset' wasn't being buggily updated, we wouldn't need this. (!?) */
fseek(fd, Size+offset_now, SEEK_SET); fseek(fd, Size+offset_now, SEEK_SET);
} }
static void static void
do_image_resources(FILE *fd) do_image_resources(FILE *fd)
{ {
@ -1574,6 +1597,7 @@ load_image(char *name)
GDrawable *drawable = NULL; GDrawable *drawable = NULL;
GPixelRgn pixel_rgn; GPixelRgn pixel_rgn;
gint32 iter; gint32 iter;
fpos_t tmpfpos;
IFDBG printf("------- %s ---------------------------------\n",name); IFDBG printf("------- %s ---------------------------------\n",name);
@ -1615,6 +1639,8 @@ load_image(char *name)
gimp_image_new (PSDheader.columns, PSDheader.rows, gimagetype); gimp_image_new (PSDheader.columns, PSDheader.rows, gimagetype);
gimp_image_set_filename (image_ID, name); gimp_image_set_filename (image_ID, name);
fgetpos (fd, &tmpfpos);
for (lnum=0; lnum<psd_image.num_layers; lnum++) for (lnum=0; lnum<psd_image.num_layers; lnum++)
{ {
gint numc; gint numc;
@ -1633,6 +1659,7 @@ load_image(char *name)
{ {
merged_data = xmalloc(psd_image.layer[lnum].width * merged_data = xmalloc(psd_image.layer[lnum].width *
psd_image.layer[lnum].height); psd_image.layer[lnum].height);
seek_to_and_unpack_pixeldata(fd, lnum, 0);
memcpy(merged_data, psd_image.layer[lnum].channel[0].data, memcpy(merged_data, psd_image.layer[lnum].channel[0].data,
psd_image.layer[lnum].width * psd_image.layer[lnum].width *
psd_image.layer[lnum].height); psd_image.layer[lnum].height);
@ -1642,6 +1669,8 @@ load_image(char *name)
} }
else else
{ {
seek_to_and_unpack_pixeldata(fd, lnum, 0);
seek_to_and_unpack_pixeldata(fd, lnum, 1);
merged_data = merged_data =
chans_to_GRAYA (psd_image.layer[lnum].channel[1].data, chans_to_GRAYA (psd_image.layer[lnum].channel[1].data,
psd_image.layer[lnum].channel[0].data, psd_image.layer[lnum].channel[0].data,
@ -1668,6 +1697,9 @@ load_image(char *name)
IFDBG printf("It's RGB.\n"); IFDBG printf("It's RGB.\n");
if (!psd_layer_has_alpha(&psd_image.layer[lnum])) if (!psd_layer_has_alpha(&psd_image.layer[lnum]))
{ {
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 = merged_data =
chans_to_RGB (psd_image.layer[lnum].channel[0].data, chans_to_RGB (psd_image.layer[lnum].channel[0].data,
psd_image.layer[lnum].channel[1].data, psd_image.layer[lnum].channel[1].data,
@ -1684,6 +1716,10 @@ load_image(char *name)
} }
else 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);
merged_data = merged_data =
chans_to_RGBA (psd_image.layer[lnum].channel[1].data, chans_to_RGBA (psd_image.layer[lnum].channel[1].data,
psd_image.layer[lnum].channel[2].data, psd_image.layer[lnum].channel[2].data,
@ -1733,6 +1769,7 @@ load_image(char *name)
lm_data = xmalloc(psd_image.layer[lnum].width * lm_data = xmalloc(psd_image.layer[lnum].width *
psd_image.layer[lnum].height); psd_image.layer[lnum].height);
{ {
seek_to_and_unpack_pixeldata(fd, lnum, iter);
/* PS layer masks can be a different size to /* PS layer masks can be a different size to
their owning layer, so we have to resize them. */ their owning layer, so we have to resize them. */
resize_mask(psd_image.layer[lnum].channel[iter].data, resize_mask(psd_image.layer[lnum].channel[iter].data,
@ -1804,8 +1841,9 @@ load_image(char *name)
gimp_progress_update ((double)(lnum+1.0) / gimp_progress_update ((double)(lnum+1.0) /
(double)psd_image.num_layers); (double)psd_image.num_layers);
} }
}
fsetpos (fd, &tmpfpos);
}
if ((psd_image.num_aux_channels > 0) && if ((psd_image.num_aux_channels > 0) &&
@ -1821,7 +1859,7 @@ load_image(char *name)
if (want_aux || (psd_image.num_layers==0)) /* PS2-style - NO LAYERS. */ if (want_aux || (psd_image.num_layers==0)) /* Photoshop2-style: NO LAYERS. */
{ {
IFDBG printf("Image data %ld chars\n", PSDheader.imgdatalen); IFDBG printf("Image data %ld chars\n", PSDheader.imgdatalen);
@ -1879,7 +1917,8 @@ load_image(char *name)
if (PSDheader.bpp != 8) if (PSDheader.bpp != 8)
{ {
printf("%s: The GIMP only supports 8-bit deep PSD images\n", printf("%s: The GIMP only supports 8-bit deep PSD images "
"at this time.\n",
prog_name); prog_name);
return(-1); return(-1);
} }
@ -1963,9 +2002,6 @@ load_image(char *name)
dest = xmalloc( step * PSDheader.columns * PSDheader.rows ); dest = xmalloc( step * PSDheader.columns * PSDheader.rows );
gimp_progress_update ((double)0.0);
if (PSDheader.compression == 1) if (PSDheader.compression == 1)
{ {
nguchars = PSDheader.columns * PSDheader.rows; nguchars = PSDheader.columns * PSDheader.rows;
@ -1973,16 +2009,15 @@ load_image(char *name)
xfread(fd, temp, PSDheader.imgdatalen, "image data"); xfread(fd, temp, PSDheader.imgdatalen, "image data");
if (!cmyk) if (!cmyk)
{ {
gimp_progress_update ((double)0.25); gimp_progress_update ((double)1.00);
decode(PSDheader.imgdatalen, nguchars, temp, dest, step); decode(PSDheader.imgdatalen, nguchars, temp, dest, step);
} }
else else
{ {
gimp_progress_update ((double)0.25); gimp_progress_update ((double)1.00);
cmykbuf = xmalloc(step * nguchars); cmykbuf = xmalloc(step * nguchars);
decode(PSDheader.imgdatalen, nguchars, temp, cmykbuf, step); decode(PSDheader.imgdatalen, nguchars, temp, cmykbuf, step);
gimp_progress_update ((double)0.50);
cmyk2rgb(cmykbuf, dest, PSDheader.columns, PSDheader.rows, cmyk2rgb(cmykbuf, dest, PSDheader.columns, PSDheader.rows,
step > 4); step > 4);
g_free(cmykbuf); g_free(cmykbuf);
@ -1993,18 +2028,17 @@ load_image(char *name)
{ {
if (!cmyk) if (!cmyk)
{ {
gimp_progress_update ((double)0.50); gimp_progress_update ((double)1.00);
xfread_interlaced(fd, dest, PSDheader.imgdatalen, xfread_interlaced(fd, dest, PSDheader.imgdatalen,
"raw image data", step); "raw image data", step);
} }
else else
{ {
gimp_progress_update ((double)0.25); gimp_progress_update ((double)1.00);
cmykbuf = xmalloc(PSDheader.imgdatalen); cmykbuf = xmalloc(PSDheader.imgdatalen);
xfread_interlaced(fd, cmykbuf, PSDheader.imgdatalen, xfread_interlaced(fd, cmykbuf, PSDheader.imgdatalen,
"raw cmyk image data", step); "raw cmyk image data", step);
gimp_progress_update ((double)0.50);
cmykp2rgb(cmykbuf, dest, cmykp2rgb(cmykbuf, dest,
PSDheader.columns, PSDheader.rows, step > 4); PSDheader.columns, PSDheader.rows, step > 4);
g_free(cmykbuf); g_free(cmykbuf);
@ -2022,7 +2056,7 @@ load_image(char *name)
} }
else else
{ {
gimp_progress_update ((double)0.96); gimp_progress_update ((double)1.00);
if (channels == step) /* gimp bpp == psd bpp */ if (channels == step) /* gimp bpp == psd bpp */
{ {
@ -2100,18 +2134,27 @@ decode(long clen, long uclen, char * src, char * dst, int step)
l = clen; l = clen;
for (i = 0; i < PSDheader.rows*PSDheader.channels; ++i) for (i = 0; i < PSDheader.rows*PSDheader.channels; ++i)
{
l -= PSDheader.rowlength[i]; l -= PSDheader.rowlength[i];
}
if (l) if (l)
{
g_warning("decode: %ld should be zero\n", (long)l); g_warning("decode: %ld should be zero\n", (long)l);
}
w = PSDheader.rowlength; w = PSDheader.rowlength;
packbitsdecode(&clen, uclen, src, dst++, step); packbitsdecode(&clen, uclen, src, dst++, step);
for (j = 0; j < step-1; ++j) {
for (j = 0; j < step-1; ++j)
{
for (i = 0; i < PSDheader.rows; ++i) for (i = 0; i < PSDheader.rows; ++i)
{
src += *w++; src += *w++;
}
packbitsdecode(&clen, uclen, src, dst++, step); packbitsdecode(&clen, uclen, src, dst++, step);
} }
IFDBG printf("clen %ld\n", clen); IFDBG printf("clen %ld\n", clen);
} }
@ -2308,7 +2351,7 @@ cmyk_to_rgb(gint *c, gint *m, gint *y, gint *k)
} }
void static void
dumpchunk(size_t n, FILE * fd, gchar *why) dumpchunk(size_t n, FILE * fd, gchar *why)
{ {
guint32 i; guint32 i;
@ -2324,9 +2367,10 @@ dumpchunk(size_t n, FILE * fd, gchar *why)
} }
void static void
throwchunk(size_t n, FILE * fd, gchar *why) throwchunk(size_t n, FILE * fd, gchar *why)
{ {
#if 0
guchar *tmpchunk; guchar *tmpchunk;
if (n==0) if (n==0)
@ -2337,6 +2381,19 @@ throwchunk(size_t n, FILE * fd, gchar *why)
tmpchunk = xmalloc(n); tmpchunk = xmalloc(n);
xfread(fd, tmpchunk, n, why); xfread(fd, tmpchunk, n, why);
g_free(tmpchunk); g_free(tmpchunk);
#else
if (n==0)
{
return;
}
if (fseek(fd, n, SEEK_CUR) != 0)
{
printf("%s: unable to seek forward while reading '%s' chunk\n",
prog_name, why);
gimp_quit();
}
#endif
} }