summaryrefslogtreecommitdiffstats
path: root/filters/chalk/xcf/xcf/xcf-save.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'filters/chalk/xcf/xcf/xcf-save.cpp')
-rw-r--r--filters/chalk/xcf/xcf/xcf-save.cpp1826
1 files changed, 1826 insertions, 0 deletions
diff --git a/filters/chalk/xcf/xcf/xcf-save.cpp b/filters/chalk/xcf/xcf/xcf-save.cpp
new file mode 100644
index 000000000..a0dbb9b5a
--- /dev/null
+++ b/filters/chalk/xcf/xcf/xcf-save.cpp
@@ -0,0 +1,1826 @@
+/* The GIMP -- an image manipulation program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <string.h> /* strcpy, strlen */
+
+#include <glib-object.h>
+
+#include "libgimpbase/gimpbase.h"
+#include "libgimpcolor/gimpcolor.h"
+
+#include "core/core-types.h"
+
+#include "base/tile.h"
+#include "base/tile-manager.h"
+#include "base/tile-manager-private.h"
+
+#include "core/gimpchannel.h"
+#include "core/gimpdrawable.h"
+#include "core/gimpgrid.h"
+#include "core/gimpimage.h"
+#include "core/gimpimage-grid.h"
+#include "core/gimpimage-guides.h"
+#include "core/gimplayer.h"
+#include "core/gimplayer-floating-sel.h"
+#include "core/gimplayermask.h"
+#include "core/gimplist.h"
+#include "core/gimpparasitelist.h"
+#include "core/gimpunit.h"
+
+#include "text/gimptextlayer.h"
+#include "text/gimptextlayer-xcf.h"
+
+#include "vectors/gimpanchor.h"
+#include "vectors/gimpstroke.h"
+#include "vectors/gimpbezierstroke.h"
+#include "vectors/gimpvectors.h"
+#include "vectors/gimpvectors-compat.h"
+
+#include "xcf-private.h"
+#include "xcf-read.h"
+#include "xcf-seek.h"
+#include "xcf-write.h"
+
+#include "gimp-intl.h"
+
+
+static bool xcf_save_image_props (XcfInfo *info,
+ KisImage *gimage,
+ GError **error);
+static bool xcf_save_layer_props (XcfInfo *info,
+ KisImage *gimage,
+ KisLayer *layer,
+ GError **error);
+static bool xcf_save_channel_props (XcfInfo *info,
+ KisImage *gimage,
+ GimpChannel *channel,
+ GError **error);
+static bool xcf_save_prop (XcfInfo *info,
+ KisImage *gimage,
+ PropType prop_type,
+ GError **error,
+ ...);
+static bool xcf_save_layer (XcfInfo *info,
+ KisImage *gimage,
+ KisLayer *layer,
+ GError **error);
+static bool xcf_save_channel (XcfInfo *info,
+ KisImage *gimage,
+ GimpChannel *channel,
+ GError **error);
+static bool xcf_save_hierarchy (XcfInfo *info,
+ TileManager *tiles,
+ GError **error);
+static bool xcf_save_level (XcfInfo *info,
+ TileManager *tiles,
+ GError **error);
+static bool xcf_save_tile (XcfInfo *info,
+ Tile *tile,
+ GError **error);
+static bool xcf_save_tile_rle (XcfInfo *info,
+ Tile *tile,
+ guchar *rlebuf,
+ GError **error);
+static bool xcf_save_parasite (XcfInfo *info,
+ KisAnnotation *parasite,
+ GError **error);
+static bool xcf_save_parasite_list (XcfInfo *info,
+ KisAnnotationList *parasite,
+ GError **error);
+static bool xcf_save_old_paths (XcfInfo *info,
+ KisImage *gimage,
+ GError **error);
+static bool xcf_save_vectors (XcfInfo *info,
+ KisImage *gimage,
+ GError **error);
+
+
+/* private convenience macros */
+#define xcf_write_int32_check_error(info, data, count) G_STMT_START { \
+ info->cp += xcf_write_int32 (info->fp, data, count, &tmp_error); \
+ if (tmp_error) \
+ { \
+ g_propagate_error (error, tmp_error); \
+ return FALSE; \
+ } \
+ } G_STMT_END
+
+#define xcf_write_int8_check_error(info, data, count) G_STMT_START { \
+ info->cp += xcf_write_int8 (info->fp, data, count, &tmp_error); \
+ if (tmp_error) \
+ { \
+ g_propagate_error (error, tmp_error); \
+ return FALSE; \
+ } \
+ } G_STMT_END
+
+#define xcf_write_float_check_error(info, data, count) G_STMT_START { \
+ info->cp += xcf_write_float (info->fp, data, count, &tmp_error); \
+ if (tmp_error) \
+ { \
+ g_propagate_error (error, tmp_error); \
+ return FALSE; \
+ } \
+ } G_STMT_END
+
+#define xcf_write_string_check_error(info, data, count) G_STMT_START { \
+ info->cp += xcf_write_string (info->fp, data, count, &tmp_error); \
+ if (tmp_error) \
+ { \
+ g_propagate_error (error, tmp_error); \
+ return FALSE; \
+ } \
+ } G_STMT_END
+
+#define xcf_write_int32_print_error(info, data, count) G_STMT_START { \
+ info->cp += xcf_write_int32 (info->fp, data, count, &error); \
+ if (error) \
+ { \
+ g_message (_("Error saving XCF file: %s"), \
+ error->message); \
+ return FALSE; \
+ } \
+ } G_STMT_END
+
+#define xcf_write_int8_print_error(info, data, count) G_STMT_START { \
+ info->cp += xcf_write_int8 (info->fp, data, count, &error); \
+ if (error) \
+ { \
+ g_message (_("Error saving XCF file: %s"), \
+ error->message); \
+ return FALSE; \
+ } \
+ } G_STMT_END
+
+#define xcf_write_float_print_error(info, data, count) G_STMT_START { \
+ info->cp += xcf_write_float (info->fp, data, count, &error); \
+ if (error) \
+ { \
+ g_message (_("Error saving XCF file: %s"), \
+ error->message); \
+ return FALSE; \
+ } \
+ } G_STMT_END
+
+#define xcf_write_string_print_error(info, data, count) G_STMT_START { \
+ info->cp += xcf_write_string (info->fp, data, count, &error); \
+ if (error) \
+ { \
+ g_message (_("Error saving XCF file: %s"), \
+ error->message); \
+ return FALSE; \
+ } \
+ } G_STMT_END
+
+#define xcf_write_prop_type_check_error(info, prop_type) G_STMT_START { \
+ TQ_INT32 _prop_int32 = prop_type; \
+ xcf_write_int32_check_error (info, &_prop_int32, 1); \
+ } G_STMT_END
+
+#define xcf_write_prop_type_print_error(info, prop_type) G_STMT_START { \
+ TQ_INT32 _prop_int32 = prop_type; \
+ xcf_write_int32_print_error (info, &_prop_int32, 1); \
+ } G_STMT_END
+
+#define xcf_check_error(x) G_STMT_START { \
+ if (! (x)) \
+ return FALSE; \
+ } G_STMT_END
+
+#define xcf_print_error(x) G_STMT_START { \
+ if (! (x)) \
+ { \
+ g_message (_("Error saving XCF file: %s"), \
+ error->message); \
+ return FALSE; \
+ } \
+ } G_STMT_END
+
+
+void
+xcf_save_choose_format (XcfInfo *info,
+ KisImage *gimage)
+{
+ KisLayer *layer;
+ GList *list;
+
+ TQ_INT32 save_version = 0; /* default to oldest */
+
+ if (gimage->cmap)
+ save_version = 1; /* need version 1 for colormaps */
+
+ for (list = GIMP_LIST (gimage->layers)->list;
+ list && save_version < 2;
+ list = g_list_next (list))
+ {
+ layer = GIMP_LAYER (list->data);
+
+ switch (layer->mode)
+ {
+ /* new layer modes not supported by gimp-1.2 */
+ case GIMP_SOFTLIGHT_MODE:
+ case GIMP_GRAIN_EXTRACT_MODE:
+ case GIMP_GRAIN_MERGE_MODE:
+ case GIMP_COLOR_ERASE_MODE:
+ save_version = 2;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ info->file_version = save_version;
+}
+
+TQ_INT32
+xcf_save_image (XcfInfo *info,
+ KisImage *gimage)
+{
+ KisLayer *layer;
+ KisLayer *floating_layer;
+ GimpChannel *channel;
+ TQ_INT32 saved_pos;
+ TQ_INT32 offset;
+ TQ_UINT32 nlayers;
+ TQ_UINT32 nchannels;
+ GList *list;
+ bool have_selection;
+ TQ_INT32 t1, t2, t3, t4;
+ TQCString version_tag[14];
+ GError *error = NULL;
+
+ floating_layer = gimp_image_floating_sel (gimage);
+ if (floating_layer)
+ floating_sel_relax (floating_layer, FALSE);
+
+ /* write out the tag information for the image */
+ if (info->file_version > 0)
+ {
+ sprintf (version_tag, "gimp xcf v%03d", info->file_version);
+ }
+ else
+ {
+ strcpy (version_tag, "gimp xcf file");
+ }
+ xcf_write_int8_print_error (info, (TQ_UINT8 *) version_tag, 14);
+
+ /* write out the width, height and image type information for the image */
+ xcf_write_int32_print_error (info, (TQ_INT32 *) &gimage->width, 1);
+ xcf_write_int32_print_error (info, (TQ_INT32 *) &gimage->height, 1);
+ xcf_write_int32_print_error (info, (TQ_INT32 *) &gimage->base_type, 1);
+
+ /* determine the number of layers and channels in the image */
+ nlayers = (TQ_UINT32) gimp_container_num_children (gimage->layers);
+ nchannels = (TQ_UINT32) gimp_container_num_children (gimage->channels);
+
+ /* check and see if we have to save out the selection */
+ have_selection = gimp_channel_bounds (gimp_image_get_mask (gimage),
+ &t1, &t2, &t3, &t4);
+ if (have_selection)
+ nchannels += 1;
+
+ /* write the property information for the image.
+ */
+
+ xcf_print_error (xcf_save_image_props (info, gimage, &error));
+
+ /* save the current file position as it is the start of where
+ * we place the layer offset information.
+ */
+ saved_pos = info->cp;
+
+ /* seek to after the offset lists */
+ xcf_print_error (xcf_seek_pos (info,
+ info->cp + (nlayers + nchannels + 2) * 4,
+ &error));
+
+ for (list = GIMP_LIST (gimage->layers)->list;
+ list;
+ list = g_list_next (list))
+ {
+ layer = (KisLayer *) list->data;
+
+ /* save the start offset of where we are writing
+ * out the next layer.
+ */
+ offset = info->cp;
+
+ /* write out the layer. */
+ xcf_print_error (xcf_save_layer (info, gimage, layer, &error));
+
+ /* seek back to where we are to write out the next
+ * layer offset and write it out.
+ */
+ xcf_print_error (xcf_seek_pos (info, saved_pos, &error));
+ xcf_write_int32_print_error (info, &offset, 1);
+
+ /* increment the location we are to write out the
+ * next offset.
+ */
+ saved_pos = info->cp;
+
+ /* seek to the end of the file which is where
+ * we will write out the next layer.
+ */
+ xcf_print_error (xcf_seek_end (info, &error));
+ }
+
+ /* write out a '0' offset position to indicate the end
+ * of the layer offsets.
+ */
+ offset = 0;
+ xcf_print_error (xcf_seek_pos (info, saved_pos, &error));
+ xcf_write_int32_print_error (info, &offset, 1);
+ saved_pos = info->cp;
+ xcf_print_error (xcf_seek_end (info, &error));
+
+ list = GIMP_LIST (gimage->channels)->list;
+
+ while (list || have_selection)
+ {
+ if (list)
+ {
+ channel = (GimpChannel *) list->data;
+
+ list = g_list_next (list);
+ }
+ else
+ {
+ channel = gimage->selection_mask;
+ have_selection = FALSE;
+ }
+
+ /* save the start offset of where we are writing
+ * out the next channel.
+ */
+ offset = info->cp;
+
+ /* write out the layer. */
+ xcf_print_error (xcf_save_channel (info, gimage, channel, &error));
+
+ /* seek back to where we are to write out the next
+ * channel offset and write it out.
+ */
+ xcf_print_error (xcf_seek_pos (info, saved_pos, &error));
+ xcf_write_int32_print_error (info, &offset, 1);
+
+ /* increment the location we are to write out the
+ * next offset.
+ */
+ saved_pos = info->cp;
+
+ /* seek to the end of the file which is where
+ * we will write out the next channel.
+ */
+ xcf_print_error (xcf_seek_end (info, &error));
+ }
+
+ /* write out a '0' offset position to indicate the end
+ * of the channel offsets.
+ */
+ offset = 0;
+ xcf_print_error (xcf_seek_pos (info, saved_pos, &error));
+ xcf_write_int32_print_error (info, &offset, 1);
+ saved_pos = info->cp;
+
+ if (floating_layer)
+ floating_sel_rigor (floating_layer, FALSE);
+
+ return !ferror(info->fp);
+}
+
+static bool
+xcf_save_image_props (XcfInfo *info,
+ KisImage *gimage,
+ GError **error)
+{
+ KisAnnotation *parasite = NULL;
+ GimpUnit unit = gimp_image_get_unit (gimage);
+
+ /* check and see if we should save the colormap property */
+ if (gimage->cmap)
+ xcf_check_error (xcf_save_prop (info, gimage, PROP_COLORMAP, error,
+ gimage->num_cols, gimage->cmap));
+
+ if (info->compression != COMPRESS_NONE)
+ xcf_check_error (xcf_save_prop (info, gimage, PROP_COMPRESSION,
+ error, info->compression));
+
+ if (gimage->guides)
+ xcf_check_error (xcf_save_prop (info, gimage, PROP_GUIDES,
+ error, gimage->guides));
+
+ xcf_check_error (xcf_save_prop (info, gimage, PROP_RESOLUTION, error,
+ gimage->xresolution, gimage->yresolution));
+
+ xcf_check_error (xcf_save_prop (info, gimage, PROP_TATTOO, error,
+ gimage->tattoo_state));
+
+ if (gimp_parasite_list_length (gimage->parasites) > 0)
+ xcf_check_error (xcf_save_prop (info, gimage, PROP_PARASITES,
+ error, gimage->parasites));
+
+ if (unit < _gimp_unit_get_number_of_built_in_units (gimage->gimp))
+ xcf_check_error (xcf_save_prop (info, gimage, PROP_UNIT, error, unit));
+
+ if (gimp_container_num_children (gimage->vectors) > 0)
+ {
+ if (gimp_vectors_compat_is_compatible (gimage))
+ xcf_check_error (xcf_save_prop (info, gimage, PROP_PATHS, error));
+ else
+ xcf_check_error (xcf_save_prop (info, gimage, PROP_VECTORS, error));
+ }
+
+ if (unit >= _gimp_unit_get_number_of_built_in_units (gimage->gimp))
+ xcf_check_error (xcf_save_prop (info, gimage, PROP_USER_UNIT, error, unit));
+
+ if (GIMP_IS_GRID (gimage->grid))
+ {
+ GimpGrid *grid = gimp_image_get_grid (gimage);
+
+ parasite = gimp_grid_to_parasite (grid);
+ gimp_parasite_list_add (GIMP_IMAGE (gimage)->parasites, parasite);
+ }
+
+ if (gimp_parasite_list_length (GIMP_IMAGE (gimage)->parasites) > 0)
+ {
+ xcf_check_error (xcf_save_prop (info, gimage, PROP_PARASITES, error,
+ GIMP_IMAGE (gimage)->parasites));
+ }
+
+ if (parasite)
+ {
+ gimp_parasite_list_remove (GIMP_IMAGE (gimage)->parasites,
+ gimp_parasite_name (parasite));
+ gimp_parasite_free (parasite);
+ }
+
+ xcf_check_error (xcf_save_prop (info, gimage, PROP_END, error));
+
+ return TRUE;
+}
+
+static bool
+xcf_save_layer_props (XcfInfo *info,
+ KisImage *gimage,
+ KisLayer *layer,
+ GError **error)
+{
+ KisAnnotation *parasite = NULL;
+
+ if (layer == gimp_image_get_active_layer (gimage))
+ xcf_check_error (xcf_save_prop (info, gimage, PROP_ACTIVE_LAYER, error));
+
+ if (layer == gimp_image_floating_sel (gimage))
+ {
+ info->floating_sel_drawable = layer->fs.drawable;
+ xcf_check_error (xcf_save_prop (info, gimage, PROP_FLOATING_SELECTION,
+ error));
+ }
+
+ xcf_check_error (xcf_save_prop (info, gimage, PROP_OPACITY, error,
+ layer->opacity));
+ xcf_check_error (xcf_save_prop (info, gimage, PROP_VISIBLE, error,
+ gimp_item_get_visible (GIMP_ITEM (layer))));
+ xcf_check_error (xcf_save_prop (info, gimage, PROP_LINKED, error,
+ gimp_item_get_linked (GIMP_ITEM (layer))));
+ xcf_check_error (xcf_save_prop (info, gimage, PROP_LOCK_ALPHA,
+ error, layer->lock_alpha));
+
+ if (layer->mask)
+ {
+ xcf_check_error (xcf_save_prop (info, gimage, PROP_APPLY_MASK,
+ error, layer->mask->apply_mask));
+ xcf_check_error (xcf_save_prop (info, gimage, PROP_EDIT_MASK,
+ error, layer->mask->edit_mask));
+ xcf_check_error (xcf_save_prop (info, gimage, PROP_SHOW_MASK,
+ error, layer->mask->show_mask));
+ }
+ else
+ {
+ xcf_check_error (xcf_save_prop (info, gimage, PROP_APPLY_MASK,
+ error, FALSE));
+ xcf_check_error (xcf_save_prop (info, gimage, PROP_EDIT_MASK,
+ error, FALSE));
+ xcf_check_error (xcf_save_prop (info, gimage, PROP_SHOW_MASK,
+ error, FALSE));
+ }
+
+ xcf_check_error (xcf_save_prop (info, gimage, PROP_OFFSETS, error,
+ GIMP_ITEM (layer)->offset_x,
+ GIMP_ITEM (layer)->offset_y));
+ xcf_check_error (xcf_save_prop (info, gimage, PROP_MODE, error,
+ layer->mode));
+ xcf_check_error (xcf_save_prop (info, gimage, PROP_TATTOO, error,
+ GIMP_ITEM (layer)->tattoo));
+
+ if (GIMP_IS_TEXT_LAYER (layer) && GIMP_TEXT_LAYER (layer)->text)
+ {
+ GimpTextLayer *text_layer = GIMP_TEXT_LAYER (layer);
+ TQ_INT32 flags = gimp_text_layer_get_xcf_flags (text_layer);
+
+ gimp_text_layer_xcf_save_prepare (text_layer);
+
+ if (flags)
+ xcf_check_error (xcf_save_prop (info,
+ gimage, PROP_TEXT_LAYER_FLAGS, error,
+ flags));
+ }
+
+ if (gimp_parasite_list_length (GIMP_ITEM (layer)->parasites) > 0)
+ {
+ xcf_check_error (xcf_save_prop (info, gimage, PROP_PARASITES, error,
+ GIMP_ITEM (layer)->parasites));
+ }
+
+ if (parasite)
+ {
+ gimp_parasite_list_remove (GIMP_ITEM (layer)->parasites,
+ gimp_parasite_name (parasite));
+ gimp_parasite_free (parasite);
+ }
+
+ xcf_check_error (xcf_save_prop (info, gimage, PROP_END, error));
+
+ return TRUE;
+}
+
+static bool
+xcf_save_channel_props (XcfInfo *info,
+ KisImage *gimage,
+ GimpChannel *channel,
+ GError **error)
+{
+ guchar col[3];
+
+ if (channel == gimp_image_get_active_channel (gimage))
+ xcf_check_error (xcf_save_prop (info, gimage, PROP_ACTIVE_CHANNEL, error));
+
+ if (channel == gimage->selection_mask)
+ xcf_check_error (xcf_save_prop (info, gimage, PROP_SELECTION, error));
+
+ xcf_check_error (xcf_save_prop (info, gimage, PROP_OPACITY, error,
+ channel->color.a));
+ xcf_check_error (xcf_save_prop (info, gimage, PROP_VISIBLE, error,
+ gimp_item_get_visible (GIMP_ITEM (channel))));
+ xcf_check_error (xcf_save_prop (info, gimage, PROP_LINKED, error,
+ gimp_item_get_linked (GIMP_ITEM (channel))));
+ xcf_check_error (xcf_save_prop (info, gimage, PROP_SHOW_MASKED, error,
+ channel->show_masked));
+
+ gimp_rgb_get_uchar (&channel->color, &col[0], &col[1], &col[2]);
+ xcf_check_error (xcf_save_prop (info, gimage, PROP_COLOR, error, col));
+
+ xcf_check_error (xcf_save_prop (info, gimage, PROP_TATTOO, error,
+ GIMP_ITEM (channel)->tattoo));
+
+ if (gimp_parasite_list_length (GIMP_ITEM (channel)->parasites) > 0)
+ xcf_check_error (xcf_save_prop (info, gimage, PROP_PARASITES, error,
+ GIMP_ITEM (channel)->parasites));
+
+ xcf_check_error (xcf_save_prop (info, gimage, PROP_END, error));
+
+ return TRUE;
+}
+
+static bool
+xcf_save_prop (XcfInfo *info,
+ KisImage *gimage,
+ PropType prop_type,
+ GError **error,
+ ...)
+{
+ TQ_INT32 size;
+ va_list args;
+
+ GError *tmp_error = NULL;
+
+ va_start (args, error);
+
+ switch (prop_type)
+ {
+ case PROP_END:
+ size = 0;
+
+ xcf_write_prop_type_check_error (info, prop_type);
+ xcf_write_int32_check_error (info, &size, 1);
+ break;
+
+ case PROP_COLORMAP:
+ {
+ TQ_INT32 ncolors;
+ guchar *colors;
+
+ ncolors = va_arg (args, TQ_INT32);
+ colors = va_arg (args, guchar*);
+ size = 4 + ncolors * 3;
+
+ xcf_write_prop_type_check_error (info, prop_type);
+ xcf_write_int32_check_error (info, &size, 1);
+ xcf_write_int32_check_error (info, &ncolors, 1);
+ xcf_write_int8_check_error (info, colors, ncolors * 3);
+ }
+ break;
+
+ case PROP_ACTIVE_LAYER:
+ case PROP_ACTIVE_CHANNEL:
+ case PROP_SELECTION:
+ size = 0;
+
+ xcf_write_prop_type_check_error (info, prop_type);
+ xcf_write_int32_check_error (info, &size, 1);
+ break;
+
+ case PROP_FLOATING_SELECTION:
+ {
+ TQ_INT32 dummy;
+
+ dummy = 0;
+ size = 4;
+
+ xcf_write_prop_type_check_error (info, prop_type);
+ xcf_write_int32_check_error (info, &size, 1);
+ info->floating_sel_offset = info->cp;
+ xcf_write_int32_check_error (info, &dummy, 1);
+ }
+ break;
+
+ case PROP_OPACITY:
+ {
+ gdouble opacity;
+ TQ_INT32 uint_opacity;
+
+ opacity = va_arg (args, gdouble);
+
+ uint_opacity = opacity * 255.999;
+
+ size = 4;
+
+ xcf_write_prop_type_check_error (info, prop_type);
+ xcf_write_int32_check_error (info, &size, 1);
+ xcf_write_int32_check_error (info, &uint_opacity, 1);
+ }
+ break;
+
+ case PROP_MODE:
+ {
+ TQ_INT3232 mode;
+
+ mode = va_arg (args, TQ_INT3232);
+ size = 4;
+
+ xcf_write_prop_type_check_error (info, prop_type);
+ xcf_write_int32_check_error (info, &size, 1);
+ xcf_write_int32_check_error (info, (TQ_INT32 *) &mode, 1);
+ }
+ break;
+
+ case PROP_VISIBLE:
+ {
+ TQ_INT32 visible;
+
+ visible = va_arg (args, TQ_INT32);
+ size = 4;
+
+ xcf_write_prop_type_check_error (info, prop_type);
+ xcf_write_int32_check_error (info, &size, 1);
+ xcf_write_int32_check_error (info, &visible, 1);
+ }
+ break;
+
+ case PROP_LINKED:
+ {
+ TQ_INT32 linked;
+
+ linked = va_arg (args, TQ_INT32);
+ size = 4;
+
+ xcf_write_prop_type_check_error (info, prop_type);
+ xcf_write_int32_check_error (info, &size, 1);
+ xcf_write_int32_check_error (info, &linked, 1);
+ }
+ break;
+
+ case PROP_LOCK_ALPHA:
+ {
+ TQ_INT32 lock_alpha;
+
+ lock_alpha = va_arg (args, TQ_INT32);
+ size = 4;
+
+ xcf_write_prop_type_check_error (info, prop_type);
+ xcf_write_int32_check_error (info, &size, 1);
+ xcf_write_int32_check_error (info, &lock_alpha, 1);
+ }
+ break;
+
+ case PROP_APPLY_MASK:
+ {
+ TQ_INT32 apply_mask;
+
+ apply_mask = va_arg (args, TQ_INT32);
+ size = 4;
+
+ xcf_write_prop_type_check_error (info, prop_type);
+ xcf_write_int32_check_error (info, &size, 1);
+ xcf_write_int32_check_error (info, &apply_mask, 1);
+ }
+ break;
+
+ case PROP_EDIT_MASK:
+ {
+ TQ_INT32 edit_mask;
+
+ edit_mask = va_arg (args, TQ_INT32);
+ size = 4;
+
+ xcf_write_prop_type_check_error (info, prop_type);
+ xcf_write_int32_check_error (info, &size, 1);
+ xcf_write_int32_check_error (info, &edit_mask, 1);
+ }
+ break;
+
+ case PROP_SHOW_MASK:
+ {
+ TQ_INT32 show_mask;
+
+ show_mask = va_arg (args, TQ_INT32);
+ size = 4;
+
+ xcf_write_prop_type_check_error (info, prop_type);
+ xcf_write_int32_check_error (info, &size, 1);
+ xcf_write_int32_check_error (info, &show_mask, 1);
+ }
+ break;
+
+ case PROP_SHOW_MASKED:
+ {
+ TQ_INT32 show_masked;
+
+ show_masked = va_arg (args, TQ_INT32);
+ size = 4;
+
+ xcf_write_prop_type_check_error (info, prop_type);
+ xcf_write_int32_check_error (info, &size, 1);
+ xcf_write_int32_check_error (info, &show_masked, 1);
+ }
+ break;
+
+ case PROP_OFFSETS:
+ {
+ TQ_INT3232 offsets[2];
+
+ offsets[0] = va_arg (args, TQ_INT3232);
+ offsets[1] = va_arg (args, TQ_INT3232);
+ size = 8;
+
+ xcf_write_prop_type_check_error (info, prop_type);
+ xcf_write_int32_check_error (info, &size, 1);
+ xcf_write_int32_check_error (info, (TQ_INT32 *) offsets, 2);
+ }
+ break;
+
+ case PROP_COLOR:
+ {
+ guchar *color;
+
+ color = va_arg (args, guchar*);
+ size = 3;
+
+ xcf_write_prop_type_check_error (info, prop_type);
+ xcf_write_int32_check_error (info, &size, 1);
+ xcf_write_int8_check_error (info, color, 3);
+ }
+ break;
+
+ case PROP_COMPRESSION:
+ {
+ TQ_UINT8 compression;
+
+ compression = (TQ_UINT8) va_arg (args, TQ_INT32);
+ size = 1;
+
+ xcf_write_prop_type_check_error (info, prop_type);
+ xcf_write_int32_check_error (info, &size, 1);
+ xcf_write_int8_check_error (info, &compression, 1);
+ }
+ break;
+
+ case PROP_GUIDES:
+ {
+ GList *guides;
+ GimpGuide *guide;
+ TQ_INT3232 position;
+ TQ_INT328 orientation;
+ TQ_INT32 nguides;
+
+ guides = va_arg (args, GList *);
+ nguides = g_list_length (guides);
+
+ size = nguides * (4 + 1);
+
+ xcf_write_prop_type_check_error (info, prop_type);
+ xcf_write_int32_check_error (info, &size, 1);
+
+ for (; guides; guides = g_list_next (guides))
+ {
+ guide = (GimpGuide *) guides->data;
+
+ position = guide->position;
+
+ switch (guide->orientation)
+ {
+ case GIMP_ORIENTATION_HORIZONTAL:
+ orientation = XCF_ORIENTATION_HORIZONTAL;
+ break;
+
+ case GIMP_ORIENTATION_VERTICAL:
+ orientation = XCF_ORIENTATION_VERTICAL;
+ break;
+
+ default:
+ g_warning ("%s: skipping guide with bad orientation",
+ G_STRFUNC);
+ continue;
+ }
+
+ xcf_write_int32_check_error (info, (TQ_INT32 *) &position, 1);
+ xcf_write_int8_check_error (info, (TQ_UINT8 *) &orientation, 1);
+ }
+ }
+ break;
+
+ case PROP_RESOLUTION:
+ {
+ float xresolution, yresolution;
+
+ /* we pass in floats,
+ but they are promoted to double by the compiler */
+ xresolution = va_arg (args, double);
+ yresolution = va_arg (args, double);
+
+ size = 4*2;
+
+ xcf_write_prop_type_check_error (info, prop_type);
+ xcf_write_int32_check_error (info, &size, 1);
+
+ xcf_write_float_check_error (info, &xresolution, 1);
+ xcf_write_float_check_error (info, &yresolution, 1);
+ }
+ break;
+
+ case PROP_TATTOO:
+ {
+ TQ_INT32 tattoo;
+
+ tattoo = va_arg (args, TQ_INT32);
+ size = 4;
+
+ xcf_write_prop_type_check_error (info, prop_type);
+ xcf_write_int32_check_error (info, &size, 1);
+ xcf_write_int32_check_error (info, &tattoo, 1);
+ }
+ break;
+
+ case PROP_PARASITES:
+ {
+ KisAnnotationList *list;
+ TQ_INT32 base, length;
+ long pos;
+
+ list = va_arg (args, KisAnnotationList *);
+
+ if (gimp_parasite_list_persistent_length (list) > 0)
+ {
+ xcf_write_prop_type_check_error (info, prop_type);
+
+ /* because we don't know how much room the parasite list will take
+ * we save the file position and write the length later
+ */
+ pos = info->cp;
+ xcf_write_int32_check_error (info, &length, 1);
+ base = info->cp;
+
+ xcf_check_error (xcf_save_parasite_list (info, list, error));
+
+ length = info->cp - base;
+ /* go back to the saved position and write the length */
+ xcf_check_error (xcf_seek_pos (info, pos, error));
+ xcf_write_int32 (info->fp, &length, 1, &tmp_error);
+ if (tmp_error)
+ {
+ g_propagate_error (error, tmp_error);
+ return FALSE;
+ }
+
+ xcf_check_error (xcf_seek_end (info, error));
+ }
+ }
+ break;
+
+ case PROP_UNIT:
+ {
+ TQ_INT32 unit;
+
+ unit = va_arg (args, TQ_INT32);
+
+ size = 4;
+
+ xcf_write_prop_type_check_error (info, prop_type);
+ xcf_write_int32_check_error (info, &size, 1);
+ xcf_write_int32_check_error (info, &unit, 1);
+ }
+ break;
+
+ case PROP_PATHS:
+ {
+ TQ_INT32 base, length;
+ glong pos;
+
+ xcf_write_prop_type_check_error (info, prop_type);
+
+ /* because we don't know how much room the paths list will take
+ * we save the file position and write the length later
+ */
+ pos = info->cp;
+ xcf_write_int32_check_error (info, &length, 1);
+
+ base = info->cp;
+
+ xcf_check_error (xcf_save_old_paths (info, gimage, error));
+
+ length = info->cp - base;
+
+ /* go back to the saved position and write the length */
+ xcf_check_error (xcf_seek_pos (info, pos, error));
+ xcf_write_int32 (info->fp, &length, 1, &tmp_error);
+ if (tmp_error)
+ {
+ g_propagate_error (error, tmp_error);
+ return FALSE;
+ }
+
+ xcf_check_error (xcf_seek_end (info, error));
+ }
+ break;
+
+ case PROP_USER_UNIT:
+ {
+ GimpUnit unit;
+ const TQCString *unit_strings[5];
+ float factor;
+ TQ_INT32 digits;
+
+ unit = va_arg (args, TQ_INT32);
+
+ /* write the entire unit definition */
+ unit_strings[0] = _gimp_unit_get_identifier (gimage->gimp, unit);
+ factor = _gimp_unit_get_factor (gimage->gimp, unit);
+ digits = _gimp_unit_get_digits (gimage->gimp, unit);
+ unit_strings[1] = _gimp_unit_get_symbol (gimage->gimp, unit);
+ unit_strings[2] = _gimp_unit_get_abbreviation (gimage->gimp, unit);
+ unit_strings[3] = _gimp_unit_get_singular (gimage->gimp, unit);
+ unit_strings[4] = _gimp_unit_get_plural (gimage->gimp, unit);
+
+ size =
+ 2 * 4 +
+ strlen (unit_strings[0]) ? strlen (unit_strings[0]) + 5 : 4 +
+ strlen (unit_strings[1]) ? strlen (unit_strings[1]) + 5 : 4 +
+ strlen (unit_strings[2]) ? strlen (unit_strings[2]) + 5 : 4 +
+ strlen (unit_strings[3]) ? strlen (unit_strings[3]) + 5 : 4 +
+ strlen (unit_strings[4]) ? strlen (unit_strings[4]) + 5 : 4;
+
+ xcf_write_prop_type_check_error (info, prop_type);
+ xcf_write_int32_check_error (info, &size, 1);
+ xcf_write_float_check_error (info, &factor, 1);
+ xcf_write_int32_check_error (info, &digits, 1);
+ xcf_write_string_check_error (info, (TQCString **) unit_strings, 5);
+ }
+ break;
+
+ case PROP_VECTORS:
+ {
+ TQ_INT32 base, length;
+ glong pos;
+
+ xcf_write_prop_type_check_error (info, prop_type);
+
+ /* because we don't know how much room the paths list will take
+ * we save the file position and write the length later
+ */
+ pos = info->cp;
+ xcf_write_int32_check_error (info, &length, 1);
+
+ base = info->cp;
+
+ xcf_check_error (xcf_save_vectors (info, gimage, error));
+
+ length = info->cp - base;
+
+ /* go back to the saved position and write the length */
+ xcf_check_error (xcf_seek_pos (info, pos, error));
+ xcf_write_int32 (info->fp, &length, 1, &tmp_error);
+ if (tmp_error)
+ {
+ g_propagate_error (error, tmp_error);
+ return FALSE;
+ }
+
+ xcf_check_error (xcf_seek_end (info, error));
+ }
+ break;
+
+ case PROP_TEXT_LAYER_FLAGS:
+ {
+ TQ_INT32 flags;
+
+ flags = va_arg (args, TQ_INT32);
+ size = 4;
+
+ xcf_write_prop_type_check_error (info, prop_type);
+ xcf_write_int32_check_error (info, &size, 1);
+ xcf_write_int32_check_error (info, &flags, 1);
+ }
+ break;
+ }
+
+ va_end (args);
+
+ return TRUE;
+}
+
+static bool
+xcf_save_layer (XcfInfo *info,
+ KisImage *gimage,
+ KisLayer *layer,
+ GError **error)
+{
+ TQ_INT32 saved_pos;
+ TQ_INT32 offset;
+
+ GError *tmp_error = NULL;
+
+ /* check and see if this is the drawable that the floating
+ * selection is attached to.
+ */
+ if (GIMP_DRAWABLE (layer) == info->floating_sel_drawable)
+ {
+ saved_pos = info->cp;
+ xcf_check_error (xcf_seek_pos (info, info->floating_sel_offset, error));
+ xcf_write_int32_check_error (info, &saved_pos, 1);
+ xcf_check_error (xcf_seek_pos (info, saved_pos, error));
+ }
+
+ /* write out the width, height and image type information for the layer */
+ xcf_write_int32_check_error (info,
+ (TQ_INT32 *) &GIMP_ITEM (layer)->width, 1);
+ xcf_write_int32_check_error (info,
+ (TQ_INT32 *) &GIMP_ITEM (layer)->height, 1);
+ xcf_write_int32_check_error (info,
+ (TQ_INT32 *) &GIMP_DRAWABLE (layer)->type, 1);
+
+ /* write out the layers name */
+ xcf_write_string_check_error (info, &GIMP_OBJECT (layer)->name, 1);
+
+ /* write out the layer properties */
+ xcf_save_layer_props (info, gimage, layer, error);
+
+ /* save the current position which is where the hierarchy offset
+ * will be stored.
+ */
+ saved_pos = info->cp;
+
+ /* write out the layer tile hierarchy */
+ xcf_check_error (xcf_seek_pos (info, info->cp + 8, error));
+ offset = info->cp;
+
+ xcf_check_error (xcf_save_hierarchy (info,
+ GIMP_DRAWABLE(layer)->tiles, error));
+
+ xcf_check_error (xcf_seek_pos (info, saved_pos, error));
+ xcf_write_int32_check_error (info, &offset, 1);
+ saved_pos = info->cp;
+
+ /* write out the layer mask */
+ if (layer->mask)
+ {
+ xcf_check_error (xcf_seek_end (info, error));
+ offset = info->cp;
+
+ xcf_check_error (xcf_save_channel (info,
+ gimage, GIMP_CHANNEL(layer->mask),
+ error));
+ }
+ else
+ offset = 0;
+
+ xcf_check_error (xcf_seek_pos (info, saved_pos, error));
+ xcf_write_int32_check_error (info, &offset, 1);
+
+ return TRUE;
+}
+
+static bool
+xcf_save_channel (XcfInfo *info,
+ KisImage *gimage,
+ GimpChannel *channel,
+ GError **error)
+{
+ TQ_INT32 saved_pos;
+ TQ_INT32 offset;
+
+ GError *tmp_error = NULL;
+
+ /* check and see if this is the drawable that the floating
+ * selection is attached to.
+ */
+ if (GIMP_DRAWABLE (channel) == info->floating_sel_drawable)
+ {
+ saved_pos = info->cp;
+ xcf_check_error (xcf_seek_pos (info, info->floating_sel_offset, error));
+ xcf_write_int32_check_error (info, &saved_pos, 1);
+ xcf_check_error (xcf_seek_pos (info, saved_pos, error));
+ }
+
+ /* write out the width and height information for the channel */
+ xcf_write_int32_check_error (info,
+ (TQ_INT32 *) &GIMP_ITEM (channel)->width, 1);
+ xcf_write_int32_check_error (info,
+ (TQ_INT32 *) &GIMP_ITEM (channel)->height, 1);
+
+ /* write out the channels name */
+ xcf_write_string_check_error (info, &GIMP_OBJECT (channel)->name, 1);
+
+ /* write out the channel properties */
+ xcf_save_channel_props (info, gimage, channel, error);
+
+ /* save the current position which is where the hierarchy offset
+ * will be stored.
+ */
+ saved_pos = info->cp;
+
+ /* write out the channel tile hierarchy */
+ xcf_check_error (xcf_seek_pos (info, info->cp + 4, error));
+ offset = info->cp;
+
+ xcf_check_error (xcf_save_hierarchy (info,
+ GIMP_DRAWABLE (channel)->tiles, error));
+
+ xcf_check_error (xcf_seek_pos (info, saved_pos, error));
+ xcf_write_int32_check_error (info, &offset, 1);
+ saved_pos = info->cp;
+
+ return TRUE;
+}
+
+static TQ_INT32
+xcf_calc_levels (TQ_INT32 size,
+ TQ_INT32 tile_size)
+{
+ int levels;
+
+ levels = 1;
+ while (size > tile_size)
+ {
+ size /= 2;
+ levels += 1;
+ }
+
+ return levels;
+}
+
+
+static bool
+xcf_save_hierarchy (XcfInfo *info,
+ TileManager *tiles,
+ GError **error)
+{
+ TQ_INT32 saved_pos;
+ TQ_INT32 offset;
+ TQ_INT32 width;
+ TQ_INT32 height;
+ TQ_INT32 bpp;
+ TQ_INT32 i;
+ TQ_INT32 nlevels;
+ TQ_INT32 tmp1, tmp2;
+
+ GError *tmp_error = NULL;
+
+ width = tile_manager_width (tiles);
+ height = tile_manager_height (tiles);
+ bpp = tile_manager_bpp (tiles);
+
+ xcf_write_int32_check_error (info, (TQ_INT32 *) &width, 1);
+ xcf_write_int32_check_error (info, (TQ_INT32 *) &height, 1);
+ xcf_write_int32_check_error (info, (TQ_INT32 *) &bpp, 1);
+
+ saved_pos = info->cp;
+
+ tmp1 = xcf_calc_levels (width, TILE_WIDTH);
+ tmp2 = xcf_calc_levels (height, TILE_HEIGHT);
+ nlevels = MAX (tmp1, tmp2);
+
+ xcf_check_error (xcf_seek_pos (info, info->cp + (1 + nlevels) * 4, error));
+
+ for (i = 0; i < nlevels; i++)
+ {
+ offset = info->cp;
+
+ if (i == 0)
+ {
+ /* write out the level. */
+ xcf_check_error (xcf_save_level (info, tiles, error));
+ }
+ else
+ {
+ /* fake an empty level */
+ tmp1 = 0;
+ width /= 2;
+ height /= 2;
+ xcf_write_int32_check_error (info, (TQ_INT32 *) &width, 1);
+ xcf_write_int32_check_error (info, (TQ_INT32 *) &height, 1);
+ xcf_write_int32_check_error (info, (TQ_INT32 *) &tmp1, 1);
+ }
+
+ /* seek back to where we are to write out the next
+ * level offset and write it out.
+ */
+ xcf_check_error (xcf_seek_pos (info, saved_pos, error));
+ xcf_write_int32_check_error (info, &offset, 1);
+
+ /* increment the location we are to write out the
+ * next offset.
+ */
+ saved_pos = info->cp;
+
+ /* seek to the end of the file which is where
+ * we will write out the next level.
+ */
+ xcf_check_error (xcf_seek_end (info, error));
+ }
+
+ /* write out a '0' offset position to indicate the end
+ * of the level offsets.
+ */
+ offset = 0;
+ xcf_check_error (xcf_seek_pos (info, saved_pos, error));
+ xcf_write_int32_check_error (info, &offset, 1);
+
+ return TRUE;
+}
+
+static bool
+xcf_save_level (XcfInfo *info,
+ TileManager *level,
+ GError **error)
+{
+ TQ_INT32 saved_pos;
+ TQ_INT32 offset;
+ TQ_INT32 width;
+ TQ_INT32 height;
+ TQ_UINT32 ntiles;
+ TQ_INT32 i;
+ guchar *rlebuf;
+
+ GError *tmp_error = NULL;
+
+ width = tile_manager_width (level);
+ height = tile_manager_height (level);
+
+ xcf_write_int32_check_error (info, (TQ_INT32 *) &width, 1);
+ xcf_write_int32_check_error (info, (TQ_INT32 *) &height, 1);
+
+ saved_pos = info->cp;
+
+ /* allocate a temporary buffer to store the rle data before it is
+ written to disk */
+ rlebuf =
+ g_malloc (TILE_WIDTH * TILE_HEIGHT * tile_manager_bpp (level) * 1.5);
+
+ if (level->tiles)
+ {
+ ntiles = level->ntile_rows * level->ntile_cols;
+ xcf_check_error (xcf_seek_pos (info, info->cp + (ntiles + 1) * 4, error));
+
+ for (i = 0; i < ntiles; i++)
+ {
+ /* save the start offset of where we are writing
+ * out the next tile.
+ */
+ offset = info->cp;
+
+ /* write out the tile. */
+ switch (info->compression)
+ {
+ case COMPRESS_NONE:
+ xcf_check_error(xcf_save_tile (info, level->tiles[i], error));
+ break;
+ case COMPRESS_RLE:
+ xcf_check_error (xcf_save_tile_rle (info, level->tiles[i],
+ rlebuf, error));
+ break;
+ case COMPRESS_ZLIB:
+ g_error ("xcf: zlib compression unimplemented");
+ break;
+ case COMPRESS_FRACTAL:
+ g_error ("xcf: fractal compression unimplemented");
+ break;
+ }
+
+ /* seek back to where we are to write out the next
+ * tile offset and write it out.
+ */
+ xcf_check_error (xcf_seek_pos (info, saved_pos, error));
+ xcf_write_int32_check_error (info, &offset, 1);
+
+ /* increment the location we are to write out the
+ * next offset.
+ */
+ saved_pos = info->cp;
+
+ /* seek to the end of the file which is where
+ * we will write out the next tile.
+ */
+ xcf_check_error (xcf_seek_end (info, error));
+ }
+ }
+
+ g_free (rlebuf);
+
+ /* write out a '0' offset position to indicate the end
+ * of the level offsets.
+ */
+ offset = 0;
+ xcf_check_error (xcf_seek_pos (info, saved_pos, error));
+ xcf_write_int32_check_error (info, &offset, 1);
+
+ return TRUE;
+
+}
+
+static bool
+xcf_save_tile (XcfInfo *info,
+ Tile *tile,
+ GError **error)
+{
+ GError *tmp_error = NULL;
+
+ tile_lock (tile);
+ xcf_write_int8_check_error (info, tile_data_pointer (tile, 0, 0),
+ tile_size (tile));
+ tile_release (tile, FALSE);
+
+ return TRUE;
+}
+
+static bool
+xcf_save_tile_rle (XcfInfo *info,
+ Tile *tile,
+ guchar *rlebuf,
+ GError **error)
+{
+ guchar *data, *t;
+ unsigned int last;
+ TQ_INT32 state;
+ TQ_INT32 length;
+ TQ_INT32 count;
+ TQ_INT32 size;
+ TQ_INT32 bpp;
+ TQ_INT32 i, j;
+ TQ_INT32 len = 0;
+
+ GError *tmp_error = NULL;
+
+ tile_lock (tile);
+
+ bpp = tile_bpp (tile);
+
+ for (i = 0; i < bpp; i++)
+ {
+ data = (guchar*) tile_data_pointer (tile, 0, 0) + i;
+
+ state = 0;
+ length = 0;
+ count = 0;
+ size = tile_ewidth(tile) * tile_eheight(tile);
+ last = -1;
+
+ while (size > 0)
+ {
+ switch (state)
+ {
+ case 0:
+ /* in state 0 we try to find a long sequence of
+ * matching values.
+ */
+ if ((length == 32768) ||
+ ((size - length) <= 0) ||
+ ((length > 1) && (last != *data)))
+ {
+ count += length;
+ if (length >= 128)
+ {
+ rlebuf[len++] = 127;
+ rlebuf[len++] = (length >> 8);
+ rlebuf[len++] = length & 0x00FF;
+ rlebuf[len++] = last;
+ }
+ else
+ {
+ rlebuf[len++] = length - 1;
+ rlebuf[len++] = last;
+ }
+ size -= length;
+ length = 0;
+ }
+ else if ((length == 1) && (last != *data))
+ state = 1;
+ break;
+
+ case 1:
+ /* in state 1 we try and find a long sequence of
+ * non-matching values.
+ */
+ if ((length == 32768) ||
+ ((size - length) == 0) ||
+ ((length > 0) && (last == *data) &&
+ ((size - length) == 1 || last == data[bpp])))
+ {
+ count += length;
+ state = 0;
+
+ if (length >= 128)
+ {
+ rlebuf[len++] = 255 - 127;
+ rlebuf[len++] = (length >> 8);
+ rlebuf[len++] = length & 0x00FF;
+ }
+ else
+ {
+ rlebuf[len++] = 255 - (length - 1);
+ }
+
+ t = data - length * bpp;
+ for (j = 0; j < length; j++)
+ {
+ rlebuf[len++] = *t;
+ t += bpp;
+ }
+
+ size -= length;
+ length = 0;
+ }
+ break;
+ }
+
+ if (size > 0) {
+ length += 1;
+ last = *data;
+ data += bpp;
+ }
+ }
+
+ if (count != (tile_ewidth (tile) * tile_eheight (tile)))
+ g_message ("xcf: uh oh! xcf rle tile saving error: %d", count);
+ }
+ xcf_write_int8_check_error (info, rlebuf, len);
+ tile_release (tile, FALSE);
+
+ return TRUE;
+}
+
+static bool
+xcf_save_parasite (XcfInfo *info,
+ KisAnnotation *parasite,
+ GError **error)
+{
+ if (gimp_parasite_is_persistent (parasite))
+ {
+ GError *tmp_error = NULL;
+
+ xcf_write_string_check_error (info, &parasite->name, 1);
+ xcf_write_int32_check_error (info, &parasite->flags, 1);
+ xcf_write_int32_check_error (info, &parasite->size, 1);
+ xcf_write_int8_check_error (info, parasite->data, parasite->size);
+ }
+
+ return TRUE;
+}
+
+typedef struct
+{
+ XcfInfo *info;
+ GError *error;
+} XcfParasiteData;
+
+static void
+xcf_save_parasite_func (TQCString *key,
+ KisAnnotation *parasite,
+ XcfParasiteData *data)
+{
+ if (! data->error)
+ xcf_save_parasite (data->info, parasite, &data->error);
+}
+
+static bool
+xcf_save_parasite_list (XcfInfo *info,
+ KisAnnotationList *list,
+ GError **error)
+{
+ XcfParasiteData data;
+
+ data.info = info;
+ data.error = NULL;
+
+ gimp_parasite_list_foreach (list, (GHFunc) xcf_save_parasite_func, &data);
+
+ if (data.error)
+ {
+ g_propagate_error (error, data.error);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static bool
+xcf_save_old_paths (XcfInfo *info,
+ KisImage *gimage,
+ GError **error)
+{
+ GimpVectors *active_vectors;
+ TQ_INT32 num_paths;
+ TQ_INT32 active_index = 0;
+ GList *list;
+ GError *tmp_error = NULL;
+
+ /* Write out the following:-
+ *
+ * last_selected_row (TQ_INT32)
+ * number_of_paths (TQ_INT32)
+ *
+ * then each path:-
+ */
+
+ num_paths = gimp_container_num_children (gimage->vectors);
+
+ active_vectors = gimp_image_get_active_vectors (gimage);
+
+ if (active_vectors)
+ active_index = gimp_container_get_child_index (gimage->vectors,
+ GIMP_OBJECT (active_vectors));
+
+ xcf_write_int32_check_error (info, &active_index, 1);
+ xcf_write_int32_check_error (info, &num_paths, 1);
+
+ for (list = GIMP_LIST (gimage->vectors)->list;
+ list;
+ list = g_list_next (list))
+ {
+ GimpVectors *vectors = list->data;
+ TQCString *name;
+ TQ_INT32 locked;
+ TQ_UINT8 state;
+ TQ_INT32 version;
+ TQ_INT32 pathtype;
+ TQ_INT32 tattoo;
+ GimpVectorsCompatPoint *points;
+ TQ_INT3232 num_points;
+ TQ_INT3232 closed;
+ TQ_INT32 i;
+
+ /*
+ * name (string)
+ * locked (TQ_INT32)
+ * state (TQCString)
+ * closed (TQ_INT32)
+ * number points (TQ_INT32)
+ * version (TQ_INT32)
+ * pathtype (TQ_INT32)
+ * tattoo (TQ_INT32)
+ * then each point.
+ */
+
+ points = gimp_vectors_compat_get_points (vectors, &num_points, &closed);
+
+ /* if no points are generated because of a faulty path we should
+ * skip saving the path - this is unfortunately impossible, because
+ * we already saved the number of paths and I wont start seeking
+ * around to fix that cruft */
+
+ name = (TQCString *) gimp_object_get_name (GIMP_OBJECT (vectors));
+ locked = gimp_item_get_linked (GIMP_ITEM (vectors));
+ state = closed ? 4 : 2; /* EDIT : ADD (editing state, 1.2 compat) */
+ version = 3;
+ pathtype = 1; /* BEZIER (1.2 compat) */
+ tattoo = gimp_item_get_tattoo (GIMP_ITEM (vectors));
+
+ xcf_write_string_check_error (info, &name, 1);
+ xcf_write_int32_check_error (info, &locked, 1);
+ xcf_write_int8_check_error (info, &state, 1);
+ xcf_write_int32_check_error (info, &closed, 1);
+ xcf_write_int32_check_error (info, &num_points, 1);
+ xcf_write_int32_check_error (info, &version, 1);
+ xcf_write_int32_check_error (info, &pathtype, 1);
+ xcf_write_int32_check_error (info, &tattoo, 1);
+
+ for (i = 0; i < num_points; i++)
+ {
+ float x;
+ float y;
+
+ x = points[i].x;
+ y = points[i].y;
+
+ /*
+ * type (TQ_INT32)
+ * x (float)
+ * y (float)
+ */
+
+ xcf_write_int32_check_error (info, &points[i].type, 1);
+ xcf_write_float_check_error (info, &x, 1);
+ xcf_write_float_check_error (info, &y, 1);
+ }
+
+ g_free (points);
+ }
+
+ return TRUE;
+}
+
+static bool
+xcf_save_vectors (XcfInfo *info,
+ KisImage *gimage,
+ GError **error)
+{
+ GimpVectors *active_vectors;
+ TQ_INT32 version = 1;
+ TQ_INT32 active_index = 0;
+ TQ_INT32 num_paths;
+ GList *list;
+ GList *stroke_list;
+ GError *tmp_error = NULL;
+
+ /* Write out the following:-
+ *
+ * version (TQ_INT32)
+ * active_index (TQ_INT32)
+ * num_paths (TQ_INT32)
+ *
+ * then each path:-
+ */
+
+ active_vectors = gimp_image_get_active_vectors (gimage);
+
+ if (active_vectors)
+ active_index = gimp_container_get_child_index (gimage->vectors,
+ GIMP_OBJECT (active_vectors));
+
+ num_paths = gimp_container_num_children (gimage->vectors);
+
+ xcf_write_int32_check_error (info, &version, 1);
+ xcf_write_int32_check_error (info, &active_index, 1);
+ xcf_write_int32_check_error (info, &num_paths, 1);
+
+ for (list = GIMP_LIST (gimage->vectors)->list;
+ list;
+ list = g_list_next (list))
+ {
+ GimpVectors *vectors = list->data;
+ KisAnnotationList *parasites;
+ TQCString *name;
+ TQ_INT32 tattoo;
+ TQ_INT32 visible;
+ TQ_INT32 linked;
+ TQ_INT32 num_parasites;
+ TQ_INT32 num_strokes;
+
+ /*
+ * name (string)
+ * tattoo (TQ_INT32)
+ * visible (TQ_INT32)
+ * linked (TQ_INT32)
+ * num_parasites (TQ_INT32)
+ * num_strokes (TQ_INT32)
+ *
+ * then each parasite
+ * then each stroke
+ */
+
+ parasites = GIMP_ITEM (vectors)->parasites;
+
+ name = (TQCString *) gimp_object_get_name (GIMP_OBJECT (vectors));
+ visible = gimp_item_get_visible (GIMP_ITEM (vectors));
+ linked = gimp_item_get_linked (GIMP_ITEM (vectors));
+ tattoo = gimp_item_get_tattoo (GIMP_ITEM (vectors));
+ num_parasites = gimp_parasite_list_persistent_length (parasites);
+ num_strokes = g_list_length (vectors->strokes);
+
+ xcf_write_string_check_error (info, &name, 1);
+ xcf_write_int32_check_error (info, &tattoo, 1);
+ xcf_write_int32_check_error (info, &visible, 1);
+ xcf_write_int32_check_error (info, &linked, 1);
+ xcf_write_int32_check_error (info, &num_parasites, 1);
+ xcf_write_int32_check_error (info, &num_strokes, 1);
+
+ xcf_check_error (xcf_save_parasite_list (info, parasites, error));
+
+ for (stroke_list = g_list_first (vectors->strokes);
+ stroke_list;
+ stroke_list = g_list_next (stroke_list))
+ {
+ GimpStroke *stroke;
+ TQ_INT32 stroke_type;
+ TQ_INT32 closed;
+ TQ_INT32 num_axes;
+ GArray *control_points;
+ TQ_INT32 i;
+
+ TQ_INT32 type;
+ float coords[6];
+
+ /*
+ * stroke_type (TQ_INT32)
+ * closed (TQ_INT32)
+ * num_axes (TQ_INT32)
+ * num_control_points (TQ_INT32)
+ *
+ * then each control point.
+ */
+
+ stroke = GIMP_STROKE (stroke_list->data);
+
+ if (GIMP_IS_BEZIER_STROKE (stroke))
+ {
+ stroke_type = XCF_STROKETYPE_BEZIER_STROKE;
+ num_axes = 2; /* hardcoded, might be increased later */
+ }
+ else
+ {
+ g_printerr ("Skipping unknown stroke type!\n");
+ continue;
+ }
+
+ control_points = gimp_stroke_control_points_get (stroke, &closed);
+
+ xcf_write_int32_check_error (info, &stroke_type, 1);
+ xcf_write_int32_check_error (info, &closed, 1);
+ xcf_write_int32_check_error (info, &num_axes, 1);
+ xcf_write_int32_check_error (info, &control_points->len, 1);
+
+ for (i = 0; i < control_points->len; i++)
+ {
+ GimpAnchor *anchor;
+
+ anchor = & (g_array_index (control_points, GimpAnchor, i));
+
+ type = anchor->type;
+ coords[0] = anchor->position.x;
+ coords[1] = anchor->position.y;
+ coords[2] = anchor->position.pressure;
+ coords[3] = anchor->position.xtilt;
+ coords[4] = anchor->position.ytilt;
+ coords[5] = anchor->position.wheel;
+
+ /*
+ * type (TQ_INT32)
+ *
+ * the first num_axis elements of:
+ * [0] x (float)
+ * [1] y (float)
+ * [2] pressure (float)
+ * [3] xtilt (float)
+ * [4] ytilt (float)
+ * [5] wheel (float)
+ */
+
+ xcf_write_int32_check_error (info, &type, 1);
+ xcf_write_float_check_error (info, coords, num_axes);
+ }
+
+ g_array_free (control_points, TRUE);
+ }
+ }
+
+ return TRUE;
+}