gimp/app/xcf/xcf-load.c

1408 lines
34 KiB
C
Raw Normal View History

/* The GIMP -- an image manipulation program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "config.h"
#include <stdio.h>
#include <gtk/gtk.h>
#include "libgimpbase/gimpbase.h"
#include "libgimpcolor/gimpcolor.h"
#include "core/core-types.h"
#include "tools/tools-types.h" /* EEK */
#include "base/tile.h"
#include "base/tile-manager.h"
#include "base/tile-manager-private.h"
#include "core/gimp.h"
#include "core/gimpchannel.h"
#include "core/gimpcontainer.h"
#include "core/gimpcoreconfig.h"
#include "core/gimpdrawable.h"
#include "core/gimpimage.h"
#include "core/gimplayer.h"
#include "core/gimplayermask.h"
#include "core/gimpparasitelist.h"
#include "xcf-private.h"
#include "xcf-load.h"
#include "xcf-read.h"
#include "xcf-seek.h"
#include "floating_sel.h"
#include "path.h"
#include "pathP.h"
#include "libgimp/gimpintl.h"
static gboolean xcf_load_image_props (XcfInfo *info,
GimpImage *gimage);
static gboolean xcf_load_layer_props (XcfInfo *info,
GimpImage *gimage,
GimpLayer *layer,
gboolean *apply_mask,
gboolean *edit_mask,
gboolean *show_mask);
static gboolean xcf_load_channel_props (XcfInfo *info,
GimpImage *gimage,
GimpChannel *channel);
static gboolean xcf_load_prop (XcfInfo *info,
PropType *prop_type,
guint32 *prop_size);
static GimpLayer * xcf_load_layer (XcfInfo *info,
GimpImage *gimage);
static GimpChannel * xcf_load_channel (XcfInfo *info,
GimpImage *gimage);
static GimpLayerMask * xcf_load_layer_mask (XcfInfo *info,
GimpImage *gimage);
static gboolean xcf_load_hierarchy (XcfInfo *info,
TileManager *tiles);
static gboolean xcf_load_level (XcfInfo *info,
TileManager *tiles);
static gboolean xcf_load_tile (XcfInfo *info,
Tile *tile);
static gboolean xcf_load_tile_rle (XcfInfo *info,
Tile *tile,
gint data_length);
#ifdef SWAP_FROM_FILE
static gboolean xcf_swap_func (gint fd,
Tile *tile,
gint cmd,
gpointer user_data);
#endif
static GimpParasite *
read_a_parasite (XcfInfo *info)
{
GimpParasite *p;
p = g_new (GimpParasite, 1);
info->cp += xcf_read_string (info->fp, &p->name, 1);
info->cp += xcf_read_int32 (info->fp, &p->flags, 1);
info->cp += xcf_read_int32 (info->fp, &p->size, 1);
p->data = g_new (gchar, p->size);
info->cp += xcf_read_int8 (info->fp, p->data, p->size);
return p;
}
static PathPoint*
v1read_bz_point (XcfInfo *info)
{
PathPoint *ptr;
guint32 type;
gint32 x;
gint32 y;
info->cp += xcf_read_int32 (info->fp, &type, 1);
info->cp += xcf_read_int32 (info->fp, (guint32*)&x, 1);
info->cp += xcf_read_int32 (info->fp, (guint32*)&y, 1);
ptr = path_point_new (type, (gdouble)x, (gdouble)y);
return ptr;
}
static PathPoint *
read_bz_point (XcfInfo *info)
{
PathPoint *ptr;
guint32 type;
gfloat x;
gfloat y;
info->cp += xcf_read_int32 (info->fp, &type, 1);
info->cp += xcf_read_float (info->fp, &x, 1);
info->cp += xcf_read_float (info->fp, &y, 1);
ptr = path_point_new (type, (gdouble)x, (gdouble)y);
return ptr;
}
static Path *
read_one_path (GimpImage *gimage,
XcfInfo *info)
{
Path *bzp;
gchar *name;
guint32 locked;
guint8 state;
guint32 closed;
guint32 num_points;
guint32 version; /* changed from num_paths */
Tattoo tattoo = 0;
GSList *pts_list = NULL;
PathType ptype;
info->cp += xcf_read_string (info->fp, &name, 1);
info->cp += xcf_read_int32 (info->fp, &locked, 1);
info->cp += xcf_read_int8 (info->fp, &state, 1);
info->cp += xcf_read_int32 (info->fp, &closed, 1);
info->cp += xcf_read_int32 (info->fp, &num_points, 1);
info->cp += xcf_read_int32 (info->fp, &version, 1);
if (version == 1)
{
ptype = BEZIER;
while (num_points-- > 0)
{
PathPoint *bpt;
/* Read in a path */
bpt = v1read_bz_point (info);
pts_list = g_slist_append (pts_list, bpt);
}
}
else if (version == 2)
{
/* Had extra type field and points are stored as doubles */
info->cp += xcf_read_int32 (info->fp, (guint32 *)&ptype, 1);
while(num_points-- > 0)
{
PathPoint *bpt;
/* Read in a path */
bpt = read_bz_point (info);
pts_list = g_slist_append (pts_list, bpt);
}
}
else if (version == 3)
{
/* Has extra tatto field */
info->cp += xcf_read_int32 (info->fp, (guint32 *)&ptype, 1);
info->cp += xcf_read_int32 (info->fp, (guint32 *)&tattoo, 1);
while (num_points-- > 0)
{
PathPoint *bpt;
/* Read in a path */
bpt = read_bz_point (info);
pts_list = g_slist_append (pts_list, bpt);
}
}
else
{
g_warning ("Unknown path type. Possibly corrupt XCF file");
}
bzp = path_new (gimage, ptype, pts_list, closed, (gint)state, locked, tattoo, name);
return bzp;
}
static PathList *
read_bzpaths (GimpImage *gimage,
XcfInfo *info)
{
guint32 num_paths;
guint32 last_selected_row;
PathList *paths;
GSList *bzp_list = NULL;
info->cp += xcf_read_int32 (info->fp, &last_selected_row, 1);
info->cp += xcf_read_int32 (info->fp, &num_paths, 1);
while(num_paths-- > 0)
{
Path *bzp;
/* Read in a path */
bzp = read_one_path (gimage, info);
bzp_list = g_slist_append (bzp_list, bzp);
}
paths = path_list_new (gimage, last_selected_row, bzp_list);
return paths;
}
GimpImage *
app/core/Makefile.am app/core/core-types.h added an "application object" 2001-07-04 Michael Natterer <mitch@gimp.org> * app/core/Makefile.am * app/core/core-types.h * app/core/gimp.[ch]: added an "application object" called Gimp. Currently, it contains the image list, the clipboard, the data factories, the procedural hashtable and the tool info list. It's the toplevel object of the core object system. Finally, creating a Gimp object will return a standalone gimp core engine instance with no other global states/variables involved. * app/app_procs.[ch]: allocate a "Gimp" instance called "the_gimp" :) Removed stuff which is now done by the "Gimp" object. Merged gimp_init() into app_init() because gimp_init() is taken now. * app/context_manager.[ch]: removed stuff done by "Gimp". * app/batch.[ch] * app/gimage.[ch] * app/xcf/xcf-load.[ch] * app/xcf/xcf.[ch] * app/core/gimpedit.[ch] * app/tools/tool_manager.[ch]: pass around an additional "Gimp" argument. * app/pdb/procedural_db.[ch]: pass a "Gimp" pointer as first parameter to all internal procedures and to all procedural_db_* functions. * app/core/gimpcontext.[ch] * app/core/gimpimage.[ch]: added a "Gimp" pointer to the structs. * app/devices.c * app/errors.c * app/file-open.c * app/file-save.c * app/gimphelp.c * app/gimpunit.c * app/image_new.c * app/main.c * app/nav_window.c * app/plug_in.c * app/base/base.c * app/core/gimpdatafactory.c * app/core/gimpimage-duplicate.c * app/core/gimpimage-mask.c * app/core/gimptoolinfo.[ch] * app/gui/brush-select.c * app/gui/convert-dialog.c * app/gui/dialogs-constructors.c * app/gui/edit-commands.c * app/gui/file-open-dialog.c * app/gui/file-save-dialog.c * app/gui/gradient-editor.c * app/gui/gradient-select.c * app/gui/gui.c * app/gui/image-commands.c * app/gui/info-window.c * app/gui/menus.c * app/gui/palette-editor.c * app/gui/palette-import-dialog.c * app/gui/palette-select.c * app/gui/paths-dialog.c * app/gui/pattern-select.c * app/gui/preferences-dialog.c * app/gui/test-commands.c * app/gui/toolbox.c * app/gui/tools-commands.c * app/tools/gimpbezierselecttool.c * app/tools/gimpbucketfilltool.c * app/tools/gimppainttool.h * app/tools/gimptexttool.c * app/tools/gimptransformtool.h * app/widgets/gimpbufferview.c * app/widgets/gimpcontainerview-utils.c * app/widgets/gimpcursor.c * app/widgets/gimpdnd.c * app/widgets/gimpimagedock.c: changed accordingly. Cleaned up lots of includes. Many files still access the global "the_gimp" variable exported by app_procs.h. * tools/pdbgen/app.pl * tools/pdbgen/pdb/brush_select.pdb * tools/pdbgen/pdb/brushes.pdb * tools/pdbgen/pdb/convert.pdb * tools/pdbgen/pdb/edit.pdb * tools/pdbgen/pdb/fileops.pdb * tools/pdbgen/pdb/gradient_select.pdb * tools/pdbgen/pdb/gradients.pdb * tools/pdbgen/pdb/image.pdb * tools/pdbgen/pdb/palette.pdb * tools/pdbgen/pdb/pattern_select.pdb * tools/pdbgen/pdb/patterns.pdb * tools/pdbgen/pdb/procedural_db.pdb: changed accordingly. Don't use "the_gimp" here because all procedures get passed a "Gimp" pointer now. * app/pdb/*: regenerated.
2001-07-05 03:31:35 +08:00
xcf_load_image (Gimp *gimp,
XcfInfo *info)
{
GimpImage *gimage;
GimpLayer *layer;
GimpChannel *channel;
guint32 saved_pos;
guint32 offset;
gint width;
gint height;
gint image_type;
gint num_successful_elements = 0;
/* read in the image width, height and type */
info->cp += xcf_read_int32 (info->fp, (guint32 *) &width, 1);
info->cp += xcf_read_int32 (info->fp, (guint32 *) &height, 1);
info->cp += xcf_read_int32 (info->fp, (guint32 *) &image_type, 1);
gimage = gimp_create_image (gimp,
width, height,
image_type,
FALSE);
/* read the image properties */
if (!xcf_load_image_props (info, gimage))
goto hard_error;
while (TRUE)
{
/* read in the offset of the next layer */
info->cp += xcf_read_int32 (info->fp, &offset, 1);
/* if the offset is 0 then we are at the end
* of the layer list.
*/
if (offset == 0)
break;
/* save the current position as it is where the
* next layer offset is stored.
*/
saved_pos = info->cp;
/* seek to the layer offset */
xcf_seek_pos (info, offset);
/* read in the layer */
layer = xcf_load_layer (info, gimage);
if (!layer)
goto error;
num_successful_elements++;
/* add the layer to the image if its not the floating selection */
if (layer != info->floating_sel)
gimp_image_add_layer (gimage, layer,
gimp_container_num_children (gimage->layers));
/* restore the saved position so we'll be ready to
* read the next offset.
*/
xcf_seek_pos (info, saved_pos);
}
while (TRUE)
{
/* read in the offset of the next channel */
info->cp += xcf_read_int32 (info->fp, &offset, 1);
/* if the offset is 0 then we are at the end
* of the channel list.
*/
if (offset == 0)
break;
/* save the current position as it is where the
* next channel offset is stored.
*/
saved_pos = info->cp;
/* seek to the channel offset */
xcf_seek_pos (info, offset);
/* read in the layer */
channel = xcf_load_channel (info, gimage);
if (!channel)
goto error;
num_successful_elements++;
/* add the channel to the image if its not the selection */
if (channel != gimage->selection_mask)
gimp_image_add_channel (gimage, channel, -1);
/* restore the saved position so we'll be ready to
* read the next offset.
*/
xcf_seek_pos (info, saved_pos);
}
if (info->active_layer)
gimp_image_set_active_layer (gimage, info->active_layer);
if (info->active_channel)
gimp_image_set_active_channel (gimage, info->active_channel);
gimp_object_set_name (GIMP_OBJECT (gimage), info->filename);
return gimage;
error:
if (num_successful_elements == 0)
goto hard_error;
g_message ("XCF: This file is corrupt! I have loaded as much\n"
"of it as I can, but it is incomplete.");
return gimage;
hard_error:
g_message ("XCF: This file is corrupt! I could not even\n"
"salvage any partial image data from it.");
gtk_object_unref (GTK_OBJECT (gimage));
return NULL;
}
static gboolean
xcf_load_image_props (XcfInfo *info,
GimpImage *gimage)
{
PropType prop_type;
guint32 prop_size;
while (TRUE)
{
if (!xcf_load_prop (info, &prop_type, &prop_size))
return FALSE;
switch (prop_type)
{
case PROP_END:
return TRUE;
case PROP_COLORMAP:
if (info->file_version == 0)
{
gint i;
g_message (_("XCF warning: version 0 of XCF file format\n"
"did not save indexed colormaps correctly.\n"
"Substituting grayscale map."));
info->cp += xcf_read_int32 (info->fp, (guint32*) &gimage->num_cols, 1);
gimage->cmap = g_new (guchar, gimage->num_cols*3);
xcf_seek_pos (info, info->cp + gimage->num_cols);
for (i = 0; i<gimage->num_cols; i++)
{
gimage->cmap[i*3+0] = i;
gimage->cmap[i*3+1] = i;
gimage->cmap[i*3+2] = i;
}
}
else
{
info->cp += xcf_read_int32 (info->fp, (guint32*) &gimage->num_cols, 1);
gimage->cmap = g_new (guchar, gimage->num_cols*3);
info->cp += xcf_read_int8 (info->fp, (guint8*) gimage->cmap, gimage->num_cols*3);
}
break;
case PROP_COMPRESSION:
{
guint8 compression;
info->cp += xcf_read_int8 (info->fp, (guint8*) &compression, 1);
if ((compression != COMPRESS_NONE) &&
(compression != COMPRESS_RLE) &&
(compression != COMPRESS_ZLIB) &&
(compression != COMPRESS_FRACTAL))
{
g_message ("unknown compression type: %d", (int) compression);
return FALSE;
}
info->compression = compression;
}
break;
case PROP_GUIDES:
{
GimpGuide *guide;
gint32 position;
gint8 orientation;
gint i, nguides;
nguides = prop_size / (4 + 1);
for (i = 0; i < nguides; i++)
{
info->cp += xcf_read_int32 (info->fp, (guint32 *) &position, 1);
info->cp += xcf_read_int8 (info->fp, (guint8 *) &orientation, 1);
switch (orientation)
{
case ORIENTATION_HORIZONTAL:
guide = gimp_image_add_hguide (gimage);
break;
case ORIENTATION_VERTICAL:
guide = gimp_image_add_vguide (gimage);
break;
default:
g_message ("guide orientation out of range in XCF file");
continue;
}
guide->position = position;
}
/* this is silly as the order of guides doesn't really matter,
* but it restores the list to it's original order, which
* cannot be wrong --Mitch
*/
gimage->guides = g_list_reverse (gimage->guides);
}
break;
case PROP_RESOLUTION:
{
gfloat xres, yres;
info->cp += xcf_read_float (info->fp, &xres, 1);
info->cp += xcf_read_float (info->fp, &yres, 1);
if (xres < GIMP_MIN_RESOLUTION || xres > GIMP_MAX_RESOLUTION ||
yres < GIMP_MIN_RESOLUTION || yres > GIMP_MAX_RESOLUTION)
{
g_message ("Warning, resolution out of range in XCF file");
xres = core_config->default_xresolution;
yres = core_config->default_yresolution;
}
gimage->xresolution = xres;
gimage->yresolution = yres;
}
break;
case PROP_TATTOO:
{
info->cp += xcf_read_int32 (info->fp, &gimage->tattoo_state, 1);
}
break;
case PROP_PARASITES:
{
glong base = info->cp;
GimpParasite *p;
while (info->cp - base < prop_size)
{
p = read_a_parasite (info);
gimp_image_parasite_attach (gimage, p);
gimp_parasite_free (p);
}
if (info->cp - base != prop_size)
g_message ("Error detected while loading an image's parasites");
}
break;
case PROP_UNIT:
{
guint32 unit;
info->cp += xcf_read_int32 (info->fp, &unit, 1);
if ((unit <= GIMP_UNIT_PIXEL) ||
(unit >= gimp_unit_get_number_of_built_in_units()))
{
g_message ("Warning, unit out of range in XCF file, falling back to inches");
unit = GIMP_UNIT_INCH;
}
gimage->unit = unit;
}
break;
case PROP_PATHS:
{
PathList *paths = read_bzpaths (gimage, info);
/* add to gimage */
gimp_image_set_paths (gimage, paths);
}
break;
case PROP_USER_UNIT:
{
gchar *unit_strings[5];
float factor;
guint32 digits;
GimpUnit unit;
gint num_units;
gint i;
info->cp += xcf_read_float (info->fp, &factor, 1);
info->cp += xcf_read_int32 (info->fp, &digits, 1);
info->cp += xcf_read_string (info->fp, unit_strings, 5);
for (i = 0; i < 5; i++)
if (unit_strings[i] == NULL)
unit_strings[i] = g_strdup ("");
num_units = gimp_unit_get_number_of_units ();
for (unit = gimp_unit_get_number_of_built_in_units ();
unit < num_units; unit++)
{
/* if the factor and the identifier match some unit
* in unitrc, use the unitrc unit
*/
if ((ABS (gimp_unit_get_factor (unit) - factor) < 1e-5) &&
(strcmp (unit_strings[0],
gimp_unit_get_identifier (unit)) == 0))
{
break;
}
}
/* no match */
if (unit == num_units)
unit = gimp_unit_new (unit_strings[0],
factor,
digits,
unit_strings[1],
unit_strings[2],
unit_strings[3],
unit_strings[4]);
gimage->unit = unit;
for (i = 0; i < 5; i++)
g_free (unit_strings[i]);
}
break;
default:
g_message ("unexpected/unknown image property: %d (skipping)", prop_type);
{
guint8 buf[16];
guint amount;
while (prop_size > 0)
{
amount = MIN (16, prop_size);
info->cp += xcf_read_int8 (info->fp, buf, amount);
prop_size -= MIN (16, amount);
}
}
break;
}
}
return FALSE;
}
static gboolean
xcf_load_layer_props (XcfInfo *info,
GimpImage *gimage,
GimpLayer *layer,
gboolean *apply_mask,
gboolean *edit_mask,
gboolean *show_mask)
{
PropType prop_type;
guint32 prop_size;
while (TRUE)
{
if (!xcf_load_prop (info, &prop_type, &prop_size))
return FALSE;
switch (prop_type)
{
case PROP_END:
return TRUE;
case PROP_ACTIVE_LAYER:
info->active_layer = layer;
break;
case PROP_FLOATING_SELECTION:
info->floating_sel = layer;
info->cp += xcf_read_int32 (info->fp, (guint32*) &info->floating_sel_offset, 1);
break;
case PROP_OPACITY:
info->cp += xcf_read_int32 (info->fp, (guint32*) &layer->opacity, 1);
break;
case PROP_VISIBLE:
{
gboolean visible;
info->cp += xcf_read_int32 (info->fp, (guint32 *) &visible, 1);
gimp_drawable_set_visible (GIMP_DRAWABLE (layer),
visible ? TRUE : FALSE);
}
break;
case PROP_LINKED:
info->cp += xcf_read_int32 (info->fp, (guint32*) &layer->linked, 1);
break;
case PROP_PRESERVE_TRANSPARENCY:
info->cp += xcf_read_int32 (info->fp, (guint32*) &layer->preserve_trans, 1);
break;
case PROP_APPLY_MASK:
info->cp += xcf_read_int32 (info->fp, (guint32*) apply_mask, 1);
break;
case PROP_EDIT_MASK:
info->cp += xcf_read_int32 (info->fp, (guint32*) edit_mask, 1);
break;
case PROP_SHOW_MASK:
info->cp += xcf_read_int32 (info->fp, (guint32*) show_mask, 1);
break;
case PROP_OFFSETS:
info->cp += xcf_read_int32 (info->fp, (guint32*) &GIMP_DRAWABLE(layer)->offset_x, 1);
info->cp += xcf_read_int32 (info->fp, (guint32*) &GIMP_DRAWABLE(layer)->offset_y, 1);
break;
case PROP_MODE:
info->cp += xcf_read_int32 (info->fp, (guint32*) &layer->mode, 1);
break;
case PROP_TATTOO:
info->cp += xcf_read_int32 (info->fp,
(guint32*) &GIMP_DRAWABLE(layer)->tattoo,
1);
break;
case PROP_PARASITES:
{
long base = info->cp;
GimpParasite *p;
while (info->cp - base < prop_size)
{
p = read_a_parasite(info);
gimp_drawable_parasite_attach(GIMP_DRAWABLE(layer), p);
gimp_parasite_free(p);
}
if (info->cp - base != prop_size)
g_message ("Error detected while loading a layer's parasites");
}
break;
default:
g_message ("unexpected/unknown layer property: %d (skipping)", prop_type);
{
guint8 buf[16];
guint amount;
while (prop_size > 0)
{
amount = MIN (16, prop_size);
info->cp += xcf_read_int8 (info->fp, buf, amount);
prop_size -= MIN (16, amount);
}
}
break;
}
}
return FALSE;
}
static gboolean
xcf_load_channel_props (XcfInfo *info,
GimpImage *gimage,
GimpChannel *channel)
{
PropType prop_type;
guint32 prop_size;
while (TRUE)
{
if (!xcf_load_prop (info, &prop_type, &prop_size))
return FALSE;
switch (prop_type)
{
case PROP_END:
return TRUE;
case PROP_ACTIVE_CHANNEL:
info->active_channel = channel;
break;
case PROP_SELECTION:
gtk_object_unref (GTK_OBJECT (gimage->selection_mask));
gimage->selection_mask = channel;
channel->boundary_known = FALSE;
channel->bounds_known = FALSE;
break;
case PROP_OPACITY:
{
guint32 opacity;
info->cp += xcf_read_int32 (info->fp, &opacity, 1);
channel->color.a = opacity / 255.0;
}
break;
case PROP_VISIBLE:
{
gboolean visible;
info->cp += xcf_read_int32 (info->fp, (guint32 *) &visible, 1);
gimp_drawable_set_visible (GIMP_DRAWABLE (channel),
visible ? TRUE : FALSE);
}
break;
case PROP_SHOW_MASKED:
info->cp += xcf_read_int32 (info->fp, (guint32*) &channel->show_masked, 1);
break;
case PROP_COLOR:
{
guchar col[3];
info->cp += xcf_read_int8 (info->fp, (guint8*) col, 3);
gimp_rgb_set_uchar (&channel->color, col[0], col[1], col[2]);
}
break;
case PROP_TATTOO:
info->cp += xcf_read_int32 (info->fp, &GIMP_DRAWABLE(channel)->tattoo,
1);
break;
case PROP_PARASITES:
{
long base = info->cp;
GimpParasite *p;
while ((info->cp - base) < prop_size)
{
p = read_a_parasite(info);
gimp_drawable_parasite_attach(GIMP_DRAWABLE(channel), p);
gimp_parasite_free(p);
}
if (info->cp - base != prop_size)
g_message("Error detected while loading a channel's parasites");
}
break;
default:
g_message ("unexpected/unknown channel property: %d (skipping)", prop_type);
{
guint8 buf[16];
guint amount;
while (prop_size > 0)
{
amount = MIN (16, prop_size);
info->cp += xcf_read_int8 (info->fp, buf, amount);
prop_size -= MIN (16, amount);
}
}
break;
}
}
return FALSE;
}
static gboolean
xcf_load_prop (XcfInfo *info,
PropType *prop_type,
guint32 *prop_size)
{
info->cp += xcf_read_int32 (info->fp, (guint32*) prop_type, 1);
info->cp += xcf_read_int32 (info->fp, (guint32*) prop_size, 1);
return TRUE;
}
static GimpLayer *
xcf_load_layer (XcfInfo *info,
GimpImage *gimage)
{
GimpLayer *layer;
GimpLayerMask *layer_mask;
guint32 hierarchy_offset;
guint32 layer_mask_offset;
gboolean apply_mask;
gboolean edit_mask;
gboolean show_mask;
gint width;
gint height;
gint type;
gint add_floating_sel;
gchar *name;
/* check and see if this is the drawable the floating selection
* is attached to. if it is then we'll do the attachment at
* the end of this function.
*/
add_floating_sel = (info->cp == info->floating_sel_offset);
/* read in the layer width, height, type and name */
info->cp += xcf_read_int32 (info->fp, (guint32*) &width, 1);
info->cp += xcf_read_int32 (info->fp, (guint32*) &height, 1);
info->cp += xcf_read_int32 (info->fp, (guint32*) &type, 1);
info->cp += xcf_read_string (info->fp, &name, 1);
/* create a new layer */
layer = gimp_layer_new (gimage, width, height, type, name, 255, NORMAL_MODE);
g_free (name);
if (!layer)
return NULL;
/* read in the layer properties */
if (!xcf_load_layer_props (info, gimage, layer,
&apply_mask, &edit_mask, &show_mask))
goto error;
/* read the hierarchy and layer mask offsets */
info->cp += xcf_read_int32 (info->fp, &hierarchy_offset, 1);
info->cp += xcf_read_int32 (info->fp, &layer_mask_offset, 1);
/* read in the hierarchy */
xcf_seek_pos (info, hierarchy_offset);
if (!xcf_load_hierarchy (info, GIMP_DRAWABLE(layer)->tiles))
goto error;
/* read in the layer mask */
if (layer_mask_offset != 0)
{
xcf_seek_pos (info, layer_mask_offset);
layer_mask = xcf_load_layer_mask (info, gimage);
if (!layer_mask)
goto error;
/* set the offsets of the layer_mask */
GIMP_DRAWABLE (layer_mask)->offset_x = GIMP_DRAWABLE (layer)->offset_x;
GIMP_DRAWABLE (layer_mask)->offset_y = GIMP_DRAWABLE (layer)->offset_y;
gimp_layer_add_mask (layer, layer_mask, FALSE);
layer->mask->apply_mask = apply_mask;
layer->mask->edit_mask = edit_mask;
layer->mask->show_mask = show_mask;
}
/* attach the floating selection... */
if (add_floating_sel)
{
GimpLayer *floating_sel;
floating_sel = info->floating_sel;
floating_sel_attach (floating_sel, GIMP_DRAWABLE (layer));
}
return layer;
error:
gtk_object_unref (GTK_OBJECT (layer));
return NULL;
}
static GimpChannel *
xcf_load_channel (XcfInfo *info,
GimpImage *gimage)
{
GimpChannel *channel;
guint32 hierarchy_offset;
gint width;
gint height;
gint add_floating_sel;
gchar *name;
GimpRGB color = { 0.0, 0.0, 0.0, 1.0 };
/* check and see if this is the drawable the floating selection
* is attached to. if it is then we'll do the attachment at
* the end of this function.
*/
add_floating_sel = (info->cp == info->floating_sel_offset);
/* read in the layer width, height and name */
info->cp += xcf_read_int32 (info->fp, (guint32*) &width, 1);
info->cp += xcf_read_int32 (info->fp, (guint32*) &height, 1);
info->cp += xcf_read_string (info->fp, &name, 1);
/* create a new channel */
channel = gimp_channel_new (gimage, width, height, name, &color);
g_free (name);
if (!channel)
return NULL;
/* read in the channel properties */
if (!xcf_load_channel_props (info, gimage, channel))
goto error;
/* read the hierarchy and layer mask offsets */
info->cp += xcf_read_int32 (info->fp, &hierarchy_offset, 1);
/* read in the hierarchy */
xcf_seek_pos (info, hierarchy_offset);
if (!xcf_load_hierarchy (info, GIMP_DRAWABLE (channel)->tiles))
goto error;
/* attach the floating selection... */
if (add_floating_sel)
{
GimpLayer *floating_sel;
floating_sel = info->floating_sel;
floating_sel_attach (floating_sel, GIMP_DRAWABLE (channel));
}
return channel;
error:
gtk_object_unref (GTK_OBJECT (channel));
return NULL;
}
static GimpLayerMask *
xcf_load_layer_mask (XcfInfo *info,
GimpImage *gimage)
{
GimpLayerMask *layer_mask;
guint32 hierarchy_offset;
gint width;
gint height;
gint add_floating_sel;
gchar *name;
GimpRGB color = { 0.0, 0.0, 0.0, 1.0 };
/* check and see if this is the drawable the floating selection
* is attached to. if it is then we'll do the attachment at
* the end of this function.
*/
add_floating_sel = (info->cp == info->floating_sel_offset);
/* read in the layer width, height and name */
info->cp += xcf_read_int32 (info->fp, (guint32*) &width, 1);
info->cp += xcf_read_int32 (info->fp, (guint32*) &height, 1);
info->cp += xcf_read_string (info->fp, &name, 1);
/* create a new layer mask */
layer_mask = gimp_layer_mask_new (gimage, width, height, name, &color);
g_free (name);
if (!layer_mask)
return NULL;
/* read in the layer_mask properties */
if (!xcf_load_channel_props (info, gimage, GIMP_CHANNEL (layer_mask)))
goto error;
/* read the hierarchy and layer mask offsets */
info->cp += xcf_read_int32 (info->fp, &hierarchy_offset, 1);
/* read in the hierarchy */
xcf_seek_pos (info, hierarchy_offset);
if (!xcf_load_hierarchy (info, GIMP_DRAWABLE (layer_mask)->tiles))
goto error;
/* attach the floating selection... */
if (add_floating_sel)
{
GimpLayer *floating_sel;
floating_sel = info->floating_sel;
floating_sel_attach (floating_sel, GIMP_DRAWABLE (layer_mask));
}
return layer_mask;
error:
gtk_object_unref (GTK_OBJECT (layer_mask));
return NULL;
}
static gboolean
xcf_load_hierarchy (XcfInfo *info,
TileManager *tiles)
{
guint32 saved_pos;
guint32 offset;
guint32 junk;
gint width;
gint height;
gint bpp;
info->cp += xcf_read_int32 (info->fp, (guint32 *) &width, 1);
info->cp += xcf_read_int32 (info->fp, (guint32 *) &height, 1);
info->cp += xcf_read_int32 (info->fp, (guint32 *) &bpp, 1);
/* make sure the values in the file correspond to the values
* calculated when the TileManager was created.
*/
if (width != tile_manager_width (tiles) ||
height != tile_manager_height (tiles) ||
bpp != tile_manager_bpp (tiles))
return FALSE;
/* load in the levels...we make sure that the number of levels
* calculated when the TileManager was created is the same
* as the number of levels found in the file.
*/
info->cp += xcf_read_int32 (info->fp, &offset, 1); /* top level */
/* discard offsets for layers below first, if any.
*/
do
{
info->cp += xcf_read_int32 (info->fp, &junk, 1);
}
while (junk != 0);
/* save the current position as it is where the
* next level offset is stored.
*/
saved_pos = info->cp;
/* seek to the level offset */
xcf_seek_pos (info, offset);
/* read in the level */
if (!xcf_load_level (info, tiles))
return FALSE;
/* restore the saved position so we'll be ready to
* read the next offset.
*/
xcf_seek_pos (info, saved_pos);
return TRUE;
}
static gboolean
xcf_load_level (XcfInfo *info,
TileManager *tiles)
{
guint32 saved_pos;
guint32 offset, offset2;
guint ntiles;
gint width;
gint height;
gint i;
gint fail;
Tile *previous;
Tile *tile;
info->cp += xcf_read_int32 (info->fp, (guint32*) &width, 1);
info->cp += xcf_read_int32 (info->fp, (guint32*) &height, 1);
if (width != tile_manager_width (tiles) ||
height != tile_manager_height (tiles))
return FALSE;
/* read in the first tile offset.
* if it is '0', then this tile level is empty
* and we can simply return.
*/
info->cp += xcf_read_int32 (info->fp, &offset, 1);
if (offset == 0)
return TRUE;
/* Initialise the reference for the in-memory tile-compression
*/
previous = NULL;
ntiles = tiles->ntile_rows * tiles->ntile_cols;
for (i = 0; i < ntiles; i++)
{
fail = FALSE;
if (offset == 0)
{
g_message ("not enough tiles found in level");
return FALSE;
}
/* save the current position as it is where the
* next tile offset is stored.
*/
saved_pos = info->cp;
/* read in the offset of the next tile so we can calculate the amount
of data needed for this tile*/
info->cp += xcf_read_int32 (info->fp, &offset2, 1);
/* if the offset is 0 then we need to read in the maximum possible
allowing for negative compression */
if (offset2 == 0)
offset2 = offset + TILE_WIDTH * TILE_WIDTH * 4* 1.5;
/* 1.5 is probably more
than we need to allow */
/* seek to the tile offset */
xcf_seek_pos (info, offset);
/* get the tile from the tile manager */
tile = tile_manager_get (tiles, i, TRUE, TRUE);
/* read in the tile */
switch (info->compression)
{
case COMPRESS_NONE:
if (!xcf_load_tile (info, tile))
fail = TRUE;
break;
case COMPRESS_RLE:
if (!xcf_load_tile_rle (info, tile, offset2 - offset))
fail = TRUE;
break;
case COMPRESS_ZLIB:
g_error ("xcf: zlib compression unimplemented");
fail = TRUE;
break;
case COMPRESS_FRACTAL:
g_error ("xcf: fractal compression unimplemented");
fail = TRUE;
break;
}
if (fail)
{
tile_release (tile, TRUE);
return FALSE;
}
/* To potentially save memory, we compare the
* newly-fetched tile against the last one, and
* if they're the same we copy-on-write mirror one against
* the other.
*/
if (previous != NULL)
{
tile_lock (previous);
if (tile_ewidth (tile) == tile_ewidth (previous) &&
tile_eheight (tile) == tile_eheight (previous) &&
tile_bpp (tile) == tile_bpp (previous) &&
memcmp (tile_data_pointer (tile, 0, 0),
tile_data_pointer (previous, 0, 0),
tile_size (tile)) == 0)
tile_manager_map (tiles, i, previous);
tile_release (previous, FALSE);
}
tile_release (tile, TRUE);
previous = tile_manager_get (tiles, i, FALSE, FALSE);
/* restore the saved position so we'll be ready to
* read the next offset.
*/
xcf_seek_pos (info, saved_pos);
/* read in the offset of the next tile */
info->cp += xcf_read_int32 (info->fp, &offset, 1);
}
if (offset != 0)
{
g_message ("encountered garbage after reading level: %d", offset);
return FALSE;
}
return TRUE;
}
static gboolean
xcf_load_tile (XcfInfo *info,
Tile *tile)
{
#ifdef SWAP_FROM_FILE
if (!info->swap_num)
{
info->ref_count = g_new (int, 1);
info->swap_num = tile_swap_add (info->filename,
xcf_swap_func,
info->ref_count);
}
tile->swap_num = info->swap_num;
tile->swap_offset = info->cp;
*info->ref_count += 1;
#else
info->cp += xcf_read_int8 (info->fp, tile_data_pointer(tile, 0, 0),
tile_size (tile));
#endif
return TRUE;
}
static gboolean
xcf_load_tile_rle (XcfInfo *info,
Tile *tile,
int data_length)
{
guchar *data;
guchar val;
gint size;
gint count;
gint length;
gint bpp;
gint i, j;
gint nmemb_read_successfully;
guchar *xcfdata, *xcfodata, *xcfdatalimit;
data = tile_data_pointer (tile, 0, 0);
bpp = tile_bpp (tile);
xcfdata = xcfodata = g_malloc (data_length);
/* we have to use fread instead of xcf_read_* because we may be
reading past the end of the file here */
nmemb_read_successfully = fread ((gchar*) xcfdata, sizeof (char),
data_length, info->fp);
info->cp += nmemb_read_successfully;
xcfdatalimit = &xcfodata[nmemb_read_successfully - 1];
for (i = 0; i < bpp; i++)
{
data = (guchar*) tile_data_pointer (tile, 0, 0) + i;
size = tile_ewidth (tile) * tile_eheight (tile);
count = 0;
while (size > 0)
{
if (xcfdata > xcfdatalimit)
{
goto bogus_rle;
}
val = *xcfdata++;
length = val;
if (length >= 128)
{
length = 255 - (length - 1);
if (length == 128)
{
if (xcfdata >= xcfdatalimit)
{
goto bogus_rle;
}
length = (*xcfdata << 8) + xcfdata[1];
xcfdata += 2;
}
count += length;
size -= length;
if (size < 0)
{
goto bogus_rle;
}
if (&xcfdata[length-1] > xcfdatalimit)
{
goto bogus_rle;
}
while (length-- > 0)
{
*data = *xcfdata++;
data += bpp;
}
}
else
{
length += 1;
if (length == 128)
{
if (xcfdata >= xcfdatalimit)
{
goto bogus_rle;
}
length = (*xcfdata << 8) + xcfdata[1];
xcfdata += 2;
}
count += length;
size -= length;
if (size < 0)
{
goto bogus_rle;
}
if (xcfdata > xcfdatalimit)
{
goto bogus_rle;
}
val = *xcfdata++;
for (j = 0; j < length; j++)
{
*data = val;
data += bpp;
}
}
}
}
g_free (xcfodata);
return TRUE;
bogus_rle:
if (xcfodata)
g_free (xcfodata);
return FALSE;
}
#ifdef SWAP_FROM_FILE
static gboolean
xcf_swap_func (gint fd,
Tile *tile,
gint cmd,
gpointer user_data)
{
gint bytes;
gint err;
gint nleft;
gint *ref_count;
switch (cmd)
{
case SWAP_IN:
lseek (fd, tile->swap_offset, SEEK_SET);
bytes = tile_size (tile);
tile_alloc (tile);
nleft = bytes;
while (nleft > 0)
{
do {
err = read (fd, tile->data + bytes - nleft, nleft);
} while ((err == -1) && ((errno == EAGAIN) || (errno == EINTR)));
if (err <= 0)
{
g_message ("unable to read tile data from xcf file: %d ( %d ) bytes read", err, nleft);
return FALSE;
}
nleft -= err;
}
break;
case SWAP_OUT:
case SWAP_DELETE:
case SWAP_COMPRESS:
ref_count = user_data;
*ref_count -= 1;
if (*ref_count == 0)
{
tile_swap_remove (tile->swap_num);
g_free (ref_count);
}
tile->swap_num = 1;
tile->swap_offset = -1;
return TRUE;
}
return FALSE;
}
#endif