2006-12-10 05:33:38 +08:00
|
|
|
/* GIMP - The GNU Image Manipulation Program
|
2001-07-04 02:38:56 +08:00
|
|
|
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
|
|
|
*
|
2009-01-18 06:28:01 +08:00
|
|
|
* This program is free software: you can redistribute it and/or modify
|
2001-07-04 02:38:56 +08:00
|
|
|
* it under the terms of the GNU General Public License as published by
|
2009-01-18 06:28:01 +08:00
|
|
|
* the Free Software Foundation; either version 3 of the License, or
|
2001-07-04 02:38:56 +08:00
|
|
|
* (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
|
2018-07-12 05:27:07 +08:00
|
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
2001-07-04 02:38:56 +08:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
2008-10-10 04:24:04 +08:00
|
|
|
#include <string.h>
|
2014-09-15 21:33:22 +08:00
|
|
|
#include <zlib.h>
|
2001-07-04 02:38:56 +08:00
|
|
|
|
2011-04-28 21:50:39 +08:00
|
|
|
#include <cairo.h>
|
2008-10-10 04:24:04 +08:00
|
|
|
#include <gegl.h>
|
2012-05-03 09:36:22 +08:00
|
|
|
#include <gdk-pixbuf/gdk-pixbuf.h>
|
2001-07-04 02:38:56 +08:00
|
|
|
|
|
|
|
#include "libgimpbase/gimpbase.h"
|
|
|
|
#include "libgimpcolor/gimpcolor.h"
|
2024-01-23 00:49:10 +08:00
|
|
|
#include "libgimpconfig/gimpconfig.h"
|
2001-07-04 02:38:56 +08:00
|
|
|
|
|
|
|
#include "core/core-types.h"
|
|
|
|
|
2002-11-19 04:50:31 +08:00
|
|
|
#include "config/gimpcoreconfig.h"
|
|
|
|
|
2018-07-07 01:00:35 +08:00
|
|
|
#include "gegl/gimp-babl.h"
|
2012-04-03 05:32:38 +08:00
|
|
|
#include "gegl/gimp-gegl-tile-compat.h"
|
|
|
|
|
2001-07-06 00:21:36 +08:00
|
|
|
#include "core/gimp.h"
|
2001-07-04 02:38:56 +08:00
|
|
|
#include "core/gimpcontainer.h"
|
2023-06-19 22:54:21 +08:00
|
|
|
#include "core/gimpdrawable-filters.h"
|
2009-02-04 07:57:11 +08:00
|
|
|
#include "core/gimpdrawable-private.h" /* eek */
|
2023-06-19 22:54:21 +08:00
|
|
|
#include "core/gimpdrawablefilter.h"
|
|
|
|
#include "core/gimpfilterstack.h"
|
2003-10-10 22:11:47 +08:00
|
|
|
#include "core/gimpgrid.h"
|
2009-08-31 03:28:59 +08:00
|
|
|
#include "core/gimpgrouplayer.h"
|
2001-07-04 02:38:56 +08:00
|
|
|
#include "core/gimpimage.h"
|
2022-08-19 06:30:04 +08:00
|
|
|
#include "core/gimpimage-color-profile.h"
|
2007-10-26 17:48:07 +08:00
|
|
|
#include "core/gimpimage-colormap.h"
|
2003-07-05 03:55:58 +08:00
|
|
|
#include "core/gimpimage-grid.h"
|
include the new "paint-funcs/paint-funcs-types.h".
2001-11-28 Michael Natterer <mitch@gimp.org>
* app/base/base-types.h: include the new
"paint-funcs/paint-funcs-types.h".
* app/paint-funcs/Makefile.am
* app/paint-funcs/paint-funcs-types.h: new file. Includes
"base/base-types.h".
* app/paint-funcs/paint-funcs.[ch]: removed the enums here,
include "paint-funcs-types.h".
* app/widgets/widgets-types.h: include "display/display-types.h"
* app/display/display-types.h: include "widgets/widgets-types.h".
* app/tools/tools-types.h: include "display/display-types.h"
* app/gui/gui-types.h: include "tools/tools-types.h".
The order of namespaces/dependencies should be (but is not):
(base, paint-funcs) -> (core, file, xcf, pdb) ->
(widgets, display) -> tools -> gui
* app/path.c: include "tools/tools-types.h".
* app/core/Makefile.am
* app/core/gimpimage-guides.[ch]
* app/core/gimpimage-merge.[ch]
* app/core/gimpimage-resize.[ch]
* app/core/gimpimage-scale.[ch]: new files.
* app/core/gimpimage.[ch]: removed the stuff which is in the new
files. Reordered all functions in both the .h and .c files,
commented the groups of functions.
* app/core/gimpcontainer.c: create the handler_id using a counter,
not the address of a pointer, because the address *may* be the
same twice, added debugging output.
* app/core/gimpviewable.[ch]: added primitive support for getting
a preview GdkPixbuf.
* app/nav_window.c
* app/undo.c
* app/undo_history.c
* app/core/gimpimage-duplicate.c
* app/core/gimpimage-mask.[ch]
* app/display/gimpdisplay.c
* app/display/gimpdisplayshell-callbacks.c
* app/display/gimpdisplayshell-dnd.c
* app/display/gimpdisplayshell-render.c
* app/display/gimpdisplayshell-scale.c
* app/display/gimpdisplayshell-scroll.c
* app/gui/image-commands.c
* app/gui/info-window.c
* app/gui/layers-commands.c
* app/gui/palette-import-dialog.c
* app/tools/gimpbycolorselecttool.c
* app/tools/gimpeditselectiontool.c
* app/tools/gimpmeasuretool.c
* app/tools/gimpmovetool.c
* app/widgets/gimpcontainerview-utils.c
* app/xcf/xcf-load.c: changed accordingly, some cleanup.
* tools/pdbgen/pdb/guides.pdb
* tools/pdbgen/pdb/image.pdb: changed accordingly, reordered functions.
* app/plug_in.c: set the labels of the "Repeat" and "Re-Show" menu
items to the name of the last plug-in (Fixes #50986).
* app/display/gimpdisplayshell.[ch]: set the labels of "Undo" and
"Redo" to the resp. undo names. Much simplified the WM icon stuff
by removing most code and using gimp_viewable_get_new_preview_pixbuf().
* app/widgets/gimpbrushfactoryview.c: forgot to assign the GQuark
returned by gimp_container_add_handler().
* app/pdb/guides_cmds.c
* app/pdb/image_cmds.c
* libgimp/gimpimage_pdb.[ch]: regenerated.
2001-11-29 01:51:06 +08:00
|
|
|
#include "core/gimpimage-guides.h"
|
2013-10-20 00:38:01 +08:00
|
|
|
#include "core/gimpimage-metadata.h"
|
2010-02-04 05:16:02 +08:00
|
|
|
#include "core/gimpimage-private.h"
|
2006-08-11 01:10:12 +08:00
|
|
|
#include "core/gimpimage-sample-points.h"
|
2016-01-28 02:18:53 +08:00
|
|
|
#include "core/gimpimage-symmetry.h"
|
2010-02-08 18:11:33 +08:00
|
|
|
#include "core/gimpimage-undo.h"
|
2021-12-16 07:35:20 +08:00
|
|
|
#include "core/gimpitemlist.h"
|
2009-08-31 03:28:59 +08:00
|
|
|
#include "core/gimpitemstack.h"
|
2016-05-20 22:46:26 +08:00
|
|
|
#include "core/gimplayer-floating-selection.h"
|
2015-06-17 19:21:01 +08:00
|
|
|
#include "core/gimplayer-new.h"
|
2001-07-04 02:38:56 +08:00
|
|
|
#include "core/gimplayermask.h"
|
2001-07-10 03:48:30 +08:00
|
|
|
#include "core/gimpparasitelist.h"
|
2006-07-12 04:21:18 +08:00
|
|
|
#include "core/gimpprogress.h"
|
2003-09-03 07:07:40 +08:00
|
|
|
#include "core/gimpselection.h"
|
2016-01-28 02:18:53 +08:00
|
|
|
#include "core/gimpsymmetry.h"
|
2003-10-14 23:20:59 +08:00
|
|
|
#include "core/gimptemplate.h"
|
2001-07-04 02:38:56 +08:00
|
|
|
|
app: future-proof XCF layer blend/composite props
The layer blend space, composite space, and composite mode
properties have a special AUTO value, which may map to different
concrete values based on the layer mode. Make sure we can change
this mapping in the future, without affecting existing XCFs (saved
after this commit), by encoding these properties as follows:
When saving an XCF, if the property has a concrete (non-AUTO)
value, which is always positive, encode it as is. If the property
is AUTO, which is always 0, encode it as the negative of the value
it actually maps to at the time of saving (note that in some cases
AUTO may map to AUTO, in which case it's encoded as 0).
When loading an XCF, if the encoded property (stored in the file)
is nonnegative, use it as is. Otherwise, compare the negative of
the encoded property to the value AUTO maps to at the time of
loading. If the values are equal, set the property to AUTO;
otherwise, use the concrete value (i.e., the negative of the value
stored in the XCF).
Note that XCFs saved prior to this commit still load fine, it's
simply that if we change the AUTO mapping in the future, all their
AUTO properties will keep being loaded as AUTO, even if the
resulting concrete values will have changed.
2017-05-21 20:01:39 +08:00
|
|
|
#include "operations/layer-modes/gimp-layer-modes.h"
|
|
|
|
|
2004-03-18 23:27:23 +08:00
|
|
|
#include "text/gimptextlayer.h"
|
2003-10-28 05:50:41 +08:00
|
|
|
#include "text/gimptextlayer-xcf.h"
|
2003-06-24 21:59:36 +08:00
|
|
|
|
2003-09-09 23:46:59 +08:00
|
|
|
#include "vectors/gimpanchor.h"
|
|
|
|
#include "vectors/gimpstroke.h"
|
|
|
|
#include "vectors/gimpbezierstroke.h"
|
2003-05-15 03:21:42 +08:00
|
|
|
#include "vectors/gimpvectors.h"
|
2003-05-23 03:02:38 +08:00
|
|
|
#include "vectors/gimpvectors-compat.h"
|
2003-05-15 03:21:42 +08:00
|
|
|
|
2001-07-04 02:38:56 +08:00
|
|
|
#include "xcf-private.h"
|
|
|
|
#include "xcf-load.h"
|
|
|
|
#include "xcf-read.h"
|
|
|
|
#include "xcf-seek.h"
|
2017-10-04 18:43:56 +08:00
|
|
|
#include "xcf-utils.h"
|
2001-07-04 02:38:56 +08:00
|
|
|
|
2014-07-10 04:36:55 +08:00
|
|
|
#include "gimp-log.h"
|
2003-03-26 00:38:19 +08:00
|
|
|
#include "gimp-intl.h"
|
2001-07-04 02:38:56 +08:00
|
|
|
|
2006-07-12 04:21:18 +08:00
|
|
|
|
2012-10-03 04:00:16 +08:00
|
|
|
#define MAX_XCF_PARASITE_DATA_LEN (256L * 1024 * 1024)
|
|
|
|
|
2004-03-03 23:54:19 +08:00
|
|
|
/* #define GIMP_XCF_PATH_DEBUG */
|
2001-07-04 02:38:56 +08:00
|
|
|
|
2023-06-19 22:54:21 +08:00
|
|
|
/* Filters can not be created until a layer is attached
|
|
|
|
* to an image, so we use this struct to store relevant
|
|
|
|
* information until then */
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
GeglNode *operation;
|
|
|
|
gchar *name;
|
|
|
|
gchar *icon_name;
|
2024-01-04 02:57:08 +08:00
|
|
|
gchar *operation_name;
|
2023-06-19 22:54:21 +08:00
|
|
|
GimpChannel *mask;
|
|
|
|
gboolean is_visible;
|
|
|
|
gdouble opacity;
|
|
|
|
GimpLayerMode paint_mode;
|
|
|
|
GimpLayerColorSpace blend_space;
|
|
|
|
GimpLayerColorSpace composite_space;
|
|
|
|
GimpLayerCompositeMode composite_mode;
|
|
|
|
GimpFilterRegion region;
|
2024-01-04 02:57:08 +08:00
|
|
|
|
|
|
|
gboolean unsupported_operation;
|
2023-06-19 22:54:21 +08:00
|
|
|
} FilterData;
|
2012-10-03 04:00:16 +08:00
|
|
|
|
2012-04-03 05:32:38 +08:00
|
|
|
static void xcf_load_add_masks (GimpImage *image);
|
2023-06-19 22:54:21 +08:00
|
|
|
static void xcf_load_add_effects (XcfInfo *info,
|
|
|
|
GimpImage *image);
|
2012-04-03 05:32:38 +08:00
|
|
|
static gboolean xcf_load_image_props (XcfInfo *info,
|
|
|
|
GimpImage *image);
|
|
|
|
static gboolean xcf_load_layer_props (XcfInfo *info,
|
|
|
|
GimpImage *image,
|
|
|
|
GimpLayer **layer,
|
|
|
|
GList **item_path,
|
|
|
|
gboolean *apply_mask,
|
|
|
|
gboolean *edit_mask,
|
|
|
|
gboolean *show_mask,
|
|
|
|
guint32 *text_layer_flags,
|
|
|
|
guint32 *group_layer_flags);
|
2019-07-11 21:04:30 +08:00
|
|
|
static gboolean xcf_check_layer_props (XcfInfo *info,
|
2019-07-11 22:32:45 +08:00
|
|
|
GList **item_path,
|
|
|
|
gboolean *is_group_layer,
|
|
|
|
gboolean *is_text_layer);
|
2012-04-03 05:32:38 +08:00
|
|
|
static gboolean xcf_load_channel_props (XcfInfo *info,
|
|
|
|
GimpImage *image,
|
|
|
|
GimpChannel **channel);
|
2023-06-19 22:54:21 +08:00
|
|
|
static gboolean xcf_load_effect_props (XcfInfo *info,
|
|
|
|
FilterData *filter);
|
2022-10-20 06:11:34 +08:00
|
|
|
static gboolean xcf_load_vectors_props (XcfInfo *info,
|
|
|
|
GimpImage *image,
|
|
|
|
GimpVectors **vectors);
|
2012-04-03 05:32:38 +08:00
|
|
|
static gboolean xcf_load_prop (XcfInfo *info,
|
|
|
|
PropType *prop_type,
|
|
|
|
guint32 *prop_size);
|
|
|
|
static GimpLayer * xcf_load_layer (XcfInfo *info,
|
|
|
|
GimpImage *image,
|
|
|
|
GList **item_path);
|
|
|
|
static GimpChannel * xcf_load_channel (XcfInfo *info,
|
|
|
|
GimpImage *image);
|
2023-06-19 22:54:21 +08:00
|
|
|
static FilterData * xcf_load_effect (XcfInfo *info,
|
app: a few fixups to MR !958 (initial non-destructive editing).
- Do not leak allocated return value of gegl_node_to_xml_full().
- When merging layer effects, use gimp_drawable_filter_commit(), making
sure we use the exact same code path as when applying layer effects
destructively from the start. This also ensures that filters are
properly removed from the filter stack (unlike
gimp_drawable_merge_filter()), which was the reason why the rendering
was wrong (hence getting the buffer without effects first, then
reapplying it after was only a workaround to an actual bug).
- When removing a filter, verify the object still exists before doing
anything with it. If this was the last reference, we don't want to
call functions on this object. In gimp_drawable_filter_commit(), we
set up a weak pointer. In gimp_drawable_filter_remove_filter() itself,
we save the pointer to the filter's drawable before actual removal (as
we don't want to dereference a freed object later on).
- export_merge_layer_effects() should merge filters recursively through
layer groups.
- clean up the XCF code:
* No need to wrap the effect pointers list into 2 zero offset. Only
have one zero offset to indicate the list end.
* Add the layer effect mask in the effect structure (not in the layer
structure), similar as for layer masks.
* Effect name and icon made as main data in the structure, not as
properties.
2024-01-03 00:12:35 +08:00
|
|
|
GimpImage *image,
|
2023-06-19 22:54:21 +08:00
|
|
|
GimpDrawable *drawable);
|
|
|
|
static void xcf_load_free_effect (FilterData *data);
|
|
|
|
static void xcf_load_free_effects (GList *effects);
|
2022-10-20 06:11:34 +08:00
|
|
|
static GimpVectors * xcf_load_vectors (XcfInfo *info,
|
|
|
|
GimpImage *image);
|
2012-04-03 05:32:38 +08:00
|
|
|
static GimpLayerMask * xcf_load_layer_mask (XcfInfo *info,
|
|
|
|
GimpImage *image);
|
|
|
|
static gboolean xcf_load_buffer (XcfInfo *info,
|
|
|
|
GeglBuffer *buffer);
|
|
|
|
static gboolean xcf_load_level (XcfInfo *info,
|
|
|
|
GeglBuffer *buffer);
|
|
|
|
static gboolean xcf_load_tile (XcfInfo *info,
|
|
|
|
GeglBuffer *buffer,
|
2012-04-21 05:45:22 +08:00
|
|
|
GeglRectangle *tile_rect,
|
|
|
|
const Babl *format);
|
2012-04-03 05:32:38 +08:00
|
|
|
static gboolean xcf_load_tile_rle (XcfInfo *info,
|
|
|
|
GeglBuffer *buffer,
|
|
|
|
GeglRectangle *tile_rect,
|
2012-04-21 05:45:22 +08:00
|
|
|
const Babl *format,
|
2012-04-03 05:32:38 +08:00
|
|
|
gint data_length);
|
2014-09-15 21:33:22 +08:00
|
|
|
static gboolean xcf_load_tile_zlib (XcfInfo *info,
|
|
|
|
GeglBuffer *buffer,
|
|
|
|
GeglRectangle *tile_rect,
|
|
|
|
const Babl *format,
|
|
|
|
gint data_length);
|
2012-04-03 05:32:38 +08:00
|
|
|
static GimpParasite * xcf_load_parasite (XcfInfo *info);
|
|
|
|
static gboolean xcf_load_old_paths (XcfInfo *info,
|
|
|
|
GimpImage *image);
|
|
|
|
static gboolean xcf_load_old_path (XcfInfo *info,
|
|
|
|
GimpImage *image);
|
2022-10-20 06:11:34 +08:00
|
|
|
static gboolean xcf_load_old_vectors (XcfInfo *info,
|
2012-04-03 05:32:38 +08:00
|
|
|
GimpImage *image);
|
2022-10-20 06:11:34 +08:00
|
|
|
static gboolean xcf_load_old_vector (XcfInfo *info,
|
2012-04-03 05:32:38 +08:00
|
|
|
GimpImage *image);
|
|
|
|
|
|
|
|
static gboolean xcf_skip_unknown_prop (XcfInfo *info,
|
|
|
|
gsize size);
|
2010-08-07 06:26:58 +08:00
|
|
|
|
2019-07-11 21:04:30 +08:00
|
|
|
static gboolean xcf_item_path_is_parent (GList *path,
|
|
|
|
GList *parent_path);
|
|
|
|
static void xcf_fix_item_path (GimpLayer *layer,
|
|
|
|
GList **path,
|
|
|
|
GList *broken_paths);
|
2001-07-04 02:38:56 +08:00
|
|
|
|
2006-07-12 04:21:18 +08:00
|
|
|
#define xcf_progress_update(info) G_STMT_START \
|
|
|
|
{ \
|
|
|
|
if (info->progress) \
|
|
|
|
gimp_progress_pulse (info->progress); \
|
|
|
|
} G_STMT_END
|
|
|
|
|
|
|
|
|
2001-07-04 02:38:56 +08:00
|
|
|
GimpImage *
|
2007-12-03 15:44:49 +08:00
|
|
|
xcf_load_image (Gimp *gimp,
|
|
|
|
XcfInfo *info,
|
|
|
|
GError **error)
|
2001-07-04 02:38:56 +08:00
|
|
|
{
|
2013-06-23 22:51:24 +08:00
|
|
|
GimpImage *image = NULL;
|
2006-04-02 23:37:25 +08:00
|
|
|
const GimpParasite *parasite;
|
2013-10-20 00:38:01 +08:00
|
|
|
gboolean has_metadata = FALSE;
|
2017-03-23 18:44:41 +08:00
|
|
|
goffset saved_pos;
|
|
|
|
goffset offset;
|
2006-04-02 23:37:25 +08:00
|
|
|
gint width;
|
|
|
|
gint height;
|
|
|
|
gint image_type;
|
Initial space invasion commit in GIMP
All babl formats now have a space equivalent to a color profile,
determining the format's primaries and TRCs. This commit makes GIMP
aware of this.
libgimp:
- enum GimpPrecision: rename GAMMA values to NON_LINEAR and keep GAMMA
as deprecated aliases, add PERCEPTUAL values so we now have LINEAR,
NON_LINEAR and PERCPTUAL for each encoding, matching the babl
encoding variants RGB, R'G'B' and R~G~B~.
- gimp_color_transform_can_gegl_copy() now returns TRUE if both
profiles can return a babl space, increasing the amount of fast babl
color conversions significantly.
- TODO: no solution yet for getting libgimp drawable proxy buffers in
the right format with space.
plug-ins:
- follow the GimpPrecision change.
- TODO: everything else unchanged and partly broken or sub-optimal,
like setting a new image's color profile too late.
app:
- add enum GimpTRCType { LINEAR, NON_LINEAR, PERCEPTUAL } as
replacement for all "linear" booleans.
- change gimp-babl functions to take babl spaces and GimpTRCType
parameters and support all sorts of new perceptual ~ formats.
- a lot of places changed in the early days of goat invasion didn't
take advantage of gimp-babl utility functions and constructed
formats manually. They all needed revisiting and many now use much
simpler code calling gimp-babl API.
- change gimp_babl_format_get_color_profile() to really extract a
newly allocated color profile from the format, and add
gimp_babl_get_builtin_color_profile() which does the same as
gimp_babl_format_get_color_profile() did before. Visited all callers
to decide whether they are looking for the format's actual profile,
or for one of the builtin profiles, simplifying code that only needs
builtin profiles.
- drawables have a new get_space_api(), get_linear() is now get_trc().
- images now have a "layer space" and an API to get it,
gimp_image_get_layer_format() returns formats in that space.
- an image's layer space is created from the image's color profile,
change gimpimage-color-profile to deal with that correctly
- change many babl_format() calls to babl_format_with_space() and take
the space from passed formats or drawables
- add function gimp_layer_fix_format_space() which replaces the
layer's buffer with one that has the image's layer format, but
doesn't change pixel values
- use gimp_layer_fix_format_space() to make sure layers loaded from
XCF and created by plug-ins have the right space when added to the
image, because it's impossible to always assign the right space upon
layer creation
- "assign color profile" and "discard color profile" now require use
of gimp_layer_fix_format_space() too because the profile is now
embedded in all formats via the space. Add
gimp_image_assign_color_profile() which does all that and call it
instead of a simple gimp_image_set_color_profile(), also from the
PDB set-color-profile functions, which are essentially "assign" and
"discard" calls.
- generally, make sure a new image's color profile is set before
adding layers to it, gimp_image_set_color_profile() is more than
before considered know-what-you-are-doing API.
- take special precaution in all places that call
gimp_drawable_convert_type(), we now must pass a new_profile from
all callers that convert layers within the same image (such as
image_convert_type, image_convert_precision), because the layer's
new space can't be determined from the image's layer format during
the call.
- change all "linear" properties to "trc", in all config objects like
for levels and curves, in the histogram, in the widgets. This results
in some GUI that now has three choices instead of two.
TODO: we might want to reduce that back to two later.
- keep "linear" boolean properties around as compat if needed for file
pasring, but always convert the parsed parsed boolean to
GimpTRCType.
- TODO: the image's "enable color management" switch is currently
broken, will fix that in another commit.
2018-07-21 20:23:01 +08:00
|
|
|
GimpPrecision precision = GIMP_PRECISION_U8_NON_LINEAR;
|
2006-04-02 23:37:25 +08:00
|
|
|
gint num_successful_elements = 0;
|
2019-07-11 21:04:30 +08:00
|
|
|
gint n_broken_layers = 0;
|
|
|
|
gint n_broken_channels = 0;
|
2022-10-20 06:11:34 +08:00
|
|
|
gint n_broken_vectors = 0;
|
2019-07-11 21:04:30 +08:00
|
|
|
GList *broken_paths = NULL;
|
2020-02-22 18:41:47 +08:00
|
|
|
GList *group_layers = NULL;
|
2016-01-28 02:18:53 +08:00
|
|
|
GList *syms;
|
|
|
|
GList *iter;
|
2001-07-04 02:38:56 +08:00
|
|
|
|
|
|
|
/* read in the image width, height and type */
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_int32 (info, (guint32 *) &width, 1);
|
|
|
|
xcf_read_int32 (info, (guint32 *) &height, 1);
|
|
|
|
xcf_read_int32 (info, (guint32 *) &image_type, 1);
|
2022-06-06 04:48:10 +08:00
|
|
|
if (image_type < GIMP_RGB || image_type > GIMP_INDEXED)
|
2014-06-07 22:33:17 +08:00
|
|
|
goto hard_error;
|
2001-07-04 02:38:56 +08:00
|
|
|
|
2022-06-06 04:48:10 +08:00
|
|
|
/* Be lenient with corrupt image dimensions.
|
|
|
|
* Hopefully layer dimensions will be valid. */
|
|
|
|
if (width <= 0 || height <= 0 ||
|
|
|
|
width > GIMP_MAX_IMAGE_SIZE || height > GIMP_MAX_IMAGE_SIZE)
|
|
|
|
{
|
|
|
|
GIMP_LOG (XCF, "Invalid image size %d x %d, setting to 1x1.", width, height);
|
|
|
|
width = 1;
|
|
|
|
height = 1;
|
|
|
|
}
|
|
|
|
|
2012-04-27 22:42:19 +08:00
|
|
|
if (info->file_version >= 4)
|
2013-06-23 22:51:24 +08:00
|
|
|
{
|
|
|
|
gint p;
|
|
|
|
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_int32 (info, (guint32 *) &p, 1);
|
2013-06-23 22:51:24 +08:00
|
|
|
|
|
|
|
if (info->file_version == 4)
|
|
|
|
{
|
|
|
|
switch (p)
|
|
|
|
{
|
Initial space invasion commit in GIMP
All babl formats now have a space equivalent to a color profile,
determining the format's primaries and TRCs. This commit makes GIMP
aware of this.
libgimp:
- enum GimpPrecision: rename GAMMA values to NON_LINEAR and keep GAMMA
as deprecated aliases, add PERCEPTUAL values so we now have LINEAR,
NON_LINEAR and PERCPTUAL for each encoding, matching the babl
encoding variants RGB, R'G'B' and R~G~B~.
- gimp_color_transform_can_gegl_copy() now returns TRUE if both
profiles can return a babl space, increasing the amount of fast babl
color conversions significantly.
- TODO: no solution yet for getting libgimp drawable proxy buffers in
the right format with space.
plug-ins:
- follow the GimpPrecision change.
- TODO: everything else unchanged and partly broken or sub-optimal,
like setting a new image's color profile too late.
app:
- add enum GimpTRCType { LINEAR, NON_LINEAR, PERCEPTUAL } as
replacement for all "linear" booleans.
- change gimp-babl functions to take babl spaces and GimpTRCType
parameters and support all sorts of new perceptual ~ formats.
- a lot of places changed in the early days of goat invasion didn't
take advantage of gimp-babl utility functions and constructed
formats manually. They all needed revisiting and many now use much
simpler code calling gimp-babl API.
- change gimp_babl_format_get_color_profile() to really extract a
newly allocated color profile from the format, and add
gimp_babl_get_builtin_color_profile() which does the same as
gimp_babl_format_get_color_profile() did before. Visited all callers
to decide whether they are looking for the format's actual profile,
or for one of the builtin profiles, simplifying code that only needs
builtin profiles.
- drawables have a new get_space_api(), get_linear() is now get_trc().
- images now have a "layer space" and an API to get it,
gimp_image_get_layer_format() returns formats in that space.
- an image's layer space is created from the image's color profile,
change gimpimage-color-profile to deal with that correctly
- change many babl_format() calls to babl_format_with_space() and take
the space from passed formats or drawables
- add function gimp_layer_fix_format_space() which replaces the
layer's buffer with one that has the image's layer format, but
doesn't change pixel values
- use gimp_layer_fix_format_space() to make sure layers loaded from
XCF and created by plug-ins have the right space when added to the
image, because it's impossible to always assign the right space upon
layer creation
- "assign color profile" and "discard color profile" now require use
of gimp_layer_fix_format_space() too because the profile is now
embedded in all formats via the space. Add
gimp_image_assign_color_profile() which does all that and call it
instead of a simple gimp_image_set_color_profile(), also from the
PDB set-color-profile functions, which are essentially "assign" and
"discard" calls.
- generally, make sure a new image's color profile is set before
adding layers to it, gimp_image_set_color_profile() is more than
before considered know-what-you-are-doing API.
- take special precaution in all places that call
gimp_drawable_convert_type(), we now must pass a new_profile from
all callers that convert layers within the same image (such as
image_convert_type, image_convert_precision), because the layer's
new space can't be determined from the image's layer format during
the call.
- change all "linear" properties to "trc", in all config objects like
for levels and curves, in the histogram, in the widgets. This results
in some GUI that now has three choices instead of two.
TODO: we might want to reduce that back to two later.
- keep "linear" boolean properties around as compat if needed for file
pasring, but always convert the parsed parsed boolean to
GimpTRCType.
- TODO: the image's "enable color management" switch is currently
broken, will fix that in another commit.
2018-07-21 20:23:01 +08:00
|
|
|
case 0: precision = GIMP_PRECISION_U8_NON_LINEAR; break;
|
|
|
|
case 1: precision = GIMP_PRECISION_U16_NON_LINEAR; break;
|
|
|
|
case 2: precision = GIMP_PRECISION_U32_LINEAR; break;
|
|
|
|
case 3: precision = GIMP_PRECISION_HALF_LINEAR; break;
|
|
|
|
case 4: precision = GIMP_PRECISION_FLOAT_LINEAR; break;
|
2013-06-23 22:51:24 +08:00
|
|
|
default:
|
|
|
|
goto hard_error;
|
|
|
|
}
|
|
|
|
}
|
2014-03-09 08:44:43 +08:00
|
|
|
else if (info->file_version == 5 ||
|
|
|
|
info->file_version == 6)
|
|
|
|
{
|
|
|
|
switch (p)
|
|
|
|
{
|
Initial space invasion commit in GIMP
All babl formats now have a space equivalent to a color profile,
determining the format's primaries and TRCs. This commit makes GIMP
aware of this.
libgimp:
- enum GimpPrecision: rename GAMMA values to NON_LINEAR and keep GAMMA
as deprecated aliases, add PERCEPTUAL values so we now have LINEAR,
NON_LINEAR and PERCPTUAL for each encoding, matching the babl
encoding variants RGB, R'G'B' and R~G~B~.
- gimp_color_transform_can_gegl_copy() now returns TRUE if both
profiles can return a babl space, increasing the amount of fast babl
color conversions significantly.
- TODO: no solution yet for getting libgimp drawable proxy buffers in
the right format with space.
plug-ins:
- follow the GimpPrecision change.
- TODO: everything else unchanged and partly broken or sub-optimal,
like setting a new image's color profile too late.
app:
- add enum GimpTRCType { LINEAR, NON_LINEAR, PERCEPTUAL } as
replacement for all "linear" booleans.
- change gimp-babl functions to take babl spaces and GimpTRCType
parameters and support all sorts of new perceptual ~ formats.
- a lot of places changed in the early days of goat invasion didn't
take advantage of gimp-babl utility functions and constructed
formats manually. They all needed revisiting and many now use much
simpler code calling gimp-babl API.
- change gimp_babl_format_get_color_profile() to really extract a
newly allocated color profile from the format, and add
gimp_babl_get_builtin_color_profile() which does the same as
gimp_babl_format_get_color_profile() did before. Visited all callers
to decide whether they are looking for the format's actual profile,
or for one of the builtin profiles, simplifying code that only needs
builtin profiles.
- drawables have a new get_space_api(), get_linear() is now get_trc().
- images now have a "layer space" and an API to get it,
gimp_image_get_layer_format() returns formats in that space.
- an image's layer space is created from the image's color profile,
change gimpimage-color-profile to deal with that correctly
- change many babl_format() calls to babl_format_with_space() and take
the space from passed formats or drawables
- add function gimp_layer_fix_format_space() which replaces the
layer's buffer with one that has the image's layer format, but
doesn't change pixel values
- use gimp_layer_fix_format_space() to make sure layers loaded from
XCF and created by plug-ins have the right space when added to the
image, because it's impossible to always assign the right space upon
layer creation
- "assign color profile" and "discard color profile" now require use
of gimp_layer_fix_format_space() too because the profile is now
embedded in all formats via the space. Add
gimp_image_assign_color_profile() which does all that and call it
instead of a simple gimp_image_set_color_profile(), also from the
PDB set-color-profile functions, which are essentially "assign" and
"discard" calls.
- generally, make sure a new image's color profile is set before
adding layers to it, gimp_image_set_color_profile() is more than
before considered know-what-you-are-doing API.
- take special precaution in all places that call
gimp_drawable_convert_type(), we now must pass a new_profile from
all callers that convert layers within the same image (such as
image_convert_type, image_convert_precision), because the layer's
new space can't be determined from the image's layer format during
the call.
- change all "linear" properties to "trc", in all config objects like
for levels and curves, in the histogram, in the widgets. This results
in some GUI that now has three choices instead of two.
TODO: we might want to reduce that back to two later.
- keep "linear" boolean properties around as compat if needed for file
pasring, but always convert the parsed parsed boolean to
GimpTRCType.
- TODO: the image's "enable color management" switch is currently
broken, will fix that in another commit.
2018-07-21 20:23:01 +08:00
|
|
|
case 100: precision = GIMP_PRECISION_U8_LINEAR; break;
|
|
|
|
case 150: precision = GIMP_PRECISION_U8_NON_LINEAR; break;
|
|
|
|
case 200: precision = GIMP_PRECISION_U16_LINEAR; break;
|
|
|
|
case 250: precision = GIMP_PRECISION_U16_NON_LINEAR; break;
|
|
|
|
case 300: precision = GIMP_PRECISION_U32_LINEAR; break;
|
|
|
|
case 350: precision = GIMP_PRECISION_U32_NON_LINEAR; break;
|
|
|
|
case 400: precision = GIMP_PRECISION_HALF_LINEAR; break;
|
|
|
|
case 450: precision = GIMP_PRECISION_HALF_NON_LINEAR; break;
|
|
|
|
case 500: precision = GIMP_PRECISION_FLOAT_LINEAR; break;
|
|
|
|
case 550: precision = GIMP_PRECISION_FLOAT_NON_LINEAR; break;
|
2014-03-09 08:44:43 +08:00
|
|
|
default:
|
|
|
|
goto hard_error;
|
|
|
|
}
|
|
|
|
}
|
2013-06-23 22:51:24 +08:00
|
|
|
else
|
|
|
|
{
|
|
|
|
precision = p;
|
|
|
|
}
|
|
|
|
}
|
2012-04-27 22:42:19 +08:00
|
|
|
|
2014-07-10 04:36:55 +08:00
|
|
|
GIMP_LOG (XCF, "version=%d, width=%d, height=%d, image_type=%d, precision=%d",
|
|
|
|
info->file_version, width, height, image_type, precision);
|
|
|
|
|
2018-07-07 01:00:35 +08:00
|
|
|
if (! gimp_babl_is_valid (image_type, precision))
|
|
|
|
{
|
|
|
|
gimp_message_literal (gimp, G_OBJECT (info->progress),
|
|
|
|
GIMP_MESSAGE_ERROR,
|
|
|
|
_("Invalid image mode and precision combination."));
|
|
|
|
goto hard_error;
|
|
|
|
}
|
|
|
|
|
2012-04-27 22:42:19 +08:00
|
|
|
image = gimp_create_image (gimp, width, height, image_type, precision,
|
2012-04-22 23:31:32 +08:00
|
|
|
FALSE);
|
2001-07-04 02:38:56 +08:00
|
|
|
|
2006-03-29 01:08:36 +08:00
|
|
|
gimp_image_undo_disable (image);
|
2005-08-05 22:13:10 +08:00
|
|
|
|
2006-07-12 04:21:18 +08:00
|
|
|
xcf_progress_update (info);
|
|
|
|
|
2001-07-04 02:38:56 +08:00
|
|
|
/* read the image properties */
|
2006-03-29 01:08:36 +08:00
|
|
|
if (! xcf_load_image_props (info, image))
|
2001-07-04 02:38:56 +08:00
|
|
|
goto hard_error;
|
|
|
|
|
2014-07-10 04:36:55 +08:00
|
|
|
GIMP_LOG (XCF, "image props loaded");
|
|
|
|
|
2021-12-23 05:53:15 +08:00
|
|
|
/* Order matters for item sets. */
|
|
|
|
info->layer_sets = g_list_reverse (info->layer_sets);
|
|
|
|
info->channel_sets = g_list_reverse (info->channel_sets);
|
|
|
|
|
2022-08-19 06:30:04 +08:00
|
|
|
/* check for simulation intent parasite */
|
|
|
|
parasite = gimp_image_parasite_find (GIMP_IMAGE (image),
|
|
|
|
"image-simulation-intent");
|
|
|
|
if (parasite)
|
|
|
|
{
|
|
|
|
guint32 parasite_size;
|
|
|
|
const guint8 *intent;
|
|
|
|
GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image);
|
|
|
|
|
|
|
|
intent = (const guint8 *) gimp_parasite_get_data (parasite, ¶site_size);
|
|
|
|
|
|
|
|
if (parasite_size == 1)
|
|
|
|
{
|
|
|
|
if (*intent != GIMP_COLOR_RENDERING_INTENT_PERCEPTUAL &&
|
|
|
|
*intent != GIMP_COLOR_RENDERING_INTENT_RELATIVE_COLORIMETRIC &&
|
|
|
|
*intent != GIMP_COLOR_RENDERING_INTENT_SATURATION &&
|
|
|
|
*intent != GIMP_COLOR_RENDERING_INTENT_ABSOLUTE_COLORIMETRIC)
|
|
|
|
{
|
|
|
|
gimp_message (info->gimp, G_OBJECT (info->progress),
|
|
|
|
GIMP_MESSAGE_ERROR,
|
|
|
|
"Unknown simulation rendering intent: %d",
|
|
|
|
*intent);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
gimp_image_set_simulation_intent (image,
|
|
|
|
(GimpColorRenderingIntent) *intent);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
gimp_message (info->gimp, G_OBJECT (info->progress),
|
|
|
|
GIMP_MESSAGE_ERROR,
|
|
|
|
"Invalid simulation intent data");
|
|
|
|
}
|
|
|
|
|
|
|
|
gimp_parasite_list_remove (private->parasites,
|
|
|
|
gimp_parasite_get_name (parasite));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* check for simulation bpc parasite */
|
|
|
|
parasite = gimp_image_parasite_find (GIMP_IMAGE (image),
|
|
|
|
"image-simulation-bpc");
|
|
|
|
if (parasite)
|
|
|
|
{
|
|
|
|
guint32 parasite_size;
|
|
|
|
const guint8 *bpc;
|
|
|
|
gboolean status = FALSE;
|
|
|
|
GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image);
|
|
|
|
|
|
|
|
bpc = (const guint8 *) gimp_parasite_get_data (parasite, ¶site_size);
|
|
|
|
|
|
|
|
if (parasite_size == 1)
|
|
|
|
{
|
|
|
|
if (*bpc)
|
|
|
|
status = TRUE;
|
|
|
|
|
|
|
|
gimp_image_set_simulation_bpc (image, status);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
gimp_message (info->gimp, G_OBJECT (info->progress),
|
|
|
|
GIMP_MESSAGE_ERROR,
|
|
|
|
"Invalid simulation bpc data");
|
|
|
|
}
|
|
|
|
|
|
|
|
gimp_parasite_list_remove (private->parasites,
|
|
|
|
gimp_parasite_get_name (parasite));
|
|
|
|
}
|
|
|
|
|
2003-07-05 03:55:58 +08:00
|
|
|
/* check for a GimpGrid parasite */
|
2006-03-29 01:08:36 +08:00
|
|
|
parasite = gimp_image_parasite_find (GIMP_IMAGE (image),
|
2003-07-05 03:55:58 +08:00
|
|
|
gimp_grid_parasite_name ());
|
|
|
|
if (parasite)
|
|
|
|
{
|
|
|
|
GimpGrid *grid = gimp_grid_from_parasite (parasite);
|
|
|
|
|
|
|
|
if (grid)
|
|
|
|
{
|
2010-02-04 06:42:32 +08:00
|
|
|
GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image);
|
|
|
|
|
|
|
|
gimp_parasite_list_remove (private->parasites,
|
2021-04-06 00:36:44 +08:00
|
|
|
gimp_parasite_get_name (parasite));
|
2003-07-05 03:55:58 +08:00
|
|
|
|
2006-03-29 01:08:36 +08:00
|
|
|
gimp_image_set_grid (GIMP_IMAGE (image), grid, FALSE);
|
2008-03-31 01:03:18 +08:00
|
|
|
g_object_unref (grid);
|
2003-07-05 03:55:58 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-20 00:38:01 +08:00
|
|
|
/* check for a metadata parasite */
|
|
|
|
parasite = gimp_image_parasite_find (GIMP_IMAGE (image),
|
|
|
|
"gimp-image-metadata");
|
|
|
|
if (parasite)
|
|
|
|
{
|
2018-07-06 09:02:40 +08:00
|
|
|
GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image);
|
|
|
|
GimpMetadata *metadata = NULL;
|
2021-01-30 06:52:03 +08:00
|
|
|
gchar *meta_string;
|
|
|
|
guint32 parasite_data_size;
|
2018-07-06 09:02:40 +08:00
|
|
|
|
2021-01-30 06:52:03 +08:00
|
|
|
meta_string = (gchar *) gimp_parasite_get_data (parasite, ¶site_data_size);
|
2018-07-06 09:02:40 +08:00
|
|
|
if (meta_string)
|
2021-01-30 06:52:03 +08:00
|
|
|
{
|
|
|
|
meta_string = g_strndup (meta_string, parasite_data_size);
|
|
|
|
metadata = gimp_metadata_deserialize (meta_string);
|
|
|
|
g_free (meta_string);
|
|
|
|
}
|
2013-10-20 00:38:01 +08:00
|
|
|
|
|
|
|
if (metadata)
|
|
|
|
{
|
|
|
|
has_metadata = TRUE;
|
|
|
|
|
|
|
|
gimp_image_set_metadata (image, metadata, FALSE);
|
|
|
|
g_object_unref (metadata);
|
|
|
|
}
|
|
|
|
|
|
|
|
gimp_parasite_list_remove (private->parasites,
|
2021-04-06 00:36:44 +08:00
|
|
|
gimp_parasite_get_name (parasite));
|
2013-10-20 00:38:01 +08:00
|
|
|
}
|
|
|
|
|
2016-01-28 02:18:53 +08:00
|
|
|
/* check for symmetry parasites */
|
|
|
|
syms = gimp_image_symmetry_list ();
|
|
|
|
for (iter = syms; iter; iter = g_list_next (iter))
|
|
|
|
{
|
|
|
|
GType type = (GType) iter->data;
|
|
|
|
gchar *parasite_name = gimp_symmetry_parasite_name (type);
|
|
|
|
|
|
|
|
parasite = gimp_image_parasite_find (image,
|
|
|
|
parasite_name);
|
|
|
|
g_free (parasite_name);
|
|
|
|
if (parasite)
|
|
|
|
{
|
|
|
|
GimpSymmetry *sym = gimp_symmetry_from_parasite (parasite,
|
|
|
|
image,
|
|
|
|
type);
|
|
|
|
|
|
|
|
if (sym)
|
|
|
|
{
|
|
|
|
GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image);
|
|
|
|
|
|
|
|
gimp_parasite_list_remove (private->parasites,
|
2021-04-06 00:36:44 +08:00
|
|
|
gimp_parasite_get_name (parasite));
|
2016-01-28 02:18:53 +08:00
|
|
|
|
|
|
|
gimp_image_symmetry_add (image, sym);
|
|
|
|
|
|
|
|
g_signal_emit_by_name (sym, "active-changed", NULL);
|
|
|
|
if (sym->active)
|
|
|
|
gimp_image_set_active_symmetry (image, type);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
g_list_free (syms);
|
|
|
|
|
2013-10-20 00:38:01 +08:00
|
|
|
/* migrate the old "exif-data" parasite */
|
|
|
|
parasite = gimp_image_parasite_find (GIMP_IMAGE (image),
|
|
|
|
"exif-data");
|
|
|
|
if (parasite)
|
|
|
|
{
|
|
|
|
GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image);
|
|
|
|
|
|
|
|
if (has_metadata)
|
|
|
|
{
|
|
|
|
g_printerr ("xcf-load: inconsistent metadata discovered: XCF file "
|
|
|
|
"has both 'gimp-image-metadata' and 'exif-data' "
|
|
|
|
"parasites, dropping old 'exif-data'\n");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-01-04 02:36:22 +08:00
|
|
|
GimpMetadata *metadata = gimp_image_get_metadata (image);
|
|
|
|
GError *my_error = NULL;
|
2021-01-30 06:52:03 +08:00
|
|
|
const guchar *parasite_data;
|
|
|
|
guint32 parasite_data_size;
|
|
|
|
|
|
|
|
parasite_data = (const guchar *) gimp_parasite_get_data (parasite, ¶site_data_size);
|
2013-10-20 00:38:01 +08:00
|
|
|
|
2017-01-04 02:36:22 +08:00
|
|
|
if (metadata)
|
|
|
|
g_object_ref (metadata);
|
|
|
|
else
|
|
|
|
metadata = gimp_metadata_new ();
|
2013-10-20 00:38:01 +08:00
|
|
|
|
|
|
|
if (! gimp_metadata_set_from_exif (metadata,
|
2021-01-30 06:52:03 +08:00
|
|
|
parasite_data, parasite_data_size,
|
2013-10-20 00:38:01 +08:00
|
|
|
&my_error))
|
|
|
|
{
|
|
|
|
gimp_message (gimp, G_OBJECT (info->progress),
|
|
|
|
GIMP_MESSAGE_WARNING,
|
|
|
|
_("Corrupt 'exif-data' parasite discovered.\n"
|
2013-10-30 05:48:46 +08:00
|
|
|
"Exif data could not be migrated: %s"),
|
2013-10-20 00:38:01 +08:00
|
|
|
my_error->message);
|
|
|
|
g_clear_error (&my_error);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
gimp_image_set_metadata (image, metadata, FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
g_object_unref (metadata);
|
|
|
|
}
|
|
|
|
|
|
|
|
gimp_parasite_list_remove (private->parasites,
|
2021-04-06 00:36:44 +08:00
|
|
|
gimp_parasite_get_name (parasite));
|
2013-10-20 00:38:01 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* migrate the old "gimp-metadata" parasite */
|
|
|
|
parasite = gimp_image_parasite_find (GIMP_IMAGE (image),
|
|
|
|
"gimp-metadata");
|
|
|
|
if (parasite)
|
|
|
|
{
|
2016-04-23 04:49:06 +08:00
|
|
|
GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image);
|
2021-01-30 06:52:03 +08:00
|
|
|
const gchar *xmp_data;
|
|
|
|
guint32 xmp_length;
|
|
|
|
|
|
|
|
xmp_data = (gchar *) gimp_parasite_get_data (parasite, &xmp_length);
|
2013-10-20 00:38:01 +08:00
|
|
|
|
|
|
|
if (has_metadata)
|
|
|
|
{
|
|
|
|
g_printerr ("xcf-load: inconsistent metadata discovered: XCF file "
|
|
|
|
"has both 'gimp-image-metadata' and 'gimp-metadata' "
|
|
|
|
"parasites, dropping old 'gimp-metadata'\n");
|
|
|
|
}
|
2016-04-23 04:49:06 +08:00
|
|
|
else if (xmp_length < 14 ||
|
|
|
|
strncmp (xmp_data, "GIMP_XMP_1", 10) != 0)
|
|
|
|
{
|
|
|
|
gimp_message (gimp, G_OBJECT (info->progress),
|
|
|
|
GIMP_MESSAGE_WARNING,
|
|
|
|
_("Corrupt 'gimp-metadata' parasite discovered.\n"
|
|
|
|
"XMP data could not be migrated."));
|
|
|
|
}
|
2013-10-20 00:38:01 +08:00
|
|
|
else
|
|
|
|
{
|
2017-01-04 02:36:22 +08:00
|
|
|
GimpMetadata *metadata = gimp_image_get_metadata (image);
|
|
|
|
GError *my_error = NULL;
|
2013-10-20 00:38:01 +08:00
|
|
|
|
2017-01-04 02:36:22 +08:00
|
|
|
if (metadata)
|
|
|
|
g_object_ref (metadata);
|
|
|
|
else
|
|
|
|
metadata = gimp_metadata_new ();
|
2013-10-20 00:38:01 +08:00
|
|
|
|
|
|
|
if (! gimp_metadata_set_from_xmp (metadata,
|
2016-04-23 04:49:06 +08:00
|
|
|
(const guint8 *) xmp_data + 10,
|
|
|
|
xmp_length - 10,
|
2013-10-20 00:38:01 +08:00
|
|
|
&my_error))
|
|
|
|
{
|
2021-01-23 01:44:00 +08:00
|
|
|
/* XMP metadata from 2.8.x or earlier can be really messed up.
|
|
|
|
* Let's make the message more user friendly so they will
|
|
|
|
* understand that we can't do anything about it.
|
|
|
|
* See issue #987. */
|
2013-10-20 00:38:01 +08:00
|
|
|
gimp_message (gimp, G_OBJECT (info->progress),
|
|
|
|
GIMP_MESSAGE_WARNING,
|
2021-01-23 01:44:00 +08:00
|
|
|
_("Corrupt XMP metadata saved by an older version of "
|
|
|
|
"GIMP could not be converted and will be ignored.\n"
|
|
|
|
"If you don't know what XMP is, you most likely don't "
|
|
|
|
"need it. Reported error: %s."),
|
2013-10-20 00:38:01 +08:00
|
|
|
my_error->message);
|
|
|
|
g_clear_error (&my_error);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
gimp_image_set_metadata (image, metadata, FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
g_object_unref (metadata);
|
|
|
|
}
|
|
|
|
|
|
|
|
gimp_parasite_list_remove (private->parasites,
|
2021-04-06 00:36:44 +08:00
|
|
|
gimp_parasite_get_name (parasite));
|
2013-10-20 00:38:01 +08:00
|
|
|
}
|
|
|
|
|
2015-09-16 05:02:54 +08:00
|
|
|
/* check for a gimp-xcf-compatibility-mode parasite */
|
2015-09-01 22:16:41 +08:00
|
|
|
parasite = gimp_image_parasite_find (GIMP_IMAGE (image),
|
2015-09-02 17:47:51 +08:00
|
|
|
"gimp-xcf-compatibility-mode");
|
2015-09-01 22:16:41 +08:00
|
|
|
if (parasite)
|
|
|
|
{
|
|
|
|
GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image);
|
|
|
|
|
2017-03-24 01:03:27 +08:00
|
|
|
/* just ditch it, it's unused but shouldn't be re-saved */
|
2015-09-01 22:16:41 +08:00
|
|
|
gimp_parasite_list_remove (private->parasites,
|
2021-04-06 00:36:44 +08:00
|
|
|
gimp_parasite_get_name (parasite));
|
2015-09-01 22:16:41 +08:00
|
|
|
}
|
2015-09-16 05:02:54 +08:00
|
|
|
|
2006-07-12 04:21:18 +08:00
|
|
|
xcf_progress_update (info);
|
|
|
|
|
2001-07-04 02:38:56 +08:00
|
|
|
while (TRUE)
|
|
|
|
{
|
2009-08-31 03:07:22 +08:00
|
|
|
GimpLayer *layer;
|
2009-08-31 03:28:59 +08:00
|
|
|
GList *item_path = NULL;
|
2009-08-31 03:07:22 +08:00
|
|
|
|
2001-07-04 02:38:56 +08:00
|
|
|
/* read in the offset of the next layer */
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_offset (info, &offset, 1);
|
2001-07-04 02:38:56 +08:00
|
|
|
|
|
|
|
/* if the offset is 0 then we are at the end
|
|
|
|
* of the layer list.
|
|
|
|
*/
|
|
|
|
if (offset == 0)
|
2004-11-02 02:33:09 +08:00
|
|
|
break;
|
2001-07-04 02:38:56 +08:00
|
|
|
|
|
|
|
/* save the current position as it is where the
|
|
|
|
* next layer offset is stored.
|
|
|
|
*/
|
|
|
|
saved_pos = info->cp;
|
|
|
|
|
2022-06-06 06:44:45 +08:00
|
|
|
if (offset < saved_pos)
|
|
|
|
{
|
|
|
|
GIMP_LOG (XCF, "Invalid layer offset: %" G_GOFFSET_FORMAT
|
|
|
|
" at offset: %" G_GOFFSET_FORMAT, offset, saved_pos);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2001-07-04 02:38:56 +08:00
|
|
|
/* seek to the layer offset */
|
2002-12-20 14:26:34 +08:00
|
|
|
if (! xcf_seek_pos (info, offset, NULL))
|
|
|
|
goto error;
|
2001-07-04 02:38:56 +08:00
|
|
|
|
|
|
|
/* read in the layer */
|
2009-08-31 03:28:59 +08:00
|
|
|
layer = xcf_load_layer (info, image, &item_path);
|
2019-07-11 21:04:30 +08:00
|
|
|
if (! layer)
|
|
|
|
{
|
|
|
|
n_broken_layers++;
|
|
|
|
|
|
|
|
if (! xcf_seek_pos (info, saved_pos, NULL))
|
2019-07-11 22:32:45 +08:00
|
|
|
{
|
|
|
|
if (item_path)
|
|
|
|
g_list_free (item_path);
|
|
|
|
|
|
|
|
goto error;
|
|
|
|
}
|
2019-07-11 21:04:30 +08:00
|
|
|
|
|
|
|
/* Don't just stop at the first broken layer. Load as much as
|
|
|
|
* possible.
|
|
|
|
*/
|
|
|
|
if (! item_path)
|
|
|
|
{
|
|
|
|
GimpContainer *layers = gimp_image_get_layers (image);
|
|
|
|
|
|
|
|
item_path = g_list_prepend (NULL,
|
|
|
|
GUINT_TO_POINTER (gimp_container_get_n_children (layers)));
|
|
|
|
|
|
|
|
broken_paths = g_list_prepend (broken_paths, item_path);
|
|
|
|
}
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (broken_paths && item_path)
|
|
|
|
{
|
|
|
|
/* Item paths may be a problem when layers are missing. */
|
|
|
|
xcf_fix_item_path (layer, &item_path, broken_paths);
|
|
|
|
}
|
2001-07-04 02:38:56 +08:00
|
|
|
|
|
|
|
num_successful_elements++;
|
|
|
|
|
2006-07-12 04:21:18 +08:00
|
|
|
xcf_progress_update (info);
|
|
|
|
|
2020-02-22 18:41:47 +08:00
|
|
|
/* suspend layer-group size updates */
|
|
|
|
if (GIMP_IS_GROUP_LAYER (layer))
|
|
|
|
{
|
|
|
|
GimpGroupLayer *group = GIMP_GROUP_LAYER (layer);
|
|
|
|
|
|
|
|
group_layers = g_list_prepend (group_layers, group);
|
|
|
|
|
|
|
|
gimp_group_layer_suspend_resize (group, FALSE);
|
|
|
|
}
|
|
|
|
|
2001-07-04 02:38:56 +08:00
|
|
|
/* add the layer to the image if its not the floating selection */
|
|
|
|
if (layer != info->floating_sel)
|
2009-08-31 03:28:59 +08:00
|
|
|
{
|
|
|
|
GimpContainer *layers = gimp_image_get_layers (image);
|
|
|
|
GimpContainer *container;
|
|
|
|
GimpLayer *parent;
|
|
|
|
|
|
|
|
if (item_path)
|
|
|
|
{
|
2009-08-31 21:15:30 +08:00
|
|
|
if (info->floating_sel)
|
|
|
|
{
|
|
|
|
/* there is a floating selection, but it will get
|
|
|
|
* added after all layers are loaded, so toplevel
|
|
|
|
* layer indices are off-by-one. Adjust item paths
|
|
|
|
* accordingly:
|
|
|
|
*/
|
|
|
|
gint toplevel_index;
|
|
|
|
|
|
|
|
toplevel_index = GPOINTER_TO_UINT (item_path->data);
|
|
|
|
|
|
|
|
toplevel_index--;
|
|
|
|
|
|
|
|
item_path->data = GUINT_TO_POINTER (toplevel_index);
|
|
|
|
}
|
|
|
|
|
2009-08-31 03:28:59 +08:00
|
|
|
parent = GIMP_LAYER
|
|
|
|
(gimp_item_stack_get_parent_by_path (GIMP_ITEM_STACK (layers),
|
|
|
|
item_path,
|
|
|
|
NULL));
|
|
|
|
|
|
|
|
container = gimp_viewable_get_children (GIMP_VIEWABLE (parent));
|
2012-02-07 21:06:12 +08:00
|
|
|
|
|
|
|
g_list_free (item_path);
|
2009-08-31 03:28:59 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
parent = NULL;
|
|
|
|
container = layers;
|
|
|
|
}
|
|
|
|
|
|
|
|
gimp_image_add_layer (image, layer,
|
|
|
|
parent,
|
|
|
|
gimp_container_get_n_children (container),
|
|
|
|
FALSE);
|
|
|
|
}
|
2001-07-04 02:38:56 +08:00
|
|
|
|
|
|
|
/* restore the saved position so we'll be ready to
|
|
|
|
* read the next offset.
|
|
|
|
*/
|
2002-12-20 14:26:34 +08:00
|
|
|
if (! xcf_seek_pos (info, saved_pos, NULL))
|
|
|
|
goto error;
|
2001-07-04 02:38:56 +08:00
|
|
|
}
|
|
|
|
|
2020-02-22 18:41:47 +08:00
|
|
|
/* resume layer-group size updates, in reverse order */
|
|
|
|
for (iter = group_layers; iter; iter = g_list_next (iter))
|
|
|
|
{
|
|
|
|
GimpGroupLayer *group = iter->data;
|
|
|
|
|
|
|
|
gimp_group_layer_resume_resize (group, FALSE);
|
|
|
|
}
|
|
|
|
g_clear_pointer (&group_layers, g_list_free);
|
|
|
|
|
2019-07-11 21:04:30 +08:00
|
|
|
if (broken_paths)
|
2019-07-11 22:32:45 +08:00
|
|
|
{
|
|
|
|
g_list_free_full (broken_paths, (GDestroyNotify) g_list_free);
|
|
|
|
broken_paths = NULL;
|
|
|
|
}
|
2019-07-11 21:04:30 +08:00
|
|
|
|
2001-07-04 02:38:56 +08:00
|
|
|
while (TRUE)
|
|
|
|
{
|
2009-08-31 03:07:22 +08:00
|
|
|
GimpChannel *channel;
|
|
|
|
|
2001-07-04 02:38:56 +08:00
|
|
|
/* read in the offset of the next channel */
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_offset (info, &offset, 1);
|
2001-07-04 02:38:56 +08:00
|
|
|
|
|
|
|
/* if the offset is 0 then we are at the end
|
|
|
|
* of the channel list.
|
|
|
|
*/
|
|
|
|
if (offset == 0)
|
2004-11-02 02:33:09 +08:00
|
|
|
break;
|
2001-07-04 02:38:56 +08:00
|
|
|
|
|
|
|
/* save the current position as it is where the
|
|
|
|
* next channel offset is stored.
|
|
|
|
*/
|
|
|
|
saved_pos = info->cp;
|
|
|
|
|
2022-06-06 06:44:45 +08:00
|
|
|
if (offset < saved_pos)
|
|
|
|
{
|
|
|
|
GIMP_LOG (XCF, "Invalid channel offset: %" G_GOFFSET_FORMAT
|
|
|
|
" at offset: % "G_GOFFSET_FORMAT, offset, saved_pos);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2001-07-04 02:38:56 +08:00
|
|
|
/* seek to the channel offset */
|
2002-12-20 14:26:34 +08:00
|
|
|
if (! xcf_seek_pos (info, offset, NULL))
|
|
|
|
goto error;
|
2001-07-04 02:38:56 +08:00
|
|
|
|
2009-08-30 17:52:06 +08:00
|
|
|
/* read in the channel */
|
2006-03-29 01:08:36 +08:00
|
|
|
channel = xcf_load_channel (info, image);
|
2001-07-04 02:38:56 +08:00
|
|
|
if (!channel)
|
2019-07-11 21:04:30 +08:00
|
|
|
{
|
|
|
|
n_broken_channels++;
|
2022-06-06 06:44:45 +08:00
|
|
|
GIMP_LOG (XCF, "Failed to load channel.");
|
2019-07-11 21:04:30 +08:00
|
|
|
|
|
|
|
if (! xcf_seek_pos (info, saved_pos, NULL))
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
2001-07-04 02:38:56 +08:00
|
|
|
|
|
|
|
num_successful_elements++;
|
|
|
|
|
2006-07-12 04:21:18 +08:00
|
|
|
xcf_progress_update (info);
|
|
|
|
|
2001-07-04 02:38:56 +08:00
|
|
|
/* add the channel to the image if its not the selection */
|
2007-12-26 00:21:40 +08:00
|
|
|
if (channel != gimp_image_get_mask (image))
|
2006-11-22 18:19:53 +08:00
|
|
|
gimp_image_add_channel (image, channel,
|
2009-08-04 01:21:51 +08:00
|
|
|
NULL, /* FIXME tree */
|
2010-02-04 06:00:31 +08:00
|
|
|
gimp_container_get_n_children (gimp_image_get_channels (image)),
|
2008-10-10 03:40:41 +08:00
|
|
|
FALSE);
|
2001-07-04 02:38:56 +08:00
|
|
|
|
|
|
|
/* restore the saved position so we'll be ready to
|
|
|
|
* read the next offset.
|
|
|
|
*/
|
2002-12-20 14:26:34 +08:00
|
|
|
if (! xcf_seek_pos (info, saved_pos, NULL))
|
|
|
|
goto error;
|
2001-07-04 02:38:56 +08:00
|
|
|
}
|
|
|
|
|
2019-07-11 21:04:30 +08:00
|
|
|
if (n_broken_layers == 0 && n_broken_channels == 0)
|
2023-06-19 22:54:21 +08:00
|
|
|
{
|
|
|
|
xcf_load_add_masks (image);
|
|
|
|
xcf_load_add_effects (info, image);
|
|
|
|
}
|
2009-09-04 01:11:02 +08:00
|
|
|
|
2004-01-26 23:34:47 +08:00
|
|
|
if (info->floating_sel && info->floating_sel_drawable)
|
Initial space invasion commit in GIMP
All babl formats now have a space equivalent to a color profile,
determining the format's primaries and TRCs. This commit makes GIMP
aware of this.
libgimp:
- enum GimpPrecision: rename GAMMA values to NON_LINEAR and keep GAMMA
as deprecated aliases, add PERCEPTUAL values so we now have LINEAR,
NON_LINEAR and PERCPTUAL for each encoding, matching the babl
encoding variants RGB, R'G'B' and R~G~B~.
- gimp_color_transform_can_gegl_copy() now returns TRUE if both
profiles can return a babl space, increasing the amount of fast babl
color conversions significantly.
- TODO: no solution yet for getting libgimp drawable proxy buffers in
the right format with space.
plug-ins:
- follow the GimpPrecision change.
- TODO: everything else unchanged and partly broken or sub-optimal,
like setting a new image's color profile too late.
app:
- add enum GimpTRCType { LINEAR, NON_LINEAR, PERCEPTUAL } as
replacement for all "linear" booleans.
- change gimp-babl functions to take babl spaces and GimpTRCType
parameters and support all sorts of new perceptual ~ formats.
- a lot of places changed in the early days of goat invasion didn't
take advantage of gimp-babl utility functions and constructed
formats manually. They all needed revisiting and many now use much
simpler code calling gimp-babl API.
- change gimp_babl_format_get_color_profile() to really extract a
newly allocated color profile from the format, and add
gimp_babl_get_builtin_color_profile() which does the same as
gimp_babl_format_get_color_profile() did before. Visited all callers
to decide whether they are looking for the format's actual profile,
or for one of the builtin profiles, simplifying code that only needs
builtin profiles.
- drawables have a new get_space_api(), get_linear() is now get_trc().
- images now have a "layer space" and an API to get it,
gimp_image_get_layer_format() returns formats in that space.
- an image's layer space is created from the image's color profile,
change gimpimage-color-profile to deal with that correctly
- change many babl_format() calls to babl_format_with_space() and take
the space from passed formats or drawables
- add function gimp_layer_fix_format_space() which replaces the
layer's buffer with one that has the image's layer format, but
doesn't change pixel values
- use gimp_layer_fix_format_space() to make sure layers loaded from
XCF and created by plug-ins have the right space when added to the
image, because it's impossible to always assign the right space upon
layer creation
- "assign color profile" and "discard color profile" now require use
of gimp_layer_fix_format_space() too because the profile is now
embedded in all formats via the space. Add
gimp_image_assign_color_profile() which does all that and call it
instead of a simple gimp_image_set_color_profile(), also from the
PDB set-color-profile functions, which are essentially "assign" and
"discard" calls.
- generally, make sure a new image's color profile is set before
adding layers to it, gimp_image_set_color_profile() is more than
before considered know-what-you-are-doing API.
- take special precaution in all places that call
gimp_drawable_convert_type(), we now must pass a new_profile from
all callers that convert layers within the same image (such as
image_convert_type, image_convert_precision), because the layer's
new space can't be determined from the image's layer format during
the call.
- change all "linear" properties to "trc", in all config objects like
for levels and curves, in the histogram, in the widgets. This results
in some GUI that now has three choices instead of two.
TODO: we might want to reduce that back to two later.
- keep "linear" boolean properties around as compat if needed for file
pasring, but always convert the parsed parsed boolean to
GimpTRCType.
- TODO: the image's "enable color management" switch is currently
broken, will fix that in another commit.
2018-07-21 20:23:01 +08:00
|
|
|
{
|
|
|
|
/* we didn't fix the loaded floating selection's format before
|
|
|
|
* because we didn't know if it needed the layer space
|
|
|
|
*/
|
|
|
|
if (GIMP_IS_LAYER (info->floating_sel_drawable) &&
|
|
|
|
gimp_drawable_is_gray (GIMP_DRAWABLE (info->floating_sel)))
|
|
|
|
gimp_layer_fix_format_space (info->floating_sel, TRUE, FALSE);
|
|
|
|
|
|
|
|
floating_sel_attach (info->floating_sel, info->floating_sel_drawable);
|
|
|
|
}
|
2004-01-26 23:34:47 +08:00
|
|
|
|
2022-10-20 06:11:34 +08:00
|
|
|
if (info->file_version >= 18)
|
|
|
|
{
|
|
|
|
while (TRUE)
|
|
|
|
{
|
|
|
|
GimpVectors *vectors;
|
|
|
|
|
|
|
|
/* read in the offset of the next path */
|
|
|
|
xcf_read_offset (info, &offset, 1);
|
|
|
|
|
|
|
|
/* if the offset is 0 then we are at the end
|
|
|
|
* of the path list.
|
|
|
|
*/
|
|
|
|
if (offset == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* save the current position as it is where the
|
|
|
|
* next channel offset is stored.
|
|
|
|
*/
|
|
|
|
saved_pos = info->cp;
|
|
|
|
|
|
|
|
if (offset < saved_pos)
|
|
|
|
{
|
|
|
|
GIMP_LOG (XCF, "Invalid path offset: %" G_GOFFSET_FORMAT
|
|
|
|
" at offset: % "G_GOFFSET_FORMAT, offset, saved_pos);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* seek to the path offset */
|
|
|
|
if (! xcf_seek_pos (info, offset, NULL))
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
/* read in the path */
|
|
|
|
vectors = xcf_load_vectors (info, image);
|
|
|
|
if (! vectors)
|
|
|
|
{
|
|
|
|
n_broken_vectors++;
|
|
|
|
GIMP_LOG (XCF, "Failed to load path.");
|
|
|
|
|
|
|
|
if (! xcf_seek_pos (info, saved_pos, NULL))
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
num_successful_elements++;
|
|
|
|
|
|
|
|
xcf_progress_update (info);
|
|
|
|
|
|
|
|
gimp_image_add_vectors (image, vectors,
|
|
|
|
NULL, /* can't be a tree */
|
|
|
|
gimp_container_get_n_children (gimp_image_get_vectors (image)),
|
|
|
|
FALSE);
|
|
|
|
|
|
|
|
/* restore the saved position so we'll be ready to
|
|
|
|
* read the next offset.
|
|
|
|
*/
|
|
|
|
if (! xcf_seek_pos (info, saved_pos, NULL))
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-15 03:36:58 +08:00
|
|
|
if (info->selected_layers)
|
|
|
|
{
|
|
|
|
gimp_image_set_selected_layers (image, info->selected_layers);
|
|
|
|
g_clear_pointer (&info->selected_layers, g_list_free);
|
|
|
|
}
|
2001-07-04 02:38:56 +08:00
|
|
|
|
2021-06-20 06:00:53 +08:00
|
|
|
if (info->selected_channels)
|
|
|
|
gimp_image_set_selected_channels (image, info->selected_channels);
|
2001-07-04 02:38:56 +08:00
|
|
|
|
2022-10-20 06:11:34 +08:00
|
|
|
if (info->selected_vectors)
|
|
|
|
gimp_image_set_selected_vectors (image, info->selected_vectors);
|
|
|
|
|
2021-12-16 07:35:20 +08:00
|
|
|
/* We don't have linked items concept anymore. We transform formerly
|
|
|
|
* linked items into stored sets of named items instead.
|
|
|
|
*/
|
|
|
|
if (info->linked_layers)
|
|
|
|
{
|
|
|
|
GimpItemList *set;
|
|
|
|
|
|
|
|
set = gimp_item_list_named_new (image, GIMP_TYPE_LAYER,
|
|
|
|
_("Linked Layers"),
|
|
|
|
info->linked_layers);
|
|
|
|
gimp_image_store_item_set (image, set);
|
|
|
|
g_clear_pointer (&info->linked_layers, g_list_free);
|
|
|
|
}
|
|
|
|
if (info->linked_channels)
|
|
|
|
{
|
|
|
|
GimpItemList *set;
|
|
|
|
|
|
|
|
set = gimp_item_list_named_new (image, GIMP_TYPE_CHANNEL,
|
|
|
|
_("Linked Channels"),
|
|
|
|
info->linked_channels);
|
|
|
|
gimp_image_store_item_set (image, set);
|
2021-12-23 05:53:15 +08:00
|
|
|
g_clear_pointer (&info->linked_channels, g_list_free);
|
2021-12-16 07:35:20 +08:00
|
|
|
}
|
|
|
|
if (info->linked_paths)
|
|
|
|
{
|
2021-12-23 05:53:15 +08:00
|
|
|
/* It is kind of ugly but vectors are really implemented as
|
|
|
|
* exception in our XCF spec and building over it seems like a
|
|
|
|
* mistake. Since I'm seriously not sure this would be much of an
|
|
|
|
* issue, I'll let it as it for now.
|
|
|
|
* Note that it's still possible to multi-select paths. It's only
|
|
|
|
* not possible to store these selections.
|
|
|
|
*
|
|
|
|
* Only warn for more than 1 linked path. Less is kind of
|
|
|
|
* pointless and doesn't deserve worrying people for no reason.
|
|
|
|
*/
|
|
|
|
if (g_list_length (info->linked_paths) > 1)
|
|
|
|
g_printerr ("xcf: some paths were linked. "
|
|
|
|
"GIMP does not support linked paths since version 3.0.\n");
|
|
|
|
|
|
|
|
#if 0
|
2021-12-16 07:35:20 +08:00
|
|
|
GimpItemList *set;
|
|
|
|
|
|
|
|
set = gimp_item_list_named_new (image, GIMP_TYPE_VECTORS,
|
|
|
|
_("Linked Paths"),
|
|
|
|
info->linked_paths);
|
|
|
|
gimp_image_store_item_set (image, set);
|
2021-12-23 05:53:15 +08:00
|
|
|
#endif
|
|
|
|
g_clear_pointer (&info->linked_paths, g_list_free);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (iter = g_list_last (info->layer_sets); iter; iter = iter->prev)
|
|
|
|
{
|
|
|
|
if (iter->data)
|
|
|
|
gimp_image_store_item_set (image, iter->data);
|
|
|
|
}
|
|
|
|
g_list_free (info->layer_sets);
|
|
|
|
|
|
|
|
for (iter = g_list_last (info->channel_sets); iter; iter = iter->prev)
|
|
|
|
{
|
|
|
|
if (iter->data)
|
|
|
|
gimp_image_store_item_set (image, iter->data);
|
2021-12-16 07:35:20 +08:00
|
|
|
}
|
2021-12-23 05:53:15 +08:00
|
|
|
g_list_free (info->channel_sets);
|
2021-12-16 07:35:20 +08:00
|
|
|
|
2016-09-19 02:19:42 +08:00
|
|
|
if (info->file)
|
|
|
|
gimp_image_set_file (image, info->file);
|
2001-07-04 02:38:56 +08:00
|
|
|
|
2003-12-06 23:53:26 +08:00
|
|
|
if (info->tattoo_state > 0)
|
2006-03-29 01:08:36 +08:00
|
|
|
gimp_image_set_tattoo_state (image, info->tattoo_state);
|
2003-12-06 23:53:26 +08:00
|
|
|
|
2022-10-20 06:11:34 +08:00
|
|
|
if (n_broken_layers > 0 || n_broken_channels > 0 || n_broken_vectors > 0)
|
2019-07-11 21:04:30 +08:00
|
|
|
goto error;
|
|
|
|
|
2006-03-29 01:08:36 +08:00
|
|
|
gimp_image_undo_enable (image);
|
2005-08-05 22:13:10 +08:00
|
|
|
|
2006-03-29 01:08:36 +08:00
|
|
|
return image;
|
2001-07-04 02:38:56 +08:00
|
|
|
|
|
|
|
error:
|
|
|
|
if (num_successful_elements == 0)
|
|
|
|
goto hard_error;
|
|
|
|
|
2020-02-22 18:41:47 +08:00
|
|
|
g_clear_pointer (&group_layers, g_list_free);
|
|
|
|
|
|
|
|
if (broken_paths)
|
|
|
|
{
|
|
|
|
g_list_free_full (broken_paths, (GDestroyNotify) g_list_free);
|
|
|
|
broken_paths = NULL;
|
|
|
|
}
|
|
|
|
|
2008-11-04 20:33:09 +08:00
|
|
|
gimp_message_literal (gimp, G_OBJECT (info->progress), GIMP_MESSAGE_WARNING,
|
2013-09-15 00:59:20 +08:00
|
|
|
_("This XCF file is corrupt! I have loaded as much "
|
|
|
|
"of it as I can, but it is incomplete."));
|
2001-07-04 02:38:56 +08:00
|
|
|
|
2009-09-04 01:11:02 +08:00
|
|
|
xcf_load_add_masks (image);
|
2023-06-19 22:54:21 +08:00
|
|
|
xcf_load_add_effects (info, image);
|
2009-09-04 01:11:02 +08:00
|
|
|
|
2006-03-29 01:08:36 +08:00
|
|
|
gimp_image_undo_enable (image);
|
2005-08-05 22:13:10 +08:00
|
|
|
|
2006-03-29 01:08:36 +08:00
|
|
|
return image;
|
2001-07-04 02:38:56 +08:00
|
|
|
|
2003-09-01 07:46:05 +08:00
|
|
|
hard_error:
|
2020-02-22 18:41:47 +08:00
|
|
|
g_clear_pointer (&group_layers, g_list_free);
|
|
|
|
|
|
|
|
if (broken_paths)
|
|
|
|
{
|
|
|
|
g_list_free_full (broken_paths, (GDestroyNotify) g_list_free);
|
|
|
|
broken_paths = NULL;
|
|
|
|
}
|
|
|
|
|
2008-11-04 20:33:09 +08:00
|
|
|
g_set_error_literal (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
2013-09-15 00:59:20 +08:00
|
|
|
_("This XCF file is corrupt! I could not even "
|
|
|
|
"salvage any partial image data from it."));
|
2001-07-04 02:38:56 +08:00
|
|
|
|
2020-02-22 18:41:47 +08:00
|
|
|
g_clear_object (&image);
|
2001-07-04 02:38:56 +08:00
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2009-09-04 01:11:02 +08:00
|
|
|
static void
|
|
|
|
xcf_load_add_masks (GimpImage *image)
|
|
|
|
{
|
|
|
|
GList *layers;
|
|
|
|
GList *list;
|
|
|
|
|
|
|
|
layers = gimp_image_get_layer_list (image);
|
|
|
|
|
|
|
|
for (list = layers; list; list = g_list_next (list))
|
|
|
|
{
|
|
|
|
GimpLayer *layer = list->data;
|
|
|
|
GimpLayerMask *mask;
|
|
|
|
|
|
|
|
mask = g_object_get_data (G_OBJECT (layer), "gimp-layer-mask");
|
|
|
|
|
|
|
|
if (mask)
|
|
|
|
{
|
2012-03-18 01:30:13 +08:00
|
|
|
gboolean apply_mask;
|
|
|
|
gboolean edit_mask;
|
|
|
|
gboolean show_mask;
|
|
|
|
|
|
|
|
apply_mask = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (layer),
|
|
|
|
"gimp-layer-mask-apply"));
|
|
|
|
edit_mask = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (layer),
|
|
|
|
"gimp-layer-mask-edit"));
|
|
|
|
show_mask = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (layer),
|
|
|
|
"gimp-layer-mask-show"));
|
|
|
|
|
2009-09-04 01:11:02 +08:00
|
|
|
gimp_layer_add_mask (layer, mask, FALSE, NULL);
|
|
|
|
|
2012-03-18 01:30:13 +08:00
|
|
|
gimp_layer_set_apply_mask (layer, apply_mask, FALSE);
|
|
|
|
gimp_layer_set_edit_mask (layer, edit_mask);
|
|
|
|
gimp_layer_set_show_mask (layer, show_mask, FALSE);
|
|
|
|
|
|
|
|
g_object_set_data (G_OBJECT (layer), "gimp-layer-mask", NULL);
|
|
|
|
g_object_set_data (G_OBJECT (layer), "gimp-layer-mask-apply", NULL);
|
|
|
|
g_object_set_data (G_OBJECT (layer), "gimp-layer-mask-edit", NULL);
|
|
|
|
g_object_set_data (G_OBJECT (layer), "gimp-layer-mask-show", NULL);
|
2009-09-04 01:11:02 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
g_list_free (layers);
|
|
|
|
}
|
|
|
|
|
2023-06-19 22:54:21 +08:00
|
|
|
static void
|
|
|
|
xcf_load_add_effects (XcfInfo *info,
|
|
|
|
GimpImage *image)
|
|
|
|
{
|
|
|
|
GList *layers;
|
|
|
|
GList *list;
|
|
|
|
|
|
|
|
layers = gimp_image_get_layer_list (image);
|
|
|
|
|
|
|
|
for (list = layers; list; list = g_list_next (list))
|
|
|
|
{
|
|
|
|
GimpLayer *layer = list->data;
|
|
|
|
GList *effects_nodes;
|
|
|
|
|
|
|
|
effects_nodes = g_object_get_data (G_OBJECT (layer), "gimp-layer-effects");
|
|
|
|
|
|
|
|
if (effects_nodes)
|
|
|
|
{
|
|
|
|
GList *iter;
|
|
|
|
|
|
|
|
for (iter = effects_nodes; iter; iter = iter->next)
|
|
|
|
{
|
|
|
|
FilterData *data = iter->data;
|
|
|
|
|
|
|
|
if (! data->icon_name)
|
|
|
|
data->icon_name = g_strdup ("gimp-gegl");
|
|
|
|
|
2024-01-04 02:57:08 +08:00
|
|
|
if (! data->unsupported_operation)
|
2023-06-19 22:54:21 +08:00
|
|
|
{
|
|
|
|
GimpDrawableFilter *filter = NULL;
|
|
|
|
|
|
|
|
filter = gimp_drawable_filter_new (GIMP_DRAWABLE (layer),
|
2024-01-04 02:57:08 +08:00
|
|
|
data->name, data->operation,
|
2023-06-19 22:54:21 +08:00
|
|
|
data->icon_name);
|
|
|
|
|
|
|
|
gimp_drawable_filter_set_opacity (filter, data->opacity);
|
|
|
|
gimp_drawable_filter_set_mode (filter, data->paint_mode,
|
|
|
|
data->blend_space,
|
|
|
|
data->composite_space,
|
|
|
|
data->composite_mode);
|
|
|
|
gimp_drawable_filter_set_region (filter, data->region);
|
|
|
|
|
|
|
|
gimp_drawable_filter_apply (filter, NULL);
|
|
|
|
|
|
|
|
g_object_set (filter,
|
|
|
|
"mask", data->mask,
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
gimp_drawable_filter_commit (filter, TRUE, NULL, FALSE);
|
|
|
|
|
|
|
|
gimp_drawable_filter_layer_mask_freeze (filter);
|
|
|
|
|
|
|
|
g_object_unref (filter);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
g_object_set_data (G_OBJECT (layer), "gimp-layer-effects", NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
g_list_free (layers);
|
|
|
|
}
|
|
|
|
|
2001-07-04 02:38:56 +08:00
|
|
|
static gboolean
|
|
|
|
xcf_load_image_props (XcfInfo *info,
|
2006-03-29 01:08:36 +08:00
|
|
|
GimpImage *image)
|
2001-07-04 02:38:56 +08:00
|
|
|
{
|
|
|
|
PropType prop_type;
|
|
|
|
guint32 prop_size;
|
|
|
|
|
|
|
|
while (TRUE)
|
|
|
|
{
|
2007-07-05 23:12:43 +08:00
|
|
|
if (! xcf_load_prop (info, &prop_type, &prop_size))
|
2004-11-02 02:33:09 +08:00
|
|
|
return FALSE;
|
2001-07-04 02:38:56 +08:00
|
|
|
|
|
|
|
switch (prop_type)
|
2004-11-02 02:33:09 +08:00
|
|
|
{
|
|
|
|
case PROP_END:
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
case PROP_COLORMAP:
|
2007-12-22 00:37:01 +08:00
|
|
|
{
|
|
|
|
guint32 n_colors;
|
|
|
|
guchar cmap[GIMP_IMAGE_COLORMAP_SIZE];
|
2004-11-02 02:33:09 +08:00
|
|
|
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_int32 (info, &n_colors, 1);
|
2004-11-02 02:33:09 +08:00
|
|
|
|
2010-10-16 06:52:11 +08:00
|
|
|
if (n_colors > (GIMP_IMAGE_COLORMAP_SIZE / 3))
|
|
|
|
{
|
|
|
|
gimp_message (info->gimp, G_OBJECT (info->progress),
|
|
|
|
GIMP_MESSAGE_ERROR,
|
|
|
|
"Maximum colormap size (%d) exceeded",
|
2013-09-15 00:59:20 +08:00
|
|
|
GIMP_IMAGE_COLORMAP_SIZE);
|
2010-10-16 06:52:11 +08:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2007-12-22 00:37:01 +08:00
|
|
|
if (info->file_version == 0)
|
|
|
|
{
|
|
|
|
gint i;
|
2004-11-02 02:33:09 +08:00
|
|
|
|
2008-11-04 20:33:09 +08:00
|
|
|
gimp_message_literal (info->gimp, G_OBJECT (info->progress),
|
2013-09-15 00:59:20 +08:00
|
|
|
GIMP_MESSAGE_WARNING,
|
|
|
|
_("XCF warning: version 0 of XCF file format\n"
|
|
|
|
"did not save indexed colormaps correctly.\n"
|
|
|
|
"Substituting grayscale map."));
|
2007-12-22 00:37:01 +08:00
|
|
|
|
|
|
|
if (! xcf_seek_pos (info, info->cp + n_colors, NULL))
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
for (i = 0; i < n_colors; i++)
|
|
|
|
{
|
|
|
|
cmap[i * 3 + 0] = i;
|
|
|
|
cmap[i * 3 + 1] = i;
|
|
|
|
cmap[i * 3 + 2] = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_int8 (info, cmap, n_colors * 3);
|
2007-12-22 00:37:01 +08:00
|
|
|
}
|
|
|
|
|
2013-07-15 04:01:21 +08:00
|
|
|
/* only set color map if image is indexed, this is just
|
|
|
|
* sanity checking to make sure gimp doesn't end up with
|
|
|
|
* an image state that is impossible.
|
2007-12-22 00:37:01 +08:00
|
|
|
*/
|
2012-05-08 03:57:33 +08:00
|
|
|
if (gimp_image_get_base_type (image) == GIMP_INDEXED)
|
2023-12-09 15:53:44 +08:00
|
|
|
_gimp_image_set_colormap (image, cmap, n_colors, FALSE);
|
2014-07-10 04:36:55 +08:00
|
|
|
|
|
|
|
GIMP_LOG (XCF, "prop colormap n_colors=%d", n_colors);
|
2007-12-22 00:37:01 +08:00
|
|
|
}
|
2004-11-02 02:33:09 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case PROP_COMPRESSION:
|
|
|
|
{
|
|
|
|
guint8 compression;
|
|
|
|
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_int8 (info, (guint8 *) &compression, 1);
|
2004-11-02 02:33:09 +08:00
|
|
|
|
|
|
|
if ((compression != COMPRESS_NONE) &&
|
|
|
|
(compression != COMPRESS_RLE) &&
|
|
|
|
(compression != COMPRESS_ZLIB) &&
|
|
|
|
(compression != COMPRESS_FRACTAL))
|
|
|
|
{
|
2006-10-09 16:17:22 +08:00
|
|
|
gimp_message (info->gimp, G_OBJECT (info->progress),
|
|
|
|
GIMP_MESSAGE_ERROR,
|
2007-07-06 18:02:31 +08:00
|
|
|
"Unknown compression type: %d",
|
2016-12-21 11:05:32 +08:00
|
|
|
(gint) compression);
|
2004-11-02 02:33:09 +08:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
info->compression = compression;
|
2014-07-10 04:36:55 +08:00
|
|
|
|
2017-03-24 01:03:27 +08:00
|
|
|
gimp_image_set_xcf_compression (image,
|
|
|
|
compression >= COMPRESS_ZLIB);
|
|
|
|
|
2014-07-10 04:36:55 +08:00
|
|
|
GIMP_LOG (XCF, "prop compression=%d", compression);
|
2004-11-02 02:33:09 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PROP_GUIDES:
|
|
|
|
{
|
2010-02-04 05:16:02 +08:00
|
|
|
GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image);
|
|
|
|
gint32 position;
|
|
|
|
gint8 orientation;
|
|
|
|
gint i, nguides;
|
2004-11-02 02:33:09 +08:00
|
|
|
|
|
|
|
nguides = prop_size / (4 + 1);
|
|
|
|
for (i = 0; i < nguides; i++)
|
|
|
|
{
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_int32 (info, (guint32 *) &position, 1);
|
|
|
|
xcf_read_int8 (info, (guint8 *) &orientation, 1);
|
2003-04-30 20:51:11 +08:00
|
|
|
|
2021-02-14 00:34:48 +08:00
|
|
|
/* Some very old XCF had -1 guides which have been
|
|
|
|
* skipped since 2003 (commit 909a28ced2).
|
|
|
|
* Then XCF up to version 14 only had positive guide
|
|
|
|
* positions.
|
|
|
|
* Since XCF 15 (GIMP 3.0), off-canvas guides became a
|
|
|
|
* thing.
|
|
|
|
*/
|
|
|
|
if (info->file_version < 15 && position < 0)
|
2003-04-30 20:51:11 +08:00
|
|
|
continue;
|
2001-07-04 02:38:56 +08:00
|
|
|
|
2014-07-10 04:36:55 +08:00
|
|
|
GIMP_LOG (XCF, "prop guide orientation=%d position=%d",
|
|
|
|
orientation, position);
|
|
|
|
|
2004-11-02 02:33:09 +08:00
|
|
|
switch (orientation)
|
|
|
|
{
|
|
|
|
case XCF_ORIENTATION_HORIZONTAL:
|
2021-02-14 00:34:48 +08:00
|
|
|
if (info->file_version < 15 && position > gimp_image_get_height (image))
|
|
|
|
gimp_message (info->gimp, G_OBJECT (info->progress),
|
|
|
|
GIMP_MESSAGE_WARNING,
|
|
|
|
"Ignoring off-canvas horizontal guide (position %d) in XCF %d file",
|
|
|
|
position, info->file_version);
|
|
|
|
else
|
|
|
|
gimp_image_add_hguide (image, position, FALSE);
|
2004-11-02 02:33:09 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case XCF_ORIENTATION_VERTICAL:
|
2021-02-14 00:34:48 +08:00
|
|
|
if (info->file_version < 15 && position > gimp_image_get_width (image))
|
|
|
|
gimp_message (info->gimp, G_OBJECT (info->progress),
|
|
|
|
GIMP_MESSAGE_WARNING,
|
|
|
|
"Ignoring off-canvas vertical guide (position %d) in XCF %d file",
|
|
|
|
position, info->file_version);
|
|
|
|
else
|
|
|
|
gimp_image_add_vguide (image, position, FALSE);
|
2004-11-02 02:33:09 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2008-11-04 20:33:09 +08:00
|
|
|
gimp_message_literal (info->gimp, G_OBJECT (info->progress),
|
2013-09-15 00:59:20 +08:00
|
|
|
GIMP_MESSAGE_WARNING,
|
|
|
|
"Guide orientation out of range in XCF file");
|
2004-11-02 02:33:09 +08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* this is silly as the order of guides doesn't really matter,
|
2010-02-04 05:16:02 +08:00
|
|
|
* but it restores the list to its original order, which
|
2004-11-02 02:33:09 +08:00
|
|
|
* cannot be wrong --Mitch
|
|
|
|
*/
|
2010-02-04 05:16:02 +08:00
|
|
|
private->guides = g_list_reverse (private->guides);
|
2004-11-02 02:33:09 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2006-08-11 01:10:12 +08:00
|
|
|
case PROP_SAMPLE_POINTS:
|
2018-07-16 07:42:19 +08:00
|
|
|
{
|
|
|
|
gint n_sample_points, i;
|
|
|
|
|
|
|
|
n_sample_points = prop_size / (5 * 4);
|
|
|
|
for (i = 0; i < n_sample_points; i++)
|
|
|
|
{
|
|
|
|
GimpSamplePoint *sample_point;
|
|
|
|
gint32 x, y;
|
|
|
|
GimpColorPickMode pick_mode;
|
|
|
|
guint32 padding[2] = { 0, };
|
|
|
|
|
|
|
|
xcf_read_int32 (info, (guint32 *) &x, 1);
|
|
|
|
xcf_read_int32 (info, (guint32 *) &y, 1);
|
|
|
|
xcf_read_int32 (info, (guint32 *) &pick_mode, 1);
|
|
|
|
xcf_read_int32 (info, (guint32 *) padding, 2);
|
|
|
|
|
|
|
|
GIMP_LOG (XCF, "prop sample point x=%d y=%d mode=%d",
|
|
|
|
x, y, pick_mode);
|
|
|
|
|
2018-10-23 05:34:11 +08:00
|
|
|
if (pick_mode > GIMP_COLOR_PICK_MODE_LAST)
|
|
|
|
pick_mode = GIMP_COLOR_PICK_MODE_PIXEL;
|
|
|
|
|
2018-07-16 07:42:19 +08:00
|
|
|
sample_point = gimp_image_add_sample_point_at_pos (image,
|
|
|
|
x, y, FALSE);
|
|
|
|
gimp_image_set_sample_point_pick_mode (image, sample_point,
|
|
|
|
pick_mode, FALSE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PROP_OLD_SAMPLE_POINTS:
|
2006-08-11 01:10:12 +08:00
|
|
|
{
|
|
|
|
gint32 x, y;
|
|
|
|
gint i, n_sample_points;
|
|
|
|
|
2018-07-16 07:42:19 +08:00
|
|
|
/* if there are already sample points, we loaded the new
|
|
|
|
* prop before
|
|
|
|
*/
|
|
|
|
if (gimp_image_get_sample_points (image))
|
|
|
|
{
|
|
|
|
if (! xcf_skip_unknown_prop (info, prop_size))
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2006-08-11 01:10:12 +08:00
|
|
|
n_sample_points = prop_size / (4 + 4);
|
|
|
|
for (i = 0; i < n_sample_points; i++)
|
|
|
|
{
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_int32 (info, (guint32 *) &x, 1);
|
|
|
|
xcf_read_int32 (info, (guint32 *) &y, 1);
|
2006-08-11 01:10:12 +08:00
|
|
|
|
2018-07-16 07:42:19 +08:00
|
|
|
GIMP_LOG (XCF, "prop old sample point x=%d y=%d", x, y);
|
2014-07-10 04:36:55 +08:00
|
|
|
|
2006-08-11 01:10:12 +08:00
|
|
|
gimp_image_add_sample_point_at_pos (image, x, y, FALSE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2004-11-02 02:33:09 +08:00
|
|
|
case PROP_RESOLUTION:
|
|
|
|
{
|
|
|
|
gfloat xres, yres;
|
|
|
|
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_float (info, &xres, 1);
|
|
|
|
xcf_read_float (info, &yres, 1);
|
2007-12-22 00:37:01 +08:00
|
|
|
|
2014-07-10 04:36:55 +08:00
|
|
|
GIMP_LOG (XCF, "prop resolution x=%f y=%f", xres, yres);
|
|
|
|
|
2004-11-02 02:33:09 +08:00
|
|
|
if (xres < GIMP_MIN_RESOLUTION || xres > GIMP_MAX_RESOLUTION ||
|
|
|
|
yres < GIMP_MIN_RESOLUTION || yres > GIMP_MAX_RESOLUTION)
|
|
|
|
{
|
2011-03-02 17:16:43 +08:00
|
|
|
GimpTemplate *template = image->gimp->config->default_image;
|
|
|
|
|
2008-11-04 20:33:09 +08:00
|
|
|
gimp_message_literal (info->gimp, G_OBJECT (info->progress),
|
2013-09-15 00:59:20 +08:00
|
|
|
GIMP_MESSAGE_WARNING,
|
|
|
|
"Warning, resolution out of range in XCF file");
|
2011-03-02 17:16:43 +08:00
|
|
|
xres = gimp_template_get_resolution_x (template);
|
|
|
|
yres = gimp_template_get_resolution_y (template);
|
2004-11-02 02:33:09 +08:00
|
|
|
}
|
2007-12-22 00:37:01 +08:00
|
|
|
|
2007-12-27 01:33:41 +08:00
|
|
|
gimp_image_set_resolution (image, xres, yres);
|
2004-11-02 02:33:09 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PROP_TATTOO:
|
|
|
|
{
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_int32 (info, &info->tattoo_state, 1);
|
2014-07-10 04:36:55 +08:00
|
|
|
|
|
|
|
GIMP_LOG (XCF, "prop tattoo state=%d", info->tattoo_state);
|
2004-11-02 02:33:09 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PROP_PARASITES:
|
|
|
|
{
|
2017-03-23 18:44:41 +08:00
|
|
|
goffset base = info->cp;
|
2004-11-02 02:33:09 +08:00
|
|
|
|
|
|
|
while (info->cp - base < prop_size)
|
|
|
|
{
|
2014-03-22 07:11:15 +08:00
|
|
|
GimpParasite *p = xcf_load_parasite (info);
|
|
|
|
GError *error = NULL;
|
2012-10-03 04:00:16 +08:00
|
|
|
|
|
|
|
if (! p)
|
2022-04-05 05:33:29 +08:00
|
|
|
{
|
|
|
|
gimp_message (info->gimp, G_OBJECT (info->progress),
|
|
|
|
GIMP_MESSAGE_WARNING,
|
|
|
|
"Invalid image parasite found. "
|
|
|
|
"Possibly corrupt XCF file.");
|
|
|
|
|
|
|
|
xcf_seek_pos (info, base + prop_size, NULL);
|
|
|
|
continue;
|
|
|
|
}
|
2012-10-03 04:00:16 +08:00
|
|
|
|
2014-03-22 07:11:15 +08:00
|
|
|
if (! gimp_image_parasite_validate (image, p, &error))
|
|
|
|
{
|
|
|
|
gimp_message (info->gimp, G_OBJECT (info->progress),
|
|
|
|
GIMP_MESSAGE_WARNING,
|
|
|
|
"Warning, invalid image parasite in XCF file: %s",
|
|
|
|
error->message);
|
|
|
|
g_clear_error (&error);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2019-05-30 22:44:23 +08:00
|
|
|
gimp_image_parasite_attach (image, p, FALSE);
|
2014-03-22 07:11:15 +08:00
|
|
|
}
|
|
|
|
|
2004-11-02 02:33:09 +08:00
|
|
|
gimp_parasite_free (p);
|
|
|
|
}
|
2007-12-22 00:37:01 +08:00
|
|
|
|
2004-11-02 02:33:09 +08:00
|
|
|
if (info->cp - base != prop_size)
|
2008-11-04 20:33:09 +08:00
|
|
|
gimp_message_literal (info->gimp, G_OBJECT (info->progress),
|
2013-09-15 00:59:20 +08:00
|
|
|
GIMP_MESSAGE_WARNING,
|
|
|
|
"Error while loading an image's parasites");
|
2004-11-02 02:33:09 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PROP_UNIT:
|
|
|
|
{
|
|
|
|
guint32 unit;
|
|
|
|
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_int32 (info, &unit, 1);
|
2004-11-02 02:33:09 +08:00
|
|
|
|
2014-07-10 04:36:55 +08:00
|
|
|
GIMP_LOG (XCF, "prop unit=%d", unit);
|
|
|
|
|
2004-11-02 02:33:09 +08:00
|
|
|
if ((unit <= GIMP_UNIT_PIXEL) ||
|
2010-02-21 23:46:39 +08:00
|
|
|
(unit >= gimp_unit_get_number_of_built_in_units ()))
|
2004-11-02 02:33:09 +08:00
|
|
|
{
|
2008-11-04 20:33:09 +08:00
|
|
|
gimp_message_literal (info->gimp, G_OBJECT (info->progress),
|
2013-09-15 00:59:20 +08:00
|
|
|
GIMP_MESSAGE_WARNING,
|
|
|
|
"Warning, unit out of range in XCF file, "
|
|
|
|
"falling back to inches");
|
2004-11-02 02:33:09 +08:00
|
|
|
unit = GIMP_UNIT_INCH;
|
|
|
|
}
|
2003-09-01 07:46:05 +08:00
|
|
|
|
2010-02-04 02:53:45 +08:00
|
|
|
gimp_image_set_unit (image, unit);
|
2004-11-02 02:33:09 +08:00
|
|
|
}
|
|
|
|
break;
|
2001-07-04 02:38:56 +08:00
|
|
|
|
2004-11-02 02:33:09 +08:00
|
|
|
case PROP_PATHS:
|
2022-04-30 04:40:32 +08:00
|
|
|
{
|
|
|
|
goffset base = info->cp;
|
|
|
|
|
2022-10-20 06:11:34 +08:00
|
|
|
if (info->file_version >= 18)
|
|
|
|
gimp_message (info->gimp, G_OBJECT (info->progress),
|
|
|
|
GIMP_MESSAGE_WARNING,
|
|
|
|
"XCF %d file should not contain PROP_PATHS image properties",
|
|
|
|
info->file_version);
|
|
|
|
|
2022-04-30 04:40:32 +08:00
|
|
|
if (! xcf_load_old_paths (info, image))
|
|
|
|
xcf_seek_pos (info, base + prop_size, NULL);
|
|
|
|
}
|
2004-11-02 02:33:09 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case PROP_USER_UNIT:
|
|
|
|
{
|
|
|
|
gchar *unit_strings[5];
|
|
|
|
float factor;
|
|
|
|
guint32 digits;
|
|
|
|
GimpUnit unit;
|
|
|
|
gint num_units;
|
|
|
|
gint i;
|
|
|
|
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_float (info, &factor, 1);
|
|
|
|
xcf_read_int32 (info, &digits, 1);
|
|
|
|
xcf_read_string (info, unit_strings, 5);
|
2004-11-02 02:33:09 +08:00
|
|
|
|
|
|
|
for (i = 0; i < 5; i++)
|
|
|
|
if (unit_strings[i] == NULL)
|
|
|
|
unit_strings[i] = g_strdup ("");
|
|
|
|
|
2010-02-21 23:46:39 +08:00
|
|
|
num_units = gimp_unit_get_number_of_units ();
|
2004-11-02 02:33:09 +08:00
|
|
|
|
2010-02-21 23:46:39 +08:00
|
|
|
for (unit = gimp_unit_get_number_of_built_in_units ();
|
2004-11-02 02:33:09 +08:00
|
|
|
unit < num_units; unit++)
|
|
|
|
{
|
|
|
|
/* if the factor and the identifier match some unit
|
|
|
|
* in unitrc, use the unitrc unit
|
|
|
|
*/
|
2010-02-21 23:46:39 +08:00
|
|
|
if ((ABS (gimp_unit_get_factor (unit) - factor) < 1e-5) &&
|
2004-11-02 02:33:09 +08:00
|
|
|
(strcmp (unit_strings[0],
|
2010-02-21 23:46:39 +08:00
|
|
|
gimp_unit_get_identifier (unit)) == 0))
|
2004-11-02 02:33:09 +08:00
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* no match */
|
|
|
|
if (unit == num_units)
|
2010-02-21 23:46:39 +08:00
|
|
|
unit = gimp_unit_new (unit_strings[0],
|
|
|
|
factor,
|
|
|
|
digits,
|
|
|
|
unit_strings[1],
|
|
|
|
unit_strings[2],
|
|
|
|
unit_strings[3],
|
|
|
|
unit_strings[4]);
|
2004-11-02 02:33:09 +08:00
|
|
|
|
2010-02-04 02:53:45 +08:00
|
|
|
gimp_image_set_unit (image, unit);
|
2004-11-02 02:33:09 +08:00
|
|
|
|
|
|
|
for (i = 0; i < 5; i++)
|
|
|
|
g_free (unit_strings[i]);
|
|
|
|
}
|
|
|
|
break;
|
2001-07-04 02:38:56 +08:00
|
|
|
|
2003-09-09 23:46:59 +08:00
|
|
|
case PROP_VECTORS:
|
|
|
|
{
|
2017-03-23 18:44:41 +08:00
|
|
|
goffset base = info->cp;
|
2003-09-09 23:46:59 +08:00
|
|
|
|
2022-10-20 06:11:34 +08:00
|
|
|
if (info->file_version >= 18)
|
|
|
|
gimp_message (info->gimp, G_OBJECT (info->progress),
|
|
|
|
GIMP_MESSAGE_WARNING,
|
|
|
|
"XCF %d file should not contain PROP_VECTORS image properties",
|
|
|
|
info->file_version);
|
|
|
|
|
|
|
|
if (xcf_load_old_vectors (info, image))
|
2003-09-09 23:46:59 +08:00
|
|
|
{
|
|
|
|
if (base + prop_size != info->cp)
|
|
|
|
{
|
2006-07-11 00:40:26 +08:00
|
|
|
g_printerr ("Mismatch in PROP_VECTORS size: "
|
2017-03-23 19:24:38 +08:00
|
|
|
"skipping %" G_GOFFSET_FORMAT " bytes.\n",
|
2006-07-11 00:40:26 +08:00
|
|
|
base + prop_size - info->cp);
|
2003-09-09 23:46:59 +08:00
|
|
|
xcf_seek_pos (info, base + prop_size, NULL);
|
|
|
|
}
|
2003-09-11 00:22:33 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2003-09-09 23:46:59 +08:00
|
|
|
/* skip silently since we don't understand the format and
|
2022-10-20 06:11:34 +08:00
|
|
|
* xcf_load_old_vectors already explained what was wrong
|
2003-09-11 00:22:33 +08:00
|
|
|
*/
|
2003-09-09 23:46:59 +08:00
|
|
|
xcf_seek_pos (info, base + prop_size, NULL);
|
|
|
|
}
|
|
|
|
}
|
2004-11-02 02:33:09 +08:00
|
|
|
break;
|
2003-09-09 23:46:59 +08:00
|
|
|
|
2021-12-23 05:53:15 +08:00
|
|
|
case PROP_ITEM_SET:
|
|
|
|
{
|
|
|
|
GimpItemList *set = NULL;
|
|
|
|
gchar *label;
|
|
|
|
GType item_type = 0;
|
|
|
|
guint32 itype;
|
|
|
|
guint32 method;
|
|
|
|
|
|
|
|
xcf_read_int32 (info, &itype, 1);
|
|
|
|
xcf_read_int32 (info, &method, 1);
|
|
|
|
xcf_read_string (info, &label, 1);
|
|
|
|
|
|
|
|
if (itype == 0)
|
|
|
|
item_type = GIMP_TYPE_LAYER;
|
|
|
|
else
|
|
|
|
item_type = GIMP_TYPE_CHANNEL;
|
|
|
|
|
|
|
|
if (itype > 1)
|
|
|
|
{
|
|
|
|
g_printerr ("xcf: unsupported item set '%s' type: %d (skipping)\n",
|
|
|
|
label ? label : "unnamed", itype);
|
|
|
|
/* Only case where we break because we wouldn't even
|
|
|
|
* know where to categorize the item set anyway. */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else if (label == NULL)
|
|
|
|
{
|
|
|
|
g_printerr ("xcf: item set without a name or pattern (skipping)\n");
|
|
|
|
}
|
|
|
|
else if (method != G_MAXUINT32 && method > GIMP_SELECT_GLOB_PATTERN)
|
|
|
|
{
|
|
|
|
g_printerr ("xcf: unsupported item set '%s' selection method attribute: 0x%x (skipping)\n",
|
|
|
|
label, method);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (method == G_MAXUINT32)
|
|
|
|
{
|
|
|
|
/* Don't use gimp_item_list_named_new() because it
|
|
|
|
* doesn't allow NULL items (it would try to get the
|
|
|
|
* selected items instead).
|
|
|
|
*/
|
|
|
|
set = g_object_new (GIMP_TYPE_ITEM_LIST,
|
|
|
|
"image", image,
|
|
|
|
"name", label,
|
|
|
|
"is-pattern", FALSE,
|
|
|
|
"item-type", item_type,
|
|
|
|
"items", NULL,
|
|
|
|
NULL);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
set = gimp_item_list_pattern_new (image, item_type,
|
|
|
|
method, label);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Note: we are still adding invalid item sets as NULL on
|
|
|
|
* purpose, in order not to break order-base association
|
|
|
|
* between PROP_ITEM_SET and PROP_ITEM_SET_ITEM.
|
|
|
|
*/
|
|
|
|
if (item_type == GIMP_TYPE_LAYER)
|
|
|
|
info->layer_sets = g_list_prepend (info->layer_sets, set);
|
|
|
|
else
|
|
|
|
info->channel_sets = g_list_prepend (info->channel_sets, set);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2004-11-02 02:33:09 +08:00
|
|
|
default:
|
2004-03-18 23:27:23 +08:00
|
|
|
#ifdef GIMP_UNSTABLE
|
2006-07-11 00:40:26 +08:00
|
|
|
g_printerr ("unexpected/unknown image property: %d (skipping)\n",
|
2004-03-18 23:27:23 +08:00
|
|
|
prop_type);
|
|
|
|
#endif
|
2010-08-07 06:26:58 +08:00
|
|
|
if (! xcf_skip_unknown_prop (info, prop_size))
|
|
|
|
return FALSE;
|
2004-11-02 02:33:09 +08:00
|
|
|
break;
|
|
|
|
}
|
2001-07-04 02:38:56 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
2009-08-30 19:14:28 +08:00
|
|
|
xcf_load_layer_props (XcfInfo *info,
|
|
|
|
GimpImage *image,
|
|
|
|
GimpLayer **layer,
|
2009-08-31 03:28:59 +08:00
|
|
|
GList **item_path,
|
2009-08-30 19:14:28 +08:00
|
|
|
gboolean *apply_mask,
|
|
|
|
gboolean *edit_mask,
|
|
|
|
gboolean *show_mask,
|
2011-09-26 03:57:20 +08:00
|
|
|
guint32 *text_layer_flags,
|
|
|
|
guint32 *group_layer_flags)
|
2001-07-04 02:38:56 +08:00
|
|
|
{
|
|
|
|
PropType prop_type;
|
|
|
|
guint32 prop_size;
|
|
|
|
|
|
|
|
while (TRUE)
|
|
|
|
{
|
2007-07-05 23:12:43 +08:00
|
|
|
if (! xcf_load_prop (info, &prop_type, &prop_size))
|
2004-11-02 02:33:09 +08:00
|
|
|
return FALSE;
|
2001-07-04 02:38:56 +08:00
|
|
|
|
|
|
|
switch (prop_type)
|
2004-11-02 02:33:09 +08:00
|
|
|
{
|
|
|
|
case PROP_END:
|
|
|
|
return TRUE;
|
2004-03-18 23:27:23 +08:00
|
|
|
|
2004-11-02 02:33:09 +08:00
|
|
|
case PROP_ACTIVE_LAYER:
|
2020-04-15 03:36:58 +08:00
|
|
|
info->selected_layers = g_list_prepend (info->selected_layers, *layer);
|
2004-11-02 02:33:09 +08:00
|
|
|
break;
|
2004-03-18 23:27:23 +08:00
|
|
|
|
2004-11-02 02:33:09 +08:00
|
|
|
case PROP_FLOATING_SELECTION:
|
2009-08-30 19:14:28 +08:00
|
|
|
info->floating_sel = *layer;
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_offset (info, &info->floating_sel_offset, 1);
|
2004-11-02 02:33:09 +08:00
|
|
|
break;
|
2004-03-18 23:27:23 +08:00
|
|
|
|
2004-11-02 02:33:09 +08:00
|
|
|
case PROP_OPACITY:
|
2002-03-04 22:52:54 +08:00
|
|
|
{
|
|
|
|
guint32 opacity;
|
|
|
|
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_int32 (info, &opacity, 1);
|
|
|
|
|
2009-08-30 19:14:28 +08:00
|
|
|
gimp_layer_set_opacity (*layer, (gdouble) opacity / 255.0, FALSE);
|
2002-03-04 22:52:54 +08:00
|
|
|
}
|
2004-11-02 02:33:09 +08:00
|
|
|
break;
|
2004-03-18 23:27:23 +08:00
|
|
|
|
2015-10-17 03:59:11 +08:00
|
|
|
case PROP_FLOAT_OPACITY:
|
|
|
|
{
|
|
|
|
gfloat opacity;
|
|
|
|
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_float (info, &opacity, 1);
|
|
|
|
|
2015-10-17 03:59:11 +08:00
|
|
|
gimp_layer_set_opacity (*layer, opacity, FALSE);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2004-11-02 02:33:09 +08:00
|
|
|
case PROP_VISIBLE:
|
|
|
|
{
|
|
|
|
gboolean visible;
|
2001-07-04 02:38:56 +08:00
|
|
|
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_int32 (info, (guint32 *) &visible, 1);
|
|
|
|
|
2009-08-30 19:14:28 +08:00
|
|
|
gimp_item_set_visible (GIMP_ITEM (*layer), visible, FALSE);
|
2004-11-02 02:33:09 +08:00
|
|
|
}
|
|
|
|
break;
|
2004-03-18 23:27:23 +08:00
|
|
|
|
2004-11-02 02:33:09 +08:00
|
|
|
case PROP_LINKED:
|
2003-05-09 04:26:01 +08:00
|
|
|
{
|
|
|
|
gboolean linked;
|
|
|
|
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_int32 (info, (guint32 *) &linked, 1);
|
|
|
|
|
2021-12-16 07:35:20 +08:00
|
|
|
if (linked)
|
|
|
|
info->linked_layers = g_list_prepend (info->linked_layers, *layer);
|
2003-05-09 04:26:01 +08:00
|
|
|
}
|
2004-11-02 02:33:09 +08:00
|
|
|
break;
|
2004-03-18 23:27:23 +08:00
|
|
|
|
2016-11-15 06:02:43 +08:00
|
|
|
case PROP_COLOR_TAG:
|
|
|
|
{
|
|
|
|
GimpColorTag color_tag;
|
|
|
|
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_int32 (info, (guint32 *) &color_tag, 1);
|
|
|
|
|
2016-11-15 06:02:43 +08:00
|
|
|
gimp_item_set_color_tag (GIMP_ITEM (*layer), color_tag, FALSE);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2009-08-30 18:44:35 +08:00
|
|
|
case PROP_LOCK_CONTENT:
|
|
|
|
{
|
|
|
|
gboolean lock_content;
|
|
|
|
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_int32 (info, (guint32 *) &lock_content, 1);
|
2009-08-31 03:02:43 +08:00
|
|
|
|
|
|
|
if (gimp_item_can_lock_content (GIMP_ITEM (*layer)))
|
|
|
|
gimp_item_set_lock_content (GIMP_ITEM (*layer),
|
|
|
|
lock_content, FALSE);
|
2009-08-30 18:44:35 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2005-07-11 05:17:22 +08:00
|
|
|
case PROP_LOCK_ALPHA:
|
2007-12-19 03:12:43 +08:00
|
|
|
{
|
|
|
|
gboolean lock_alpha;
|
|
|
|
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_int32 (info, (guint32 *) &lock_alpha, 1);
|
2009-08-31 03:02:43 +08:00
|
|
|
|
|
|
|
if (gimp_layer_can_lock_alpha (*layer))
|
|
|
|
gimp_layer_set_lock_alpha (*layer, lock_alpha, FALSE);
|
2007-12-19 03:12:43 +08:00
|
|
|
}
|
2004-11-02 02:33:09 +08:00
|
|
|
break;
|
2004-03-18 23:27:23 +08:00
|
|
|
|
2012-11-09 18:17:25 +08:00
|
|
|
case PROP_LOCK_POSITION:
|
|
|
|
{
|
|
|
|
gboolean lock_position;
|
|
|
|
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_int32 (info, (guint32 *) &lock_position, 1);
|
2012-11-09 18:17:25 +08:00
|
|
|
|
|
|
|
if (gimp_item_can_lock_position (GIMP_ITEM (*layer)))
|
|
|
|
gimp_item_set_lock_position (GIMP_ITEM (*layer),
|
|
|
|
lock_position, FALSE);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2022-02-15 21:44:11 +08:00
|
|
|
case PROP_LOCK_VISIBILITY:
|
|
|
|
{
|
|
|
|
gboolean lock_visibility;
|
|
|
|
|
|
|
|
xcf_read_int32 (info, (guint32 *) &lock_visibility, 1);
|
|
|
|
|
|
|
|
if (gimp_item_can_lock_visibility (GIMP_ITEM (*layer)))
|
|
|
|
gimp_item_set_lock_visibility (GIMP_ITEM (*layer),
|
|
|
|
lock_visibility, FALSE);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2004-11-02 02:33:09 +08:00
|
|
|
case PROP_APPLY_MASK:
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_int32 (info, (guint32 *) apply_mask, 1);
|
2004-11-02 02:33:09 +08:00
|
|
|
break;
|
2004-03-18 23:27:23 +08:00
|
|
|
|
2004-11-02 02:33:09 +08:00
|
|
|
case PROP_EDIT_MASK:
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_int32 (info, (guint32 *) edit_mask, 1);
|
2004-11-02 02:33:09 +08:00
|
|
|
break;
|
2004-03-18 23:27:23 +08:00
|
|
|
|
2004-11-02 02:33:09 +08:00
|
|
|
case PROP_SHOW_MASK:
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_int32 (info, (guint32 *) show_mask, 1);
|
2004-11-02 02:33:09 +08:00
|
|
|
break;
|
2004-03-18 23:27:23 +08:00
|
|
|
|
2004-11-02 02:33:09 +08:00
|
|
|
case PROP_OFFSETS:
|
2008-11-04 05:17:50 +08:00
|
|
|
{
|
2019-01-03 22:02:55 +08:00
|
|
|
gint32 offset_x;
|
|
|
|
gint32 offset_y;
|
2008-11-04 05:17:50 +08:00
|
|
|
|
2019-01-03 22:02:55 +08:00
|
|
|
xcf_read_int32 (info, (guint32 *) &offset_x, 1);
|
|
|
|
xcf_read_int32 (info, (guint32 *) &offset_y, 1);
|
|
|
|
|
|
|
|
if (offset_x < -GIMP_MAX_IMAGE_SIZE ||
|
|
|
|
offset_x > GIMP_MAX_IMAGE_SIZE)
|
|
|
|
{
|
|
|
|
g_printerr ("unexpected item offset_x (%d) in XCF, "
|
|
|
|
"setting to 0\n", offset_x);
|
|
|
|
offset_x = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (offset_y < -GIMP_MAX_IMAGE_SIZE ||
|
|
|
|
offset_y > GIMP_MAX_IMAGE_SIZE)
|
|
|
|
{
|
|
|
|
g_printerr ("unexpected item offset_y (%d) in XCF, "
|
|
|
|
"setting to 0\n", offset_y);
|
|
|
|
offset_y = 0;
|
|
|
|
}
|
2008-11-04 05:17:50 +08:00
|
|
|
|
2009-08-30 19:14:28 +08:00
|
|
|
gimp_item_set_offset (GIMP_ITEM (*layer), offset_x, offset_y);
|
2008-11-04 05:17:50 +08:00
|
|
|
}
|
2004-11-02 02:33:09 +08:00
|
|
|
break;
|
2004-03-18 23:27:23 +08:00
|
|
|
|
2004-11-02 02:33:09 +08:00
|
|
|
case PROP_MODE:
|
2007-12-19 03:12:43 +08:00
|
|
|
{
|
2017-03-23 19:24:38 +08:00
|
|
|
GimpLayerMode mode;
|
2007-12-19 03:12:43 +08:00
|
|
|
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_int32 (info, (guint32 *) &mode, 1);
|
2015-04-28 05:48:00 +08:00
|
|
|
|
2017-01-09 08:27:20 +08:00
|
|
|
if (mode == GIMP_LAYER_MODE_OVERLAY_LEGACY)
|
|
|
|
mode = GIMP_LAYER_MODE_SOFTLIGHT_LEGACY;
|
2015-04-28 05:48:00 +08:00
|
|
|
|
2017-03-23 19:24:38 +08:00
|
|
|
gimp_layer_set_mode (*layer, mode, FALSE);
|
2007-12-19 03:12:43 +08:00
|
|
|
}
|
2004-11-02 02:33:09 +08:00
|
|
|
break;
|
2004-03-18 23:27:23 +08:00
|
|
|
|
2017-02-13 06:49:26 +08:00
|
|
|
case PROP_BLEND_SPACE:
|
2017-02-02 07:38:25 +08:00
|
|
|
{
|
app: future-proof XCF layer blend/composite props
The layer blend space, composite space, and composite mode
properties have a special AUTO value, which may map to different
concrete values based on the layer mode. Make sure we can change
this mapping in the future, without affecting existing XCFs (saved
after this commit), by encoding these properties as follows:
When saving an XCF, if the property has a concrete (non-AUTO)
value, which is always positive, encode it as is. If the property
is AUTO, which is always 0, encode it as the negative of the value
it actually maps to at the time of saving (note that in some cases
AUTO may map to AUTO, in which case it's encoded as 0).
When loading an XCF, if the encoded property (stored in the file)
is nonnegative, use it as is. Otherwise, compare the negative of
the encoded property to the value AUTO maps to at the time of
loading. If the values are equal, set the property to AUTO;
otherwise, use the concrete value (i.e., the negative of the value
stored in the XCF).
Note that XCFs saved prior to this commit still load fine, it's
simply that if we change the AUTO mapping in the future, all their
AUTO properties will keep being loaded as AUTO, even if the
resulting concrete values will have changed.
2017-05-21 20:01:39 +08:00
|
|
|
gint32 blend_space;
|
2017-02-02 07:38:25 +08:00
|
|
|
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_int32 (info, (guint32 *) &blend_space, 1);
|
2017-02-02 07:38:25 +08:00
|
|
|
|
app: future-proof XCF layer blend/composite props
The layer blend space, composite space, and composite mode
properties have a special AUTO value, which may map to different
concrete values based on the layer mode. Make sure we can change
this mapping in the future, without affecting existing XCFs (saved
after this commit), by encoding these properties as follows:
When saving an XCF, if the property has a concrete (non-AUTO)
value, which is always positive, encode it as is. If the property
is AUTO, which is always 0, encode it as the negative of the value
it actually maps to at the time of saving (note that in some cases
AUTO may map to AUTO, in which case it's encoded as 0).
When loading an XCF, if the encoded property (stored in the file)
is nonnegative, use it as is. Otherwise, compare the negative of
the encoded property to the value AUTO maps to at the time of
loading. If the values are equal, set the property to AUTO;
otherwise, use the concrete value (i.e., the negative of the value
stored in the XCF).
Note that XCFs saved prior to this commit still load fine, it's
simply that if we change the AUTO mapping in the future, all their
AUTO properties will keep being loaded as AUTO, even if the
resulting concrete values will have changed.
2017-05-21 20:01:39 +08:00
|
|
|
/* if blend_space < 0 it was originally AUTO, and its negative is
|
|
|
|
* the actual value AUTO used to map to at the time the file was
|
|
|
|
* saved. if AUTO still maps to the same value, keep using AUTO
|
|
|
|
* for the property; otherwise, use the concrete value.
|
|
|
|
*/
|
|
|
|
if (blend_space < 0)
|
|
|
|
{
|
|
|
|
GimpLayerMode mode = gimp_layer_get_mode (*layer);
|
|
|
|
|
|
|
|
blend_space = -blend_space;
|
|
|
|
|
|
|
|
if (blend_space == gimp_layer_mode_get_blend_space (mode))
|
|
|
|
blend_space = GIMP_LAYER_COLOR_SPACE_AUTO;
|
|
|
|
else
|
|
|
|
GIMP_LOG (XCF, "BLEND_SPACE: AUTO => %d", blend_space);
|
|
|
|
}
|
|
|
|
|
2017-03-23 19:24:38 +08:00
|
|
|
gimp_layer_set_blend_space (*layer, blend_space, FALSE);
|
2017-02-13 06:49:26 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PROP_COMPOSITE_SPACE:
|
|
|
|
{
|
app: future-proof XCF layer blend/composite props
The layer blend space, composite space, and composite mode
properties have a special AUTO value, which may map to different
concrete values based on the layer mode. Make sure we can change
this mapping in the future, without affecting existing XCFs (saved
after this commit), by encoding these properties as follows:
When saving an XCF, if the property has a concrete (non-AUTO)
value, which is always positive, encode it as is. If the property
is AUTO, which is always 0, encode it as the negative of the value
it actually maps to at the time of saving (note that in some cases
AUTO may map to AUTO, in which case it's encoded as 0).
When loading an XCF, if the encoded property (stored in the file)
is nonnegative, use it as is. Otherwise, compare the negative of
the encoded property to the value AUTO maps to at the time of
loading. If the values are equal, set the property to AUTO;
otherwise, use the concrete value (i.e., the negative of the value
stored in the XCF).
Note that XCFs saved prior to this commit still load fine, it's
simply that if we change the AUTO mapping in the future, all their
AUTO properties will keep being loaded as AUTO, even if the
resulting concrete values will have changed.
2017-05-21 20:01:39 +08:00
|
|
|
gint32 composite_space;
|
2017-02-13 06:49:26 +08:00
|
|
|
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_int32 (info, (guint32 *) &composite_space, 1);
|
2017-02-13 06:49:26 +08:00
|
|
|
|
app: future-proof XCF layer blend/composite props
The layer blend space, composite space, and composite mode
properties have a special AUTO value, which may map to different
concrete values based on the layer mode. Make sure we can change
this mapping in the future, without affecting existing XCFs (saved
after this commit), by encoding these properties as follows:
When saving an XCF, if the property has a concrete (non-AUTO)
value, which is always positive, encode it as is. If the property
is AUTO, which is always 0, encode it as the negative of the value
it actually maps to at the time of saving (note that in some cases
AUTO may map to AUTO, in which case it's encoded as 0).
When loading an XCF, if the encoded property (stored in the file)
is nonnegative, use it as is. Otherwise, compare the negative of
the encoded property to the value AUTO maps to at the time of
loading. If the values are equal, set the property to AUTO;
otherwise, use the concrete value (i.e., the negative of the value
stored in the XCF).
Note that XCFs saved prior to this commit still load fine, it's
simply that if we change the AUTO mapping in the future, all their
AUTO properties will keep being loaded as AUTO, even if the
resulting concrete values will have changed.
2017-05-21 20:01:39 +08:00
|
|
|
/* if composite_space < 0 it was originally AUTO, and its negative
|
|
|
|
* is the actual value AUTO used to map to at the time the file was
|
|
|
|
* saved. if AUTO still maps to the same value, keep using AUTO
|
|
|
|
* for the property; otherwise, use the concrete value.
|
|
|
|
*/
|
|
|
|
if (composite_space < 0)
|
|
|
|
{
|
|
|
|
GimpLayerMode mode = gimp_layer_get_mode (*layer);
|
|
|
|
|
|
|
|
composite_space = -composite_space;
|
|
|
|
|
|
|
|
if (composite_space == gimp_layer_mode_get_composite_space (mode))
|
|
|
|
composite_space = GIMP_LAYER_COLOR_SPACE_AUTO;
|
|
|
|
else
|
|
|
|
GIMP_LOG (XCF, "COMPOSITE_SPACE: AUTO => %d", composite_space);
|
|
|
|
}
|
|
|
|
|
2017-03-23 19:24:38 +08:00
|
|
|
gimp_layer_set_composite_space (*layer, composite_space, FALSE);
|
2017-02-13 06:49:26 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PROP_COMPOSITE_MODE:
|
|
|
|
{
|
app: future-proof XCF layer blend/composite props
The layer blend space, composite space, and composite mode
properties have a special AUTO value, which may map to different
concrete values based on the layer mode. Make sure we can change
this mapping in the future, without affecting existing XCFs (saved
after this commit), by encoding these properties as follows:
When saving an XCF, if the property has a concrete (non-AUTO)
value, which is always positive, encode it as is. If the property
is AUTO, which is always 0, encode it as the negative of the value
it actually maps to at the time of saving (note that in some cases
AUTO may map to AUTO, in which case it's encoded as 0).
When loading an XCF, if the encoded property (stored in the file)
is nonnegative, use it as is. Otherwise, compare the negative of
the encoded property to the value AUTO maps to at the time of
loading. If the values are equal, set the property to AUTO;
otherwise, use the concrete value (i.e., the negative of the value
stored in the XCF).
Note that XCFs saved prior to this commit still load fine, it's
simply that if we change the AUTO mapping in the future, all their
AUTO properties will keep being loaded as AUTO, even if the
resulting concrete values will have changed.
2017-05-21 20:01:39 +08:00
|
|
|
gint32 composite_mode;
|
2017-02-13 06:49:26 +08:00
|
|
|
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_int32 (info, (guint32 *) &composite_mode, 1);
|
2017-02-13 06:49:26 +08:00
|
|
|
|
app: future-proof XCF layer blend/composite props
The layer blend space, composite space, and composite mode
properties have a special AUTO value, which may map to different
concrete values based on the layer mode. Make sure we can change
this mapping in the future, without affecting existing XCFs (saved
after this commit), by encoding these properties as follows:
When saving an XCF, if the property has a concrete (non-AUTO)
value, which is always positive, encode it as is. If the property
is AUTO, which is always 0, encode it as the negative of the value
it actually maps to at the time of saving (note that in some cases
AUTO may map to AUTO, in which case it's encoded as 0).
When loading an XCF, if the encoded property (stored in the file)
is nonnegative, use it as is. Otherwise, compare the negative of
the encoded property to the value AUTO maps to at the time of
loading. If the values are equal, set the property to AUTO;
otherwise, use the concrete value (i.e., the negative of the value
stored in the XCF).
Note that XCFs saved prior to this commit still load fine, it's
simply that if we change the AUTO mapping in the future, all their
AUTO properties will keep being loaded as AUTO, even if the
resulting concrete values will have changed.
2017-05-21 20:01:39 +08:00
|
|
|
/* if composite_mode < 0 it was originally AUTO, and its negative
|
|
|
|
* is the actual value AUTO used to map to at the time the file was
|
|
|
|
* saved. if AUTO still maps to the same value, keep using AUTO
|
|
|
|
* for the property; otherwise, use the concrete value.
|
|
|
|
*/
|
|
|
|
if (composite_mode < 0)
|
|
|
|
{
|
|
|
|
GimpLayerMode mode = gimp_layer_get_mode (*layer);
|
|
|
|
|
|
|
|
composite_mode = -composite_mode;
|
|
|
|
|
|
|
|
if (composite_mode == gimp_layer_mode_get_composite_mode (mode))
|
|
|
|
composite_mode = GIMP_LAYER_COMPOSITE_AUTO;
|
|
|
|
else
|
|
|
|
GIMP_LOG (XCF, "COMPOSITE_MODE: AUTO => %d", composite_mode);
|
|
|
|
}
|
|
|
|
|
2017-03-23 19:24:38 +08:00
|
|
|
gimp_layer_set_composite_mode (*layer, composite_mode, FALSE);
|
2017-02-02 07:38:25 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2004-11-02 02:33:09 +08:00
|
|
|
case PROP_TATTOO:
|
2007-12-19 03:12:43 +08:00
|
|
|
{
|
|
|
|
GimpTattoo tattoo;
|
|
|
|
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_int32 (info, (guint32 *) &tattoo, 1);
|
|
|
|
|
2009-08-30 19:14:28 +08:00
|
|
|
gimp_item_set_tattoo (GIMP_ITEM (*layer), tattoo);
|
2007-12-19 03:12:43 +08:00
|
|
|
}
|
2004-11-02 02:33:09 +08:00
|
|
|
break;
|
2004-03-18 23:27:23 +08:00
|
|
|
|
|
|
|
case PROP_PARASITES:
|
|
|
|
{
|
2017-03-23 18:44:41 +08:00
|
|
|
goffset base = info->cp;
|
2004-03-18 23:27:23 +08:00
|
|
|
|
|
|
|
while (info->cp - base < prop_size)
|
|
|
|
{
|
2014-03-22 07:11:15 +08:00
|
|
|
GimpParasite *p = xcf_load_parasite (info);
|
|
|
|
GError *error = NULL;
|
2012-10-03 04:00:16 +08:00
|
|
|
|
|
|
|
if (! p)
|
|
|
|
return FALSE;
|
|
|
|
|
2014-03-22 07:11:15 +08:00
|
|
|
if (! gimp_item_parasite_validate (GIMP_ITEM (*layer), p, &error))
|
|
|
|
{
|
|
|
|
gimp_message (info->gimp, G_OBJECT (info->progress),
|
|
|
|
GIMP_MESSAGE_WARNING,
|
|
|
|
"Warning, invalid layer parasite in XCF file: %s",
|
|
|
|
error->message);
|
|
|
|
g_clear_error (&error);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
gimp_item_parasite_attach (GIMP_ITEM (*layer), p, FALSE);
|
|
|
|
}
|
|
|
|
|
2004-03-18 23:27:23 +08:00
|
|
|
gimp_parasite_free (p);
|
|
|
|
}
|
2007-07-05 23:12:43 +08:00
|
|
|
|
2004-03-18 23:27:23 +08:00
|
|
|
if (info->cp - base != prop_size)
|
2008-11-04 20:33:09 +08:00
|
|
|
gimp_message_literal (info->gimp, G_OBJECT (info->progress),
|
2013-09-15 00:59:20 +08:00
|
|
|
GIMP_MESSAGE_WARNING,
|
|
|
|
"Error while loading a layer's parasites");
|
2004-03-18 23:27:23 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PROP_TEXT_LAYER_FLAGS:
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_int32 (info, text_layer_flags, 1);
|
2004-03-18 23:27:23 +08:00
|
|
|
break;
|
|
|
|
|
2009-08-31 03:28:59 +08:00
|
|
|
case PROP_GROUP_ITEM:
|
|
|
|
{
|
|
|
|
GimpLayer *group;
|
2020-04-15 03:36:58 +08:00
|
|
|
gboolean is_selected_layer;
|
2016-06-20 22:14:41 +08:00
|
|
|
|
|
|
|
/* We're going to delete *layer, Don't leave its pointers
|
|
|
|
* in @info. After that, we'll restore them back with the
|
|
|
|
* new pointer. See bug #767873.
|
|
|
|
*/
|
2020-04-15 03:36:58 +08:00
|
|
|
is_selected_layer = (g_list_find (info->selected_layers, *layer ) != NULL);
|
|
|
|
if (is_selected_layer)
|
|
|
|
info->selected_layers = g_list_remove (info->selected_layers, *layer);
|
2016-06-20 22:14:41 +08:00
|
|
|
|
|
|
|
if (*layer == info->floating_sel)
|
|
|
|
info->floating_sel = NULL;
|
2009-08-31 03:28:59 +08:00
|
|
|
|
|
|
|
group = gimp_group_layer_new (image);
|
|
|
|
|
|
|
|
gimp_object_set_name (GIMP_OBJECT (group),
|
|
|
|
gimp_object_get_name (*layer));
|
|
|
|
|
|
|
|
g_object_ref_sink (*layer);
|
|
|
|
g_object_unref (*layer);
|
|
|
|
*layer = group;
|
2016-06-20 22:14:41 +08:00
|
|
|
|
2020-04-15 03:36:58 +08:00
|
|
|
if (is_selected_layer)
|
|
|
|
info->selected_layers = g_list_prepend (info->selected_layers, *layer);
|
2016-06-20 22:14:41 +08:00
|
|
|
|
|
|
|
/* Don't restore info->floating_sel because group layers
|
|
|
|
* can't be floating selections
|
|
|
|
*/
|
2009-08-31 03:28:59 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PROP_ITEM_PATH:
|
|
|
|
{
|
2017-03-23 18:44:41 +08:00
|
|
|
goffset base = info->cp;
|
|
|
|
GList *path = NULL;
|
2009-08-31 03:28:59 +08:00
|
|
|
|
|
|
|
while (info->cp - base < prop_size)
|
|
|
|
{
|
|
|
|
guint32 index;
|
|
|
|
|
2017-03-23 19:24:38 +08:00
|
|
|
if (xcf_read_int32 (info, &index, 1) != 4)
|
2014-06-07 00:35:25 +08:00
|
|
|
{
|
|
|
|
g_list_free (path);
|
|
|
|
return FALSE;
|
|
|
|
}
|
2009-08-31 03:28:59 +08:00
|
|
|
|
|
|
|
path = g_list_append (path, GUINT_TO_POINTER (index));
|
|
|
|
}
|
|
|
|
|
|
|
|
*item_path = path;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2011-09-26 03:57:20 +08:00
|
|
|
case PROP_GROUP_ITEM_FLAGS:
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_int32 (info, group_layer_flags, 1);
|
2011-09-26 03:57:20 +08:00
|
|
|
break;
|
|
|
|
|
2021-12-23 05:53:15 +08:00
|
|
|
case PROP_ITEM_SET_ITEM:
|
|
|
|
{
|
|
|
|
GimpItemList *set;
|
|
|
|
guint32 n;
|
|
|
|
|
|
|
|
xcf_read_int32 (info, &n, 1);
|
|
|
|
set = g_list_nth_data (info->layer_sets, n);
|
|
|
|
if (set == NULL)
|
|
|
|
g_printerr ("xcf: layer '%s' cannot be added to unknown layer set at index %d (skipping)\n",
|
|
|
|
gimp_object_get_name (*layer), n);
|
|
|
|
else if (! g_type_is_a (G_TYPE_FROM_INSTANCE (*layer),
|
|
|
|
gimp_item_list_get_item_type (set)))
|
|
|
|
g_printerr ("xcf: layer '%s' cannot be added to item set '%s' with item type %s (skipping)\n",
|
|
|
|
gimp_object_get_name (*layer), gimp_object_get_name (set),
|
|
|
|
g_type_name (gimp_item_list_get_item_type (set)));
|
|
|
|
else if (gimp_item_list_is_pattern (set, NULL))
|
|
|
|
g_printerr ("xcf: layer '%s' cannot be added to pattern item set '%s' (skipping)\n",
|
|
|
|
gimp_object_get_name (*layer), gimp_object_get_name (set));
|
|
|
|
else
|
|
|
|
gimp_item_list_add (set, GIMP_ITEM (*layer));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2004-11-02 02:33:09 +08:00
|
|
|
default:
|
2006-07-11 00:40:26 +08:00
|
|
|
#ifdef GIMP_UNSTABLE
|
|
|
|
g_printerr ("unexpected/unknown layer property: %d (skipping)\n",
|
|
|
|
prop_type);
|
|
|
|
#endif
|
2010-08-07 06:26:58 +08:00
|
|
|
if (! xcf_skip_unknown_prop (info, prop_size))
|
|
|
|
return FALSE;
|
2004-11-02 02:33:09 +08:00
|
|
|
break;
|
|
|
|
}
|
2001-07-04 02:38:56 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2019-07-11 21:04:30 +08:00
|
|
|
static gboolean
|
2019-07-11 22:32:45 +08:00
|
|
|
xcf_check_layer_props (XcfInfo *info,
|
|
|
|
GList **item_path,
|
|
|
|
gboolean *is_group_layer,
|
|
|
|
gboolean *is_text_layer)
|
2019-07-11 21:04:30 +08:00
|
|
|
{
|
|
|
|
PropType prop_type;
|
|
|
|
guint32 prop_size;
|
|
|
|
|
2019-07-11 22:32:45 +08:00
|
|
|
g_return_val_if_fail (*is_group_layer == FALSE, FALSE);
|
|
|
|
g_return_val_if_fail (*is_text_layer == FALSE, FALSE);
|
|
|
|
|
2019-07-11 21:04:30 +08:00
|
|
|
while (TRUE)
|
|
|
|
{
|
|
|
|
if (! xcf_load_prop (info, &prop_type, &prop_size))
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
switch (prop_type)
|
|
|
|
{
|
|
|
|
case PROP_END:
|
|
|
|
return TRUE;
|
|
|
|
|
2019-07-11 22:32:45 +08:00
|
|
|
case PROP_TEXT_LAYER_FLAGS:
|
|
|
|
*is_text_layer = TRUE;
|
|
|
|
|
|
|
|
if (! xcf_skip_unknown_prop (info, prop_size))
|
|
|
|
return FALSE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PROP_GROUP_ITEM:
|
|
|
|
case PROP_GROUP_ITEM_FLAGS:
|
|
|
|
*is_group_layer = TRUE;
|
|
|
|
|
|
|
|
if (! xcf_skip_unknown_prop (info, prop_size))
|
|
|
|
return FALSE;
|
|
|
|
break;
|
|
|
|
|
2019-07-11 21:04:30 +08:00
|
|
|
case PROP_ITEM_PATH:
|
|
|
|
{
|
|
|
|
goffset base = info->cp;
|
|
|
|
GList *path = NULL;
|
|
|
|
|
|
|
|
while (info->cp - base < prop_size)
|
|
|
|
{
|
|
|
|
guint32 index;
|
|
|
|
|
|
|
|
if (xcf_read_int32 (info, &index, 1) != 4)
|
|
|
|
{
|
|
|
|
g_list_free (path);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
path = g_list_append (path, GUINT_TO_POINTER (index));
|
|
|
|
}
|
|
|
|
|
|
|
|
*item_path = path;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PROP_ACTIVE_LAYER:
|
|
|
|
case PROP_FLOATING_SELECTION:
|
|
|
|
case PROP_OPACITY:
|
|
|
|
case PROP_FLOAT_OPACITY:
|
|
|
|
case PROP_VISIBLE:
|
|
|
|
case PROP_LINKED:
|
|
|
|
case PROP_COLOR_TAG:
|
|
|
|
case PROP_LOCK_CONTENT:
|
|
|
|
case PROP_LOCK_ALPHA:
|
|
|
|
case PROP_LOCK_POSITION:
|
2022-02-15 21:44:11 +08:00
|
|
|
case PROP_LOCK_VISIBILITY:
|
2019-07-11 21:04:30 +08:00
|
|
|
case PROP_APPLY_MASK:
|
|
|
|
case PROP_EDIT_MASK:
|
|
|
|
case PROP_SHOW_MASK:
|
|
|
|
case PROP_OFFSETS:
|
|
|
|
case PROP_MODE:
|
|
|
|
case PROP_BLEND_SPACE:
|
|
|
|
case PROP_COMPOSITE_SPACE:
|
|
|
|
case PROP_COMPOSITE_MODE:
|
|
|
|
case PROP_TATTOO:
|
|
|
|
case PROP_PARASITES:
|
2021-12-23 05:53:15 +08:00
|
|
|
case PROP_ITEM_SET_ITEM:
|
2019-07-11 21:04:30 +08:00
|
|
|
if (! xcf_skip_unknown_prop (info, prop_size))
|
|
|
|
return FALSE;
|
|
|
|
/* Just ignore for now. */
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
#ifdef GIMP_UNSTABLE
|
|
|
|
g_printerr ("unexpected/unknown layer property: %d (skipping)\n",
|
|
|
|
prop_type);
|
|
|
|
#endif
|
|
|
|
if (! xcf_skip_unknown_prop (info, prop_size))
|
|
|
|
return FALSE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2001-07-04 02:38:56 +08:00
|
|
|
static gboolean
|
2003-09-03 07:07:40 +08:00
|
|
|
xcf_load_channel_props (XcfInfo *info,
|
2006-03-29 01:08:36 +08:00
|
|
|
GimpImage *image,
|
2004-11-02 02:33:09 +08:00
|
|
|
GimpChannel **channel)
|
2001-07-04 02:38:56 +08:00
|
|
|
{
|
|
|
|
PropType prop_type;
|
|
|
|
guint32 prop_size;
|
|
|
|
|
|
|
|
while (TRUE)
|
|
|
|
{
|
2007-07-05 23:12:43 +08:00
|
|
|
if (! xcf_load_prop (info, &prop_type, &prop_size))
|
2004-11-02 02:33:09 +08:00
|
|
|
return FALSE;
|
2001-07-04 02:38:56 +08:00
|
|
|
|
|
|
|
switch (prop_type)
|
2004-11-02 02:33:09 +08:00
|
|
|
{
|
|
|
|
case PROP_END:
|
|
|
|
return TRUE;
|
2004-03-18 23:27:23 +08:00
|
|
|
|
2004-11-02 02:33:09 +08:00
|
|
|
case PROP_ACTIVE_CHANNEL:
|
2021-06-20 06:00:53 +08:00
|
|
|
info->selected_channels = g_list_prepend (info->selected_channels, *channel);
|
2004-11-02 02:33:09 +08:00
|
|
|
break;
|
2004-03-18 23:27:23 +08:00
|
|
|
|
2004-11-02 02:33:09 +08:00
|
|
|
case PROP_SELECTION:
|
2007-12-26 00:21:40 +08:00
|
|
|
{
|
2012-05-14 07:44:41 +08:00
|
|
|
GimpChannel *mask;
|
2021-06-20 06:00:53 +08:00
|
|
|
GList *iter;
|
2007-12-26 00:21:40 +08:00
|
|
|
|
2016-06-20 22:14:41 +08:00
|
|
|
/* We're going to delete *channel, Don't leave its pointer
|
|
|
|
* in @info. See bug #767873.
|
|
|
|
*/
|
2021-06-20 06:00:53 +08:00
|
|
|
for (iter = info->selected_channels; iter; iter = iter->next)
|
|
|
|
if (*channel == iter->data)
|
|
|
|
{
|
|
|
|
info->selected_channels = g_list_delete_link (info->selected_channels, iter);
|
|
|
|
break;
|
|
|
|
}
|
2016-06-20 22:14:41 +08:00
|
|
|
|
2012-05-14 07:44:41 +08:00
|
|
|
mask =
|
2007-12-26 00:21:40 +08:00
|
|
|
gimp_selection_new (image,
|
2008-11-03 08:09:01 +08:00
|
|
|
gimp_item_get_width (GIMP_ITEM (*channel)),
|
|
|
|
gimp_item_get_height (GIMP_ITEM (*channel)));
|
2012-05-14 07:44:41 +08:00
|
|
|
gimp_image_take_mask (image, mask);
|
2007-12-26 00:21:40 +08:00
|
|
|
|
2018-02-14 02:16:30 +08:00
|
|
|
gimp_drawable_steal_buffer (GIMP_DRAWABLE (mask),
|
|
|
|
GIMP_DRAWABLE (*channel));
|
2007-12-26 00:21:40 +08:00
|
|
|
g_object_unref (*channel);
|
|
|
|
*channel = mask;
|
2016-06-20 22:14:41 +08:00
|
|
|
|
2021-06-20 06:00:53 +08:00
|
|
|
/* Don't restore info->selected_channels because the
|
2016-06-20 22:14:41 +08:00
|
|
|
* selection can't be the active channel
|
|
|
|
*/
|
2007-12-26 00:21:40 +08:00
|
|
|
}
|
2004-11-02 02:33:09 +08:00
|
|
|
break;
|
2004-03-18 23:27:23 +08:00
|
|
|
|
2004-11-02 02:33:09 +08:00
|
|
|
case PROP_OPACITY:
|
|
|
|
{
|
|
|
|
guint32 opacity;
|
2001-07-04 02:38:56 +08:00
|
|
|
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_int32 (info, &opacity, 1);
|
|
|
|
|
2007-12-19 03:12:43 +08:00
|
|
|
gimp_channel_set_opacity (*channel, opacity / 255.0, FALSE);
|
2004-11-02 02:33:09 +08:00
|
|
|
}
|
|
|
|
break;
|
2004-03-18 23:27:23 +08:00
|
|
|
|
2015-10-17 03:59:11 +08:00
|
|
|
case PROP_FLOAT_OPACITY:
|
|
|
|
{
|
|
|
|
gfloat opacity;
|
|
|
|
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_float (info, &opacity, 1);
|
|
|
|
|
2015-10-17 03:59:11 +08:00
|
|
|
gimp_channel_set_opacity (*channel, opacity, FALSE);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2004-11-02 02:33:09 +08:00
|
|
|
case PROP_VISIBLE:
|
|
|
|
{
|
|
|
|
gboolean visible;
|
2001-07-04 02:38:56 +08:00
|
|
|
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_int32 (info, (guint32 *) &visible, 1);
|
|
|
|
|
|
|
|
gimp_item_set_visible (GIMP_ITEM (*channel), visible, FALSE);
|
2004-11-02 02:33:09 +08:00
|
|
|
}
|
|
|
|
break;
|
2004-03-18 23:27:23 +08:00
|
|
|
|
2016-11-15 06:02:43 +08:00
|
|
|
case PROP_COLOR_TAG:
|
|
|
|
{
|
|
|
|
GimpColorTag color_tag;
|
|
|
|
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_int32 (info, (guint32 *) &color_tag, 1);
|
|
|
|
|
2016-11-15 06:02:43 +08:00
|
|
|
gimp_item_set_color_tag (GIMP_ITEM (*channel), color_tag, FALSE);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2004-11-02 02:33:09 +08:00
|
|
|
case PROP_LINKED:
|
2003-05-14 02:30:15 +08:00
|
|
|
{
|
|
|
|
gboolean linked;
|
|
|
|
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_int32 (info, (guint32 *) &linked, 1);
|
|
|
|
|
2021-12-16 07:35:20 +08:00
|
|
|
if (linked)
|
|
|
|
info->linked_channels = g_list_prepend (info->linked_channels, *channel);
|
2003-05-14 02:30:15 +08:00
|
|
|
}
|
2004-11-02 02:33:09 +08:00
|
|
|
break;
|
2004-03-18 23:27:23 +08:00
|
|
|
|
2009-08-30 18:44:35 +08:00
|
|
|
case PROP_LOCK_CONTENT:
|
|
|
|
{
|
|
|
|
gboolean lock_content;
|
|
|
|
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_int32 (info, (guint32 *) &lock_content, 1);
|
|
|
|
|
|
|
|
if (gimp_item_can_lock_content (GIMP_ITEM (*channel)))
|
|
|
|
gimp_item_set_lock_content (GIMP_ITEM (*channel),
|
|
|
|
lock_content, FALSE);
|
2009-08-30 18:44:35 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2012-11-09 18:17:25 +08:00
|
|
|
case PROP_LOCK_POSITION:
|
|
|
|
{
|
|
|
|
gboolean lock_position;
|
|
|
|
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_int32 (info, (guint32 *) &lock_position, 1);
|
|
|
|
|
|
|
|
if (gimp_item_can_lock_position (GIMP_ITEM (*channel)))
|
|
|
|
gimp_item_set_lock_position (GIMP_ITEM (*channel),
|
|
|
|
lock_position, FALSE);
|
2012-11-09 18:17:25 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2022-02-15 21:44:11 +08:00
|
|
|
case PROP_LOCK_VISIBILITY:
|
|
|
|
{
|
|
|
|
gboolean lock_visibility;
|
|
|
|
|
|
|
|
xcf_read_int32 (info, (guint32 *) &lock_visibility, 1);
|
|
|
|
|
|
|
|
if (gimp_item_can_lock_visibility (GIMP_ITEM (*channel)))
|
|
|
|
gimp_item_set_lock_visibility (GIMP_ITEM (*channel),
|
|
|
|
lock_visibility, FALSE);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2004-11-02 02:33:09 +08:00
|
|
|
case PROP_SHOW_MASKED:
|
2003-09-03 07:07:40 +08:00
|
|
|
{
|
|
|
|
gboolean show_masked;
|
|
|
|
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_int32 (info, (guint32 *) &show_masked, 1);
|
|
|
|
|
2003-09-03 07:07:40 +08:00
|
|
|
gimp_channel_set_show_masked (*channel, show_masked);
|
|
|
|
}
|
2004-11-02 02:33:09 +08:00
|
|
|
break;
|
2004-03-18 23:27:23 +08:00
|
|
|
|
2004-11-02 02:33:09 +08:00
|
|
|
case PROP_COLOR:
|
|
|
|
{
|
|
|
|
guchar col[3];
|
2001-07-04 02:38:56 +08:00
|
|
|
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_int8 (info, (guint8 *) col, 3);
|
|
|
|
|
2023-11-20 07:20:03 +08:00
|
|
|
gegl_color_set_pixel ((*channel)->color, babl_format ("R'G'B' u8"), col);
|
2004-11-02 02:33:09 +08:00
|
|
|
}
|
|
|
|
break;
|
2004-03-18 23:27:23 +08:00
|
|
|
|
2017-03-25 05:35:57 +08:00
|
|
|
case PROP_FLOAT_COLOR:
|
|
|
|
{
|
|
|
|
gfloat col[3];
|
|
|
|
|
|
|
|
xcf_read_float (info, col, 3);
|
|
|
|
|
2023-11-20 07:20:03 +08:00
|
|
|
/* TODO: is the channel color in sRGB or in the image's color space? */
|
|
|
|
gegl_color_set_pixel ((*channel)->color, babl_format ("R'G'B' float"), col);
|
2017-03-25 05:35:57 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2004-11-02 02:33:09 +08:00
|
|
|
case PROP_TATTOO:
|
2007-12-19 03:12:43 +08:00
|
|
|
{
|
|
|
|
GimpTattoo tattoo;
|
|
|
|
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_int32 (info, (guint32 *) &tattoo, 1);
|
|
|
|
|
2007-12-19 03:12:43 +08:00
|
|
|
gimp_item_set_tattoo (GIMP_ITEM (*channel), tattoo);
|
|
|
|
}
|
2004-11-02 02:33:09 +08:00
|
|
|
break;
|
2004-03-18 23:27:23 +08:00
|
|
|
|
|
|
|
case PROP_PARASITES:
|
|
|
|
{
|
2017-03-23 18:44:41 +08:00
|
|
|
goffset base = info->cp;
|
2004-03-18 23:27:23 +08:00
|
|
|
|
|
|
|
while ((info->cp - base) < prop_size)
|
|
|
|
{
|
2014-03-22 07:11:15 +08:00
|
|
|
GimpParasite *p = xcf_load_parasite (info);
|
|
|
|
GError *error = NULL;
|
2012-10-03 04:00:16 +08:00
|
|
|
|
|
|
|
if (! p)
|
|
|
|
return FALSE;
|
|
|
|
|
2014-03-22 07:11:15 +08:00
|
|
|
if (! gimp_item_parasite_validate (GIMP_ITEM (*channel), p,
|
|
|
|
&error))
|
|
|
|
{
|
|
|
|
gimp_message (info->gimp, G_OBJECT (info->progress),
|
|
|
|
GIMP_MESSAGE_WARNING,
|
|
|
|
"Warning, invalid channel parasite in XCF file: %s",
|
|
|
|
error->message);
|
|
|
|
g_clear_error (&error);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
gimp_item_parasite_attach (GIMP_ITEM (*channel), p, FALSE);
|
|
|
|
}
|
|
|
|
|
2004-03-18 23:27:23 +08:00
|
|
|
gimp_parasite_free (p);
|
|
|
|
}
|
2007-07-05 23:12:43 +08:00
|
|
|
|
2004-03-18 23:27:23 +08:00
|
|
|
if (info->cp - base != prop_size)
|
2008-11-04 20:33:09 +08:00
|
|
|
gimp_message_literal (info->gimp, G_OBJECT (info->progress),
|
2013-09-15 00:59:20 +08:00
|
|
|
GIMP_MESSAGE_WARNING,
|
|
|
|
"Error while loading a channel's parasites");
|
2004-03-18 23:27:23 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2021-12-23 05:53:15 +08:00
|
|
|
case PROP_ITEM_SET_ITEM:
|
|
|
|
{
|
|
|
|
GimpItemList *set;
|
|
|
|
guint32 n;
|
|
|
|
|
|
|
|
xcf_read_int32 (info, &n, 1);
|
|
|
|
set = g_list_nth_data (info->channel_sets, n);
|
|
|
|
if (set == NULL)
|
|
|
|
g_printerr ("xcf: unknown channel set: %d (skipping)\n", n);
|
|
|
|
else if (! g_type_is_a (G_TYPE_FROM_INSTANCE (*channel),
|
|
|
|
gimp_item_list_get_item_type (set)))
|
|
|
|
g_printerr ("xcf: channel '%s' cannot be added to item set '%s' with item type %s (skipping)\n",
|
|
|
|
gimp_object_get_name (*channel), gimp_object_get_name (set),
|
|
|
|
g_type_name (gimp_item_list_get_item_type (set)));
|
|
|
|
else
|
|
|
|
gimp_item_list_add (set, GIMP_ITEM (*channel));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2004-11-02 02:33:09 +08:00
|
|
|
default:
|
2004-03-18 23:27:23 +08:00
|
|
|
#ifdef GIMP_UNSTABLE
|
2006-07-11 00:40:26 +08:00
|
|
|
g_printerr ("unexpected/unknown channel property: %d (skipping)\n",
|
2004-03-18 23:27:23 +08:00
|
|
|
prop_type);
|
|
|
|
#endif
|
2010-08-07 06:26:58 +08:00
|
|
|
if (! xcf_skip_unknown_prop (info, prop_size))
|
|
|
|
return FALSE;
|
2004-11-02 02:33:09 +08:00
|
|
|
break;
|
|
|
|
}
|
2001-07-04 02:38:56 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2023-06-19 22:54:21 +08:00
|
|
|
static gboolean
|
|
|
|
xcf_load_effect_props (XcfInfo *info,
|
|
|
|
FilterData *filter)
|
|
|
|
{
|
|
|
|
PropType prop_type;
|
|
|
|
guint32 prop_size;
|
|
|
|
|
|
|
|
while (TRUE)
|
|
|
|
{
|
2024-01-23 00:49:10 +08:00
|
|
|
goffset next_prop;
|
|
|
|
|
2023-06-19 22:54:21 +08:00
|
|
|
if (! xcf_load_prop (info, &prop_type, &prop_size))
|
|
|
|
return FALSE;
|
|
|
|
|
2024-01-23 00:49:10 +08:00
|
|
|
next_prop = info->cp + prop_size;
|
2023-06-19 22:54:21 +08:00
|
|
|
switch (prop_type)
|
|
|
|
{
|
|
|
|
case PROP_END:
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
case PROP_VISIBLE:
|
|
|
|
{
|
|
|
|
gboolean visible;
|
|
|
|
|
|
|
|
xcf_read_int32 (info, (guint32 *) &visible, 1);
|
|
|
|
filter->is_visible = visible;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2024-01-23 00:49:10 +08:00
|
|
|
case PROP_FLOAT_OPACITY:
|
2023-06-19 22:54:21 +08:00
|
|
|
{
|
2024-01-23 00:49:10 +08:00
|
|
|
gfloat opacity;
|
2023-06-19 22:54:21 +08:00
|
|
|
|
2024-01-23 00:49:10 +08:00
|
|
|
xcf_read_float (info, &opacity, 1);
|
|
|
|
|
|
|
|
filter->opacity = (gdouble) opacity;
|
2023-06-19 22:54:21 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PROP_MODE:
|
|
|
|
{
|
|
|
|
GimpLayerMode mode;
|
|
|
|
|
|
|
|
xcf_read_int32 (info, (guint32 *) &mode, 1);
|
|
|
|
|
|
|
|
if (mode == GIMP_LAYER_MODE_OVERLAY_LEGACY)
|
|
|
|
mode = GIMP_LAYER_MODE_SOFTLIGHT_LEGACY;
|
|
|
|
|
|
|
|
filter->paint_mode = mode;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PROP_BLEND_SPACE:
|
|
|
|
{
|
|
|
|
gint32 blend_space;
|
|
|
|
|
|
|
|
xcf_read_int32 (info, (guint32 *) &blend_space, 1);
|
|
|
|
|
|
|
|
/* TODO: Revisit when blend space can be set
|
|
|
|
* on filter effects */
|
|
|
|
blend_space = GIMP_LAYER_COLOR_SPACE_AUTO;
|
|
|
|
|
|
|
|
filter->blend_space = blend_space;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PROP_COMPOSITE_SPACE:
|
|
|
|
{
|
|
|
|
gint32 composite_space;
|
|
|
|
|
|
|
|
xcf_read_int32 (info, (guint32 *) &composite_space, 1);
|
|
|
|
|
|
|
|
/* TODO: Revisit when composite space can be set
|
|
|
|
* on filter effects */
|
|
|
|
composite_space = GIMP_LAYER_COLOR_SPACE_AUTO;
|
|
|
|
|
|
|
|
filter->composite_space = composite_space;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PROP_COMPOSITE_MODE:
|
|
|
|
{
|
|
|
|
gint32 composite_mode;
|
|
|
|
|
|
|
|
xcf_read_int32 (info, (guint32 *) &composite_mode, 1);
|
|
|
|
|
|
|
|
/* TODO: Revisit when composite mode can be set
|
|
|
|
* on filter effects */
|
|
|
|
composite_mode = GIMP_LAYER_COMPOSITE_AUTO;
|
|
|
|
|
|
|
|
filter->composite_mode = composite_mode;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PROP_FILTER_REGION:
|
|
|
|
{
|
|
|
|
GimpFilterRegion region;
|
|
|
|
|
|
|
|
xcf_read_int32 (info, (guint32 *) ®ion, 1);
|
|
|
|
|
|
|
|
filter->region = region;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2024-01-23 00:49:10 +08:00
|
|
|
case PROP_FILTER_ARGUMENT:
|
2024-01-04 02:57:08 +08:00
|
|
|
{
|
2024-01-23 00:49:10 +08:00
|
|
|
GParamSpec *pspec;
|
|
|
|
gchar *filter_prop_name;
|
|
|
|
guint32 filter_type = FILTER_PROP_UNKNOWN;
|
|
|
|
GValue filter_prop_value = G_VALUE_INIT;
|
|
|
|
gboolean valid_prop_value = TRUE;
|
2024-01-04 02:57:08 +08:00
|
|
|
|
|
|
|
xcf_read_string (info, &filter_prop_name, 1);
|
|
|
|
xcf_read_int32 (info, (guint32 *) &filter_type, 1);
|
|
|
|
|
|
|
|
/* Check if valid property first */
|
2024-01-23 00:49:10 +08:00
|
|
|
if (! (pspec = gegl_operation_find_property (filter->operation_name,
|
|
|
|
filter_prop_name)))
|
|
|
|
{
|
|
|
|
gimp_message (info->gimp, G_OBJECT (info->progress),
|
|
|
|
GIMP_MESSAGE_WARNING,
|
|
|
|
"XCF Warning: filter \"%s\" does not "
|
|
|
|
"have the %s property. It was not set.",
|
|
|
|
filter->operation_name, filter_prop_name);
|
|
|
|
g_free (filter_prop_name);
|
|
|
|
break;
|
|
|
|
}
|
2024-01-04 02:57:08 +08:00
|
|
|
|
|
|
|
switch (filter_type)
|
|
|
|
{
|
|
|
|
case FILTER_PROP_INT:
|
2024-02-08 23:38:05 +08:00
|
|
|
case FILTER_PROP_UINT:
|
2024-01-23 00:49:10 +08:00
|
|
|
case FILTER_PROP_ENUM:
|
2024-01-04 02:57:08 +08:00
|
|
|
{
|
|
|
|
guint32 value;
|
|
|
|
|
|
|
|
xcf_read_int32 (info, (guint32 *) &value, 1);
|
2024-01-23 00:49:10 +08:00
|
|
|
g_value_init (&filter_prop_value, pspec->value_type);
|
|
|
|
if (filter_type == FILTER_PROP_INT)
|
|
|
|
g_value_set_int (&filter_prop_value, value);
|
2024-02-08 23:38:05 +08:00
|
|
|
else if (filter_type == FILTER_PROP_UINT)
|
|
|
|
g_value_set_uint (&filter_prop_value, value);
|
2024-01-23 00:49:10 +08:00
|
|
|
else
|
|
|
|
g_value_set_enum (&filter_prop_value, value);
|
2024-01-04 02:57:08 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case FILTER_PROP_BOOL:
|
|
|
|
{
|
|
|
|
gboolean value;
|
|
|
|
|
|
|
|
xcf_read_int32 (info, (guint32 *) &value, 1);
|
|
|
|
g_value_init (&filter_prop_value, G_TYPE_BOOLEAN);
|
|
|
|
g_value_set_boolean (&filter_prop_value, value);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case FILTER_PROP_FLOAT:
|
|
|
|
{
|
|
|
|
gfloat value;
|
|
|
|
|
|
|
|
xcf_read_float (info, &value, 1);
|
|
|
|
g_value_init (&filter_prop_value, G_TYPE_FLOAT);
|
|
|
|
g_value_set_float (&filter_prop_value, value);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case FILTER_PROP_STRING:
|
|
|
|
{
|
|
|
|
gchar *value;
|
|
|
|
|
|
|
|
xcf_read_string (info, &value, 1);
|
|
|
|
g_value_init (&filter_prop_value, G_TYPE_STRING);
|
|
|
|
g_value_set_string (&filter_prop_value, value);
|
|
|
|
|
|
|
|
g_free (value);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2024-01-23 00:49:10 +08:00
|
|
|
case FILTER_PROP_CONFIG:
|
|
|
|
{
|
|
|
|
GObject *config;
|
|
|
|
gchar *serialized;
|
|
|
|
GError *error = NULL;
|
|
|
|
|
|
|
|
if (! g_type_is_a (pspec->value_type, GIMP_TYPE_CONFIG))
|
|
|
|
{
|
|
|
|
gimp_message (info->gimp, G_OBJECT (info->progress),
|
|
|
|
GIMP_MESSAGE_WARNING,
|
|
|
|
"XCF Warning: property '%s' of filter '%s' is a %s, which does not implement GimpConfig interface.\n",
|
|
|
|
filter_prop_name, filter->operation_name, g_type_name (pspec->value_type));
|
|
|
|
valid_prop_value = FALSE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
xcf_read_string (info, &serialized, 1);
|
|
|
|
g_value_init (&filter_prop_value, pspec->value_type);
|
|
|
|
config = g_object_new (pspec->value_type, NULL);
|
|
|
|
if (gimp_config_deserialize_string (GIMP_CONFIG (config), serialized, -1, NULL, &error))
|
|
|
|
{
|
|
|
|
g_value_set_object (&filter_prop_value, config);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
gimp_message (info->gimp, G_OBJECT (info->progress),
|
|
|
|
GIMP_MESSAGE_WARNING,
|
|
|
|
"XCF Warning: failure to deserialize config object for property '%s' of filter '%s': %s\n"
|
|
|
|
"Serialized config was: %s",
|
|
|
|
filter_prop_name, filter->operation_name,
|
|
|
|
error->message, serialized);
|
|
|
|
valid_prop_value = FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
g_object_unref (config);
|
|
|
|
g_free (serialized);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2024-01-04 02:57:08 +08:00
|
|
|
default:
|
2024-01-23 00:49:10 +08:00
|
|
|
gimp_message (info->gimp, G_OBJECT (info->progress),
|
|
|
|
GIMP_MESSAGE_WARNING,
|
|
|
|
"XCF Warning: property '%s' of filter '%s' holds unsupported type %s.\n",
|
|
|
|
filter_prop_name, filter->operation_name, g_type_name (pspec->value_type));
|
|
|
|
valid_prop_value = FALSE;
|
2024-01-04 02:57:08 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2024-01-23 00:49:10 +08:00
|
|
|
if (valid_prop_value)
|
|
|
|
gegl_node_set_property (filter->operation, filter_prop_name,
|
|
|
|
&filter_prop_value);
|
2024-01-04 02:57:08 +08:00
|
|
|
else
|
2024-01-23 00:49:10 +08:00
|
|
|
xcf_seek_pos (info, next_prop, NULL);
|
2024-01-04 02:57:08 +08:00
|
|
|
|
|
|
|
g_value_unset (&filter_prop_value);
|
|
|
|
g_free (filter_prop_name);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2023-06-19 22:54:21 +08:00
|
|
|
default:
|
|
|
|
#ifdef GIMP_UNSTABLE
|
|
|
|
g_printerr ("unexpected/unknown effect property: %d (skipping)\n",
|
|
|
|
prop_type);
|
|
|
|
#endif
|
|
|
|
if (! xcf_skip_unknown_prop (info, prop_size))
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2022-10-20 06:11:34 +08:00
|
|
|
static gboolean
|
|
|
|
xcf_load_vectors_props (XcfInfo *info,
|
|
|
|
GimpImage *image,
|
|
|
|
GimpVectors **vectors)
|
|
|
|
{
|
|
|
|
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_SELECTED_PATH:
|
|
|
|
info->selected_vectors = g_list_prepend (info->selected_vectors, *vectors);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PROP_VISIBLE:
|
|
|
|
{
|
|
|
|
gboolean visible;
|
|
|
|
|
|
|
|
xcf_read_int32 (info, (guint32 *) &visible, 1);
|
|
|
|
|
|
|
|
gimp_item_set_visible (GIMP_ITEM (*vectors), visible, FALSE);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PROP_COLOR_TAG:
|
|
|
|
{
|
|
|
|
GimpColorTag color_tag;
|
|
|
|
|
|
|
|
xcf_read_int32 (info, (guint32 *) &color_tag, 1);
|
|
|
|
|
|
|
|
gimp_item_set_color_tag (GIMP_ITEM (*vectors), color_tag, FALSE);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PROP_LOCK_CONTENT:
|
|
|
|
{
|
|
|
|
gboolean lock_content;
|
|
|
|
|
|
|
|
xcf_read_int32 (info, (guint32 *) &lock_content, 1);
|
|
|
|
|
|
|
|
if (gimp_item_can_lock_content (GIMP_ITEM (*vectors)))
|
|
|
|
gimp_item_set_lock_content (GIMP_ITEM (*vectors),
|
|
|
|
lock_content, FALSE);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PROP_LOCK_POSITION:
|
|
|
|
{
|
|
|
|
gboolean lock_position;
|
|
|
|
|
|
|
|
xcf_read_int32 (info, (guint32 *) &lock_position, 1);
|
|
|
|
|
|
|
|
if (gimp_item_can_lock_position (GIMP_ITEM (*vectors)))
|
|
|
|
gimp_item_set_lock_position (GIMP_ITEM (*vectors),
|
|
|
|
lock_position, FALSE);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PROP_LOCK_VISIBILITY:
|
|
|
|
{
|
|
|
|
gboolean lock_visibility;
|
|
|
|
|
|
|
|
xcf_read_int32 (info, (guint32 *) &lock_visibility, 1);
|
|
|
|
|
|
|
|
if (gimp_item_can_lock_visibility (GIMP_ITEM (*vectors)))
|
|
|
|
gimp_item_set_lock_visibility (GIMP_ITEM (*vectors),
|
|
|
|
lock_visibility, FALSE);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PROP_TATTOO:
|
|
|
|
{
|
|
|
|
GimpTattoo tattoo;
|
|
|
|
|
|
|
|
xcf_read_int32 (info, (guint32 *) &tattoo, 1);
|
|
|
|
|
|
|
|
gimp_item_set_tattoo (GIMP_ITEM (*vectors), tattoo);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PROP_PARASITES:
|
|
|
|
{
|
|
|
|
goffset base = info->cp;
|
|
|
|
|
|
|
|
while ((info->cp - base) < prop_size)
|
|
|
|
{
|
|
|
|
GimpParasite *p = xcf_load_parasite (info);
|
|
|
|
GError *error = NULL;
|
|
|
|
|
|
|
|
if (! p)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
if (! gimp_item_parasite_validate (GIMP_ITEM (*vectors), p,
|
|
|
|
&error))
|
|
|
|
{
|
|
|
|
gimp_message (info->gimp, G_OBJECT (info->progress),
|
|
|
|
GIMP_MESSAGE_WARNING,
|
|
|
|
"Warning, invalid path parasite in XCF file: %s",
|
|
|
|
error->message);
|
|
|
|
g_clear_error (&error);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
gimp_item_parasite_attach (GIMP_ITEM (*vectors), p, FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
gimp_parasite_free (p);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (info->cp - base != prop_size)
|
|
|
|
gimp_message_literal (info->gimp, G_OBJECT (info->progress),
|
|
|
|
GIMP_MESSAGE_WARNING,
|
|
|
|
"Error while loading a path's parasites");
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
case PROP_ITEM_SET_ITEM:
|
|
|
|
{
|
|
|
|
GimpItemList *set;
|
|
|
|
guint32 n;
|
|
|
|
|
|
|
|
xcf_read_int32 (info, &n, 1);
|
|
|
|
set = g_list_nth_data (info->vectors_sets, n);
|
|
|
|
if (set == NULL)
|
|
|
|
g_printerr ("xcf: unknown path set: %d (skipping)\n", n);
|
|
|
|
else if (! g_type_is_a (G_TYPE_FROM_INSTANCE (*vectors),
|
|
|
|
gimp_item_list_get_item_type (set)))
|
|
|
|
g_printerr ("xcf: path '%s' cannot be added to item set '%s' with item type %s (skipping)\n",
|
|
|
|
gimp_object_get_name (*vectors), gimp_object_get_name (set),
|
|
|
|
g_type_name (gimp_item_list_get_item_type (set)));
|
|
|
|
else
|
|
|
|
gimp_item_list_add (set, GIMP_ITEM (*vectors));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
default:
|
|
|
|
#ifdef GIMP_UNSTABLE
|
|
|
|
g_printerr ("unexpected/unknown path property: %d (skipping)\n",
|
|
|
|
prop_type);
|
|
|
|
#endif
|
|
|
|
if (! xcf_skip_unknown_prop (info, prop_size))
|
|
|
|
return FALSE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2001-07-04 02:38:56 +08:00
|
|
|
static gboolean
|
|
|
|
xcf_load_prop (XcfInfo *info,
|
2004-11-02 02:33:09 +08:00
|
|
|
PropType *prop_type,
|
|
|
|
guint32 *prop_size)
|
2001-07-04 02:38:56 +08:00
|
|
|
{
|
2017-03-23 19:24:38 +08:00
|
|
|
if (G_UNLIKELY (xcf_read_int32 (info, (guint32 *) prop_type, 1) != 4))
|
2007-07-05 23:12:43 +08:00
|
|
|
return FALSE;
|
|
|
|
|
2017-03-23 19:24:38 +08:00
|
|
|
if (G_UNLIKELY (xcf_read_int32 (info, (guint32 *) prop_size, 1) != 4))
|
2007-07-05 23:12:43 +08:00
|
|
|
return FALSE;
|
|
|
|
|
2022-04-05 05:33:29 +08:00
|
|
|
GIMP_LOG (XCF, "prop type=%d size=%u", *prop_type, *prop_size);
|
|
|
|
|
2001-07-04 02:38:56 +08:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static GimpLayer *
|
2009-08-31 03:28:59 +08:00
|
|
|
xcf_load_layer (XcfInfo *info,
|
|
|
|
GimpImage *image,
|
|
|
|
GList **item_path)
|
2001-07-04 02:38:56 +08:00
|
|
|
{
|
2017-01-04 02:36:22 +08:00
|
|
|
GimpLayer *layer;
|
|
|
|
GimpLayerMask *layer_mask;
|
2017-03-23 18:44:41 +08:00
|
|
|
goffset hierarchy_offset;
|
2023-06-19 22:54:21 +08:00
|
|
|
goffset effects_offset = 0;
|
2017-03-23 18:44:41 +08:00
|
|
|
goffset layer_mask_offset;
|
2017-01-04 02:36:22 +08:00
|
|
|
gboolean apply_mask = TRUE;
|
|
|
|
gboolean edit_mask = FALSE;
|
|
|
|
gboolean show_mask = FALSE;
|
2020-04-15 03:36:58 +08:00
|
|
|
GList *selected;
|
2022-01-07 04:03:51 +08:00
|
|
|
GList *linked;
|
2024-01-04 02:57:08 +08:00
|
|
|
GList *filter_data_list = NULL;
|
2017-01-04 02:36:22 +08:00
|
|
|
gboolean floating;
|
|
|
|
guint32 group_layer_flags = 0;
|
|
|
|
guint32 text_layer_flags = 0;
|
2024-01-04 02:57:08 +08:00
|
|
|
gint filter_count = 0;
|
2017-01-04 02:36:22 +08:00
|
|
|
gint width;
|
|
|
|
gint height;
|
|
|
|
gint type;
|
|
|
|
GimpImageBaseType base_type;
|
|
|
|
gboolean has_alpha;
|
|
|
|
const Babl *format;
|
|
|
|
gboolean is_fs_drawable;
|
|
|
|
gchar *name;
|
2022-06-06 06:44:45 +08:00
|
|
|
goffset cur_offset;
|
2001-07-04 02:38:56 +08:00
|
|
|
|
|
|
|
/* check and see if this is the drawable the floating selection
|
2004-01-26 23:34:47 +08:00
|
|
|
* is attached to. if it is then we'll do the attachment in our caller.
|
2001-07-04 02:38:56 +08:00
|
|
|
*/
|
2004-01-26 23:34:47 +08:00
|
|
|
is_fs_drawable = (info->cp == info->floating_sel_offset);
|
2001-07-04 02:38:56 +08:00
|
|
|
|
|
|
|
/* read in the layer width, height, type and name */
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_int32 (info, (guint32 *) &width, 1);
|
|
|
|
xcf_read_int32 (info, (guint32 *) &height, 1);
|
|
|
|
xcf_read_int32 (info, (guint32 *) &type, 1);
|
|
|
|
xcf_read_string (info, &name, 1);
|
2001-07-04 02:38:56 +08:00
|
|
|
|
2014-07-10 04:36:55 +08:00
|
|
|
GIMP_LOG (XCF, "width=%d, height=%d, type=%d, name='%s'",
|
|
|
|
width, height, type, name);
|
|
|
|
|
2012-05-11 20:22:50 +08:00
|
|
|
switch (type)
|
|
|
|
{
|
|
|
|
case GIMP_RGB_IMAGE:
|
|
|
|
base_type = GIMP_RGB;
|
|
|
|
has_alpha = FALSE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GIMP_RGBA_IMAGE:
|
|
|
|
base_type = GIMP_RGB;
|
|
|
|
has_alpha = TRUE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GIMP_GRAY_IMAGE:
|
|
|
|
base_type = GIMP_GRAY;
|
|
|
|
has_alpha = FALSE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GIMP_GRAYA_IMAGE:
|
|
|
|
base_type = GIMP_GRAY;
|
|
|
|
has_alpha = TRUE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GIMP_INDEXED_IMAGE:
|
|
|
|
base_type = GIMP_INDEXED;
|
|
|
|
has_alpha = FALSE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GIMP_INDEXEDA_IMAGE:
|
|
|
|
base_type = GIMP_INDEXED;
|
|
|
|
has_alpha = TRUE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2021-08-28 00:10:08 +08:00
|
|
|
g_free (name);
|
2012-05-11 20:22:50 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2022-06-06 04:48:10 +08:00
|
|
|
if (width <= 0 || height <= 0 ||
|
|
|
|
width > GIMP_MAX_IMAGE_SIZE || height > GIMP_MAX_IMAGE_SIZE)
|
2019-07-11 21:04:30 +08:00
|
|
|
{
|
2019-07-11 22:32:45 +08:00
|
|
|
gboolean is_group_layer = FALSE;
|
|
|
|
gboolean is_text_layer = FALSE;
|
|
|
|
goffset saved_pos;
|
|
|
|
|
|
|
|
saved_pos = info->cp;
|
|
|
|
/* Load item path and check if this is a group or text layer. */
|
|
|
|
xcf_check_layer_props (info, item_path, &is_group_layer, &is_text_layer);
|
|
|
|
if ((is_text_layer || is_group_layer) &&
|
|
|
|
xcf_seek_pos (info, saved_pos, NULL))
|
|
|
|
{
|
|
|
|
/* Something is wrong, but leave a chance to the layer because
|
|
|
|
* anyway group and text layer depends on their contents.
|
|
|
|
*/
|
|
|
|
width = height = 1;
|
|
|
|
g_clear_pointer (item_path, g_list_free);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-08-28 00:10:08 +08:00
|
|
|
g_free (name);
|
2019-07-11 22:32:45 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
2019-07-11 21:04:30 +08:00
|
|
|
}
|
2014-06-07 22:33:17 +08:00
|
|
|
|
Initial space invasion commit in GIMP
All babl formats now have a space equivalent to a color profile,
determining the format's primaries and TRCs. This commit makes GIMP
aware of this.
libgimp:
- enum GimpPrecision: rename GAMMA values to NON_LINEAR and keep GAMMA
as deprecated aliases, add PERCEPTUAL values so we now have LINEAR,
NON_LINEAR and PERCPTUAL for each encoding, matching the babl
encoding variants RGB, R'G'B' and R~G~B~.
- gimp_color_transform_can_gegl_copy() now returns TRUE if both
profiles can return a babl space, increasing the amount of fast babl
color conversions significantly.
- TODO: no solution yet for getting libgimp drawable proxy buffers in
the right format with space.
plug-ins:
- follow the GimpPrecision change.
- TODO: everything else unchanged and partly broken or sub-optimal,
like setting a new image's color profile too late.
app:
- add enum GimpTRCType { LINEAR, NON_LINEAR, PERCEPTUAL } as
replacement for all "linear" booleans.
- change gimp-babl functions to take babl spaces and GimpTRCType
parameters and support all sorts of new perceptual ~ formats.
- a lot of places changed in the early days of goat invasion didn't
take advantage of gimp-babl utility functions and constructed
formats manually. They all needed revisiting and many now use much
simpler code calling gimp-babl API.
- change gimp_babl_format_get_color_profile() to really extract a
newly allocated color profile from the format, and add
gimp_babl_get_builtin_color_profile() which does the same as
gimp_babl_format_get_color_profile() did before. Visited all callers
to decide whether they are looking for the format's actual profile,
or for one of the builtin profiles, simplifying code that only needs
builtin profiles.
- drawables have a new get_space_api(), get_linear() is now get_trc().
- images now have a "layer space" and an API to get it,
gimp_image_get_layer_format() returns formats in that space.
- an image's layer space is created from the image's color profile,
change gimpimage-color-profile to deal with that correctly
- change many babl_format() calls to babl_format_with_space() and take
the space from passed formats or drawables
- add function gimp_layer_fix_format_space() which replaces the
layer's buffer with one that has the image's layer format, but
doesn't change pixel values
- use gimp_layer_fix_format_space() to make sure layers loaded from
XCF and created by plug-ins have the right space when added to the
image, because it's impossible to always assign the right space upon
layer creation
- "assign color profile" and "discard color profile" now require use
of gimp_layer_fix_format_space() too because the profile is now
embedded in all formats via the space. Add
gimp_image_assign_color_profile() which does all that and call it
instead of a simple gimp_image_set_color_profile(), also from the
PDB set-color-profile functions, which are essentially "assign" and
"discard" calls.
- generally, make sure a new image's color profile is set before
adding layers to it, gimp_image_set_color_profile() is more than
before considered know-what-you-are-doing API.
- take special precaution in all places that call
gimp_drawable_convert_type(), we now must pass a new_profile from
all callers that convert layers within the same image (such as
image_convert_type, image_convert_precision), because the layer's
new space can't be determined from the image's layer format during
the call.
- change all "linear" properties to "trc", in all config objects like
for levels and curves, in the histogram, in the widgets. This results
in some GUI that now has three choices instead of two.
TODO: we might want to reduce that back to two later.
- keep "linear" boolean properties around as compat if needed for file
pasring, but always convert the parsed parsed boolean to
GimpTRCType.
- TODO: the image's "enable color management" switch is currently
broken, will fix that in another commit.
2018-07-21 20:23:01 +08:00
|
|
|
if (base_type == GIMP_GRAY)
|
|
|
|
{
|
|
|
|
/* do not use gimp_image_get_layer_format() because it might
|
|
|
|
* be the floating selection of a channel or mask
|
|
|
|
*/
|
|
|
|
format = gimp_image_get_format (image, base_type,
|
|
|
|
gimp_image_get_precision (image),
|
|
|
|
has_alpha,
|
|
|
|
NULL /* we will fix the space later */);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
format = gimp_image_get_layer_format (image, has_alpha);
|
|
|
|
}
|
2012-04-12 00:40:30 +08:00
|
|
|
|
2001-07-04 02:38:56 +08:00
|
|
|
/* create a new layer */
|
2006-03-29 01:08:36 +08:00
|
|
|
layer = gimp_layer_new (image, width, height,
|
2018-07-16 20:47:10 +08:00
|
|
|
format, name,
|
|
|
|
GIMP_OPACITY_OPAQUE, GIMP_LAYER_MODE_NORMAL);
|
2001-07-04 02:38:56 +08:00
|
|
|
g_free (name);
|
2003-06-24 21:59:36 +08:00
|
|
|
if (! layer)
|
2001-07-04 02:38:56 +08:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* read in the layer properties */
|
2009-08-31 03:28:59 +08:00
|
|
|
if (! xcf_load_layer_props (info, image, &layer, item_path,
|
2004-03-18 23:27:23 +08:00
|
|
|
&apply_mask, &edit_mask, &show_mask,
|
2011-09-26 03:57:20 +08:00
|
|
|
&text_layer_flags, &group_layer_flags))
|
2001-07-04 02:38:56 +08:00
|
|
|
goto error;
|
|
|
|
|
2014-07-10 04:36:55 +08:00
|
|
|
GIMP_LOG (XCF, "layer props loaded");
|
|
|
|
|
2006-07-12 04:21:18 +08:00
|
|
|
xcf_progress_update (info);
|
|
|
|
|
2003-10-28 05:50:41 +08:00
|
|
|
/* call the evil text layer hack that might change our layer pointer */
|
2020-04-15 03:36:58 +08:00
|
|
|
selected = g_list_find (info->selected_layers, layer);
|
2022-01-07 04:03:51 +08:00
|
|
|
linked = g_list_find (info->linked_layers, layer);
|
2003-10-28 05:50:41 +08:00
|
|
|
floating = (info->floating_sel == layer);
|
2003-06-24 21:59:36 +08:00
|
|
|
|
2003-10-28 05:50:41 +08:00
|
|
|
if (gimp_text_layer_xcf_load_hack (&layer))
|
2003-06-25 03:45:55 +08:00
|
|
|
{
|
2004-03-18 23:27:23 +08:00
|
|
|
gimp_text_layer_set_xcf_flags (GIMP_TEXT_LAYER (layer),
|
|
|
|
text_layer_flags);
|
|
|
|
|
2020-04-15 03:36:58 +08:00
|
|
|
if (selected)
|
|
|
|
{
|
|
|
|
info->selected_layers = g_list_delete_link (info->selected_layers, selected);
|
|
|
|
info->selected_layers = g_list_prepend (info->selected_layers, layer);
|
|
|
|
}
|
2022-01-07 04:03:51 +08:00
|
|
|
if (linked)
|
|
|
|
{
|
|
|
|
info->linked_layers = g_list_delete_link (info->linked_layers, linked);
|
|
|
|
info->linked_layers = g_list_prepend (info->linked_layers, layer);
|
|
|
|
}
|
2003-07-08 18:38:13 +08:00
|
|
|
if (floating)
|
|
|
|
info->floating_sel = layer;
|
2003-06-25 03:45:55 +08:00
|
|
|
}
|
2003-06-24 21:59:36 +08:00
|
|
|
|
Initial space invasion commit in GIMP
All babl formats now have a space equivalent to a color profile,
determining the format's primaries and TRCs. This commit makes GIMP
aware of this.
libgimp:
- enum GimpPrecision: rename GAMMA values to NON_LINEAR and keep GAMMA
as deprecated aliases, add PERCEPTUAL values so we now have LINEAR,
NON_LINEAR and PERCPTUAL for each encoding, matching the babl
encoding variants RGB, R'G'B' and R~G~B~.
- gimp_color_transform_can_gegl_copy() now returns TRUE if both
profiles can return a babl space, increasing the amount of fast babl
color conversions significantly.
- TODO: no solution yet for getting libgimp drawable proxy buffers in
the right format with space.
plug-ins:
- follow the GimpPrecision change.
- TODO: everything else unchanged and partly broken or sub-optimal,
like setting a new image's color profile too late.
app:
- add enum GimpTRCType { LINEAR, NON_LINEAR, PERCEPTUAL } as
replacement for all "linear" booleans.
- change gimp-babl functions to take babl spaces and GimpTRCType
parameters and support all sorts of new perceptual ~ formats.
- a lot of places changed in the early days of goat invasion didn't
take advantage of gimp-babl utility functions and constructed
formats manually. They all needed revisiting and many now use much
simpler code calling gimp-babl API.
- change gimp_babl_format_get_color_profile() to really extract a
newly allocated color profile from the format, and add
gimp_babl_get_builtin_color_profile() which does the same as
gimp_babl_format_get_color_profile() did before. Visited all callers
to decide whether they are looking for the format's actual profile,
or for one of the builtin profiles, simplifying code that only needs
builtin profiles.
- drawables have a new get_space_api(), get_linear() is now get_trc().
- images now have a "layer space" and an API to get it,
gimp_image_get_layer_format() returns formats in that space.
- an image's layer space is created from the image's color profile,
change gimpimage-color-profile to deal with that correctly
- change many babl_format() calls to babl_format_with_space() and take
the space from passed formats or drawables
- add function gimp_layer_fix_format_space() which replaces the
layer's buffer with one that has the image's layer format, but
doesn't change pixel values
- use gimp_layer_fix_format_space() to make sure layers loaded from
XCF and created by plug-ins have the right space when added to the
image, because it's impossible to always assign the right space upon
layer creation
- "assign color profile" and "discard color profile" now require use
of gimp_layer_fix_format_space() too because the profile is now
embedded in all formats via the space. Add
gimp_image_assign_color_profile() which does all that and call it
instead of a simple gimp_image_set_color_profile(), also from the
PDB set-color-profile functions, which are essentially "assign" and
"discard" calls.
- generally, make sure a new image's color profile is set before
adding layers to it, gimp_image_set_color_profile() is more than
before considered know-what-you-are-doing API.
- take special precaution in all places that call
gimp_drawable_convert_type(), we now must pass a new_profile from
all callers that convert layers within the same image (such as
image_convert_type, image_convert_precision), because the layer's
new space can't be determined from the image's layer format during
the call.
- change all "linear" properties to "trc", in all config objects like
for levels and curves, in the histogram, in the widgets. This results
in some GUI that now has three choices instead of two.
TODO: we might want to reduce that back to two later.
- keep "linear" boolean properties around as compat if needed for file
pasring, but always convert the parsed parsed boolean to
GimpTRCType.
- TODO: the image's "enable color management" switch is currently
broken, will fix that in another commit.
2018-07-21 20:23:01 +08:00
|
|
|
/* if this is not the floating selection, we can fix the layer's
|
|
|
|
* space already now, the function will do nothing if we already
|
|
|
|
* created the layer with the right format
|
|
|
|
*/
|
|
|
|
if (! floating && base_type == GIMP_GRAY)
|
|
|
|
gimp_layer_fix_format_space (layer, FALSE, FALSE);
|
|
|
|
|
2001-07-04 02:38:56 +08:00
|
|
|
/* read the hierarchy and layer mask offsets */
|
2022-06-06 06:44:45 +08:00
|
|
|
cur_offset = info->cp;
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_offset (info, &hierarchy_offset, 1);
|
|
|
|
xcf_read_offset (info, &layer_mask_offset, 1);
|
2023-06-19 22:54:21 +08:00
|
|
|
if (info->file_version >= 20)
|
|
|
|
xcf_read_offset (info, &effects_offset, 1);
|
2001-07-04 02:38:56 +08:00
|
|
|
|
2009-08-31 03:28:59 +08:00
|
|
|
/* read in the hierarchy (ignore it for group layers, both as an
|
|
|
|
* optimization and because the hierarchy's extents don't match
|
|
|
|
* the group layer's tiles)
|
|
|
|
*/
|
|
|
|
if (! gimp_viewable_get_children (GIMP_VIEWABLE (layer)))
|
|
|
|
{
|
2022-06-06 06:44:45 +08:00
|
|
|
if (hierarchy_offset < cur_offset)
|
|
|
|
{
|
|
|
|
GIMP_LOG (XCF, "Invalid layer hierarchy offset!");
|
|
|
|
goto error;
|
|
|
|
}
|
2009-08-31 03:28:59 +08:00
|
|
|
if (! xcf_seek_pos (info, hierarchy_offset, NULL))
|
|
|
|
goto error;
|
2002-12-20 14:26:34 +08:00
|
|
|
|
2014-07-10 04:36:55 +08:00
|
|
|
GIMP_LOG (XCF, "loading buffer");
|
|
|
|
|
2012-04-03 05:32:38 +08:00
|
|
|
if (! xcf_load_buffer (info,
|
|
|
|
gimp_drawable_get_buffer (GIMP_DRAWABLE (layer))))
|
2009-08-31 03:28:59 +08:00
|
|
|
goto error;
|
2001-07-04 02:38:56 +08:00
|
|
|
|
2014-07-10 04:36:55 +08:00
|
|
|
GIMP_LOG (XCF, "buffer loaded");
|
|
|
|
|
2009-08-31 03:28:59 +08:00
|
|
|
xcf_progress_update (info);
|
|
|
|
}
|
2011-09-26 03:57:20 +08:00
|
|
|
else
|
|
|
|
{
|
2011-10-08 02:05:52 +08:00
|
|
|
gboolean expanded = group_layer_flags & XCF_GROUP_ITEM_EXPANDED;
|
|
|
|
|
|
|
|
gimp_viewable_set_expanded (GIMP_VIEWABLE (layer), expanded);
|
2011-09-26 03:57:20 +08:00
|
|
|
}
|
2006-07-12 04:21:18 +08:00
|
|
|
|
2023-06-19 22:54:21 +08:00
|
|
|
cur_offset += info->bytes_per_offset;
|
|
|
|
|
2001-07-04 02:38:56 +08:00
|
|
|
/* read in the layer mask */
|
|
|
|
if (layer_mask_offset != 0)
|
|
|
|
{
|
2022-06-06 06:44:45 +08:00
|
|
|
if (layer_mask_offset < cur_offset)
|
|
|
|
{
|
|
|
|
GIMP_LOG (XCF, "Invalid layer mask offset!");
|
|
|
|
goto error;
|
|
|
|
}
|
2002-12-20 14:26:34 +08:00
|
|
|
if (! xcf_seek_pos (info, layer_mask_offset, NULL))
|
|
|
|
goto error;
|
2001-07-04 02:38:56 +08:00
|
|
|
|
2006-03-29 01:08:36 +08:00
|
|
|
layer_mask = xcf_load_layer_mask (info, image);
|
2003-06-24 21:59:36 +08:00
|
|
|
if (! layer_mask)
|
2004-11-02 02:33:09 +08:00
|
|
|
goto error;
|
2001-07-04 02:38:56 +08:00
|
|
|
|
2023-06-19 22:54:21 +08:00
|
|
|
cur_offset += info->bytes_per_offset;
|
|
|
|
|
2006-07-12 04:21:18 +08:00
|
|
|
xcf_progress_update (info);
|
|
|
|
|
2009-09-04 01:11:02 +08:00
|
|
|
/* don't add the layer mask yet, that won't work for group
|
|
|
|
* layers which update their size automatically; instead
|
|
|
|
* attach it so it can be added when all layers are loaded
|
|
|
|
*/
|
|
|
|
g_object_set_data_full (G_OBJECT (layer), "gimp-layer-mask",
|
2011-03-03 17:57:38 +08:00
|
|
|
g_object_ref_sink (layer_mask),
|
2009-09-04 01:11:02 +08:00
|
|
|
(GDestroyNotify) g_object_unref);
|
2012-03-18 01:30:13 +08:00
|
|
|
g_object_set_data (G_OBJECT (layer), "gimp-layer-mask-apply",
|
|
|
|
GINT_TO_POINTER (apply_mask));
|
|
|
|
g_object_set_data (G_OBJECT (layer), "gimp-layer-mask-edit",
|
|
|
|
GINT_TO_POINTER (edit_mask));
|
|
|
|
g_object_set_data (G_OBJECT (layer), "gimp-layer-mask-show",
|
|
|
|
GINT_TO_POINTER (show_mask));
|
2001-07-04 02:38:56 +08:00
|
|
|
}
|
2024-01-04 02:57:08 +08:00
|
|
|
else
|
|
|
|
{
|
|
|
|
/* If no layer mask, move ahead to layer effects */
|
|
|
|
cur_offset += info->bytes_per_offset;
|
|
|
|
}
|
2001-07-04 02:38:56 +08:00
|
|
|
|
2023-06-19 22:54:21 +08:00
|
|
|
/* read in any layer effects and effect masks */
|
app: a few fixups to MR !958 (initial non-destructive editing).
- Do not leak allocated return value of gegl_node_to_xml_full().
- When merging layer effects, use gimp_drawable_filter_commit(), making
sure we use the exact same code path as when applying layer effects
destructively from the start. This also ensures that filters are
properly removed from the filter stack (unlike
gimp_drawable_merge_filter()), which was the reason why the rendering
was wrong (hence getting the buffer without effects first, then
reapplying it after was only a workaround to an actual bug).
- When removing a filter, verify the object still exists before doing
anything with it. If this was the last reference, we don't want to
call functions on this object. In gimp_drawable_filter_commit(), we
set up a weak pointer. In gimp_drawable_filter_remove_filter() itself,
we save the pointer to the filter's drawable before actual removal (as
we don't want to dereference a freed object later on).
- export_merge_layer_effects() should merge filters recursively through
layer groups.
- clean up the XCF code:
* No need to wrap the effect pointers list into 2 zero offset. Only
have one zero offset to indicate the list end.
* Add the layer effect mask in the effect structure (not in the layer
structure), similar as for layer masks.
* Effect name and icon made as main data in the structure, not as
properties.
2024-01-03 00:12:35 +08:00
|
|
|
while (effects_offset != 0)
|
2023-06-19 22:54:21 +08:00
|
|
|
{
|
app: a few fixups to MR !958 (initial non-destructive editing).
- Do not leak allocated return value of gegl_node_to_xml_full().
- When merging layer effects, use gimp_drawable_filter_commit(), making
sure we use the exact same code path as when applying layer effects
destructively from the start. This also ensures that filters are
properly removed from the filter stack (unlike
gimp_drawable_merge_filter()), which was the reason why the rendering
was wrong (hence getting the buffer without effects first, then
reapplying it after was only a workaround to an actual bug).
- When removing a filter, verify the object still exists before doing
anything with it. If this was the last reference, we don't want to
call functions on this object. In gimp_drawable_filter_commit(), we
set up a weak pointer. In gimp_drawable_filter_remove_filter() itself,
we save the pointer to the filter's drawable before actual removal (as
we don't want to dereference a freed object later on).
- export_merge_layer_effects() should merge filters recursively through
layer groups.
- clean up the XCF code:
* No need to wrap the effect pointers list into 2 zero offset. Only
have one zero offset to indicate the list end.
* Add the layer effect mask in the effect structure (not in the layer
structure), similar as for layer masks.
* Effect name and icon made as main data in the structure, not as
properties.
2024-01-03 00:12:35 +08:00
|
|
|
FilterData *filter_data;
|
2023-06-19 22:54:21 +08:00
|
|
|
|
app: a few fixups to MR !958 (initial non-destructive editing).
- Do not leak allocated return value of gegl_node_to_xml_full().
- When merging layer effects, use gimp_drawable_filter_commit(), making
sure we use the exact same code path as when applying layer effects
destructively from the start. This also ensures that filters are
properly removed from the filter stack (unlike
gimp_drawable_merge_filter()), which was the reason why the rendering
was wrong (hence getting the buffer without effects first, then
reapplying it after was only a workaround to an actual bug).
- When removing a filter, verify the object still exists before doing
anything with it. If this was the last reference, we don't want to
call functions on this object. In gimp_drawable_filter_commit(), we
set up a weak pointer. In gimp_drawable_filter_remove_filter() itself,
we save the pointer to the filter's drawable before actual removal (as
we don't want to dereference a freed object later on).
- export_merge_layer_effects() should merge filters recursively through
layer groups.
- clean up the XCF code:
* No need to wrap the effect pointers list into 2 zero offset. Only
have one zero offset to indicate the list end.
* Add the layer effect mask in the effect structure (not in the layer
structure), similar as for layer masks.
* Effect name and icon made as main data in the structure, not as
properties.
2024-01-03 00:12:35 +08:00
|
|
|
if (effects_offset < cur_offset)
|
2023-06-19 22:54:21 +08:00
|
|
|
{
|
app: a few fixups to MR !958 (initial non-destructive editing).
- Do not leak allocated return value of gegl_node_to_xml_full().
- When merging layer effects, use gimp_drawable_filter_commit(), making
sure we use the exact same code path as when applying layer effects
destructively from the start. This also ensures that filters are
properly removed from the filter stack (unlike
gimp_drawable_merge_filter()), which was the reason why the rendering
was wrong (hence getting the buffer without effects first, then
reapplying it after was only a workaround to an actual bug).
- When removing a filter, verify the object still exists before doing
anything with it. If this was the last reference, we don't want to
call functions on this object. In gimp_drawable_filter_commit(), we
set up a weak pointer. In gimp_drawable_filter_remove_filter() itself,
we save the pointer to the filter's drawable before actual removal (as
we don't want to dereference a freed object later on).
- export_merge_layer_effects() should merge filters recursively through
layer groups.
- clean up the XCF code:
* No need to wrap the effect pointers list into 2 zero offset. Only
have one zero offset to indicate the list end.
* Add the layer effect mask in the effect structure (not in the layer
structure), similar as for layer masks.
* Effect name and icon made as main data in the structure, not as
properties.
2024-01-03 00:12:35 +08:00
|
|
|
GIMP_LOG (XCF, "Invalid effect offset: %" G_GOFFSET_FORMAT
|
|
|
|
" at offset: %" G_GOFFSET_FORMAT, effects_offset, cur_offset);
|
|
|
|
goto error;
|
|
|
|
}
|
2023-06-19 22:54:21 +08:00
|
|
|
|
app: a few fixups to MR !958 (initial non-destructive editing).
- Do not leak allocated return value of gegl_node_to_xml_full().
- When merging layer effects, use gimp_drawable_filter_commit(), making
sure we use the exact same code path as when applying layer effects
destructively from the start. This also ensures that filters are
properly removed from the filter stack (unlike
gimp_drawable_merge_filter()), which was the reason why the rendering
was wrong (hence getting the buffer without effects first, then
reapplying it after was only a workaround to an actual bug).
- When removing a filter, verify the object still exists before doing
anything with it. If this was the last reference, we don't want to
call functions on this object. In gimp_drawable_filter_commit(), we
set up a weak pointer. In gimp_drawable_filter_remove_filter() itself,
we save the pointer to the filter's drawable before actual removal (as
we don't want to dereference a freed object later on).
- export_merge_layer_effects() should merge filters recursively through
layer groups.
- clean up the XCF code:
* No need to wrap the effect pointers list into 2 zero offset. Only
have one zero offset to indicate the list end.
* Add the layer effect mask in the effect structure (not in the layer
structure), similar as for layer masks.
* Effect name and icon made as main data in the structure, not as
properties.
2024-01-03 00:12:35 +08:00
|
|
|
/* seek to the effect offset */
|
|
|
|
if (! xcf_seek_pos (info, effects_offset, NULL))
|
|
|
|
goto error;
|
2023-06-19 22:54:21 +08:00
|
|
|
|
app: a few fixups to MR !958 (initial non-destructive editing).
- Do not leak allocated return value of gegl_node_to_xml_full().
- When merging layer effects, use gimp_drawable_filter_commit(), making
sure we use the exact same code path as when applying layer effects
destructively from the start. This also ensures that filters are
properly removed from the filter stack (unlike
gimp_drawable_merge_filter()), which was the reason why the rendering
was wrong (hence getting the buffer without effects first, then
reapplying it after was only a workaround to an actual bug).
- When removing a filter, verify the object still exists before doing
anything with it. If this was the last reference, we don't want to
call functions on this object. In gimp_drawable_filter_commit(), we
set up a weak pointer. In gimp_drawable_filter_remove_filter() itself,
we save the pointer to the filter's drawable before actual removal (as
we don't want to dereference a freed object later on).
- export_merge_layer_effects() should merge filters recursively through
layer groups.
- clean up the XCF code:
* No need to wrap the effect pointers list into 2 zero offset. Only
have one zero offset to indicate the list end.
* Add the layer effect mask in the effect structure (not in the layer
structure), similar as for layer masks.
* Effect name and icon made as main data in the structure, not as
properties.
2024-01-03 00:12:35 +08:00
|
|
|
filter_data = xcf_load_effect (info, image, GIMP_DRAWABLE (layer));
|
|
|
|
if (! filter_data)
|
|
|
|
goto error;
|
2023-06-19 22:54:21 +08:00
|
|
|
|
app: a few fixups to MR !958 (initial non-destructive editing).
- Do not leak allocated return value of gegl_node_to_xml_full().
- When merging layer effects, use gimp_drawable_filter_commit(), making
sure we use the exact same code path as when applying layer effects
destructively from the start. This also ensures that filters are
properly removed from the filter stack (unlike
gimp_drawable_merge_filter()), which was the reason why the rendering
was wrong (hence getting the buffer without effects first, then
reapplying it after was only a workaround to an actual bug).
- When removing a filter, verify the object still exists before doing
anything with it. If this was the last reference, we don't want to
call functions on this object. In gimp_drawable_filter_commit(), we
set up a weak pointer. In gimp_drawable_filter_remove_filter() itself,
we save the pointer to the filter's drawable before actual removal (as
we don't want to dereference a freed object later on).
- export_merge_layer_effects() should merge filters recursively through
layer groups.
- clean up the XCF code:
* No need to wrap the effect pointers list into 2 zero offset. Only
have one zero offset to indicate the list end.
* Add the layer effect mask in the effect structure (not in the layer
structure), similar as for layer masks.
* Effect name and icon made as main data in the structure, not as
properties.
2024-01-03 00:12:35 +08:00
|
|
|
xcf_progress_update (info);
|
2023-06-19 22:54:21 +08:00
|
|
|
|
app: a few fixups to MR !958 (initial non-destructive editing).
- Do not leak allocated return value of gegl_node_to_xml_full().
- When merging layer effects, use gimp_drawable_filter_commit(), making
sure we use the exact same code path as when applying layer effects
destructively from the start. This also ensures that filters are
properly removed from the filter stack (unlike
gimp_drawable_merge_filter()), which was the reason why the rendering
was wrong (hence getting the buffer without effects first, then
reapplying it after was only a workaround to an actual bug).
- When removing a filter, verify the object still exists before doing
anything with it. If this was the last reference, we don't want to
call functions on this object. In gimp_drawable_filter_commit(), we
set up a weak pointer. In gimp_drawable_filter_remove_filter() itself,
we save the pointer to the filter's drawable before actual removal (as
we don't want to dereference a freed object later on).
- export_merge_layer_effects() should merge filters recursively through
layer groups.
- clean up the XCF code:
* No need to wrap the effect pointers list into 2 zero offset. Only
have one zero offset to indicate the list end.
* Add the layer effect mask in the effect structure (not in the layer
structure), similar as for layer masks.
* Effect name and icon made as main data in the structure, not as
properties.
2024-01-03 00:12:35 +08:00
|
|
|
filter_data_list = g_list_prepend (filter_data_list, filter_data);
|
|
|
|
filter_count++;
|
2023-06-19 22:54:21 +08:00
|
|
|
|
app: a few fixups to MR !958 (initial non-destructive editing).
- Do not leak allocated return value of gegl_node_to_xml_full().
- When merging layer effects, use gimp_drawable_filter_commit(), making
sure we use the exact same code path as when applying layer effects
destructively from the start. This also ensures that filters are
properly removed from the filter stack (unlike
gimp_drawable_merge_filter()), which was the reason why the rendering
was wrong (hence getting the buffer without effects first, then
reapplying it after was only a workaround to an actual bug).
- When removing a filter, verify the object still exists before doing
anything with it. If this was the last reference, we don't want to
call functions on this object. In gimp_drawable_filter_commit(), we
set up a weak pointer. In gimp_drawable_filter_remove_filter() itself,
we save the pointer to the filter's drawable before actual removal (as
we don't want to dereference a freed object later on).
- export_merge_layer_effects() should merge filters recursively through
layer groups.
- clean up the XCF code:
* No need to wrap the effect pointers list into 2 zero offset. Only
have one zero offset to indicate the list end.
* Add the layer effect mask in the effect structure (not in the layer
structure), similar as for layer masks.
* Effect name and icon made as main data in the structure, not as
properties.
2024-01-03 00:12:35 +08:00
|
|
|
xcf_progress_update (info);
|
2023-06-19 22:54:21 +08:00
|
|
|
|
app: a few fixups to MR !958 (initial non-destructive editing).
- Do not leak allocated return value of gegl_node_to_xml_full().
- When merging layer effects, use gimp_drawable_filter_commit(), making
sure we use the exact same code path as when applying layer effects
destructively from the start. This also ensures that filters are
properly removed from the filter stack (unlike
gimp_drawable_merge_filter()), which was the reason why the rendering
was wrong (hence getting the buffer without effects first, then
reapplying it after was only a workaround to an actual bug).
- When removing a filter, verify the object still exists before doing
anything with it. If this was the last reference, we don't want to
call functions on this object. In gimp_drawable_filter_commit(), we
set up a weak pointer. In gimp_drawable_filter_remove_filter() itself,
we save the pointer to the filter's drawable before actual removal (as
we don't want to dereference a freed object later on).
- export_merge_layer_effects() should merge filters recursively through
layer groups.
- clean up the XCF code:
* No need to wrap the effect pointers list into 2 zero offset. Only
have one zero offset to indicate the list end.
* Add the layer effect mask in the effect structure (not in the layer
structure), similar as for layer masks.
* Effect name and icon made as main data in the structure, not as
properties.
2024-01-03 00:12:35 +08:00
|
|
|
/* restore the saved position so we'll be ready to
|
|
|
|
* read the next offset.
|
|
|
|
*/
|
|
|
|
cur_offset += info->bytes_per_offset;
|
|
|
|
if (! xcf_seek_pos (info, cur_offset, NULL))
|
|
|
|
goto error;
|
2023-06-19 22:54:21 +08:00
|
|
|
|
app: a few fixups to MR !958 (initial non-destructive editing).
- Do not leak allocated return value of gegl_node_to_xml_full().
- When merging layer effects, use gimp_drawable_filter_commit(), making
sure we use the exact same code path as when applying layer effects
destructively from the start. This also ensures that filters are
properly removed from the filter stack (unlike
gimp_drawable_merge_filter()), which was the reason why the rendering
was wrong (hence getting the buffer without effects first, then
reapplying it after was only a workaround to an actual bug).
- When removing a filter, verify the object still exists before doing
anything with it. If this was the last reference, we don't want to
call functions on this object. In gimp_drawable_filter_commit(), we
set up a weak pointer. In gimp_drawable_filter_remove_filter() itself,
we save the pointer to the filter's drawable before actual removal (as
we don't want to dereference a freed object later on).
- export_merge_layer_effects() should merge filters recursively through
layer groups.
- clean up the XCF code:
* No need to wrap the effect pointers list into 2 zero offset. Only
have one zero offset to indicate the list end.
* Add the layer effect mask in the effect structure (not in the layer
structure), similar as for layer masks.
* Effect name and icon made as main data in the structure, not as
properties.
2024-01-03 00:12:35 +08:00
|
|
|
/* read in the offset of the next effect */
|
|
|
|
xcf_read_offset (info, &effects_offset, 1);
|
2023-06-19 22:54:21 +08:00
|
|
|
}
|
|
|
|
|
2024-01-04 02:57:08 +08:00
|
|
|
if (filter_count > 0)
|
|
|
|
g_object_set_data_full (G_OBJECT (layer), "gimp-layer-effects", filter_data_list,
|
|
|
|
(GDestroyNotify) xcf_load_free_effects);
|
|
|
|
|
2001-07-04 02:38:56 +08:00
|
|
|
/* attach the floating selection... */
|
2004-01-26 23:34:47 +08:00
|
|
|
if (is_fs_drawable)
|
|
|
|
info->floating_sel_drawable = GIMP_DRAWABLE (layer);
|
2001-07-04 02:38:56 +08:00
|
|
|
|
|
|
|
return layer;
|
|
|
|
|
|
|
|
error:
|
2020-04-15 03:36:58 +08:00
|
|
|
info->selected_layers = g_list_remove (info->selected_layers, layer);
|
2020-02-21 17:49:49 +08:00
|
|
|
|
|
|
|
if (info->floating_sel == layer)
|
|
|
|
info->floating_sel = NULL;
|
|
|
|
|
|
|
|
if (info->floating_sel_drawable == GIMP_DRAWABLE (layer))
|
|
|
|
info->floating_sel_drawable = NULL;
|
|
|
|
|
2003-01-06 06:07:10 +08:00
|
|
|
g_object_unref (layer);
|
2020-02-21 17:49:49 +08:00
|
|
|
|
2001-07-04 02:38:56 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static GimpChannel *
|
|
|
|
xcf_load_channel (XcfInfo *info,
|
2006-03-29 01:08:36 +08:00
|
|
|
GimpImage *image)
|
2001-07-04 02:38:56 +08:00
|
|
|
{
|
2017-01-04 02:36:22 +08:00
|
|
|
GimpChannel *channel;
|
2017-03-23 18:44:41 +08:00
|
|
|
goffset hierarchy_offset;
|
2017-01-04 02:36:22 +08:00
|
|
|
gint width;
|
|
|
|
gint height;
|
|
|
|
gboolean is_fs_drawable;
|
|
|
|
gchar *name;
|
2022-06-06 06:44:45 +08:00
|
|
|
goffset cur_offset;
|
2001-07-04 02:38:56 +08:00
|
|
|
|
|
|
|
/* check and see if this is the drawable the floating selection
|
2004-01-26 23:34:47 +08:00
|
|
|
* is attached to. if it is then we'll do the attachment in our caller.
|
2001-07-04 02:38:56 +08:00
|
|
|
*/
|
2004-01-26 23:34:47 +08:00
|
|
|
is_fs_drawable = (info->cp == info->floating_sel_offset);
|
2001-07-04 02:38:56 +08:00
|
|
|
|
|
|
|
/* read in the layer width, height and name */
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_int32 (info, (guint32 *) &width, 1);
|
|
|
|
xcf_read_int32 (info, (guint32 *) &height, 1);
|
2022-06-06 04:48:10 +08:00
|
|
|
if (width <= 0 || height <= 0 ||
|
|
|
|
width > GIMP_MAX_IMAGE_SIZE || height > GIMP_MAX_IMAGE_SIZE)
|
|
|
|
{
|
|
|
|
GIMP_LOG (XCF, "Invalid channel size %d x %d.", width, height);
|
|
|
|
return NULL;
|
|
|
|
}
|
2014-06-07 22:33:17 +08:00
|
|
|
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_string (info, &name, 1);
|
2022-06-06 04:48:10 +08:00
|
|
|
GIMP_LOG (XCF, "Channel width=%d, height=%d, name='%s'",
|
|
|
|
width, height, name);
|
2001-07-04 02:38:56 +08:00
|
|
|
|
|
|
|
/* create a new channel */
|
2023-11-20 07:20:03 +08:00
|
|
|
channel = gimp_channel_new (image, width, height, name, NULL);
|
2001-07-04 02:38:56 +08:00
|
|
|
g_free (name);
|
|
|
|
if (!channel)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* read in the channel properties */
|
2017-03-23 19:24:38 +08:00
|
|
|
if (! xcf_load_channel_props (info, image, &channel))
|
2001-07-04 02:38:56 +08:00
|
|
|
goto error;
|
|
|
|
|
2006-07-12 04:21:18 +08:00
|
|
|
xcf_progress_update (info);
|
|
|
|
|
2022-06-06 06:44:45 +08:00
|
|
|
/* read the hierarchy offset */
|
|
|
|
cur_offset = info->cp;
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_offset (info, &hierarchy_offset, 1);
|
2001-07-04 02:38:56 +08:00
|
|
|
|
2022-06-06 06:44:45 +08:00
|
|
|
if (hierarchy_offset < cur_offset)
|
|
|
|
{
|
|
|
|
GIMP_LOG (XCF, "Invalid hierarchy offset!");
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2001-07-04 02:38:56 +08:00
|
|
|
/* read in the hierarchy */
|
2017-03-23 19:24:38 +08:00
|
|
|
if (! xcf_seek_pos (info, hierarchy_offset, NULL))
|
2002-12-20 14:26:34 +08:00
|
|
|
goto error;
|
2003-09-01 07:46:05 +08:00
|
|
|
|
2017-03-23 19:24:38 +08:00
|
|
|
if (! xcf_load_buffer (info,
|
|
|
|
gimp_drawable_get_buffer (GIMP_DRAWABLE (channel))))
|
2001-07-04 02:38:56 +08:00
|
|
|
goto error;
|
|
|
|
|
2006-07-12 04:21:18 +08:00
|
|
|
xcf_progress_update (info);
|
|
|
|
|
2004-01-26 23:34:47 +08:00
|
|
|
if (is_fs_drawable)
|
|
|
|
info->floating_sel_drawable = GIMP_DRAWABLE (channel);
|
2001-07-04 02:38:56 +08:00
|
|
|
|
|
|
|
return channel;
|
|
|
|
|
2003-04-10 22:15:24 +08:00
|
|
|
error:
|
2017-11-20 01:28:36 +08:00
|
|
|
/* don't unref the selection of a partially loaded XCF */
|
|
|
|
if (channel != gimp_image_get_mask (image))
|
2020-02-21 17:49:49 +08:00
|
|
|
{
|
2021-06-20 06:00:53 +08:00
|
|
|
GList *iter;
|
|
|
|
|
|
|
|
for (iter = info->selected_channels; iter; iter = iter->next)
|
|
|
|
if (channel == iter->data)
|
|
|
|
{
|
|
|
|
info->selected_channels = g_list_delete_link (info->selected_channels, iter);
|
|
|
|
break;
|
|
|
|
}
|
2020-02-21 17:49:49 +08:00
|
|
|
|
|
|
|
if (info->floating_sel_drawable == GIMP_DRAWABLE (channel))
|
|
|
|
info->floating_sel_drawable = NULL;
|
|
|
|
|
|
|
|
g_object_unref (channel);
|
|
|
|
}
|
2017-11-20 01:28:36 +08:00
|
|
|
|
2001-07-04 02:38:56 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2023-06-19 22:54:21 +08:00
|
|
|
static FilterData *
|
|
|
|
xcf_load_effect (XcfInfo *info,
|
app: a few fixups to MR !958 (initial non-destructive editing).
- Do not leak allocated return value of gegl_node_to_xml_full().
- When merging layer effects, use gimp_drawable_filter_commit(), making
sure we use the exact same code path as when applying layer effects
destructively from the start. This also ensures that filters are
properly removed from the filter stack (unlike
gimp_drawable_merge_filter()), which was the reason why the rendering
was wrong (hence getting the buffer without effects first, then
reapplying it after was only a workaround to an actual bug).
- When removing a filter, verify the object still exists before doing
anything with it. If this was the last reference, we don't want to
call functions on this object. In gimp_drawable_filter_commit(), we
set up a weak pointer. In gimp_drawable_filter_remove_filter() itself,
we save the pointer to the filter's drawable before actual removal (as
we don't want to dereference a freed object later on).
- export_merge_layer_effects() should merge filters recursively through
layer groups.
- clean up the XCF code:
* No need to wrap the effect pointers list into 2 zero offset. Only
have one zero offset to indicate the list end.
* Add the layer effect mask in the effect structure (not in the layer
structure), similar as for layer masks.
* Effect name and icon made as main data in the structure, not as
properties.
2024-01-03 00:12:35 +08:00
|
|
|
GimpImage *image,
|
2023-06-19 22:54:21 +08:00
|
|
|
GimpDrawable *drawable)
|
|
|
|
{
|
app: a few fixups to MR !958 (initial non-destructive editing).
- Do not leak allocated return value of gegl_node_to_xml_full().
- When merging layer effects, use gimp_drawable_filter_commit(), making
sure we use the exact same code path as when applying layer effects
destructively from the start. This also ensures that filters are
properly removed from the filter stack (unlike
gimp_drawable_merge_filter()), which was the reason why the rendering
was wrong (hence getting the buffer without effects first, then
reapplying it after was only a workaround to an actual bug).
- When removing a filter, verify the object still exists before doing
anything with it. If this was the last reference, we don't want to
call functions on this object. In gimp_drawable_filter_commit(), we
set up a weak pointer. In gimp_drawable_filter_remove_filter() itself,
we save the pointer to the filter's drawable before actual removal (as
we don't want to dereference a freed object later on).
- export_merge_layer_effects() should merge filters recursively through
layer groups.
- clean up the XCF code:
* No need to wrap the effect pointers list into 2 zero offset. Only
have one zero offset to indicate the list end.
* Add the layer effect mask in the effect structure (not in the layer
structure), similar as for layer masks.
* Effect name and icon made as main data in the structure, not as
properties.
2024-01-03 00:12:35 +08:00
|
|
|
FilterData *filter;
|
|
|
|
GimpChannel *effect_mask = NULL;
|
|
|
|
goffset mask_offset = 0;
|
|
|
|
gchar *string;
|
2023-06-19 22:54:21 +08:00
|
|
|
|
|
|
|
filter = g_new0 (FilterData, 1);
|
|
|
|
|
app: a few fixups to MR !958 (initial non-destructive editing).
- Do not leak allocated return value of gegl_node_to_xml_full().
- When merging layer effects, use gimp_drawable_filter_commit(), making
sure we use the exact same code path as when applying layer effects
destructively from the start. This also ensures that filters are
properly removed from the filter stack (unlike
gimp_drawable_merge_filter()), which was the reason why the rendering
was wrong (hence getting the buffer without effects first, then
reapplying it after was only a workaround to an actual bug).
- When removing a filter, verify the object still exists before doing
anything with it. If this was the last reference, we don't want to
call functions on this object. In gimp_drawable_filter_commit(), we
set up a weak pointer. In gimp_drawable_filter_remove_filter() itself,
we save the pointer to the filter's drawable before actual removal (as
we don't want to dereference a freed object later on).
- export_merge_layer_effects() should merge filters recursively through
layer groups.
- clean up the XCF code:
* No need to wrap the effect pointers list into 2 zero offset. Only
have one zero offset to indicate the list end.
* Add the layer effect mask in the effect structure (not in the layer
structure), similar as for layer masks.
* Effect name and icon made as main data in the structure, not as
properties.
2024-01-03 00:12:35 +08:00
|
|
|
/* Effect name */
|
|
|
|
xcf_read_string (info, &string, 1);
|
|
|
|
filter->name = string;
|
|
|
|
|
|
|
|
/* Effect icon */
|
|
|
|
xcf_read_string (info, &string, 1);
|
|
|
|
filter->icon_name = string;
|
|
|
|
|
2024-01-04 02:57:08 +08:00
|
|
|
/* Effect operation */
|
app: a few fixups to MR !958 (initial non-destructive editing).
- Do not leak allocated return value of gegl_node_to_xml_full().
- When merging layer effects, use gimp_drawable_filter_commit(), making
sure we use the exact same code path as when applying layer effects
destructively from the start. This also ensures that filters are
properly removed from the filter stack (unlike
gimp_drawable_merge_filter()), which was the reason why the rendering
was wrong (hence getting the buffer without effects first, then
reapplying it after was only a workaround to an actual bug).
- When removing a filter, verify the object still exists before doing
anything with it. If this was the last reference, we don't want to
call functions on this object. In gimp_drawable_filter_commit(), we
set up a weak pointer. In gimp_drawable_filter_remove_filter() itself,
we save the pointer to the filter's drawable before actual removal (as
we don't want to dereference a freed object later on).
- export_merge_layer_effects() should merge filters recursively through
layer groups.
- clean up the XCF code:
* No need to wrap the effect pointers list into 2 zero offset. Only
have one zero offset to indicate the list end.
* Add the layer effect mask in the effect structure (not in the layer
structure), similar as for layer masks.
* Effect name and icon made as main data in the structure, not as
properties.
2024-01-03 00:12:35 +08:00
|
|
|
xcf_read_string (info, &string, 1);
|
2024-01-04 02:57:08 +08:00
|
|
|
filter->operation_name = string;
|
2023-06-19 22:54:21 +08:00
|
|
|
|
2024-01-04 02:57:08 +08:00
|
|
|
if (! gegl_has_operation (filter->operation_name))
|
|
|
|
{
|
|
|
|
filter->unsupported_operation = TRUE;
|
2023-06-19 22:54:21 +08:00
|
|
|
|
2024-01-04 02:57:08 +08:00
|
|
|
gimp_message (info->gimp, G_OBJECT (info->progress),
|
|
|
|
GIMP_MESSAGE_WARNING,
|
|
|
|
"XCF Warning: the \"%s\" (%s) filter is "
|
|
|
|
"not installed. It was discarded.",
|
|
|
|
filter->name, filter->operation_name);
|
2023-06-19 22:54:21 +08:00
|
|
|
|
2024-01-04 02:57:08 +08:00
|
|
|
return filter;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We'll set valid properties individually */
|
|
|
|
filter->operation = gegl_node_new ();
|
|
|
|
gegl_node_set (filter->operation,
|
|
|
|
"operation", filter->operation_name,
|
|
|
|
NULL);
|
2023-06-19 22:54:21 +08:00
|
|
|
|
|
|
|
/* read in the effect properties */
|
|
|
|
if (! xcf_load_effect_props (info, &(*filter)))
|
|
|
|
goto error;
|
|
|
|
|
app: a few fixups to MR !958 (initial non-destructive editing).
- Do not leak allocated return value of gegl_node_to_xml_full().
- When merging layer effects, use gimp_drawable_filter_commit(), making
sure we use the exact same code path as when applying layer effects
destructively from the start. This also ensures that filters are
properly removed from the filter stack (unlike
gimp_drawable_merge_filter()), which was the reason why the rendering
was wrong (hence getting the buffer without effects first, then
reapplying it after was only a workaround to an actual bug).
- When removing a filter, verify the object still exists before doing
anything with it. If this was the last reference, we don't want to
call functions on this object. In gimp_drawable_filter_commit(), we
set up a weak pointer. In gimp_drawable_filter_remove_filter() itself,
we save the pointer to the filter's drawable before actual removal (as
we don't want to dereference a freed object later on).
- export_merge_layer_effects() should merge filters recursively through
layer groups.
- clean up the XCF code:
* No need to wrap the effect pointers list into 2 zero offset. Only
have one zero offset to indicate the list end.
* Add the layer effect mask in the effect structure (not in the layer
structure), similar as for layer masks.
* Effect name and icon made as main data in the structure, not as
properties.
2024-01-03 00:12:35 +08:00
|
|
|
/* seek to the effect mask offset */
|
|
|
|
xcf_read_offset (info, &mask_offset, 1);
|
|
|
|
|
|
|
|
if (mask_offset != 0)
|
|
|
|
{
|
|
|
|
if (! xcf_seek_pos (info, mask_offset, NULL))
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
effect_mask = xcf_load_channel (info, image);
|
|
|
|
if (! effect_mask)
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
filter->mask = effect_mask;
|
|
|
|
|
2023-06-19 22:54:21 +08:00
|
|
|
xcf_progress_update (info);
|
|
|
|
|
|
|
|
return filter;
|
|
|
|
|
|
|
|
error:
|
|
|
|
|
|
|
|
xcf_load_free_effect (filter);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
xcf_load_free_effect (FilterData *data)
|
|
|
|
{
|
|
|
|
g_free (data->name);
|
|
|
|
g_free (data->icon_name);
|
2024-01-04 02:57:08 +08:00
|
|
|
g_free (data->operation_name);
|
2023-06-19 22:54:21 +08:00
|
|
|
g_clear_object (&data->operation);
|
|
|
|
g_clear_object (&data->mask);
|
|
|
|
g_free (data);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
xcf_load_free_effects (GList *effects)
|
|
|
|
{
|
|
|
|
g_list_free_full (effects, (GDestroyNotify) xcf_load_free_effect);
|
|
|
|
}
|
|
|
|
|
2022-10-20 06:11:34 +08:00
|
|
|
/* The new path structure since XCF 18. */
|
|
|
|
static GimpVectors *
|
|
|
|
xcf_load_vectors (XcfInfo *info,
|
|
|
|
GimpImage *image)
|
|
|
|
{
|
|
|
|
GimpVectors *vectors = NULL;
|
|
|
|
gchar *name;
|
|
|
|
guint32 version;
|
|
|
|
guint32 plength;
|
|
|
|
guint32 num_strokes;
|
|
|
|
goffset base;
|
|
|
|
gint i;
|
|
|
|
|
|
|
|
/* read in the path name. */
|
|
|
|
xcf_read_string (info, &name, 1);
|
|
|
|
|
|
|
|
GIMP_LOG (XCF, "Path name='%s'", name);
|
|
|
|
|
|
|
|
/* create a new path */
|
|
|
|
vectors = gimp_vectors_new (image, name);
|
|
|
|
g_free (name);
|
|
|
|
if (! vectors)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* Read the path's payload size. */
|
|
|
|
xcf_read_int32 (info, (guint32 *) &plength, 1);
|
|
|
|
base = info->cp;
|
|
|
|
|
|
|
|
/* read in the path properties */
|
|
|
|
if (! xcf_load_vectors_props (info, image, &vectors))
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
GIMP_LOG (XCF, "path props loaded");
|
|
|
|
|
|
|
|
xcf_progress_update (info);
|
|
|
|
|
|
|
|
xcf_read_int32 (info, (guint32 *) &version, 1);
|
|
|
|
|
|
|
|
if (version != 1)
|
|
|
|
{
|
|
|
|
gimp_message (info->gimp, G_OBJECT (info->progress),
|
|
|
|
GIMP_MESSAGE_WARNING,
|
|
|
|
"Unknown vectors version: %d (skipping)", version);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Read the number of strokes. */
|
|
|
|
xcf_read_int32 (info, &num_strokes, 1);
|
|
|
|
|
|
|
|
for (i = 0; i < num_strokes; i++)
|
|
|
|
{
|
|
|
|
guint32 stroke_type_id;
|
|
|
|
guint32 closed;
|
|
|
|
guint32 num_axes;
|
|
|
|
guint32 num_control_points;
|
|
|
|
guint32 type;
|
|
|
|
gfloat coords[13] = GIMP_COORDS_DEFAULT_VALUES;
|
|
|
|
GimpStroke *stroke;
|
|
|
|
gint j;
|
|
|
|
|
|
|
|
GimpValueArray *control_points;
|
|
|
|
GValue value = G_VALUE_INIT;
|
|
|
|
GimpAnchor anchor = { { 0, } };
|
|
|
|
GType stroke_type;
|
|
|
|
|
|
|
|
g_value_init (&value, GIMP_TYPE_ANCHOR);
|
|
|
|
|
|
|
|
xcf_read_int32 (info, &stroke_type_id, 1);
|
|
|
|
xcf_read_int32 (info, &closed, 1);
|
|
|
|
xcf_read_int32 (info, &num_axes, 1);
|
|
|
|
xcf_read_int32 (info, &num_control_points, 1);
|
|
|
|
|
|
|
|
#ifdef GIMP_XCF_PATH_DEBUG
|
|
|
|
g_printerr ("stroke_type: %d, closed: %d, num_axes %d, len %d\n",
|
|
|
|
stroke_type_id, closed, num_axes, num_control_points);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
switch (stroke_type_id)
|
|
|
|
{
|
|
|
|
case XCF_STROKETYPE_BEZIER_STROKE:
|
|
|
|
stroke_type = GIMP_TYPE_BEZIER_STROKE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
g_printerr ("skipping unknown stroke type\n");
|
|
|
|
xcf_seek_pos (info,
|
|
|
|
info->cp + 4 * num_axes * num_control_points,
|
|
|
|
NULL);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (num_axes < 2 || num_axes > 6)
|
|
|
|
{
|
|
|
|
g_printerr ("bad number of axes in stroke description\n");
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
control_points = gimp_value_array_new (num_control_points);
|
|
|
|
|
|
|
|
anchor.selected = FALSE;
|
|
|
|
|
|
|
|
for (j = 0; j < num_control_points; j++)
|
|
|
|
{
|
|
|
|
xcf_read_int32 (info, &type, 1);
|
|
|
|
xcf_read_float (info, coords, num_axes);
|
|
|
|
|
|
|
|
anchor.type = type;
|
|
|
|
anchor.position.x = coords[0];
|
|
|
|
anchor.position.y = coords[1];
|
|
|
|
anchor.position.pressure = coords[2];
|
|
|
|
anchor.position.xtilt = coords[3];
|
|
|
|
anchor.position.ytilt = coords[4];
|
|
|
|
anchor.position.wheel = coords[5];
|
|
|
|
|
|
|
|
g_value_set_boxed (&value, &anchor);
|
|
|
|
gimp_value_array_append (control_points, &value);
|
|
|
|
|
|
|
|
#ifdef GIMP_XCF_PATH_DEBUG
|
|
|
|
g_printerr ("Anchor: %d, (%f, %f, %f, %f, %f, %f)\n", type,
|
|
|
|
coords[0], coords[1], coords[2], coords[3],
|
|
|
|
coords[4], coords[5]);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
g_value_unset (&value);
|
|
|
|
|
|
|
|
stroke = g_object_new (stroke_type,
|
|
|
|
"closed", closed,
|
|
|
|
"control-points", control_points,
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
gimp_vectors_stroke_add (vectors, stroke);
|
|
|
|
|
|
|
|
g_object_unref (stroke);
|
|
|
|
gimp_value_array_unref (control_points);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (plength != info->cp - base)
|
|
|
|
{
|
|
|
|
gimp_message (info->gimp, G_OBJECT (info->progress),
|
|
|
|
GIMP_MESSAGE_WARNING,
|
|
|
|
"Path payload size does not match stored size (skipping)");
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
return vectors;
|
|
|
|
|
|
|
|
error:
|
|
|
|
|
|
|
|
xcf_seek_pos (info, base + plength, NULL);
|
|
|
|
g_clear_object (&vectors);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2001-07-04 02:38:56 +08:00
|
|
|
static GimpLayerMask *
|
|
|
|
xcf_load_layer_mask (XcfInfo *info,
|
2006-03-29 01:08:36 +08:00
|
|
|
GimpImage *image)
|
2001-07-04 02:38:56 +08:00
|
|
|
{
|
|
|
|
GimpLayerMask *layer_mask;
|
2004-01-26 17:22:06 +08:00
|
|
|
GimpChannel *channel;
|
2021-06-20 06:00:53 +08:00
|
|
|
GList *iter;
|
2017-03-23 18:44:41 +08:00
|
|
|
goffset hierarchy_offset;
|
2001-07-04 02:38:56 +08:00
|
|
|
gint width;
|
|
|
|
gint height;
|
2004-01-26 23:34:47 +08:00
|
|
|
gboolean is_fs_drawable;
|
2001-07-04 02:38:56 +08:00
|
|
|
gchar *name;
|
2002-03-04 01:38:12 +08:00
|
|
|
GimpRGB color = { 0.0, 0.0, 0.0, GIMP_OPACITY_OPAQUE };
|
2022-06-06 06:44:45 +08:00
|
|
|
goffset cur_offset;
|
2001-07-04 02:38:56 +08:00
|
|
|
|
|
|
|
/* check and see if this is the drawable the floating selection
|
2004-01-26 23:34:47 +08:00
|
|
|
* is attached to. if it is then we'll do the attachment in our caller.
|
2001-07-04 02:38:56 +08:00
|
|
|
*/
|
2004-01-26 23:34:47 +08:00
|
|
|
is_fs_drawable = (info->cp == info->floating_sel_offset);
|
2001-07-04 02:38:56 +08:00
|
|
|
|
|
|
|
/* read in the layer width, height and name */
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_int32 (info, (guint32 *) &width, 1);
|
|
|
|
xcf_read_int32 (info, (guint32 *) &height, 1);
|
2022-06-06 04:48:10 +08:00
|
|
|
if (width <= 0 || height <= 0 ||
|
|
|
|
width > GIMP_MAX_IMAGE_SIZE || height > GIMP_MAX_IMAGE_SIZE)
|
|
|
|
{
|
|
|
|
GIMP_LOG (XCF, "Invalid layer mask size %d x %d.", width, height);
|
|
|
|
return NULL;
|
|
|
|
}
|
2014-06-07 22:33:17 +08:00
|
|
|
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_string (info, &name, 1);
|
2022-06-06 04:48:10 +08:00
|
|
|
GIMP_LOG (XCF, "Layer mask width=%d, height=%d, name='%s'",
|
|
|
|
width, height, name);
|
2001-07-04 02:38:56 +08:00
|
|
|
|
|
|
|
/* create a new layer mask */
|
2006-03-29 01:08:36 +08:00
|
|
|
layer_mask = gimp_layer_mask_new (image, width, height, name, &color);
|
2001-07-04 02:38:56 +08:00
|
|
|
g_free (name);
|
2017-03-23 19:24:38 +08:00
|
|
|
if (! layer_mask)
|
2001-07-04 02:38:56 +08:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* read in the layer_mask properties */
|
2004-01-26 17:22:06 +08:00
|
|
|
channel = GIMP_CHANNEL (layer_mask);
|
2017-03-23 19:24:38 +08:00
|
|
|
if (! xcf_load_channel_props (info, image, &channel))
|
2001-07-04 02:38:56 +08:00
|
|
|
goto error;
|
|
|
|
|
2006-07-12 04:21:18 +08:00
|
|
|
xcf_progress_update (info);
|
|
|
|
|
2022-06-06 06:44:45 +08:00
|
|
|
/* read the hierarchy offset */
|
|
|
|
cur_offset = info->cp;
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_offset (info, &hierarchy_offset, 1);
|
2001-07-04 02:38:56 +08:00
|
|
|
|
2022-06-06 06:44:45 +08:00
|
|
|
if (hierarchy_offset < cur_offset)
|
|
|
|
{
|
|
|
|
GIMP_LOG (XCF, "Invalid hierarchy offset!");
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2001-07-04 02:38:56 +08:00
|
|
|
/* read in the hierarchy */
|
2002-12-20 14:26:34 +08:00
|
|
|
if (! xcf_seek_pos (info, hierarchy_offset, NULL))
|
|
|
|
goto error;
|
|
|
|
|
2017-03-23 19:24:38 +08:00
|
|
|
if (! xcf_load_buffer (info,
|
|
|
|
gimp_drawable_get_buffer (GIMP_DRAWABLE (layer_mask))))
|
2001-07-04 02:38:56 +08:00
|
|
|
goto error;
|
|
|
|
|
2006-07-12 04:21:18 +08:00
|
|
|
xcf_progress_update (info);
|
|
|
|
|
2001-07-04 02:38:56 +08:00
|
|
|
/* attach the floating selection... */
|
2004-01-26 23:34:47 +08:00
|
|
|
if (is_fs_drawable)
|
|
|
|
info->floating_sel_drawable = GIMP_DRAWABLE (layer_mask);
|
2001-07-04 02:38:56 +08:00
|
|
|
|
|
|
|
return layer_mask;
|
|
|
|
|
2003-04-10 22:15:24 +08:00
|
|
|
error:
|
2021-06-20 06:00:53 +08:00
|
|
|
for (iter = info->selected_channels; iter; iter = iter->next)
|
|
|
|
if (layer_mask == iter->data)
|
|
|
|
{
|
|
|
|
info->selected_channels = g_list_delete_link (info->selected_channels, iter);
|
|
|
|
break;
|
|
|
|
}
|
2020-02-21 17:49:49 +08:00
|
|
|
|
|
|
|
if (info->floating_sel_drawable == GIMP_DRAWABLE (layer_mask))
|
|
|
|
info->floating_sel_drawable = NULL;
|
|
|
|
|
2003-01-06 06:07:10 +08:00
|
|
|
g_object_unref (layer_mask);
|
2020-02-21 17:49:49 +08:00
|
|
|
|
2001-07-04 02:38:56 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
2012-04-03 05:32:38 +08:00
|
|
|
xcf_load_buffer (XcfInfo *info,
|
|
|
|
GeglBuffer *buffer)
|
2001-07-04 02:38:56 +08:00
|
|
|
{
|
2012-04-03 05:32:38 +08:00
|
|
|
const Babl *format;
|
2017-03-23 18:44:41 +08:00
|
|
|
goffset offset;
|
2012-04-03 05:32:38 +08:00
|
|
|
gint width;
|
|
|
|
gint height;
|
|
|
|
gint bpp;
|
2022-06-06 06:44:45 +08:00
|
|
|
goffset cur_offset;
|
2012-04-03 05:32:38 +08:00
|
|
|
|
|
|
|
format = gegl_buffer_get_format (buffer);
|
2001-07-04 02:38:56 +08:00
|
|
|
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_int32 (info, (guint32 *) &width, 1);
|
|
|
|
xcf_read_int32 (info, (guint32 *) &height, 1);
|
|
|
|
xcf_read_int32 (info, (guint32 *) &bpp, 1);
|
2001-07-04 02:38:56 +08:00
|
|
|
|
|
|
|
/* make sure the values in the file correspond to the values
|
2018-04-06 18:56:31 +08:00
|
|
|
* calculated when the GeglBuffer was created.
|
2001-07-04 02:38:56 +08:00
|
|
|
*/
|
2012-04-03 05:32:38 +08:00
|
|
|
if (width != gegl_buffer_get_width (buffer) ||
|
|
|
|
height != gegl_buffer_get_height (buffer) ||
|
|
|
|
bpp != babl_format_get_bytes_per_pixel (format))
|
2001-07-04 02:38:56 +08:00
|
|
|
return FALSE;
|
|
|
|
|
2022-06-06 06:44:45 +08:00
|
|
|
cur_offset = info->cp;
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_offset (info, &offset, 1); /* top level */
|
2001-07-04 02:38:56 +08:00
|
|
|
|
2022-06-06 06:44:45 +08:00
|
|
|
if (offset < cur_offset)
|
|
|
|
{
|
|
|
|
GIMP_LOG (XCF, "Invalid buffer offset!");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2001-07-04 02:38:56 +08:00
|
|
|
/* seek to the level offset */
|
2017-03-23 19:24:38 +08:00
|
|
|
if (! xcf_seek_pos (info, offset, NULL))
|
2002-12-20 14:26:34 +08:00
|
|
|
return FALSE;
|
2003-09-01 07:46:05 +08:00
|
|
|
|
2001-07-04 02:38:56 +08:00
|
|
|
/* read in the level */
|
2017-03-23 19:24:38 +08:00
|
|
|
if (! xcf_load_level (info, buffer))
|
2001-07-04 02:38:56 +08:00
|
|
|
return FALSE;
|
2003-09-01 07:46:05 +08:00
|
|
|
|
2014-06-07 00:35:25 +08:00
|
|
|
/* discard levels below first.
|
2001-07-04 02:38:56 +08:00
|
|
|
*/
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static gboolean
|
2012-04-03 05:32:38 +08:00
|
|
|
xcf_load_level (XcfInfo *info,
|
|
|
|
GeglBuffer *buffer)
|
2001-07-04 02:38:56 +08:00
|
|
|
{
|
2012-04-21 05:45:22 +08:00
|
|
|
const Babl *format;
|
2012-09-23 00:38:13 +08:00
|
|
|
gint bpp;
|
2017-03-23 18:44:41 +08:00
|
|
|
goffset saved_pos;
|
|
|
|
goffset offset;
|
|
|
|
goffset offset2;
|
app: limit allowable tile data size in XCFs
When loading tiles from an XCF, reject tiles whose on-disk size is
greater than 1.5 times the size of an uncompressed tile -- a limit
that is already present for the last tile in the buffer. This
should allow for the possibility of negative compression, while
restricting placing a realistic limit.
Currently, no limit is placed on the on-disk tile data size. When
loading RLE- and zlib-compressed tiles, a buffer large enough to
hold the entire on-disk tile data, up to 2GB, is allocated on the
stack, and the data is read into it. If the file is smaller than
the reported tile data size, the area of the buffer past the end
of the file is not touched. This allows a malicious XCF to write
up to 2GB of arbitrary data, at an arbitrary offset, up to 2GB,
below the stack.
Note that a similar issue had existed for earlier versions of GIMP
(see commit d7a9e6079d3e65baa516f302eb285fb2bbd97c2f), however,
since prior to 2.9 the tile data buffer was allocated on the heap,
the potential risk is far smaller.
2017-07-10 05:20:30 +08:00
|
|
|
goffset max_data_length;
|
2012-04-21 05:45:22 +08:00
|
|
|
gint n_tile_rows;
|
|
|
|
gint n_tile_cols;
|
|
|
|
guint ntiles;
|
|
|
|
gint width;
|
|
|
|
gint height;
|
|
|
|
gint i;
|
|
|
|
gint fail;
|
|
|
|
|
|
|
|
format = gegl_buffer_get_format (buffer);
|
2012-09-23 00:38:13 +08:00
|
|
|
bpp = babl_format_get_bytes_per_pixel (format);
|
2001-07-04 02:38:56 +08:00
|
|
|
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_int32 (info, (guint32 *) &width, 1);
|
|
|
|
xcf_read_int32 (info, (guint32 *) &height, 1);
|
2001-07-04 02:38:56 +08:00
|
|
|
|
2012-04-03 05:32:38 +08:00
|
|
|
if (width != gegl_buffer_get_width (buffer) ||
|
|
|
|
height != gegl_buffer_get_height (buffer))
|
2001-07-04 02:38:56 +08:00
|
|
|
return FALSE;
|
|
|
|
|
app: limit allowable tile data size in XCFs
When loading tiles from an XCF, reject tiles whose on-disk size is
greater than 1.5 times the size of an uncompressed tile -- a limit
that is already present for the last tile in the buffer. This
should allow for the possibility of negative compression, while
restricting placing a realistic limit.
Currently, no limit is placed on the on-disk tile data size. When
loading RLE- and zlib-compressed tiles, a buffer large enough to
hold the entire on-disk tile data, up to 2GB, is allocated on the
stack, and the data is read into it. If the file is smaller than
the reported tile data size, the area of the buffer past the end
of the file is not touched. This allows a malicious XCF to write
up to 2GB of arbitrary data, at an arbitrary offset, up to 2GB,
below the stack.
Note that a similar issue had existed for earlier versions of GIMP
(see commit d7a9e6079d3e65baa516f302eb285fb2bbd97c2f), however,
since prior to 2.9 the tile data buffer was allocated on the heap,
the potential risk is far smaller.
2017-07-10 05:20:30 +08:00
|
|
|
/* maximal allowable size of on-disk tile data. make it somewhat bigger than
|
|
|
|
* the uncompressed tile size, to allow for the possibility of negative
|
|
|
|
* compression.
|
|
|
|
*/
|
|
|
|
max_data_length = XCF_TILE_WIDTH * XCF_TILE_HEIGHT * bpp *
|
|
|
|
XCF_TILE_MAX_DATA_LENGTH_FACTOR /* = 1.5, currently */;
|
|
|
|
|
2001-07-04 02:38:56 +08:00
|
|
|
/* read in the first tile offset.
|
|
|
|
* if it is '0', then this tile level is empty
|
|
|
|
* and we can simply return.
|
|
|
|
*/
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_offset (info, &offset, 1);
|
2001-07-04 02:38:56 +08:00
|
|
|
if (offset == 0)
|
|
|
|
return TRUE;
|
|
|
|
|
2012-04-03 05:32:38 +08:00
|
|
|
n_tile_rows = gimp_gegl_buffer_get_n_tile_rows (buffer, XCF_TILE_HEIGHT);
|
|
|
|
n_tile_cols = gimp_gegl_buffer_get_n_tile_cols (buffer, XCF_TILE_WIDTH);
|
2001-07-04 02:38:56 +08:00
|
|
|
|
2012-04-03 05:32:38 +08:00
|
|
|
ntiles = n_tile_rows * n_tile_cols;
|
2001-07-04 02:38:56 +08:00
|
|
|
for (i = 0; i < ntiles; i++)
|
|
|
|
{
|
2012-04-03 05:32:38 +08:00
|
|
|
GeglRectangle rect;
|
|
|
|
|
2001-07-04 02:38:56 +08:00
|
|
|
fail = FALSE;
|
|
|
|
|
|
|
|
if (offset == 0)
|
2004-11-02 02:33:09 +08:00
|
|
|
{
|
2008-11-04 20:33:09 +08:00
|
|
|
gimp_message_literal (info->gimp, G_OBJECT (info->progress),
|
2013-09-15 00:59:20 +08:00
|
|
|
GIMP_MESSAGE_ERROR,
|
|
|
|
"not enough tiles found in level");
|
2004-11-02 02:33:09 +08:00
|
|
|
return FALSE;
|
|
|
|
}
|
2001-07-04 02:38:56 +08:00
|
|
|
|
|
|
|
/* 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
|
2017-03-23 18:44:41 +08:00
|
|
|
* of data needed for this tile
|
|
|
|
*/
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_offset (info, &offset2, 1);
|
2003-09-01 07:46:05 +08:00
|
|
|
|
2001-07-04 02:38:56 +08:00
|
|
|
/* if the offset is 0 then we need to read in the maximum possible
|
2017-03-23 18:44:41 +08:00
|
|
|
* allowing for negative compression
|
|
|
|
*/
|
2001-07-04 02:38:56 +08:00
|
|
|
if (offset2 == 0)
|
app: limit allowable tile data size in XCFs
When loading tiles from an XCF, reject tiles whose on-disk size is
greater than 1.5 times the size of an uncompressed tile -- a limit
that is already present for the last tile in the buffer. This
should allow for the possibility of negative compression, while
restricting placing a realistic limit.
Currently, no limit is placed on the on-disk tile data size. When
loading RLE- and zlib-compressed tiles, a buffer large enough to
hold the entire on-disk tile data, up to 2GB, is allocated on the
stack, and the data is read into it. If the file is smaller than
the reported tile data size, the area of the buffer past the end
of the file is not touched. This allows a malicious XCF to write
up to 2GB of arbitrary data, at an arbitrary offset, up to 2GB,
below the stack.
Note that a similar issue had existed for earlier versions of GIMP
(see commit d7a9e6079d3e65baa516f302eb285fb2bbd97c2f), however,
since prior to 2.9 the tile data buffer was allocated on the heap,
the potential risk is far smaller.
2017-07-10 05:20:30 +08:00
|
|
|
offset2 = offset + max_data_length;
|
2001-07-04 02:38:56 +08:00
|
|
|
|
|
|
|
/* seek to the tile offset */
|
2002-12-20 14:26:34 +08:00
|
|
|
if (! xcf_seek_pos (info, offset, NULL))
|
|
|
|
return FALSE;
|
2001-07-04 02:38:56 +08:00
|
|
|
|
app: limit allowable tile data size in XCFs
When loading tiles from an XCF, reject tiles whose on-disk size is
greater than 1.5 times the size of an uncompressed tile -- a limit
that is already present for the last tile in the buffer. This
should allow for the possibility of negative compression, while
restricting placing a realistic limit.
Currently, no limit is placed on the on-disk tile data size. When
loading RLE- and zlib-compressed tiles, a buffer large enough to
hold the entire on-disk tile data, up to 2GB, is allocated on the
stack, and the data is read into it. If the file is smaller than
the reported tile data size, the area of the buffer past the end
of the file is not touched. This allows a malicious XCF to write
up to 2GB of arbitrary data, at an arbitrary offset, up to 2GB,
below the stack.
Note that a similar issue had existed for earlier versions of GIMP
(see commit d7a9e6079d3e65baa516f302eb285fb2bbd97c2f), however,
since prior to 2.9 the tile data buffer was allocated on the heap,
the potential risk is far smaller.
2017-07-10 05:20:30 +08:00
|
|
|
if (offset2 < offset || offset2 - offset > max_data_length)
|
|
|
|
{
|
|
|
|
gimp_message (info->gimp, G_OBJECT (info->progress),
|
|
|
|
GIMP_MESSAGE_ERROR,
|
|
|
|
"invalid tile data length: %" G_GOFFSET_FORMAT,
|
|
|
|
offset2 - offset);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2018-04-06 18:56:31 +08:00
|
|
|
/* get buffer rectangle to write to */
|
2012-04-03 05:32:38 +08:00
|
|
|
gimp_gegl_buffer_get_tile_rect (buffer,
|
|
|
|
XCF_TILE_WIDTH, XCF_TILE_HEIGHT,
|
|
|
|
i, &rect);
|
2001-07-04 02:38:56 +08:00
|
|
|
|
2014-07-10 04:36:55 +08:00
|
|
|
GIMP_LOG (XCF, "loading tile %d/%d", i + 1, ntiles);
|
|
|
|
|
2001-07-04 02:38:56 +08:00
|
|
|
/* read in the tile */
|
|
|
|
switch (info->compression)
|
2004-11-02 02:33:09 +08:00
|
|
|
{
|
|
|
|
case COMPRESS_NONE:
|
2018-04-06 18:56:31 +08:00
|
|
|
if (! xcf_load_tile (info, buffer, &rect, format))
|
2004-11-02 02:33:09 +08:00
|
|
|
fail = TRUE;
|
|
|
|
break;
|
|
|
|
case COMPRESS_RLE:
|
2018-04-06 18:56:31 +08:00
|
|
|
if (! xcf_load_tile_rle (info, buffer, &rect, format,
|
|
|
|
offset2 - offset))
|
2004-11-02 02:33:09 +08:00
|
|
|
fail = TRUE;
|
|
|
|
break;
|
|
|
|
case COMPRESS_ZLIB:
|
2018-04-06 18:56:31 +08:00
|
|
|
if (! xcf_load_tile_zlib (info, buffer, &rect, format,
|
|
|
|
offset2 - offset))
|
2014-09-15 21:33:22 +08:00
|
|
|
fail = TRUE;
|
2004-11-02 02:33:09 +08:00
|
|
|
break;
|
|
|
|
case COMPRESS_FRACTAL:
|
2014-09-17 06:08:33 +08:00
|
|
|
g_printerr ("xcf: fractal compression unimplemented. "
|
|
|
|
"Possibly corrupt XCF file.");
|
2014-09-15 22:11:45 +08:00
|
|
|
fail = TRUE;
|
|
|
|
break;
|
|
|
|
default:
|
2014-09-17 06:08:33 +08:00
|
|
|
g_printerr ("xcf: unknown compression. "
|
|
|
|
"Possibly corrupt XCF file.");
|
2004-11-02 02:33:09 +08:00
|
|
|
fail = TRUE;
|
|
|
|
break;
|
|
|
|
}
|
2001-07-04 02:38:56 +08:00
|
|
|
|
2003-09-01 07:46:05 +08:00
|
|
|
if (fail)
|
2012-04-03 05:32:38 +08:00
|
|
|
return FALSE;
|
2001-07-04 02:38:56 +08:00
|
|
|
|
2014-07-10 04:36:55 +08:00
|
|
|
GIMP_LOG (XCF, "loaded tile %d/%d", i + 1, ntiles);
|
|
|
|
|
2001-07-04 02:38:56 +08:00
|
|
|
/* restore the saved position so we'll be ready to
|
|
|
|
* read the next offset.
|
|
|
|
*/
|
2002-12-20 14:26:34 +08:00
|
|
|
if (!xcf_seek_pos (info, saved_pos, NULL))
|
|
|
|
return FALSE;
|
2001-07-04 02:38:56 +08:00
|
|
|
|
|
|
|
/* read in the offset of the next tile */
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_offset (info, &offset, 1);
|
2001-07-04 02:38:56 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (offset != 0)
|
|
|
|
{
|
2006-10-09 16:17:22 +08:00
|
|
|
gimp_message (info->gimp, G_OBJECT (info->progress), GIMP_MESSAGE_ERROR,
|
2017-03-23 19:24:38 +08:00
|
|
|
"encountered garbage after reading level: %" G_GOFFSET_FORMAT,
|
2017-03-23 18:44:41 +08:00
|
|
|
offset);
|
2001-07-04 02:38:56 +08:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
2012-04-03 05:32:38 +08:00
|
|
|
xcf_load_tile (XcfInfo *info,
|
|
|
|
GeglBuffer *buffer,
|
2012-04-21 05:45:22 +08:00
|
|
|
GeglRectangle *tile_rect,
|
|
|
|
const Babl *format)
|
2001-07-04 02:38:56 +08:00
|
|
|
{
|
2012-04-21 05:45:22 +08:00
|
|
|
gint bpp = babl_format_get_bytes_per_pixel (format);
|
|
|
|
gint tile_size = bpp * tile_rect->width * tile_rect->height;
|
|
|
|
guchar *tile_data = g_alloca (tile_size);
|
2012-04-03 05:32:38 +08:00
|
|
|
|
2017-09-17 01:03:20 +08:00
|
|
|
if (info->file_version <= 11)
|
|
|
|
{
|
|
|
|
xcf_read_int8 (info, tile_data, tile_size);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
gint n_components = babl_format_get_n_components (format);
|
|
|
|
|
|
|
|
xcf_read_component (info, bpp / n_components, tile_data,
|
2017-10-01 23:43:10 +08:00
|
|
|
tile_size / bpp * n_components);
|
2017-09-17 01:03:20 +08:00
|
|
|
}
|
2012-04-03 05:32:38 +08:00
|
|
|
|
2017-10-04 18:43:56 +08:00
|
|
|
if (! xcf_data_is_zero (tile_data, tile_size))
|
|
|
|
{
|
|
|
|
gegl_buffer_set (buffer, tile_rect, 0, format, tile_data,
|
|
|
|
GEGL_AUTO_ROWSTRIDE);
|
|
|
|
}
|
2001-07-04 02:38:56 +08:00
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
2012-04-03 05:32:38 +08:00
|
|
|
xcf_load_tile_rle (XcfInfo *info,
|
|
|
|
GeglBuffer *buffer,
|
|
|
|
GeglRectangle *tile_rect,
|
2012-04-21 05:45:22 +08:00
|
|
|
const Babl *format,
|
2012-04-03 05:32:38 +08:00
|
|
|
gint data_length)
|
2001-07-04 02:38:56 +08:00
|
|
|
{
|
2012-04-21 05:45:22 +08:00
|
|
|
gint bpp = babl_format_get_bytes_per_pixel (format);
|
|
|
|
gint tile_size = bpp * tile_rect->width * tile_rect->height;
|
|
|
|
guchar *tile_data = g_alloca (tile_size);
|
2017-10-04 18:43:56 +08:00
|
|
|
guchar nonzero = FALSE;
|
2017-03-23 19:24:38 +08:00
|
|
|
gsize bytes_read;
|
2012-04-21 05:45:22 +08:00
|
|
|
gint i;
|
|
|
|
guchar *xcfdata;
|
|
|
|
guchar *xcfodata;
|
|
|
|
guchar *xcfdatalimit;
|
2003-09-01 07:46:05 +08:00
|
|
|
|
2006-10-11 17:19:12 +08:00
|
|
|
/* Workaround for bug #357809: avoid crashing on g_malloc() and skip
|
|
|
|
* this tile (return TRUE without storing data) as if it did not
|
|
|
|
* contain any data. It is better than returning FALSE, which would
|
|
|
|
* skip the whole hierarchy while there may still be some valid
|
|
|
|
* tiles in the file.
|
|
|
|
*/
|
|
|
|
if (data_length <= 0)
|
|
|
|
return TRUE;
|
|
|
|
|
2012-04-03 05:32:38 +08:00
|
|
|
xcfdata = xcfodata = g_alloca (data_length);
|
2001-07-04 02:38:56 +08:00
|
|
|
|
2013-10-09 03:22:14 +08:00
|
|
|
/* we have to read directly instead of xcf_read_* because we may be
|
2012-04-03 05:32:38 +08:00
|
|
|
* reading past the end of the file here
|
|
|
|
*/
|
2013-10-09 03:22:14 +08:00
|
|
|
g_input_stream_read_all (info->input, xcfdata, data_length,
|
2017-03-23 19:24:38 +08:00
|
|
|
&bytes_read, NULL, NULL);
|
|
|
|
info->cp += bytes_read;
|
2013-10-09 03:22:14 +08:00
|
|
|
|
2017-03-23 19:24:38 +08:00
|
|
|
if (bytes_read == 0)
|
2013-10-09 03:22:14 +08:00
|
|
|
return TRUE;
|
|
|
|
|
2017-03-23 19:24:38 +08:00
|
|
|
xcfdatalimit = &xcfodata[bytes_read - 1];
|
2003-09-01 07:46:05 +08:00
|
|
|
|
2001-07-04 02:38:56 +08:00
|
|
|
for (i = 0; i < bpp; i++)
|
|
|
|
{
|
2012-04-03 05:32:38 +08:00
|
|
|
guchar *data = tile_data + i;
|
|
|
|
gint size = tile_rect->width * tile_rect->height;
|
|
|
|
gint count = 0;
|
|
|
|
guchar val;
|
|
|
|
gint length;
|
|
|
|
gint j;
|
2001-07-04 02:38:56 +08:00
|
|
|
|
|
|
|
while (size > 0)
|
2004-11-02 02:33:09 +08:00
|
|
|
{
|
|
|
|
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++;
|
2017-10-04 18:43:56 +08:00
|
|
|
nonzero |= *data;
|
2004-11-02 02:33:09 +08:00
|
|
|
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;
|
2001-07-04 02:38:56 +08:00
|
|
|
|
|
|
|
if (size < 0)
|
2004-11-02 02:33:09 +08:00
|
|
|
{
|
|
|
|
goto bogus_rle;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (xcfdata > xcfdatalimit)
|
|
|
|
{
|
|
|
|
goto bogus_rle;
|
|
|
|
}
|
|
|
|
|
|
|
|
val = *xcfdata++;
|
2017-10-04 18:43:56 +08:00
|
|
|
nonzero |= val;
|
2004-11-02 02:33:09 +08:00
|
|
|
|
|
|
|
for (j = 0; j < length; j++)
|
|
|
|
{
|
|
|
|
*data = val;
|
|
|
|
data += bpp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2001-07-04 02:38:56 +08:00
|
|
|
}
|
2012-04-03 05:32:38 +08:00
|
|
|
|
2017-10-04 18:43:56 +08:00
|
|
|
if (nonzero)
|
2017-09-17 01:03:20 +08:00
|
|
|
{
|
2017-10-04 18:43:56 +08:00
|
|
|
if (info->file_version >= 12)
|
|
|
|
{
|
|
|
|
gint n_components = babl_format_get_n_components (format);
|
2017-09-17 01:03:20 +08:00
|
|
|
|
2017-10-04 18:43:56 +08:00
|
|
|
xcf_read_from_be (bpp / n_components, tile_data,
|
|
|
|
tile_size / bpp * n_components);
|
|
|
|
}
|
2017-09-17 01:03:20 +08:00
|
|
|
|
2017-10-04 18:43:56 +08:00
|
|
|
gegl_buffer_set (buffer, tile_rect, 0, format, tile_data,
|
|
|
|
GEGL_AUTO_ROWSTRIDE);
|
|
|
|
}
|
2012-04-03 05:32:38 +08:00
|
|
|
|
2001-07-04 02:38:56 +08:00
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
bogus_rle:
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2014-09-15 21:33:22 +08:00
|
|
|
static gboolean
|
|
|
|
xcf_load_tile_zlib (XcfInfo *info,
|
|
|
|
GeglBuffer *buffer,
|
|
|
|
GeglRectangle *tile_rect,
|
|
|
|
const Babl *format,
|
|
|
|
gint data_length)
|
|
|
|
{
|
|
|
|
z_stream strm;
|
|
|
|
int action;
|
|
|
|
int status;
|
|
|
|
gint bpp = babl_format_get_bytes_per_pixel (format);
|
|
|
|
gint tile_size = bpp * tile_rect->width * tile_rect->height;
|
|
|
|
guchar *tile_data = g_alloca (tile_size);
|
|
|
|
gsize bytes_read;
|
|
|
|
guchar *xcfdata;
|
|
|
|
|
|
|
|
/* Workaround for bug #357809: avoid crashing on g_malloc() and skip
|
|
|
|
* this tile (return TRUE without storing data) as if it did not
|
|
|
|
* contain any data. It is better than returning FALSE, which would
|
|
|
|
* skip the whole hierarchy while there may still be some valid
|
|
|
|
* tiles in the file.
|
|
|
|
*/
|
|
|
|
if (data_length <= 0)
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
xcfdata = g_alloca (data_length);
|
|
|
|
|
|
|
|
/* we have to read directly instead of xcf_read_* because we may be
|
|
|
|
* reading past the end of the file here
|
|
|
|
*/
|
|
|
|
g_input_stream_read_all (info->input, xcfdata, data_length,
|
|
|
|
&bytes_read, NULL, NULL);
|
2017-03-23 19:24:38 +08:00
|
|
|
info->cp += bytes_read;
|
2014-09-15 21:33:22 +08:00
|
|
|
|
|
|
|
if (bytes_read == 0)
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
strm.next_out = tile_data;
|
|
|
|
strm.avail_out = tile_size;
|
|
|
|
|
|
|
|
strm.zalloc = Z_NULL;
|
|
|
|
strm.zfree = Z_NULL;
|
|
|
|
strm.opaque = Z_NULL;
|
|
|
|
strm.next_in = xcfdata;
|
|
|
|
strm.avail_in = bytes_read;
|
|
|
|
|
|
|
|
/* Initialize the stream decompression. */
|
|
|
|
status = inflateInit (&strm);
|
|
|
|
if (status != Z_OK)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
action = Z_NO_FLUSH;
|
|
|
|
|
|
|
|
while (status == Z_OK)
|
|
|
|
{
|
|
|
|
if (strm.avail_in == 0)
|
|
|
|
{
|
|
|
|
action = Z_FINISH;
|
|
|
|
}
|
|
|
|
|
|
|
|
status = inflate (&strm, action);
|
|
|
|
|
|
|
|
if (status == Z_STREAM_END)
|
|
|
|
{
|
|
|
|
/* All the data was successfully decoded. */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else if (status == Z_BUF_ERROR)
|
|
|
|
{
|
|
|
|
g_printerr ("xcf: decompressed tile bigger than the expected size.");
|
|
|
|
inflateEnd (&strm);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
else if (status != Z_OK)
|
|
|
|
{
|
|
|
|
g_printerr ("xcf: tile decompression failed: %s", zError (status));
|
|
|
|
inflateEnd (&strm);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-04 18:43:56 +08:00
|
|
|
if (! xcf_data_is_zero (tile_data, tile_size))
|
2017-09-17 01:03:20 +08:00
|
|
|
{
|
2017-10-04 18:43:56 +08:00
|
|
|
if (info->file_version >= 12)
|
|
|
|
{
|
|
|
|
gint n_components = babl_format_get_n_components (format);
|
2017-09-17 01:03:20 +08:00
|
|
|
|
2017-10-04 18:43:56 +08:00
|
|
|
xcf_read_from_be (bpp / n_components, tile_data,
|
|
|
|
tile_size / bpp * n_components);
|
|
|
|
}
|
2017-09-17 01:03:20 +08:00
|
|
|
|
2017-10-04 18:43:56 +08:00
|
|
|
gegl_buffer_set (buffer, tile_rect, 0, format, tile_data,
|
|
|
|
GEGL_AUTO_ROWSTRIDE);
|
|
|
|
}
|
2014-09-15 21:33:22 +08:00
|
|
|
|
|
|
|
inflateEnd (&strm);
|
2017-09-17 01:03:20 +08:00
|
|
|
|
2014-09-15 21:33:22 +08:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2003-05-15 03:21:42 +08:00
|
|
|
static GimpParasite *
|
|
|
|
xcf_load_parasite (XcfInfo *info)
|
|
|
|
{
|
2022-04-05 05:33:29 +08:00
|
|
|
GimpParasite *parasite = NULL;
|
2007-05-22 23:12:30 +08:00
|
|
|
gchar *name;
|
2007-12-22 00:37:01 +08:00
|
|
|
guint32 flags;
|
2022-04-05 05:33:29 +08:00
|
|
|
guint32 size, size_read;
|
2007-12-22 00:37:01 +08:00
|
|
|
gpointer data;
|
2007-05-22 23:12:30 +08:00
|
|
|
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_string (info, &name, 1);
|
|
|
|
xcf_read_int32 (info, &flags, 1);
|
|
|
|
xcf_read_int32 (info, &size, 1);
|
2007-12-22 00:37:01 +08:00
|
|
|
|
2022-04-05 05:33:29 +08:00
|
|
|
GIMP_LOG (XCF, "Parasite name: %s, flags: %d, size: %d", name, flags, size);
|
|
|
|
|
2012-10-03 04:00:16 +08:00
|
|
|
if (size > MAX_XCF_PARASITE_DATA_LEN)
|
|
|
|
{
|
2014-09-17 06:08:33 +08:00
|
|
|
g_printerr ("Maximum parasite data length (%ld bytes) exceeded. "
|
|
|
|
"Possibly corrupt XCF file.", MAX_XCF_PARASITE_DATA_LEN);
|
2012-10-03 04:00:16 +08:00
|
|
|
g_free (name);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2022-04-05 05:33:29 +08:00
|
|
|
if (!name)
|
|
|
|
{
|
|
|
|
g_printerr ("Parasite has no name! Possibly corrupt XCF file.\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2007-12-22 00:37:01 +08:00
|
|
|
data = g_new (gchar, size);
|
2022-04-05 05:33:29 +08:00
|
|
|
size_read = xcf_read_int8 (info, data, size);
|
2003-05-15 03:21:42 +08:00
|
|
|
|
2022-04-05 05:33:29 +08:00
|
|
|
if (size_read != size)
|
|
|
|
{
|
|
|
|
g_printerr ("Incorrect parasite data size: read %u bytes instead of %u. "
|
|
|
|
"Possibly corrupt XCF file.\n",
|
|
|
|
size_read, size);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
parasite = gimp_parasite_new (name, flags, size, data);
|
|
|
|
}
|
2007-12-22 00:37:01 +08:00
|
|
|
|
|
|
|
g_free (name);
|
|
|
|
g_free (data);
|
2003-05-15 03:21:42 +08:00
|
|
|
|
2007-12-22 00:37:01 +08:00
|
|
|
return parasite;
|
2003-05-15 03:21:42 +08:00
|
|
|
}
|
|
|
|
|
2022-10-20 06:11:34 +08:00
|
|
|
/* Old paths are the PROP_PATHS property, even older than PROP_VECTORS. */
|
2003-05-22 01:38:14 +08:00
|
|
|
static gboolean
|
2003-05-15 03:21:42 +08:00
|
|
|
xcf_load_old_paths (XcfInfo *info,
|
2006-03-29 01:08:36 +08:00
|
|
|
GimpImage *image)
|
2003-05-15 03:21:42 +08:00
|
|
|
{
|
2003-05-22 01:38:14 +08:00
|
|
|
guint32 num_paths;
|
|
|
|
guint32 last_selected_row;
|
2003-05-15 03:21:42 +08:00
|
|
|
GimpVectors *active_vectors;
|
|
|
|
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_int32 (info, &last_selected_row, 1);
|
|
|
|
xcf_read_int32 (info, &num_paths, 1);
|
2003-05-15 03:21:42 +08:00
|
|
|
|
2022-04-30 04:40:32 +08:00
|
|
|
GIMP_LOG (XCF, "Number of old paths: %u", num_paths);
|
|
|
|
|
2003-05-15 03:21:42 +08:00
|
|
|
while (num_paths-- > 0)
|
2022-04-30 04:40:32 +08:00
|
|
|
if (! xcf_load_old_path (info, image))
|
|
|
|
return FALSE;
|
2003-05-15 03:21:42 +08:00
|
|
|
|
2009-08-02 21:48:27 +08:00
|
|
|
active_vectors =
|
2010-02-04 06:00:31 +08:00
|
|
|
GIMP_VECTORS (gimp_container_get_child_by_index (gimp_image_get_vectors (image),
|
2009-08-02 21:48:27 +08:00
|
|
|
last_selected_row));
|
2003-05-15 03:21:42 +08:00
|
|
|
|
|
|
|
if (active_vectors)
|
2023-01-11 09:18:32 +08:00
|
|
|
{
|
|
|
|
GList *list = g_list_prepend (NULL, active_vectors);
|
|
|
|
gimp_image_set_selected_vectors (image, list);
|
|
|
|
g_list_free (list);
|
|
|
|
}
|
2003-05-15 03:21:42 +08:00
|
|
|
|
2003-05-22 01:38:14 +08:00
|
|
|
return TRUE;
|
2003-05-15 03:21:42 +08:00
|
|
|
}
|
|
|
|
|
2003-05-22 01:38:14 +08:00
|
|
|
static gboolean
|
2003-05-15 03:21:42 +08:00
|
|
|
xcf_load_old_path (XcfInfo *info,
|
2006-03-29 01:08:36 +08:00
|
|
|
GimpImage *image)
|
2003-05-15 03:21:42 +08:00
|
|
|
{
|
2003-05-23 03:02:38 +08:00
|
|
|
gchar *name;
|
|
|
|
guint32 locked;
|
|
|
|
guint8 state;
|
|
|
|
guint32 closed;
|
|
|
|
guint32 num_points;
|
|
|
|
guint32 version; /* changed from num_paths */
|
|
|
|
GimpTattoo tattoo = 0;
|
|
|
|
GimpVectors *vectors;
|
|
|
|
GimpVectorsCompatPoint *points;
|
|
|
|
gint i;
|
2003-05-15 03:21:42 +08:00
|
|
|
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_string (info, &name, 1);
|
|
|
|
xcf_read_int32 (info, &locked, 1);
|
|
|
|
xcf_read_int8 (info, &state, 1);
|
|
|
|
xcf_read_int32 (info, &closed, 1);
|
|
|
|
xcf_read_int32 (info, &num_points, 1);
|
|
|
|
xcf_read_int32 (info, &version, 1);
|
2003-05-15 03:21:42 +08:00
|
|
|
|
2003-05-23 03:02:38 +08:00
|
|
|
if (version == 2)
|
2003-05-15 03:21:42 +08:00
|
|
|
{
|
2003-05-22 01:38:14 +08:00
|
|
|
guint32 dummy;
|
|
|
|
|
2003-05-15 03:21:42 +08:00
|
|
|
/* Had extra type field and points are stored as doubles */
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_int32 (info, (guint32 *) &dummy, 1);
|
2003-05-15 03:21:42 +08:00
|
|
|
}
|
|
|
|
else if (version == 3)
|
|
|
|
{
|
2003-05-22 01:38:14 +08:00
|
|
|
guint32 dummy;
|
|
|
|
|
2018-04-26 02:31:11 +08:00
|
|
|
/* Has extra tattoo field */
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_int32 (info, (guint32 *) &dummy, 1);
|
|
|
|
xcf_read_int32 (info, (guint32 *) &tattoo, 1);
|
2003-05-15 03:21:42 +08:00
|
|
|
}
|
2003-05-23 03:02:38 +08:00
|
|
|
else if (version != 1)
|
2003-05-15 03:21:42 +08:00
|
|
|
{
|
2022-04-30 04:40:32 +08:00
|
|
|
g_printerr ("Unknown path type (version: %u). Possibly corrupt XCF file.\n", version);
|
2003-05-15 03:21:42 +08:00
|
|
|
|
2021-08-28 00:10:08 +08:00
|
|
|
g_free (name);
|
2003-05-23 03:02:38 +08:00
|
|
|
return FALSE;
|
|
|
|
}
|
2003-05-15 03:21:42 +08:00
|
|
|
|
2003-09-09 23:46:59 +08:00
|
|
|
/* skip empty compatibility paths */
|
|
|
|
if (num_points == 0)
|
2012-02-07 21:06:12 +08:00
|
|
|
{
|
|
|
|
g_free (name);
|
|
|
|
return FALSE;
|
|
|
|
}
|
2003-09-09 23:46:59 +08:00
|
|
|
|
2003-05-23 03:02:38 +08:00
|
|
|
points = g_new0 (GimpVectorsCompatPoint, num_points);
|
2003-05-22 01:38:14 +08:00
|
|
|
|
2003-05-23 03:02:38 +08:00
|
|
|
for (i = 0; i < num_points; i++)
|
2003-05-15 20:32:53 +08:00
|
|
|
{
|
2003-05-23 03:02:38 +08:00
|
|
|
if (version == 1)
|
2003-05-15 20:32:53 +08:00
|
|
|
{
|
2003-05-23 03:02:38 +08:00
|
|
|
gint32 x;
|
|
|
|
gint32 y;
|
2003-05-15 20:32:53 +08:00
|
|
|
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_int32 (info, &points[i].type, 1);
|
|
|
|
xcf_read_int32 (info, (guint32 *) &x, 1);
|
|
|
|
xcf_read_int32 (info, (guint32 *) &y, 1);
|
2003-05-15 20:32:53 +08:00
|
|
|
|
2003-05-23 03:02:38 +08:00
|
|
|
points[i].x = x;
|
|
|
|
points[i].y = y;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
gfloat x;
|
|
|
|
gfloat y;
|
2003-09-01 07:46:05 +08:00
|
|
|
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_int32 (info, &points[i].type, 1);
|
|
|
|
xcf_read_float (info, &x, 1);
|
|
|
|
xcf_read_float (info, &y, 1);
|
2003-05-19 00:36:10 +08:00
|
|
|
|
2003-05-23 03:02:38 +08:00
|
|
|
points[i].x = x;
|
|
|
|
points[i].y = y;
|
|
|
|
}
|
|
|
|
}
|
2003-05-19 00:36:10 +08:00
|
|
|
|
2006-03-29 01:08:36 +08:00
|
|
|
vectors = gimp_vectors_compat_new (image, name, points, num_points, closed);
|
2003-05-15 20:32:53 +08:00
|
|
|
|
2004-11-03 19:50:37 +08:00
|
|
|
g_free (name);
|
2003-05-23 03:02:38 +08:00
|
|
|
g_free (points);
|
2003-05-15 03:21:42 +08:00
|
|
|
|
2021-12-16 07:35:20 +08:00
|
|
|
if (locked)
|
|
|
|
info->linked_paths = g_list_prepend (info->linked_paths, vectors);
|
2003-05-15 20:32:53 +08:00
|
|
|
|
2003-05-23 03:02:38 +08:00
|
|
|
if (tattoo)
|
2007-12-19 03:12:43 +08:00
|
|
|
gimp_item_set_tattoo (GIMP_ITEM (vectors), tattoo);
|
2003-05-15 03:21:42 +08:00
|
|
|
|
2006-03-29 01:08:36 +08:00
|
|
|
gimp_image_add_vectors (image, vectors,
|
2009-08-04 01:21:51 +08:00
|
|
|
NULL, /* can't be a tree */
|
2010-02-04 06:00:31 +08:00
|
|
|
gimp_container_get_n_children (gimp_image_get_vectors (image)),
|
2008-10-10 03:40:41 +08:00
|
|
|
FALSE);
|
2003-05-15 03:21:42 +08:00
|
|
|
|
2003-05-22 01:38:14 +08:00
|
|
|
return TRUE;
|
2003-05-15 03:21:42 +08:00
|
|
|
}
|
|
|
|
|
2022-10-20 06:11:34 +08:00
|
|
|
/* Old vectors are the PROP_VECTORS property up to all GIMP 2.10 versions. */
|
2003-09-09 23:46:59 +08:00
|
|
|
static gboolean
|
2022-10-20 06:11:34 +08:00
|
|
|
xcf_load_old_vectors (XcfInfo *info,
|
|
|
|
GimpImage *image)
|
2003-09-09 23:46:59 +08:00
|
|
|
{
|
|
|
|
guint32 version;
|
2003-09-11 00:22:33 +08:00
|
|
|
guint32 active_index;
|
|
|
|
guint32 num_paths;
|
2003-09-09 23:46:59 +08:00
|
|
|
GimpVectors *active_vectors;
|
|
|
|
|
2004-03-03 23:54:19 +08:00
|
|
|
#ifdef GIMP_XCF_PATH_DEBUG
|
2022-10-20 06:11:34 +08:00
|
|
|
g_printerr ("xcf_load_old_vectors\n");
|
2004-03-03 23:54:19 +08:00
|
|
|
#endif
|
2003-09-09 23:46:59 +08:00
|
|
|
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_int32 (info, &version, 1);
|
2003-09-09 23:46:59 +08:00
|
|
|
|
|
|
|
if (version != 1)
|
|
|
|
{
|
2006-10-09 16:17:22 +08:00
|
|
|
gimp_message (info->gimp, G_OBJECT (info->progress),
|
|
|
|
GIMP_MESSAGE_WARNING,
|
2006-08-09 05:06:36 +08:00
|
|
|
"Unknown vectors version: %d (skipping)", version);
|
2003-09-09 23:46:59 +08:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_int32 (info, &active_index, 1);
|
|
|
|
xcf_read_int32 (info, &num_paths, 1);
|
2003-09-09 23:46:59 +08:00
|
|
|
|
2004-03-03 23:54:19 +08:00
|
|
|
#ifdef GIMP_XCF_PATH_DEBUG
|
2003-09-11 00:22:33 +08:00
|
|
|
g_printerr ("%d paths (active: %d)\n", num_paths, active_index);
|
2004-03-03 23:54:19 +08:00
|
|
|
#endif
|
2003-09-09 23:46:59 +08:00
|
|
|
|
|
|
|
while (num_paths-- > 0)
|
2022-10-20 06:11:34 +08:00
|
|
|
if (! xcf_load_old_vector (info, image))
|
2003-09-11 00:22:33 +08:00
|
|
|
return FALSE;
|
2003-09-09 23:46:59 +08:00
|
|
|
|
2009-08-02 21:48:27 +08:00
|
|
|
/* FIXME tree */
|
|
|
|
active_vectors =
|
2010-02-04 06:00:31 +08:00
|
|
|
GIMP_VECTORS (gimp_container_get_child_by_index (gimp_image_get_vectors (image),
|
2009-08-02 21:48:27 +08:00
|
|
|
active_index));
|
2003-09-09 23:46:59 +08:00
|
|
|
|
|
|
|
if (active_vectors)
|
2023-01-11 09:18:32 +08:00
|
|
|
{
|
|
|
|
GList *list = g_list_prepend (NULL, active_vectors);
|
|
|
|
gimp_image_set_selected_vectors (image, list);
|
|
|
|
g_list_free (list);
|
|
|
|
}
|
2003-09-09 23:46:59 +08:00
|
|
|
|
2004-03-03 23:54:19 +08:00
|
|
|
#ifdef GIMP_XCF_PATH_DEBUG
|
2022-10-20 06:11:34 +08:00
|
|
|
g_printerr ("xcf_load_old_vectors: loaded %d bytes\n", info->cp - base);
|
2004-03-03 23:54:19 +08:00
|
|
|
#endif
|
2003-09-09 23:46:59 +08:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
2022-10-20 06:11:34 +08:00
|
|
|
xcf_load_old_vector (XcfInfo *info,
|
|
|
|
GimpImage *image)
|
2003-09-09 23:46:59 +08:00
|
|
|
{
|
2003-09-11 00:22:33 +08:00
|
|
|
gchar *name;
|
|
|
|
GimpTattoo tattoo = 0;
|
2003-09-12 03:52:29 +08:00
|
|
|
guint32 visible;
|
2003-09-11 00:22:33 +08:00
|
|
|
guint32 linked;
|
|
|
|
guint32 num_parasites;
|
|
|
|
guint32 num_strokes;
|
|
|
|
GimpVectors *vectors;
|
|
|
|
gint i;
|
2003-09-09 23:46:59 +08:00
|
|
|
|
2004-03-03 23:54:19 +08:00
|
|
|
#ifdef GIMP_XCF_PATH_DEBUG
|
2022-10-20 06:11:34 +08:00
|
|
|
g_printerr ("xcf_load_old_vector\n");
|
2004-03-03 23:54:19 +08:00
|
|
|
#endif
|
2003-09-09 23:46:59 +08:00
|
|
|
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_string (info, &name, 1);
|
|
|
|
xcf_read_int32 (info, &tattoo, 1);
|
|
|
|
xcf_read_int32 (info, &visible, 1);
|
|
|
|
xcf_read_int32 (info, &linked, 1);
|
|
|
|
xcf_read_int32 (info, &num_parasites, 1);
|
|
|
|
xcf_read_int32 (info, &num_strokes, 1);
|
2003-09-11 00:22:33 +08:00
|
|
|
|
2004-03-03 23:54:19 +08:00
|
|
|
#ifdef GIMP_XCF_PATH_DEBUG
|
2006-07-06 20:42:34 +08:00
|
|
|
g_printerr ("name: %s, tattoo: %d, visible: %d, linked: %d, "
|
|
|
|
"num_parasites %d, num_strokes %d\n",
|
2003-09-12 03:52:29 +08:00
|
|
|
name, tattoo, visible, linked, num_parasites, num_strokes);
|
2004-03-03 23:54:19 +08:00
|
|
|
#endif
|
2003-09-09 23:46:59 +08:00
|
|
|
|
2006-03-29 01:08:36 +08:00
|
|
|
vectors = gimp_vectors_new (image, name);
|
2012-02-07 21:06:12 +08:00
|
|
|
g_free (name);
|
2003-09-09 23:46:59 +08:00
|
|
|
|
2007-12-19 03:12:43 +08:00
|
|
|
gimp_item_set_visible (GIMP_ITEM (vectors), visible, FALSE);
|
2021-12-16 07:35:20 +08:00
|
|
|
if (linked)
|
|
|
|
info->linked_paths = g_list_prepend (info->linked_paths, vectors);
|
2003-09-11 00:22:33 +08:00
|
|
|
|
|
|
|
if (tattoo)
|
2007-12-19 03:12:43 +08:00
|
|
|
gimp_item_set_tattoo (GIMP_ITEM (vectors), tattoo);
|
2003-09-11 00:22:33 +08:00
|
|
|
|
|
|
|
for (i = 0; i < num_parasites; i++)
|
|
|
|
{
|
2006-07-06 20:42:34 +08:00
|
|
|
GimpParasite *parasite = xcf_load_parasite (info);
|
2014-03-22 07:11:15 +08:00
|
|
|
GError *error = NULL;
|
2003-09-11 00:22:33 +08:00
|
|
|
|
|
|
|
if (! parasite)
|
|
|
|
return FALSE;
|
|
|
|
|
2014-03-22 07:11:15 +08:00
|
|
|
if (! gimp_item_parasite_validate (GIMP_ITEM (vectors), parasite, &error))
|
|
|
|
{
|
|
|
|
gimp_message (info->gimp, G_OBJECT (info->progress),
|
|
|
|
GIMP_MESSAGE_WARNING,
|
|
|
|
"Warning, invalid vectors parasite in XCF file: %s",
|
|
|
|
error->message);
|
|
|
|
g_clear_error (&error);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
gimp_item_parasite_attach (GIMP_ITEM (vectors), parasite, FALSE);
|
|
|
|
}
|
|
|
|
|
2003-09-11 00:22:33 +08:00
|
|
|
gimp_parasite_free (parasite);
|
|
|
|
}
|
|
|
|
|
2003-09-09 23:46:59 +08:00
|
|
|
for (i = 0; i < num_strokes; i++)
|
|
|
|
{
|
2005-08-03 21:48:40 +08:00
|
|
|
guint32 stroke_type_id;
|
|
|
|
guint32 closed;
|
|
|
|
guint32 num_axes;
|
|
|
|
guint32 num_control_points;
|
|
|
|
guint32 type;
|
2018-05-19 00:38:45 +08:00
|
|
|
gfloat coords[13] = GIMP_COORDS_DEFAULT_VALUES;
|
2003-09-11 00:22:33 +08:00
|
|
|
GimpStroke *stroke;
|
|
|
|
gint j;
|
2003-09-09 23:46:59 +08:00
|
|
|
|
2012-05-04 06:50:23 +08:00
|
|
|
GimpValueArray *control_points;
|
2016-03-26 22:59:26 +08:00
|
|
|
GValue value = G_VALUE_INIT;
|
2012-05-04 06:50:23 +08:00
|
|
|
GimpAnchor anchor = { { 0, } };
|
|
|
|
GType stroke_type;
|
2003-09-09 23:46:59 +08:00
|
|
|
|
2003-09-11 00:22:33 +08:00
|
|
|
g_value_init (&value, GIMP_TYPE_ANCHOR);
|
2003-09-09 23:46:59 +08:00
|
|
|
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_int32 (info, &stroke_type_id, 1);
|
|
|
|
xcf_read_int32 (info, &closed, 1);
|
|
|
|
xcf_read_int32 (info, &num_axes, 1);
|
|
|
|
xcf_read_int32 (info, &num_control_points, 1);
|
2003-09-09 23:46:59 +08:00
|
|
|
|
2004-03-03 23:54:19 +08:00
|
|
|
#ifdef GIMP_XCF_PATH_DEBUG
|
2003-09-09 23:46:59 +08:00
|
|
|
g_printerr ("stroke_type: %d, closed: %d, num_axes %d, len %d\n",
|
|
|
|
stroke_type_id, closed, num_axes, num_control_points);
|
2004-03-03 23:54:19 +08:00
|
|
|
#endif
|
2003-09-09 23:46:59 +08:00
|
|
|
|
|
|
|
switch (stroke_type_id)
|
|
|
|
{
|
|
|
|
case XCF_STROKETYPE_BEZIER_STROKE:
|
2004-01-19 01:41:29 +08:00
|
|
|
stroke_type = GIMP_TYPE_BEZIER_STROKE;
|
2003-09-09 23:46:59 +08:00
|
|
|
break;
|
2003-09-11 00:22:33 +08:00
|
|
|
|
2003-09-09 23:46:59 +08:00
|
|
|
default:
|
|
|
|
g_printerr ("skipping unknown stroke type\n");
|
2003-09-10 18:40:57 +08:00
|
|
|
xcf_seek_pos (info,
|
2003-09-09 23:46:59 +08:00
|
|
|
info->cp + 4 * num_axes * num_control_points,
|
|
|
|
NULL);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2006-07-06 20:42:34 +08:00
|
|
|
if (num_axes < 2 || num_axes > 6)
|
|
|
|
{
|
|
|
|
g_printerr ("bad number of axes in stroke description\n");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2012-05-04 06:50:23 +08:00
|
|
|
control_points = gimp_value_array_new (num_control_points);
|
2003-09-09 23:46:59 +08:00
|
|
|
|
2003-09-11 00:22:33 +08:00
|
|
|
anchor.selected = FALSE;
|
2003-09-09 23:46:59 +08:00
|
|
|
|
2003-09-11 00:22:33 +08:00
|
|
|
for (j = 0; j < num_control_points; j++)
|
2003-09-09 23:46:59 +08:00
|
|
|
{
|
2017-03-23 19:24:38 +08:00
|
|
|
xcf_read_int32 (info, &type, 1);
|
|
|
|
xcf_read_float (info, coords, num_axes);
|
2003-09-09 23:46:59 +08:00
|
|
|
|
|
|
|
anchor.type = type;
|
|
|
|
anchor.position.x = coords[0];
|
|
|
|
anchor.position.y = coords[1];
|
|
|
|
anchor.position.pressure = coords[2];
|
|
|
|
anchor.position.xtilt = coords[3];
|
|
|
|
anchor.position.ytilt = coords[4];
|
|
|
|
anchor.position.wheel = coords[5];
|
|
|
|
|
|
|
|
g_value_set_boxed (&value, &anchor);
|
2012-05-04 06:50:23 +08:00
|
|
|
gimp_value_array_append (control_points, &value);
|
2003-09-09 23:46:59 +08:00
|
|
|
|
2004-03-03 23:54:19 +08:00
|
|
|
#ifdef GIMP_XCF_PATH_DEBUG
|
2003-09-09 23:46:59 +08:00
|
|
|
g_printerr ("Anchor: %d, (%f, %f, %f, %f, %f, %f)\n", type,
|
|
|
|
coords[0], coords[1], coords[2], coords[3],
|
|
|
|
coords[4], coords[5]);
|
2004-03-03 23:54:19 +08:00
|
|
|
#endif
|
2003-09-09 23:46:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
g_value_unset (&value);
|
|
|
|
|
2003-09-11 00:22:33 +08:00
|
|
|
stroke = g_object_new (stroke_type,
|
|
|
|
"closed", closed,
|
|
|
|
"control-points", control_points,
|
|
|
|
NULL);
|
2003-09-09 23:46:59 +08:00
|
|
|
|
|
|
|
gimp_vectors_stroke_add (vectors, stroke);
|
2012-02-07 21:06:12 +08:00
|
|
|
|
|
|
|
g_object_unref (stroke);
|
2012-05-04 06:50:23 +08:00
|
|
|
gimp_value_array_unref (control_points);
|
2003-09-09 23:46:59 +08:00
|
|
|
}
|
|
|
|
|
2006-03-29 01:08:36 +08:00
|
|
|
gimp_image_add_vectors (image, vectors,
|
2009-08-04 01:21:51 +08:00
|
|
|
NULL, /* FIXME tree */
|
2010-02-04 06:00:31 +08:00
|
|
|
gimp_container_get_n_children (gimp_image_get_vectors (image)),
|
2008-10-10 03:40:41 +08:00
|
|
|
FALSE);
|
2003-09-09 23:46:59 +08:00
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
2010-08-07 06:26:58 +08:00
|
|
|
|
|
|
|
static gboolean
|
|
|
|
xcf_skip_unknown_prop (XcfInfo *info,
|
|
|
|
gsize size)
|
|
|
|
{
|
|
|
|
guint8 buf[16];
|
|
|
|
guint amount;
|
|
|
|
|
|
|
|
while (size > 0)
|
|
|
|
{
|
2013-10-09 03:22:14 +08:00
|
|
|
if (g_input_stream_is_closed (info->input))
|
2010-08-07 06:26:58 +08:00
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
amount = MIN (16, size);
|
2017-03-23 19:24:38 +08:00
|
|
|
amount = xcf_read_int8 (info, buf, amount);
|
2014-06-07 22:40:39 +08:00
|
|
|
if (amount == 0)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
size -= amount;
|
2010-08-07 06:26:58 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
2019-07-11 21:04:30 +08:00
|
|
|
|
|
|
|
static gboolean
|
|
|
|
xcf_item_path_is_parent (GList *path,
|
|
|
|
GList *parent_path)
|
|
|
|
{
|
|
|
|
GList *iter = path;
|
|
|
|
GList *parent_iter = parent_path;
|
|
|
|
|
|
|
|
if (g_list_length (parent_path) >= g_list_length (path))
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
while (iter && parent_iter)
|
|
|
|
{
|
|
|
|
if (iter->data != parent_iter->data)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
iter = iter->next;
|
|
|
|
parent_iter = parent_iter->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
xcf_fix_item_path (GimpLayer *layer,
|
|
|
|
GList **path,
|
|
|
|
GList *broken_paths)
|
|
|
|
{
|
|
|
|
GList *iter;
|
|
|
|
|
|
|
|
for (iter = broken_paths; iter; iter = iter->next)
|
|
|
|
{
|
|
|
|
if (xcf_item_path_is_parent (*path, iter->data))
|
|
|
|
{
|
|
|
|
/* Not much to do when the absent path is a parent. */
|
|
|
|
g_printerr ("%s: layer '%s' moved to layer tree root because of missing parent.",
|
|
|
|
G_STRFUNC, gimp_object_get_name (layer));
|
|
|
|
g_clear_pointer (path, g_list_free);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check if a parent of path, or path itself is on the same
|
|
|
|
* tree level as any broken path; and if so, and if the broken path is
|
|
|
|
* in a lower position in the item group, decrement it.
|
|
|
|
*/
|
|
|
|
for (iter = broken_paths; iter; iter = iter->next)
|
|
|
|
{
|
|
|
|
GList *broken_path = iter->data;
|
|
|
|
GList *iter1 = *path;
|
|
|
|
GList *iter2 = broken_path;
|
|
|
|
|
|
|
|
if (g_list_length (broken_path) > g_list_length (*path))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
while (iter1 && iter2)
|
|
|
|
{
|
|
|
|
if (iter2->next && iter1->data != iter2->data)
|
|
|
|
/* Paths diverged before reaching iter2 leaf. */
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (iter2->next)
|
|
|
|
{
|
|
|
|
iter1 = iter1->next;
|
|
|
|
iter2 = iter2->next;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (GPOINTER_TO_UINT (iter2->data) < GPOINTER_TO_UINT (iter1->data))
|
|
|
|
iter1->data = GUINT_TO_POINTER (GPOINTER_TO_UINT (iter1->data) - 1);
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2024-01-23 00:49:10 +08:00
|
|
|
}
|