diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2011-06-26 00:41:16 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2011-06-26 00:41:16 +0000 |
commit | 698569f8428ca088f764d704034a1330517b98c0 (patch) | |
tree | bf45be6946ebbbee9cce5a5bcf838f4c952d87e6 /filters/chalk/xcf | |
parent | 2785103a6bd4de55bd26d79e34d0fdd4b329a73a (diff) | |
download | koffice-698569f8428ca088f764d704034a1330517b98c0.tar.gz koffice-698569f8428ca088f764d704034a1330517b98c0.zip |
Finish rebranding of Krita as Chalk
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/koffice@1238363 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'filters/chalk/xcf')
-rw-r--r-- | filters/chalk/xcf/Makefile.am | 42 | ||||
-rw-r--r-- | filters/chalk/xcf/chalk_xcf_export.desktop | 52 | ||||
-rw-r--r-- | filters/chalk/xcf/chalk_xcf_import.desktop | 52 | ||||
-rw-r--r-- | filters/chalk/xcf/xcf/README | 2 | ||||
-rw-r--r-- | filters/chalk/xcf/xcf/xcf-load.cc | 1740 | ||||
-rw-r--r-- | filters/chalk/xcf/xcf/xcf-load.h | 27 | ||||
-rw-r--r-- | filters/chalk/xcf/xcf/xcf-private.h | 95 | ||||
-rw-r--r-- | filters/chalk/xcf/xcf/xcf-read.cc | 118 | ||||
-rw-r--r-- | filters/chalk/xcf/xcf/xcf-read.h | 37 | ||||
-rw-r--r-- | filters/chalk/xcf/xcf/xcf-save.cc | 1826 | ||||
-rw-r--r-- | filters/chalk/xcf/xcf/xcf-save.h | 29 | ||||
-rw-r--r-- | filters/chalk/xcf/xcf/xcf-seek.cc | 79 | ||||
-rw-r--r-- | filters/chalk/xcf/xcf/xcf-seek.h | 30 | ||||
-rw-r--r-- | filters/chalk/xcf/xcf/xcf-write.cc | 104 | ||||
-rw-r--r-- | filters/chalk/xcf/xcf/xcf-write.h | 39 | ||||
-rw-r--r-- | filters/chalk/xcf/xcfexport.cpp | 78 | ||||
-rw-r--r-- | filters/chalk/xcf/xcfexport.h | 37 | ||||
-rw-r--r-- | filters/chalk/xcf/xcfimport.cpp | 99 | ||||
-rw-r--r-- | filters/chalk/xcf/xcfimport.h | 37 |
19 files changed, 4523 insertions, 0 deletions
diff --git a/filters/chalk/xcf/Makefile.am b/filters/chalk/xcf/Makefile.am new file mode 100644 index 00000000..88822a23 --- /dev/null +++ b/filters/chalk/xcf/Makefile.am @@ -0,0 +1,42 @@ +kde_module_LTLIBRARIES = libchalkxcfimport.la libchalkxcfexport.la + +libchalkxcfexport_la_LDFLAGS = $(KDE_PLUGIN) $(LIBMAGICK_LDFLAGS) $(KDE_RPATH) $(LIBMAGICK_RPATH) $(all_libraries) -module -avoid-version -no-undefined -lkdecore -lkdeui $(LIB_QT) -lkjs -lkdefx -lkio -lkparts \ + -L../../../lib/kofficecore/.libs/ -lkofficecore -llcms \ + -L../../../lib/kofficeui/.libs/ -lkofficeui -L../../../lib/store/.libs/ -lkstore -L../../../chalk/core/.libs/ -lchalkimage -L../../../chalk/ui/.libs/ -lchalkui -L../../../chalk/chalkcolor/.libs/ -lchalkcolor +libchalkxcfexport_la_LIBADD = \ + $(KOFFICE_LIBS) \ + $(LIBMAGICK_LIBS) \ + $(top_builddir)/chalk/libchalkcommon.la + +libchalkxcfimport_la_LDFLAGS = $(KDE_PLUGIN) $(LIBMAGICK_LDFLAGS) $(KDE_RPATH) $(LIBMAGICK_RPATH) $(all_libraries) -module -avoid-version -no-undefined -lkdecore -lkdeui $(LIB_QT) -lkjs -lkdefx -lkio -lkparts \ + -L../../../lib/kofficecore/.libs/ -lkofficecore -llcms \ + -L../../../lib/kofficeui/.libs/ -lkofficeui -L../../../lib/store/.libs/ -lkstore -L../../../chalk/core/.libs/ -lchalkimage -L../../../chalk/ui/.libs/ -lchalkui -L../../../chalk/chalkcolor/.libs/ -lchalkcolor +libchalkxcfimport_la_LIBADD = \ + $(KOFFICE_LIBS) \ + $(LIBMAGICK_LIBS) \ + $(top_builddir)/chalk/libchalkcommon.la + +INCLUDES= \ + -I$(srcdir) \ + $(KOFFICE_INCLUDES) \ + -I$(top_srcdir)/chalk \ + -I$(top_srcdir)/chalk/core \ + -I$(top_srcdir)/chalk/sdk \ + -I$(top_srcdir)/chalk/core/tiles \ + -I$(top_srcdir)/chalk/chalkcolor \ + -I$(top_srcdir)/chalk/ui \ + $(KOFFICE_INCLUDES) -I$(interfacedir) \ + $(KOPAINTER_INCLUDES) $(LIBMAGICK_CPPFLAGS) \ + $(all_includes) + +service_DATA = chalk_xcf_import.desktop chalk_xcf_export.desktop +servicedir = $(kde_servicesdir) + +libchalkxcfimport_la_SOURCES = xcfimport.cpp +libchalkxcfexport_la_SOURCES = xcfexport.cpp + +METASOURCES = AUTO + +SUBDIRS=xcf + +KDE_CXXFLAGS = $(USE_EXCEPTIONS) diff --git a/filters/chalk/xcf/chalk_xcf_export.desktop b/filters/chalk/xcf/chalk_xcf_export.desktop new file mode 100644 index 00000000..b88afaed --- /dev/null +++ b/filters/chalk/xcf/chalk_xcf_export.desktop @@ -0,0 +1,52 @@ +[Desktop Entry] +Name=Chalk XCF Export Filter +Name[bg]=Филтър за експортиране от Chalk в XCF +Name[br]=Sil ezporzh XCF evit Chalk +Name[ca]=Filtre d'exportació XCF per a Chalk +Name[cy]=Hidl Allforio XCF Chalk +Name[da]=Chalk XCF-eksportfilter +Name[de]=Chalk XCF-Exportfilter +Name[el]=Φίλτρο εξαγωγής XCF του Chalk +Name[eo]=Chalk XCF-importfiltrilo +Name[es]=Filtro de exportación a XCF de Chalk +Name[et]=Chalk XCF-i ekspordifilter +Name[fa]=پالایۀ صادرات Chalk XCF +Name[fi]=Chalk XCF -vientisuodin +Name[fr]=Filtre d'exportation XCF de Chalk +Name[fy]=Chalk XCF Eksportfilter +Name[ga]=Scagaire Easpórtála XCF Chalk +Name[gl]=Filtro de Exportación de XCF para Chalk +Name[he]=Chalk XCF מסנן יצוא +Name[hr]=Chalk XCF filtar izvoza +Name[hu]=Chalk XCF exportszűrő +Name[is]=Chalk XCF útflutningssía +Name[it]=Filtro di esportazione XCF per Chalk +Name[ja]=Chalk XCF エクスポートフィルタ +Name[km]=តម្រងនាំចេញ XCF សម្រាប់ Chalk +Name[lt]=Chalk XCF eksportavimo filtras +Name[lv]=Chalk XCF eksporta filtrs +Name[nb]=XCF-eksportfilter for Chalk +Name[nds]=XCF-Exportfilter för Chalk +Name[ne]=क्रिता XFC निर्यात फिल्टर +Name[nl]=Chalk XCF Exportfilter +Name[pl]=Filtr eksportu do formatu XCF dla Chalk +Name[pt]=Filtro de Exportação de XCF para o Chalk +Name[pt_BR]=Filtro de Exportação de XCF para o Chalk +Name[ru]=Фильтр экспорта рисунков Chalk в XCF +Name[se]=Chalk XCF-olggosfievrridansilli +Name[sk]=Exportný filter Chalk XCF +Name[sl]=Izvozni filter XCF za Krito +Name[sr]=Chalk-ин филтер за извоз у XCF +Name[sr@Latn]=Chalk-in filter za izvoz u XCF +Name[sv]=Chalk XCF-exportfilter +Name[uk]=Фільтр експорту XCF для Chalk +Name[uz]=Chalk XCF eksport filteri +Name[uz@cyrillic]=Chalk XCF экспорт филтери +Name[zh_CN]=Chalk XCF 导出过滤器 +Name[zh_TW]=Chalk XCF 匯出過濾程式 +X-KDE-Export=image/x-xcf-gimp +ServiceTypes=KOfficeFilter +Type=Service +X-KDE-Import=application/x-chalk +X-KDE-Weight=1 +X-KDE-Library=libchalkxcfexport diff --git a/filters/chalk/xcf/chalk_xcf_import.desktop b/filters/chalk/xcf/chalk_xcf_import.desktop new file mode 100644 index 00000000..b2e68d13 --- /dev/null +++ b/filters/chalk/xcf/chalk_xcf_import.desktop @@ -0,0 +1,52 @@ +[Desktop Entry] +Type=Service +Name=Chalk XCF Import Filter +Name[bg]=Филтър за импортиране от XCF в Chalk +Name[br]=Sil enporzh XCF evit Chalk +Name[ca]=Filtre d'importació XCF per a Chalk +Name[cy]=Hidl Mewnforio XCF Chalk +Name[da]=Chalk XCF-importfilter +Name[de]=Chalk XCF-Importfilter +Name[el]=Φίλτρο εισαγωγής XCF του Chalk +Name[eo]=Chalk XCF-importfiltrilo +Name[es]=Filtro de importación a XCF de Chalk +Name[et]=Chalk XCF-i impordifilter +Name[fa]=پالایۀ واردات Chalk XCF +Name[fi]=Chalk XCF -tuontisuodin +Name[fr]=Filtre d'importation XCF de Chalk +Name[fy]=Chalk XCF Ymportfilter +Name[ga]=Scagaire Iompórtála XCF Chalk +Name[gl]=Filtro de Importación de XCF para Chalk +Name[he]=Chalk XCF מסנן יבוא +Name[hr]=Chalk XCF filtar uvoza +Name[hu]=Chalk XCF importszűrő +Name[is]=Chalk XCF innflutningssía +Name[it]=Filtro di importazione XCF per Chalk +Name[ja]=Chalk XCF インポートフィルタ +Name[km]=តម្រងនាំចូល XCF សម្រាប់ Chalk +Name[lt]=Chalk XCF importavimo filtras +Name[lv]=Chalk XCF importa filtrs +Name[nb]=XCF-importfilter for Chalk +Name[nds]=XCF-Importfilter för Chalk +Name[ne]=क्रिता XCF आयात फिल्टर +Name[nl]=Chalk XCF Importfilter +Name[pl]=Filtr importu formatu XCF dla Chalk +Name[pt]=Filtro de Importação de XCF para o Chalk +Name[pt_BR]=Filtro de Importação de XCF para o Chalk +Name[ru]=Фильтр импорта рисунков XCF в Chalk +Name[se]=Chalk XCF-sisafievrridansilli +Name[sk]=XCF filter pre import do Chalk +Name[sl]=Uvozni filter XCF za Krito +Name[sr]=Chalk-ин филтер за увоз из XCF-а +Name[sr@Latn]=Chalk-in filter za uvoz iz XCF-a +Name[sv]=Chalk XCF-importfilter +Name[uk]=Фільтр імпорту XCF для Chalk +Name[uz]=Chalk XCF import filteri +Name[uz@cyrillic]=Chalk XCF импорт филтери +Name[zh_CN]=Chalk XCF 导入过滤器 +Name[zh_TW]=Chalk XCF 匯入過濾程式 +X-KDE-Export=application/x-chalk +X-KDE-Import=image/x-xcf-gimp +X-KDE-Weight=1 +X-KDE-Library=libchalkxcfimport +ServiceTypes=KOfficeFilter diff --git a/filters/chalk/xcf/xcf/README b/filters/chalk/xcf/xcf/README new file mode 100644 index 00000000..567d2ab1 --- /dev/null +++ b/filters/chalk/xcf/xcf/README @@ -0,0 +1,2 @@ +Note: this is source copy of 6 feb. 2006; when updated, use a diff from the gimp cvs +to check for changes in their file format. diff --git a/filters/chalk/xcf/xcf/xcf-load.cc b/filters/chalk/xcf/xcf/xcf-load.cc new file mode 100644 index 00000000..07b6a145 --- /dev/null +++ b/filters/chalk/xcf/xcf/xcf-load.cc @@ -0,0 +1,1740 @@ +/* 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> /* strcmp, memcmp */ + +//#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 "config/gimpcoreconfig.h" + +//#include "core/gimp.h" +//#include "core/gimpcontainer.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/gimplayertqmask.h" +//#include "core/gimpparasitelist.h" +//#include "core/gimpselection.h" +//#include "core/gimptemplate.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-load.h" +#include "xcf-read.h" +#include "xcf-seek.h" + +//#include "gimp-intl.h" + +static bool xcf_load_image_props (XcfInfo * info, KisImage * gimage); +static bool xcf_load_layer_props (XcfInfo * info, + KisImage * gimage, + KisLayer * layer, + bool * apply_tqmask, + bool * edit_tqmask, + bool * show_tqmask, + TQ_INT32 * text_layer_flags); +static bool xcf_load_channel_props (XcfInfo * info, + KisImage * gimage, + GimpChannel ** channel); +static bool xcf_load_prop (XcfInfo * info, + PropType * prop_type, TQ_INT32 * prop_size); +static KisLayer *xcf_load_layer (XcfInfo * info, KisImage * gimage); +//static GimpChannel * xcf_load_channel (XcfInfo *info, +// KisImage *gimage); +//static GimpLayerMask * xcf_load_layer_tqmask (XcfInfo *info, +// KisImage *gimage); +static bool xcf_load_hierarchy (XcfInfo * info, TileManager * tiles); +static bool xcf_load_level (XcfInfo * info, TileManager * tiles); +static bool xcf_load_tile (XcfInfo * info, Tile * tile); +static bool xcf_load_tile_rle (XcfInfo * info, + Tile * tile, TQ_INT32 data_length); +//static GimpParasite * xcf_load_parasite (XcfInfo *info); +static bool xcf_load_old_paths (XcfInfo * info, KisImage * gimage); +static bool xcf_load_old_path (XcfInfo * info, KisImage * gimage); +static bool xcf_load_vectors (XcfInfo * info, KisImage * gimage); +static bool xcf_load_vector (XcfInfo * info, KisImage * gimage); + +#ifdef SWAP_FROM_FILE +static bool xcf_swap_func (TQ_INT32 fd, + Tile * tile, TQ_INT32 cmd, gpointer user_data); +#endif + + +KisImage * +xcf_load_image (XcfInfo * info) +{ + KisImage *gimage; + KisLayer *layer; + //GimpChannel *channel; + //KisAnnotation *parasite; + TQ_INT32 saved_pos; + TQ_INT32 offset; + TQ_INT32 width; + TQ_INT32 height; + TQ_INT32 image_type; + TQ_INT32 num_successful_elements = 0; + + /* read in the image width, height and type */ + info->cp += xcf_read_int32 (info->fp, (TQ_INT32 *) & width, 1); + info->cp += xcf_read_int32 (info->fp, (TQ_INT32 *) & height, 1); + info->cp += xcf_read_int32 (info->fp, (TQ_INT32 *) & image_type, 1); + + gimage = gimp_create_image (gimp, width, height, image_type, FALSE); + + gimp_image_undo_disable (gimage); + + /* read the image properties */ + if (!xcf_load_image_props (info, gimage)) + goto hard_error; + + /* check for a GimpGrid parasite */ + parasite = gimp_image_parasite_tqfind (GIMP_IMAGE (gimage), + gimp_grid_parasite_name ()); + if (parasite) + { + GimpGrid *grid = gimp_grid_from_parasite (parasite); + + if (grid) + { + gimp_parasite_list_remove (GIMP_IMAGE (gimage)->parasites, + gimp_parasite_name (parasite)); + + gimp_image_set_grid (GIMP_IMAGE (gimage), grid, FALSE); + } + } + + while (TRUE) + { + /* read in the offset of the next layer */ + info->cp += xcf_read_int32 (info->fp, &offset, 1); + + /* if the offset is 0 then we are at the end + * of the layer list. + */ + if (offset == 0) + break; + + /* save the current position as it is where the + * next layer offset is stored. + */ + saved_pos = info->cp; + + /* seek to the layer offset */ + if (!xcf_seek_pos (info, offset, NULL)) + goto error; + + /* read in the layer */ + layer = xcf_load_layer (info, gimage); + if (!layer) + goto error; + + num_successful_elements++; + + /* add the layer to the image if its not the floating selection */ + if (layer != info->floating_sel) + gimp_image_add_layer (gimage, layer, + gimp_container_num_tqchildren (gimage->layers)); + + /* restore the saved position so we'll be ready to + * read the next offset. + */ + if (!xcf_seek_pos (info, saved_pos, NULL)) + goto error; + } + + while (TRUE) + { + /* read in the offset of the next channel */ + info->cp += xcf_read_int32 (info->fp, &offset, 1); + + /* if the offset is 0 then we are at the end + * of the channel list. + */ + if (offset == 0) + break; + + /* save the current position as it is where the + * next channel offset is stored. + */ + saved_pos = info->cp; + + /* seek to the channel offset */ + if (!xcf_seek_pos (info, offset, NULL)) + goto error; + + /* read in the layer */ + channel = xcf_load_channel (info, gimage); + if (!channel) + goto error; + + num_successful_elements++; + + /* add the channel to the image if its not the selection */ + if (channel != gimage->selection_tqmask) + gimp_image_add_channel (gimage, channel, -1); + + /* restore the saved position so we'll be ready to + * read the next offset. + */ + if (!xcf_seek_pos (info, saved_pos, NULL)) + goto error; + } + + if (info->floating_sel && info->floating_sel_drawable) + floating_sel_attach (info->floating_sel, info->floating_sel_drawable); + + if (info->active_layer) + gimp_image_set_active_layer (gimage, info->active_layer); + + if (info->active_channel) + gimp_image_set_active_channel (gimage, info->active_channel); + + gimp_image_set_filename (gimage, info->filename); + + if (info->tattoo_state > 0) + gimp_image_set_tattoo_state (gimage, info->tattoo_state); + + gimp_image_undo_enable (gimage); + + return gimage; + +error: + if (num_successful_elements == 0) + goto hard_error; + + g_message ("XCF: This file is corrupt! I have loaded as much\n" + "of it as I can, but it is incomplete."); + + gimp_image_undo_enable (gimage); + + return gimage; + +hard_error: + g_message ("XCF: This file is corrupt! I could not even\n" + "salvage any partial image data from it."); + + g_object_unref (gimage); + + return NULL; +} + +static bool +xcf_load_image_props (XcfInfo * info, KisImage * gimage) +{ + PropType prop_type; + TQ_INT32 prop_size; + + while (TRUE) + { + if (!xcf_load_prop (info, &prop_type, &prop_size)) + return FALSE; + + switch (prop_type) + { + case PROP_END: + return TRUE; + + case PROP_COLORMAP: + if (info->file_version == 0) + { + TQ_INT32 i; + + g_message (_("XCF warning: version 0 of XCF file format\n" + "did not save indexed colormaps correctly.\n" + "Substituting grayscale map.")); + info->cp += + xcf_read_int32 (info->fp, (TQ_INT32 *) & gimage->num_cols, 1); + gimage->cmap = g_new (guchar, gimage->num_cols * 3); + if (!xcf_seek_pos (info, info->cp + gimage->num_cols, NULL)) + return FALSE; + + for (i = 0; i < gimage->num_cols; i++) + { + gimage->cmap[i * 3 + 0] = i; + gimage->cmap[i * 3 + 1] = i; + gimage->cmap[i * 3 + 2] = i; + } + } + else + { + info->cp += + xcf_read_int32 (info->fp, (TQ_INT32 *) & gimage->num_cols, 1); + gimage->cmap = g_new (guchar, gimage->num_cols * 3); + info->cp += + xcf_read_int8 (info->fp, + (TQ_UINT8 *) gimage->cmap, + gimage->num_cols * 3); + } + + /* discard color map, if image is not indexed, this is just + * sanity checking to make sure gimp doesn't end up with an + * image state that is impossible. + */ + if (gimp_image_base_type (gimage) != GIMP_INDEXED) + { + g_free (gimage->cmap); + gimage->cmap = NULL; + gimage->num_cols = 0; + } + break; + + case PROP_COMPRESSION: + { + TQ_UINT8 compression; + + info->cp += + xcf_read_int8 (info->fp, (TQ_UINT8 *) & compression, 1); + + if ((compression != COMPRESS_NONE) && + (compression != COMPRESS_RLE) && + (compression != COMPRESS_ZLIB) && + (compression != COMPRESS_FRACTAL)) + { + g_message ("unknown compression type: %d", (int) compression); + return FALSE; + } + + info->compression = compression; + } + break; + + case PROP_GUIDES: + { + TQ_INT3232 position; + TQ_INT328 orientation; + TQ_INT32 i, nguides; + + nguides = prop_size / (4 + 1); + for (i = 0; i < nguides; i++) + { + info->cp += + xcf_read_int32 (info->fp, (TQ_INT32 *) & position, 1); + info->cp += + xcf_read_int8 (info->fp, (TQ_UINT8 *) & orientation, 1); + + /* skip -1 guides from old XCFs */ + if (position < 0) + continue; + + switch (orientation) + { + case XCF_ORIENTATION_HORIZONTAL: + gimp_image_add_hguide (gimage, position, FALSE); + break; + + case XCF_ORIENTATION_VERTICAL: + gimp_image_add_vguide (gimage, position, FALSE); + break; + + default: + g_message ("guide orientation out of range in XCF file"); + continue; + } + } + + /* this is silly as the order of guides doesn't really matter, + * but it restores the list to it's original order, which + * cannot be wrong --Mitch + */ + gimage->guides = g_list_reverse (gimage->guides); + } + break; + + case PROP_RESOLUTION: + { + float xres, yres; + + info->cp += xcf_read_float (info->fp, &xres, 1); + info->cp += xcf_read_float (info->fp, &yres, 1); + if (xres < GIMP_MIN_RESOLUTION || xres > GIMP_MAX_RESOLUTION || + yres < GIMP_MIN_RESOLUTION || yres > GIMP_MAX_RESOLUTION) + { + g_message ("Warning, resolution out of range in XCF file"); + xres = gimage->gimp->config->default_image->xresolution; + yres = gimage->gimp->config->default_image->yresolution; + } + gimage->xresolution = xres; + gimage->yresolution = yres; + } + break; + + case PROP_TATTOO: + { + info->cp += xcf_read_int32 (info->fp, &info->tattoo_state, 1); + } + break; + + case PROP_PARASITES: + { + glong base = info->cp; + KisAnnotation *p; + + while (info->cp - base < prop_size) + { + p = xcf_load_parasite (info); + gimp_image_parasite_attach (gimage, p); + gimp_parasite_free (p); + } + if (info->cp - base != prop_size) + g_message ("Error while loading an image's parasites"); + } + break; + + case PROP_UNIT: + { + TQ_INT32 unit; + + info->cp += xcf_read_int32 (info->fp, &unit, 1); + + if ((unit <= GIMP_UNIT_PIXEL) || + (unit >= + _gimp_unit_get_number_of_built_in_units (gimage->gimp))) + { + g_message ("Warning, unit out of range in XCF file, " + "falling back to inches"); + unit = GIMP_UNIT_INCH; + } + + gimage->resolution_unit = unit; + } + break; + + case PROP_PATHS: + xcf_load_old_paths (info, gimage); + break; + + case PROP_USER_UNIT: + { + TQCString *unit_strings[5]; + float factor; + TQ_INT32 digits; + GimpUnit unit; + TQ_INT32 num_units; + TQ_INT32 i; + + info->cp += xcf_read_float (info->fp, &factor, 1); + info->cp += xcf_read_int32 (info->fp, &digits, 1); + info->cp += xcf_read_string (info->fp, unit_strings, 5); + + for (i = 0; i < 5; i++) + if (unit_strings[i] == NULL) + unit_strings[i] = g_strdup (""); + + num_units = _gimp_unit_get_number_of_units (gimage->gimp); + + for (unit = + _gimp_unit_get_number_of_built_in_units (gimage->gimp); + unit < num_units; unit++) + { + /* if the factor and the identifier match some unit + * in unitrc, use the unitrc unit + */ + if ((ABS (_gimp_unit_get_factor (gimage->gimp, + unit) - factor) < 1e-5) && + (strcmp (unit_strings[0], + _gimp_unit_get_identifier (gimage->gimp, + unit)) == 0)) + { + break; + } + } + + /* no match */ + if (unit == num_units) + unit = _gimp_unit_new (gimage->gimp, + unit_strings[0], + factor, + digits, + unit_strings[1], + unit_strings[2], + unit_strings[3], unit_strings[4]); + + gimage->resolution_unit = unit; + + for (i = 0; i < 5; i++) + g_free (unit_strings[i]); + } + break; + + case PROP_VECTORS: + { + TQ_INT32 base = info->cp; + + if (xcf_load_vectors (info, gimage)) + { + if (base + prop_size != info->cp) + { + g_warning + ("Mismatch in PROP_VECTORS size: skipping %d bytes.", + base + prop_size - info->cp); + xcf_seek_pos (info, base + prop_size, NULL); + } + } + else + { + /* skip silently since we don't understand the format and + * xcf_load_vectors already explained what was wrong + */ + xcf_seek_pos (info, base + prop_size, NULL); + } + } + break; + + default: +#ifdef GIMP_UNSTABLE + g_printerr ("unexpected/unknown image property: %d (skipping)", + prop_type); +#endif + { + TQ_UINT8 buf[16]; + TQ_UINT32 amount; + + while (prop_size > 0) + { + amount = MIN (16, prop_size); + info->cp += xcf_read_int8 (info->fp, buf, amount); + prop_size -= MIN (16, amount); + } + } + break; + } + } + + return FALSE; +} + +static bool +xcf_load_layer_props (XcfInfo * info, + KisImage * gimage, + KisLayer * layer, + bool * apply_tqmask, + bool * edit_tqmask, + bool * show_tqmask, TQ_INT32 * text_layer_flags) +{ + PropType prop_type; + TQ_INT32 prop_size; + + while (TRUE) + { + if (!xcf_load_prop (info, &prop_type, &prop_size)) + return FALSE; + + switch (prop_type) + { + case PROP_END: + return TRUE; + + case PROP_ACTIVE_LAYER: + info->active_layer = layer; + break; + + case PROP_FLOATING_SELECTION: + info->floating_sel = layer; + info->cp += + xcf_read_int32 (info->fp, + (TQ_INT32 *) & info->floating_sel_offset, 1); + break; + + case PROP_OPACITY: + { + TQ_INT32 opacity; + + info->cp += xcf_read_int32 (info->fp, &opacity, 1); + layer->opacity = CLAMP ((gdouble) opacity / 255.0, + GIMP_OPACITY_TRANSPARENT, + GIMP_OPACITY_OPAQUE); + } + break; + + case PROP_VISIBLE: + { + bool visible; + + info->cp += xcf_read_int32 (info->fp, (TQ_INT32 *) & visible, 1); + gimp_item_set_visible (GIMP_ITEM (layer), + visible ? TRUE : FALSE, FALSE); + } + break; + + case PROP_LINKED: + { + bool linked; + + info->cp += xcf_read_int32 (info->fp, (TQ_INT32 *) & linked, 1); + gimp_item_set_linked (GIMP_ITEM (layer), + linked ? TRUE : FALSE, FALSE); + } + break; + + case PROP_LOCK_ALPHA: + info->cp += + xcf_read_int32 (info->fp, (TQ_INT32 *) & layer->lock_alpha, 1); + break; + + case PROP_APPLY_MASK: + info->cp += xcf_read_int32 (info->fp, (TQ_INT32 *) apply_tqmask, 1); + break; + + case PROP_EDIT_MASK: + info->cp += xcf_read_int32 (info->fp, (TQ_INT32 *) edit_tqmask, 1); + break; + + case PROP_SHOW_MASK: + info->cp += xcf_read_int32 (info->fp, (TQ_INT32 *) show_tqmask, 1); + break; + + case PROP_OFFSETS: + info->cp += + xcf_read_int32 (info->fp, + (TQ_INT32 *) & GIMP_ITEM (layer)->offset_x, 1); + info->cp += + xcf_read_int32 (info->fp, + (TQ_INT32 *) & GIMP_ITEM (layer)->offset_y, 1); + break; + + case PROP_MODE: + info->cp += xcf_read_int32 (info->fp, (TQ_INT32 *) & layer->mode, 1); + break; + + case PROP_TATTOO: + info->cp += xcf_read_int32 (info->fp, + (TQ_INT32 *) & GIMP_ITEM (layer)->tattoo, + 1); + break; + + case PROP_PARASITES: + { + glong base = info->cp; + KisAnnotation *p; + + while (info->cp - base < prop_size) + { + p = xcf_load_parasite (info); + gimp_item_parasite_attach (GIMP_ITEM (layer), p); + gimp_parasite_free (p); + } + if (info->cp - base != prop_size) + g_message ("Error while loading a layer's parasites"); + } + break; + + case PROP_TEXT_LAYER_FLAGS: + info->cp += xcf_read_int32 (info->fp, text_layer_flags, 1); + break; + + default: + { + TQ_UINT8 buf[16]; + TQ_UINT32 amount; + +#ifdef GIMP_UNSTABLE + g_printerr ("unexpected/unknown layer property: %d (skipping)", + prop_type); +#endif + while (prop_size > 0) + { + amount = MIN (16, prop_size); + info->cp += xcf_read_int8 (info->fp, buf, amount); + prop_size -= MIN (16, amount); + } + } + break; + } + } + + return FALSE; +} + +static bool +xcf_load_channel_props (XcfInfo * info, + KisImage * gimage, GimpChannel ** channel) +{ + PropType prop_type; + TQ_INT32 prop_size; + + while (TRUE) + { + if (!xcf_load_prop (info, &prop_type, &prop_size)) + return FALSE; + + switch (prop_type) + { + case PROP_END: + return TRUE; + + case PROP_ACTIVE_CHANNEL: + info->active_channel = *channel; + break; + + case PROP_SELECTION: + g_object_unref (gimage->selection_tqmask); + gimage->selection_tqmask = + gimp_selection_new (gimage, + gimp_item_width (GIMP_ITEM (*channel)), + gimp_item_height (GIMP_ITEM (*channel))); + g_object_ref (gimage->selection_tqmask); + gimp_item_sink (GIMP_ITEM (gimage->selection_tqmask)); + + tile_manager_unref (GIMP_DRAWABLE (gimage->selection_tqmask)->tiles); + GIMP_DRAWABLE (gimage->selection_tqmask)->tiles = + GIMP_DRAWABLE (*channel)->tiles; + GIMP_DRAWABLE (*channel)->tiles = NULL; + g_object_unref (*channel); + *channel = gimage->selection_tqmask; + (*channel)->boundary_known = FALSE; + (*channel)->bounds_known = FALSE; + break; + + case PROP_OPACITY: + { + TQ_INT32 opacity; + + info->cp += xcf_read_int32 (info->fp, &opacity, 1); + (*channel)->color.a = opacity / 255.0; + } + break; + + case PROP_VISIBLE: + { + bool visible; + + info->cp += xcf_read_int32 (info->fp, (TQ_INT32 *) & visible, 1); + gimp_item_set_visible (GIMP_ITEM (*channel), + visible ? TRUE : FALSE, FALSE); + } + break; + + case PROP_LINKED: + { + bool linked; + + info->cp += xcf_read_int32 (info->fp, (TQ_INT32 *) & linked, 1); + gimp_item_set_linked (GIMP_ITEM (*channel), + linked ? TRUE : FALSE, FALSE); + } + break; + + case PROP_SHOW_MASKED: + { + bool show_tqmasked; + + info->cp += + xcf_read_int32 (info->fp, (TQ_INT32 *) & show_tqmasked, 1); + gimp_channel_set_show_tqmasked (*channel, show_tqmasked); + } + break; + + case PROP_COLOR: + { + guchar col[3]; + + info->cp += xcf_read_int8 (info->fp, (TQ_UINT8 *) col, 3); + + gimp_rgb_set_uchar (&(*channel)->color, col[0], col[1], col[2]); + } + break; + + case PROP_TATTOO: + info->cp += + xcf_read_int32 (info->fp, &GIMP_ITEM (*channel)->tattoo, 1); + break; + + case PROP_PARASITES: + { + glong base = info->cp; + KisAnnotation *p; + + while ((info->cp - base) < prop_size) + { + p = xcf_load_parasite (info); + gimp_item_parasite_attach (GIMP_ITEM (*channel), p); + gimp_parasite_free (p); + } + if (info->cp - base != prop_size) + g_message ("Error while loading a channel's parasites"); + } + break; + + default: +#ifdef GIMP_UNSTABLE + g_printerr ("unexpected/unknown channel property: %d (skipping)", + prop_type); +#endif + + { + TQ_UINT8 buf[16]; + TQ_UINT32 amount; + + while (prop_size > 0) + { + amount = MIN (16, prop_size); + info->cp += xcf_read_int8 (info->fp, buf, amount); + prop_size -= MIN (16, amount); + } + } + break; + } + } + + return FALSE; +} + +static bool +xcf_load_prop (XcfInfo * info, PropType * prop_type, TQ_INT32 * prop_size) +{ + info->cp += xcf_read_int32 (info->fp, (TQ_INT32 *) prop_type, 1); + info->cp += xcf_read_int32 (info->fp, (TQ_INT32 *) prop_size, 1); + return TRUE; +} + +static KisLayer * +xcf_load_layer (XcfInfo * info, KisImage * gimage) +{ + KisLayer *layer; + GimpLayerMask *layer_tqmask; + TQ_INT32 hierarchy_offset; + TQ_INT32 layer_tqmask_offset; + bool apply_tqmask = TRUE; + bool edit_tqmask = FALSE; + bool show_tqmask = FALSE; + bool active; + bool floating; + TQ_INT32 text_layer_flags = 0; + TQ_INT32 width; + TQ_INT32 height; + TQ_INT32 type; + bool is_fs_drawable; + TQCString *name; + + /* check and see if this is the drawable the floating selection + * is attached to. if it is then we'll do the attachment in our caller. + */ + is_fs_drawable = (info->cp == info->floating_sel_offset); + + /* read in the layer width, height, type and name */ + info->cp += xcf_read_int32 (info->fp, (TQ_INT32 *) & width, 1); + info->cp += xcf_read_int32 (info->fp, (TQ_INT32 *) & height, 1); + info->cp += xcf_read_int32 (info->fp, (TQ_INT32 *) & type, 1); + info->cp += xcf_read_string (info->fp, &name, 1); + + /* create a new layer */ + layer = gimp_layer_new (gimage, width, height, + type, name, 255, GIMP_NORMAL_MODE); + g_free (name); + if (!layer) + return NULL; + + /* read in the layer properties */ + if (!xcf_load_layer_props (info, gimage, layer, + &apply_tqmask, &edit_tqmask, &show_tqmask, + &text_layer_flags)) + goto error; + + /* call the evil text layer hack that might change our layer pointer */ + active = (info->active_layer == layer); + floating = (info->floating_sel == layer); + + if (gimp_text_layer_xcf_load_hack (&layer)) + { + gimp_text_layer_set_xcf_flags (GIMP_TEXT_LAYER (layer), + text_layer_flags); + + if (active) + info->active_layer = layer; + if (floating) + info->floating_sel = layer; + } + + /* read the hierarchy and layer tqmask offsets */ + info->cp += xcf_read_int32 (info->fp, &hierarchy_offset, 1); + info->cp += xcf_read_int32 (info->fp, &layer_tqmask_offset, 1); + + /* read in the hierarchy */ + if (!xcf_seek_pos (info, hierarchy_offset, NULL)) + goto error; + + if (!xcf_load_hierarchy (info, GIMP_DRAWABLE (layer)->tiles)) + goto error; + + /* read in the layer tqmask */ + if (layer_tqmask_offset != 0) + { + if (!xcf_seek_pos (info, layer_tqmask_offset, NULL)) + goto error; + + layer_tqmask = xcf_load_layer_tqmask (info, gimage); + if (!layer_tqmask) + goto error; + + layer_tqmask->apply_tqmask = apply_tqmask; + layer_tqmask->edit_tqmask = edit_tqmask; + layer_tqmask->show_tqmask = show_tqmask; + + gimp_layer_add_tqmask (layer, layer_tqmask, FALSE); + } + + /* attach the floating selection... */ + if (is_fs_drawable) + info->floating_sel_drawable = GIMP_DRAWABLE (layer); + + return layer; + +error: + g_object_unref (layer); + return NULL; +} + +static GimpChannel * +xcf_load_channel (XcfInfo * info, KisImage * gimage) +{ + GimpChannel *channel; + TQ_INT32 hierarchy_offset; + TQ_INT32 width; + TQ_INT32 height; + bool is_fs_drawable; + TQCString *name; + GimpRGB color = { 0.0, 0.0, 0.0, GIMP_OPACITY_OPAQUE }; + + /* check and see if this is the drawable the floating selection + * is attached to. if it is then we'll do the attachment in our caller. + */ + is_fs_drawable = (info->cp == info->floating_sel_offset); + + /* read in the layer width, height and name */ + info->cp += xcf_read_int32 (info->fp, (TQ_INT32 *) & width, 1); + info->cp += xcf_read_int32 (info->fp, (TQ_INT32 *) & height, 1); + info->cp += xcf_read_string (info->fp, &name, 1); + + /* create a new channel */ + channel = gimp_channel_new (gimage, width, height, name, &color); + g_free (name); + if (!channel) + return NULL; + + /* read in the channel properties */ + if (!xcf_load_channel_props (info, gimage, &channel)) + goto error; + + /* read the hierarchy and layer tqmask offsets */ + info->cp += xcf_read_int32 (info->fp, &hierarchy_offset, 1); + + /* read in the hierarchy */ + if (!xcf_seek_pos (info, hierarchy_offset, NULL)) + goto error; + + if (!xcf_load_hierarchy (info, GIMP_DRAWABLE (channel)->tiles)) + goto error; + + if (is_fs_drawable) + info->floating_sel_drawable = GIMP_DRAWABLE (channel); + + return channel; + +error: + g_object_unref (channel); + return NULL; +} + +static GimpLayerMask * +xcf_load_layer_tqmask (XcfInfo * info, KisImage * gimage) +{ + GimpLayerMask *layer_tqmask; + GimpChannel *channel; + TQ_INT32 hierarchy_offset; + TQ_INT32 width; + TQ_INT32 height; + bool is_fs_drawable; + TQCString *name; + GimpRGB color = { 0.0, 0.0, 0.0, GIMP_OPACITY_OPAQUE }; + + /* check and see if this is the drawable the floating selection + * is attached to. if it is then we'll do the attachment in our caller. + */ + is_fs_drawable = (info->cp == info->floating_sel_offset); + + /* read in the layer width, height and name */ + info->cp += xcf_read_int32 (info->fp, (TQ_INT32 *) & width, 1); + info->cp += xcf_read_int32 (info->fp, (TQ_INT32 *) & height, 1); + info->cp += xcf_read_string (info->fp, &name, 1); + + /* create a new layer tqmask */ + layer_tqmask = gimp_layer_tqmask_new (gimage, width, height, name, &color); + g_free (name); + if (!layer_tqmask) + return NULL; + + /* read in the layer_tqmask properties */ + channel = GIMP_CHANNEL (layer_tqmask); + if (!xcf_load_channel_props (info, gimage, &channel)) + goto error; + + /* read the hierarchy and layer tqmask offsets */ + info->cp += xcf_read_int32 (info->fp, &hierarchy_offset, 1); + + /* read in the hierarchy */ + if (!xcf_seek_pos (info, hierarchy_offset, NULL)) + goto error; + + if (!xcf_load_hierarchy (info, GIMP_DRAWABLE (layer_tqmask)->tiles)) + goto error; + + /* attach the floating selection... */ + if (is_fs_drawable) + info->floating_sel_drawable = GIMP_DRAWABLE (layer_tqmask); + + return layer_tqmask; + +error: + g_object_unref (layer_tqmask); + return NULL; +} + +static bool +xcf_load_hierarchy (XcfInfo * info, TileManager * tiles) +{ + TQ_INT32 saved_pos; + TQ_INT32 offset; + TQ_INT32 junk; + TQ_INT32 width; + TQ_INT32 height; + TQ_INT32 bpp; + + info->cp += xcf_read_int32 (info->fp, (TQ_INT32 *) & width, 1); + info->cp += xcf_read_int32 (info->fp, (TQ_INT32 *) & height, 1); + info->cp += xcf_read_int32 (info->fp, (TQ_INT32 *) & bpp, 1); + + /* make sure the values in the file correspond to the values + * calculated when the TileManager was created. + */ + if (width != tile_manager_width (tiles) || + height != tile_manager_height (tiles) || + bpp != tile_manager_bpp (tiles)) + return FALSE; + + /* load in the levels...we make sure that the number of levels + * calculated when the TileManager was created is the same + * as the number of levels found in the file. + */ + + info->cp += xcf_read_int32 (info->fp, &offset, 1); /* top level */ + + /* discard offsets for layers below first, if any. + */ + do + { + info->cp += xcf_read_int32 (info->fp, &junk, 1); + } + while (junk != 0); + + /* save the current position as it is where the + * next level offset is stored. + */ + saved_pos = info->cp; + + /* seek to the level offset */ + if (!xcf_seek_pos (info, offset, NULL)) + return FALSE; + + /* read in the level */ + if (!xcf_load_level (info, tiles)) + return FALSE; + + /* restore the saved position so we'll be ready to + * read the next offset. + */ + if (!xcf_seek_pos (info, saved_pos, NULL)) + return FALSE; + + return TRUE; +} + + +static bool +xcf_load_level (XcfInfo * info, TileManager * tiles) +{ + TQ_INT32 saved_pos; + TQ_INT32 offset, offset2; + TQ_UINT32 ntiles; + TQ_INT32 width; + TQ_INT32 height; + TQ_INT32 i; + TQ_INT32 fail; + Tile *previous; + Tile *tile; + + info->cp += xcf_read_int32 (info->fp, (TQ_INT32 *) & width, 1); + info->cp += xcf_read_int32 (info->fp, (TQ_INT32 *) & height, 1); + + if (width != tile_manager_width (tiles) || + height != tile_manager_height (tiles)) + return FALSE; + + /* read in the first tile offset. + * if it is '0', then this tile level is empty + * and we can simply return. + */ + info->cp += xcf_read_int32 (info->fp, &offset, 1); + if (offset == 0) + return TRUE; + + /* Initialise the reference for the in-memory tile-compression + */ + previous = NULL; + + ntiles = tiles->ntile_rows * tiles->ntile_cols; + for (i = 0; i < ntiles; i++) + { + fail = FALSE; + + if (offset == 0) + { + g_message ("not enough tiles found in level"); + return FALSE; + } + + /* save the current position as it is where the + * next tile offset is stored. + */ + saved_pos = info->cp; + + /* read in the offset of the next tile so we can calculate the amount + of data needed for this tile */ + info->cp += xcf_read_int32 (info->fp, &offset2, 1); + + /* if the offset is 0 then we need to read in the maximum possible + allowing for negative compression */ + if (offset2 == 0) + offset2 = offset + TILE_WIDTH * TILE_WIDTH * 4 * 1.5; + /* 1.5 is probably more + than we need to allow */ + + /* seek to the tile offset */ + if (!xcf_seek_pos (info, offset, NULL)) + return FALSE; + + /* get the tile from the tile manager */ + tile = tile_manager_get (tiles, i, TRUE, TRUE); + + /* read in the tile */ + switch (info->compression) + { + case COMPRESS_NONE: + if (!xcf_load_tile (info, tile)) + fail = TRUE; + break; + case COMPRESS_RLE: + if (!xcf_load_tile_rle (info, tile, offset2 - offset)) + fail = TRUE; + break; + case COMPRESS_ZLIB: + g_error ("xcf: zlib compression unimplemented"); + fail = TRUE; + break; + case COMPRESS_FRACTAL: + g_error ("xcf: fractal compression unimplemented"); + fail = TRUE; + break; + } + + if (fail) + { + tile_release (tile, TRUE); + return FALSE; + } + + /* To potentially save memory, we compare the + * newly-fetched tile against the last one, and + * if they're the same we copy-on-write mirror one against + * the other. + */ + if (previous != NULL) + { + tile_lock (previous); + if (tile_ewidth (tile) == tile_ewidth (previous) && + tile_eheight (tile) == tile_eheight (previous) && + tile_bpp (tile) == tile_bpp (previous) && + memcmp (tile_data_pointer (tile, 0, 0), + tile_data_pointer (previous, 0, 0), + tile_size (tile)) == 0) + tile_manager_map (tiles, i, previous); + tile_release (previous, FALSE); + } + tile_release (tile, TRUE); + previous = tile_manager_get (tiles, i, FALSE, FALSE); + + /* restore the saved position so we'll be ready to + * read the next offset. + */ + if (!xcf_seek_pos (info, saved_pos, NULL)) + return FALSE; + + /* read in the offset of the next tile */ + info->cp += xcf_read_int32 (info->fp, &offset, 1); + } + + if (offset != 0) + { + g_message ("encountered garbage after reading level: %d", offset); + return FALSE; + } + + return TRUE; +} + +static bool +xcf_load_tile (XcfInfo * info, Tile * tile) +{ +#ifdef SWAP_FROM_FILE + + if (!info->swap_num) + { + info->ref_count = g_new (int, 1); + info->swap_num = tile_swap_add (info->filename, + xcf_swap_func, info->ref_count); + } + + tile->swap_num = info->swap_num; + tile->swap_offset = info->cp; + *info->ref_count += 1; + +#else + + info->cp += xcf_read_int8 (info->fp, tile_data_pointer (tile, 0, 0), + tile_size (tile)); + +#endif + + return TRUE; +} + +static bool +xcf_load_tile_rle (XcfInfo * info, Tile * tile, int data_length) +{ + guchar *data; + guchar val; + TQ_INT32 size; + TQ_INT32 count; + TQ_INT32 length; + TQ_INT32 bpp; + TQ_INT32 i, j; + TQ_INT32 nmemb_read_successfully; + guchar *xcfdata, *xcfodata, *xcfdatalimit; + + data = tile_data_pointer (tile, 0, 0); + bpp = tile_bpp (tile); + + xcfdata = xcfodata = g_malloc (data_length); + + /* we have to use fread instead of xcf_read_* because we may be + reading past the end of the file here */ + nmemb_read_successfully = fread ((TQCString *) xcfdata, sizeof (TQCString), + data_length, info->fp); + info->cp += nmemb_read_successfully; + + xcfdatalimit = &xcfodata[nmemb_read_successfully - 1]; + + for (i = 0; i < bpp; i++) + { + data = (guchar *) tile_data_pointer (tile, 0, 0) + i; + size = tile_ewidth (tile) * tile_eheight (tile); + count = 0; + + while (size > 0) + { + if (xcfdata > xcfdatalimit) + { + goto bogus_rle; + } + + val = *xcfdata++; + + length = val; + if (length >= 128) + { + length = 255 - (length - 1); + if (length == 128) + { + if (xcfdata >= xcfdatalimit) + { + goto bogus_rle; + } + + length = (*xcfdata << 8) + xcfdata[1]; + xcfdata += 2; + } + + count += length; + size -= length; + + if (size < 0) + { + goto bogus_rle; + } + + if (&xcfdata[length - 1] > xcfdatalimit) + { + goto bogus_rle; + } + + while (length-- > 0) + { + *data = *xcfdata++; + data += bpp; + } + } + else + { + length += 1; + if (length == 128) + { + if (xcfdata >= xcfdatalimit) + { + goto bogus_rle; + } + + length = (*xcfdata << 8) + xcfdata[1]; + xcfdata += 2; + } + + count += length; + size -= length; + + if (size < 0) + { + goto bogus_rle; + } + + if (xcfdata > xcfdatalimit) + { + goto bogus_rle; + } + + val = *xcfdata++; + + for (j = 0; j < length; j++) + { + *data = val; + data += bpp; + } + } + } + } + g_free (xcfodata); + return TRUE; + +bogus_rle: + if (xcfodata) + g_free (xcfodata); + return FALSE; +} + +static KisAnnotation * +xcf_load_parasite (XcfInfo * info) +{ + KisAnnotation *p; + + p = g_new (KisAnnotation, 1); + info->cp += xcf_read_string (info->fp, &p->name, 1); + info->cp += xcf_read_int32 (info->fp, &p->flags, 1); + info->cp += xcf_read_int32 (info->fp, &p->size, 1); + p->data = g_new (TQCString, p->size); + info->cp += xcf_read_int8 (info->fp, p->data, p->size); + + return p; +} + +static bool +xcf_load_old_paths (XcfInfo * info, KisImage * gimage) +{ + TQ_INT32 num_paths; + TQ_INT32 last_selected_row; + GimpVectors *active_vectors; + + info->cp += xcf_read_int32 (info->fp, &last_selected_row, 1); + info->cp += xcf_read_int32 (info->fp, &num_paths, 1); + + while (num_paths-- > 0) + xcf_load_old_path (info, gimage); + + active_vectors = (GimpVectors *) + gimp_container_get_child_by_index (gimage->vectors, last_selected_row); + + if (active_vectors) + gimp_image_set_active_vectors (gimage, active_vectors); + + return TRUE; +} + +static bool +xcf_load_old_path (XcfInfo * info, KisImage * gimage) +{ + TQCString *name; + TQ_INT32 locked; + TQ_UINT8 state; + TQ_INT32 closed; + TQ_INT32 num_points; + TQ_INT32 version; /* changed from num_paths */ + GimpTattoo tattoo = 0; + GimpVectors *vectors; + GimpVectorsCompatPoint *points; + TQ_INT32 i; + + info->cp += xcf_read_string (info->fp, &name, 1); + info->cp += xcf_read_int32 (info->fp, &locked, 1); + info->cp += xcf_read_int8 (info->fp, &state, 1); + info->cp += xcf_read_int32 (info->fp, &closed, 1); + info->cp += xcf_read_int32 (info->fp, &num_points, 1); + info->cp += xcf_read_int32 (info->fp, &version, 1); + + if (version == 2) + { + TQ_INT32 dummy; + + /* Had extra type field and points are stored as doubles */ + info->cp += xcf_read_int32 (info->fp, (TQ_INT32 *) & dummy, 1); + } + else if (version == 3) + { + TQ_INT32 dummy; + + /* Has extra tatto field */ + info->cp += xcf_read_int32 (info->fp, (TQ_INT32 *) & dummy, 1); + info->cp += xcf_read_int32 (info->fp, (TQ_INT32 *) & tattoo, 1); + } + else if (version != 1) + { + g_warning ("Unknown path type. Possibly corrupt XCF file"); + + return FALSE; + } + + /* skip empty compatibility paths */ + if (num_points == 0) + return FALSE; + + points = g_new0 (GimpVectorsCompatPoint, num_points); + + for (i = 0; i < num_points; i++) + { + if (version == 1) + { + TQ_INT3232 x; + TQ_INT3232 y; + + info->cp += xcf_read_int32 (info->fp, &points[i].type, 1); + info->cp += xcf_read_int32 (info->fp, (TQ_INT32 *) & x, 1); + info->cp += xcf_read_int32 (info->fp, (TQ_INT32 *) & y, 1); + + points[i].x = x; + points[i].y = y; + } + else + { + float x; + float y; + + info->cp += xcf_read_int32 (info->fp, &points[i].type, 1); + info->cp += xcf_read_float (info->fp, &x, 1); + info->cp += xcf_read_float (info->fp, &y, 1); + + points[i].x = x; + points[i].y = y; + } + } + + vectors = + gimp_vectors_compat_new (gimage, name, points, num_points, closed); + + g_free (name); + g_free (points); + + GIMP_ITEM (vectors)->linked = locked; + + if (tattoo) + GIMP_ITEM (vectors)->tattoo = tattoo; + + gimp_image_add_vectors (gimage, vectors, + gimp_container_num_tqchildren (gimage->vectors)); + + return TRUE; +} + +static bool +xcf_load_vectors (XcfInfo * info, KisImage * gimage) +{ + TQ_INT32 version; + TQ_INT32 active_index; + TQ_INT32 num_paths; + GimpVectors *active_vectors; + TQ_INT32 base; + +#ifdef GIMP_XCF_PATH_DEBUG + g_printerr ("xcf_load_vectors\n"); +#endif + + base = info->cp; + + info->cp += xcf_read_int32 (info->fp, &version, 1); + + if (version != 1) + { + g_message ("Unknown vectors version: %d (skipping)", version); + return FALSE; + } + + info->cp += xcf_read_int32 (info->fp, &active_index, 1); + info->cp += xcf_read_int32 (info->fp, &num_paths, 1); + +#ifdef GIMP_XCF_PATH_DEBUG + g_printerr ("%d paths (active: %d)\n", num_paths, active_index); +#endif + + while (num_paths-- > 0) + if (!xcf_load_vector (info, gimage)) + return FALSE; + + active_vectors = (GimpVectors *) + gimp_container_get_child_by_index (gimage->vectors, active_index); + + if (active_vectors) + gimp_image_set_active_vectors (gimage, active_vectors); + +#ifdef GIMP_XCF_PATH_DEBUG + g_printerr ("xcf_load_vectors: loaded %d bytes\n", info->cp - base); +#endif + return TRUE; +} + +static bool +xcf_load_vector (XcfInfo * info, KisImage * gimage) +{ + TQCString *name; + GimpTattoo tattoo = 0; + TQ_INT32 visible; + TQ_INT32 linked; + TQ_INT32 num_parasites; + TQ_INT32 num_strokes; + GimpVectors *vectors; + TQ_INT32 i; + +#ifdef GIMP_XCF_PATH_DEBUG + g_printerr ("xcf_load_vector\n"); +#endif + + info->cp += xcf_read_string (info->fp, &name, 1); + info->cp += xcf_read_int32 (info->fp, &tattoo, 1); + info->cp += xcf_read_int32 (info->fp, &visible, 1); + info->cp += xcf_read_int32 (info->fp, &linked, 1); + info->cp += xcf_read_int32 (info->fp, &num_parasites, 1); + info->cp += xcf_read_int32 (info->fp, &num_strokes, 1); + +#ifdef GIMP_XCF_PATH_DEBUG + g_printerr + ("name: %s, tattoo: %d, visible: %d, linked: %d, num_parasites %d, " + "num_strokes %d\n", name, tattoo, visible, linked, num_parasites, + num_strokes); +#endif + + vectors = gimp_vectors_new (gimage, name); + + GIMP_ITEM (vectors)->visible = visible ? TRUE : FALSE; + GIMP_ITEM (vectors)->linked = linked ? TRUE : FALSE; + + if (tattoo) + GIMP_ITEM (vectors)->tattoo = tattoo; + + for (i = 0; i < num_parasites; i++) + { + KisAnnotation *parasite; + + parasite = xcf_load_parasite (info); + + if (!parasite) + return FALSE; + + gimp_item_parasite_attach (GIMP_ITEM (vectors), parasite); + gimp_parasite_free (parasite); + } + + for (i = 0; i < num_strokes; i++) + { + TQ_INT32 stroke_type_id; + TQ_INT32 closed; + TQ_INT32 num_axes; + TQ_INT32 num_control_points; + TQ_INT32 type; + float coords[6] = { 0.0, 0.0, 1.0, 0.5, 0.5, 0.5 }; + GimpStroke *stroke; + TQ_INT32 j; + + GValueArray *control_points; + GValue value = { 0, }; + GimpAnchor anchor; + GType stroke_type; + + g_value_init (&value, GIMP_TYPE_ANCHOR); + + info->cp += xcf_read_int32 (info->fp, &stroke_type_id, 1); + info->cp += xcf_read_int32 (info->fp, &closed, 1); + info->cp += xcf_read_int32 (info->fp, &num_axes, 1); + info->cp += xcf_read_int32 (info->fp, &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; + } + + control_points = g_value_array_new (num_control_points); + + anchor.selected = FALSE; + + for (j = 0; j < num_control_points; j++) + { + info->cp += xcf_read_int32 (info->fp, &type, 1); + info->cp += xcf_read_float (info->fp, 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); + g_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); + } + + gimp_image_add_vectors (gimage, vectors, + gimp_container_num_tqchildren (gimage->vectors)); + + return TRUE; +} + +#ifdef SWAP_FROM_FILE + +static bool +xcf_swap_func (TQ_INT32 fd, Tile * tile, TQ_INT32 cmd, gpointer user_data) +{ + TQ_INT32 bytes; + TQ_INT32 err; + TQ_INT32 nleft; + TQ_INT32 *ref_count; + + switch (cmd) + { + case SWAP_IN: + lseek (fd, tile->swap_offset, SEEK_SET); + + bytes = tile_size (tile); + tile_alloc (tile); + + nleft = bytes; + while (nleft > 0) + { + do + { + err = read (fd, tile->data + bytes - nleft, nleft); + } + while ((err == -1) && ((errno == EAGAIN) || (errno == EINTR))); + + if (err <= 0) + { + g_message ("unable to read tile data from xcf file: " + "%d ( %d ) bytes read", err, nleft); + return FALSE; + } + + nleft -= err; + } + break; + + case SWAP_OUT: + case SWAP_DELETE: + case SWAP_COMPRESS: + ref_count = user_data; + *ref_count -= 1; + if (*ref_count == 0) + { + tile_swap_remove (tile->swap_num); + g_free (ref_count); + } + + tile->swap_num = 1; + tile->swap_offset = -1; + + return TRUE; + } + + return FALSE; +} + +#endif diff --git a/filters/chalk/xcf/xcf/xcf-load.h b/filters/chalk/xcf/xcf/xcf-load.h new file mode 100644 index 00000000..75bc0e9c --- /dev/null +++ b/filters/chalk/xcf/xcf/xcf-load.h @@ -0,0 +1,27 @@ +/* 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. + */ + +#ifndef __XCF_LOAD_H__ +#define __XCF_LOAD_H__ + +class KisImage; +class XcfInfo; + +KisImage * xcf_load_image (XcfInfo *info); + +#endif /* __XCF_LOAD_H__ */ diff --git a/filters/chalk/xcf/xcf/xcf-private.h b/filters/chalk/xcf/xcf/xcf-private.h new file mode 100644 index 00000000..3a832fbd --- /dev/null +++ b/filters/chalk/xcf/xcf/xcf-private.h @@ -0,0 +1,95 @@ +/* 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. + */ + +#ifndef __XCF_PRIVATE_H__ +#define __XCF_PRIVATE_H__ + +#include <tqcstring.h> + +typedef enum +{ + PROP_END = 0, + PROP_COLORMAP = 1, + PROP_ACTIVE_LAYER = 2, + PROP_ACTIVE_CHANNEL = 3, + PROP_SELECTION = 4, + PROP_FLOATING_SELECTION = 5, + PROP_OPACITY = 6, + PROP_MODE = 7, + PROP_VISIBLE = 8, + PROP_LINKED = 9, + PROP_LOCK_ALPHA = 10, + PROP_APPLY_MASK = 11, + PROP_EDIT_MASK = 12, + PROP_SHOW_MASK = 13, + PROP_SHOW_MASKED = 14, + PROP_OFFSETS = 15, + PROP_COLOR = 16, + PROP_COMPRESSION = 17, + PROP_GUIDES = 18, + PROP_RESOLUTION = 19, + PROP_TATTOO = 20, + PROP_PARASITES = 21, + PROP_UNIT = 22, + PROP_PATHS = 23, + PROP_USER_UNIT = 24, + PROP_VECTORS = 25, + PROP_TEXT_LAYER_FLAGS = 26 +} PropType; + +typedef enum +{ + COMPRESS_NONE = 0, + COMPRESS_RLE = 1, + COMPRESS_ZLIB = 2, /* unused */ + COMPRESS_FRACTAL = 3 /* unused */ +} XcfCompressionType; + +typedef enum +{ + XCF_ORIENTATION_HORIZONTAL = 1, + XCF_ORIENTATION_VERTICAL = 2 +} XcfQt::OrientationType; + +typedef enum +{ + XCF_STROKETYPE_STROKE = 0, + XCF_STROKETYPE_BEZIER_STROKE = 1 +} XcfStrokeType; + +typedef struct _XcfInfo XcfInfo; + +struct _XcfInfo +{ + FILE *fp; + TQ_UINT32 cp; + const TQCString filename; + //GimpTattoo tattoo_state; + KisLayer *active_layer; + //GimpChannel *active_channel; + //GimpDrawable *floating_sel_drawable; + KisLayer *floating_sel; + TQ_UINT32 floating_sel_offset; + TQ_INT32 swap_num; + TQ_INT32 *ref_count; + XcfCompressionType compression; + TQ_INT32 file_version; +}; + + +#endif /* __XCF_PRIVATE_H__ */ diff --git a/filters/chalk/xcf/xcf/xcf-read.cc b/filters/chalk/xcf/xcf/xcf-read.cc new file mode 100644 index 00000000..314daa90 --- /dev/null +++ b/filters/chalk/xcf/xcf/xcf-read.cc @@ -0,0 +1,118 @@ +/* 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 <glib-object.h> + +#include "libgimpbase/gimpbase.h" + +#include "xcf-read.h" + +#include "gimp-intl.h" + + +TQ_UINT32 +xcf_read_int32 (FILE *fp, + TQ_INT32 *data, + TQ_INT32 count) +{ + TQ_UINT32 total; + + total = count; + if (count > 0) + { + xcf_read_int8 (fp, (TQ_UINT8 *) data, count * 4); + + while (count--) + { + *data = g_ntohl (*data); + data++; + } + } + + return total * 4; +} + +TQ_UINT32 +xcf_read_float (FILE *fp, + float *data, + TQ_INT32 count) +{ + return xcf_read_int32 (fp, (TQ_INT32 *) ((void *) data), count); +} + +TQ_UINT32 +xcf_read_int8 (FILE *fp, + TQ_UINT8 *data, + TQ_INT32 count) +{ + TQ_UINT32 total; + TQ_INT32 bytes; + + total = count; + while (count > 0) + { + bytes = fread ((char *) data, sizeof (char), count, fp); + if (bytes <= 0) /* something bad happened */ + break; + count -= bytes; + data += bytes; + } + + return total; +} + +TQ_UINT32 +xcf_read_string (FILE *fp, + TQCString **data, + TQ_INT32 count) +{ + TQ_INT32 tmp; + TQ_UINT32 total; + TQ_INT32 i; + + total = 0; + for (i = 0; i < count; i++) + { + total += xcf_read_int32 (fp, &tmp, 1); + if (tmp > 0) + { + TQCString *str; + + str = g_new (TQCString, tmp); + total += xcf_read_int8 (fp, (TQ_UINT8*) str, tmp); + + if (str[tmp - 1] != '\0') + str[tmp - 1] = '\0'; + + data[i] = gimp_any_to_utf8 (str, -1, + _("Invalid UTF-8 string in XCF file")); + + g_free (str); + } + else + { + data[i] = NULL; + } + } + + return total; +} diff --git a/filters/chalk/xcf/xcf/xcf-read.h b/filters/chalk/xcf/xcf/xcf-read.h new file mode 100644 index 00000000..b90b8a41 --- /dev/null +++ b/filters/chalk/xcf/xcf/xcf-read.h @@ -0,0 +1,37 @@ +/* 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. + */ + +#ifndef __XCF_READ_H__ +#define __XCF_READ_H__ + + +TQ_UINT32 xcf_read_int32 (FILE *fp, + TQ_INT32 *data, + TQ_INT32 count); +TQ_UINT32 xcf_read_float (FILE *fp, + float *data, + TQ_INT32 count); +TQ_UINT32 xcf_read_int8 (FILE *fp, + TQ_UINT8 *data, + TQ_INT32 count); +TQ_UINT32 xcf_read_string (FILE *fp, + TQCString **data, + TQ_INT32 count); + + +#endif /* __XCF_READ_H__ */ diff --git a/filters/chalk/xcf/xcf/xcf-save.cc b/filters/chalk/xcf/xcf/xcf-save.cc new file mode 100644 index 00000000..50e5fb17 --- /dev/null +++ b/filters/chalk/xcf/xcf/xcf-save.cc @@ -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/gimplayertqmask.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_tqchildren (gimage->layers); + nchannels = (TQ_UINT32) gimp_container_num_tqchildren (gimage->channels); + + /* check and see if we have to save out the selection */ + have_selection = gimp_channel_bounds (gimp_image_get_tqmask (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_tqmask; + 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_tqchildren (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->tqmask) + { + xcf_check_error (xcf_save_prop (info, gimage, PROP_APPLY_MASK, + error, layer->tqmask->apply_tqmask)); + xcf_check_error (xcf_save_prop (info, gimage, PROP_EDIT_MASK, + error, layer->tqmask->edit_tqmask)); + xcf_check_error (xcf_save_prop (info, gimage, PROP_SHOW_MASK, + error, layer->tqmask->show_tqmask)); + } + 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_tqmask) + 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_tqmasked)); + + 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_tqmask; + + apply_tqmask = 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_tqmask, 1); + } + break; + + case PROP_EDIT_MASK: + { + TQ_INT32 edit_tqmask; + + edit_tqmask = 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_tqmask, 1); + } + break; + + case PROP_SHOW_MASK: + { + TQ_INT32 show_tqmask; + + show_tqmask = 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_tqmask, 1); + } + break; + + case PROP_SHOW_MASKED: + { + TQ_INT32 show_tqmasked; + + show_tqmasked = 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_tqmasked, 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 tqmask */ + if (layer->tqmask) + { + xcf_check_error (xcf_seek_end (info, error)); + offset = info->cp; + + xcf_check_error (xcf_save_channel (info, + gimage, GIMP_CHANNEL(layer->tqmask), + 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, ¶site->name, 1); + xcf_write_int32_check_error (info, ¶site->flags, 1); + xcf_write_int32_check_error (info, ¶site->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_tqchildren (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_tqchildren (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; +} diff --git a/filters/chalk/xcf/xcf/xcf-save.h b/filters/chalk/xcf/xcf/xcf-save.h new file mode 100644 index 00000000..cc49d2d3 --- /dev/null +++ b/filters/chalk/xcf/xcf/xcf-save.h @@ -0,0 +1,29 @@ +/* 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. + */ + +#ifndef __XCF_SAVE_H__ +#define __XCF_SAVE_H__ + + +void xcf_save_choose_format (XcfInfo *info, + KisImage *gimage); +TQ_INT32 xcf_save_image (XcfInfo *info, + KisImage *gimage); + + +#endif /* __XCF_SAVE_H__ */ diff --git a/filters/chalk/xcf/xcf/xcf-seek.cc b/filters/chalk/xcf/xcf/xcf-seek.cc new file mode 100644 index 00000000..a24271f5 --- /dev/null +++ b/filters/chalk/xcf/xcf/xcf-seek.cc @@ -0,0 +1,79 @@ +/* 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 <errno.h> + +#include <glib-object.h> + +#include "core/core-types.h" + +#include "xcf-private.h" +#include "xcf-seek.h" + +#include "gimp-intl.h" + +bool +xcf_seek_pos (XcfInfo *info, + TQ_UINT32 pos, + GError **error) +{ + if (info->cp != pos) + { + info->cp = pos; + if (fseek (info->fp, info->cp, SEEK_SET) == -1) + { + g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno), + _("Could not seek in XCF file: %s"), + g_strerror (errno)); + + return FALSE; + } + } + + return TRUE; +} + +bool +xcf_seek_end (XcfInfo *info, + GError **error) +{ + if (fseek (info->fp, 0, SEEK_END) == -1) + { + g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno), + _("Could not seek in XCF file: %s"), + g_strerror (errno)); + + return FALSE; + } + + info->cp = ftell (info->fp); + + if (fseek (info->fp, 0, SEEK_END) == -1) + { + g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno), + _("Could not seek in XCF file: %s"), + g_strerror (errno)); + + return FALSE; + } + + return TRUE; +} diff --git a/filters/chalk/xcf/xcf/xcf-seek.h b/filters/chalk/xcf/xcf/xcf-seek.h new file mode 100644 index 00000000..1bb7b7a9 --- /dev/null +++ b/filters/chalk/xcf/xcf/xcf-seek.h @@ -0,0 +1,30 @@ +/* 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. + */ + +#ifndef __XCF_SEEK_H__ +#define __XCF_SEEK_H__ + + +bool xcf_seek_pos (XcfInfo *info, + TQ_UINT32 pos, + GError **error); +bool xcf_seek_end (XcfInfo *info, + GError **error); + + +#endif /* __XCF_SEEK_H__ */ diff --git a/filters/chalk/xcf/xcf/xcf-write.cc b/filters/chalk/xcf/xcf/xcf-write.cc new file mode 100644 index 00000000..4010ab18 --- /dev/null +++ b/filters/chalk/xcf/xcf/xcf-write.cc @@ -0,0 +1,104 @@ +/* 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> /* strlen */ +#include <errno.h> + +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif + +#include <netinet/in.h> +#include <limits.h> +#include <stdlib.h> +#include <cfloat> + +#include "xcf-write.h" + +TQ_UINT32 xcf_write_int32 (FILE *fp, TQ_INT32 *data, TQ_INT32 count); +{ + TQ_INT32 tmp; + TQ_INT32 i; + + if (count > 0) + { + for (i = 0; i < count; i++) + { + tmp = htonl (data[i]); + xcf_write_int8 (fp, (TQ_UINT8*) &tmp, 4); + + if (fp->status() != IO_Ok) + { + return i * 4; + } + } + } + + return count * 4; +} + +TQ_UINT32 xcf_write_float (FILE *fp, float *data, TQ_INT32 count); +{ + return xcf_write_int32 (fp, (TQ_INT32 *)((void *)data), count, error); +} + +TQ_UINT32 xcf_write_int8 (FILE *fp, TQ_UINT8 *data, TQ_INT32 count); +{ + TQ_INT32 bytes; + bytes = fp->writeBlock( data, count ); + return bytes; +} + +TQ_UINT32 xcf_write_string (FILE *fp, TQCString *data, TQ_INT32 count); +{ + GError *tmp_error = NULL; + TQ_INT32 tmp; + TQ_UINT32 total; + TQ_INT32 i; + + total = 0; + for (i = 0; i < count; i++) + { + if (data[i]) + tmp = strlen (data[i]) + 1; + else + tmp = 0; + + xcf_write_int32 (fp, &tmp, 1, &tmp_error); + if (tmp_error) + { + g_propagate_error (error, tmp_error); + return total; + } + + if (tmp > 0) + xcf_write_int8 (fp, (TQ_UINT8*) data[i], tmp, &tmp_error); + if (tmp_error) + { + g_propagate_error (error, tmp_error); + return total; + } + + total += 4 + tmp; + } + + return total; +} diff --git a/filters/chalk/xcf/xcf/xcf-write.h b/filters/chalk/xcf/xcf/xcf-write.h new file mode 100644 index 00000000..8e21a08e --- /dev/null +++ b/filters/chalk/xcf/xcf/xcf-write.h @@ -0,0 +1,39 @@ +/* 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. + */ + +#ifndef __XCF_WRITE_H__ +#define __XCF_WRITE_H__ + +#include <tqstring.h> +#include <tqstringlist.h> +#include <tqfile.h> + +// Write count integers to the file +TQ_UINT32 xcf_write_int32 (TQFile *fp, TQ_INT32 *data, TQ_INT32 count); + +// Write count of floats to the file +TQ_UINT32 xcf_write_float (TQFile *fp, float *data, TQ_INT32 count); + +// Write count chars to the file +TQ_UINT32 xcf_write_int8 (TQFile *fp, TQ_UINT8 *data, TQ_INT32 count); + +// Write count zero-terminated strings to the file, each string preceded by its length as an integer +TQ_UINT32 xcf_write_string (TQFile *fp, TQCString *data, TQ_INT32 count); + + +#endif /* __XCF_WRITE_H__ */ diff --git a/filters/chalk/xcf/xcfexport.cpp b/filters/chalk/xcf/xcfexport.cpp new file mode 100644 index 00000000..b26fdba8 --- /dev/null +++ b/filters/chalk/xcf/xcfexport.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2005 Boudewijn Rempt <[email protected]> + * + * 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 <xcfexport.h> + +#include <kurl.h> +#include <kgenericfactory.h> + +#include <KoDocument.h> +#include <KoFilterChain.h> + +#include <kis_doc.h> +#include <kis_image.h> +#include <kis_annotation.h> +#include <kis_types.h> +#include <kis_xcf_converter.h> + +typedef KGenericFactory<XCFExport, KoFilter> XCFExportFactory; +K_EXPORT_COMPONENT_FACTORY(libchalkxcfexport, XCFExportFactory("kofficefilters")) + +XCFExport::XCFExport(KoFilter *, const char *, const TQStringList&) : KoFilter() +{ +} + +XCFExport::~XCFExport() +{ +} + +KoFilter::ConversiontqStatus XCFExport::convert(const TQCString& from, const TQCString& to) +{ + kdDebug(41008) << "xcf export! From: " << from << ", To: " << to << "\n"; + + if (from != "application/x-chalk") + return KoFilter::NotImplemented; + + KisDoc *output = dynamic_cast<KisDoc*>(m_chain->inputDocument()); + TQString filename = m_chain->outputFile(); + + if (!output) + return KoFilter::CreationError; + + + if (filename.isEmpty()) return KoFilter::FileNotFound; + + KURL url(filename); + + KisImageSP img = output->currentImage(); + if (!img) return KoFilter::ParsingError; + + KisXCFConverter ib(output, output->undoAdapter()); + + vKisAnnotationSP_it beginIt = img->beginAnnotations(); + vKisAnnotationSP_it endIt = img->endAnnotations(); + + if (ib.buildFile(url, img, beginIt, endIt) == KisImageBuilder_RESULT_OK) { + return KoFilter::OK; + } + + return KoFilter::InternalError; +} + +#include <xcfexport.moc> + diff --git a/filters/chalk/xcf/xcfexport.h b/filters/chalk/xcf/xcfexport.h new file mode 100644 index 00000000..b6acf737 --- /dev/null +++ b/filters/chalk/xcf/xcfexport.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2005 Boudewijn Rempt <[email protected]> + * + * 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. + */ +#ifndef XCFEXPORT_H_ +#define XCFEXPORT_H_ + +#include <KoFilter.h> + +class XCFExport : public KoFilter { + Q_OBJECT + TQ_OBJECT + +public: + XCFExport(KoFilter *tqparent, const char *name, const TQStringList&); + virtual ~XCFExport(); + +public: + virtual KoFilter::ConversiontqStatus convert(const TQCString& from, const TQCString& to); +}; + +#endif // XCFEXPORT_H_ + diff --git a/filters/chalk/xcf/xcfimport.cpp b/filters/chalk/xcf/xcfimport.cpp new file mode 100644 index 00000000..6f07ebcf --- /dev/null +++ b/filters/chalk/xcf/xcfimport.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2005 Boudewijn Rempt <[email protected]> + * + * 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 <tqstring.h> + +#include <xcfimport.h> +#include <kgenericfactory.h> +#include <KoDocument.h> +#include <KoFilterChain.h> + +#include <kis_doc.h> +#include <kis_view.h> +#include <kis_xcf_converter.h> +#include <kis_progress_display_interface.h> + +typedef KGenericFactory<XCFImport, KoFilter> XCFImportFactory; +K_EXPORT_COMPONENT_FACTORY(libchalkxcfimport, XCFImportFactory("kofficefilters")) + +XCFImport::XCFImport(KoFilter *, const char *, const TQStringList&) : KoFilter() +{ +} + +XCFImport::~XCFImport() +{ +} + +KoFilter::ConversiontqStatus XCFImport::convert(const TQCString&, const TQCString& to) +{ + kdDebug(41008) << "Importing using XCFImport!\n"; + + if (to != "application/x-chalk") + return KoFilter::BadMimeType; + + KisDoc * doc = dynamic_cast<KisDoc*>(m_chain -> outputDocument()); + KisView * view = static_cast<KisView*>(doc -> views().getFirst()); + + TQString filename = m_chain -> inputFile(); + + if (!doc) + return KoFilter::CreationError; + + doc -> prepareForImport(); + + + if (!filename.isEmpty()) { + + KURL url(filename); + + if (url.isEmpty()) + return KoFilter::FileNotFound; + + KisXCFConverter ib(doc, doc -> undoAdapter()); + + switch (ib.buildImage(url)) { + case KisImageBuilder_RESULT_UNSUPPORTED: + return KoFilter::NotImplemented; + break; + case KisImageBuilder_RESULT_INVALID_ARG: + return KoFilter::BadMimeType; + break; + case KisImageBuilder_RESULT_NO_URI: + case KisImageBuilder_RESULT_NOT_LOCAL: + return KoFilter::FileNotFound; + break; + case KisImageBuilder_RESULT_BAD_FETCH: + case KisImageBuilder_RESULT_EMPTY: + return KoFilter::ParsingError; + break; + case KisImageBuilder_RESULT_FAILURE: + return KoFilter::InternalError; + break; + case KisImageBuilder_RESULT_OK: + doc -> setCurrentImage( ib.image()); + return KoFilter::OK; + default: + break; + } + + } + return KoFilter::StorageCreationError; +} + +#include <xcfimport.moc> + diff --git a/filters/chalk/xcf/xcfimport.h b/filters/chalk/xcf/xcfimport.h new file mode 100644 index 00000000..038e7604 --- /dev/null +++ b/filters/chalk/xcf/xcfimport.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2005 Boudewijn Rempt <[email protected]> + * + * 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. + */ +#ifndef XCFIMPORT_H_ +#define XCFIMPORT_H_ + +#include <KoFilter.h> + +class XCFImport : public KoFilter { + Q_OBJECT + TQ_OBJECT + +public: + XCFImport(KoFilter *tqparent, const char *name, const TQStringList&); + virtual ~XCFImport(); + +public: + virtual KoFilter::ConversiontqStatus convert(const TQCString& from, const TQCString& to); +}; + +#endif // XCFIMPORT_H_ + |