[gimp/wip/xcf-delta-encoding: 1/2] app: add delta-encoded zlib compression mode for XCF tiles
- From: N/A <ell src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gimp/wip/xcf-delta-encoding: 1/2] app: add delta-encoded zlib compression mode for XCF tiles
- Date: Sat, 8 Jul 2017 00:46:50 +0000 (UTC)
commit d076a19fd4de57c8f9473298bebd3b61ebfb9a74
Author: Ell <ell_se yahoo com>
Date: Fri Jul 7 20:12:07 2017 -0400
app: add delta-encoded zlib compression mode for XCF tiles
This mode delta-encodes the tile data before compressing it with
zlib, as described in devel-docs/xcf.txt in the next commit. This
usually provides a ~10%-30% decrease in size for some classes of
images, especially photographs. While delta encoding might also
result in larger files for other classes of images, we apply it
adaptively, looking for the differentiation order that minimizes
the compressed tile size, so that we generally never pessimize
the file size, sans some negligible fixed overhard.
Note that for integer formats, delta encoding is lossless. For
floating-point formats, delta encoding is generally lossly, however
we verify that the introduced error never exceeds a certain (very
low) threshold, which can be set to 0 if necessary, to make
floating-point delta encoding lossless as well.
Add XCF version 12, which enables the new compression mode. Use
version 12, and the new mode, for all zlib-compressed XCFs.
app/core/gimpimage.c | 8 +-
app/xcf/Makefile.am | 2 +
app/xcf/xcf-load.c | 51 +++++++--
app/xcf/xcf-private.h | 5 +-
app/xcf/xcf-save.c | 136 ++++++++++++++++++++++++-
app/xcf/xcf-utils.c | 276 +++++++++++++++++++++++++++++++++++++++++++++++++
app/xcf/xcf-utils.h | 36 +++++++
app/xcf/xcf.c | 9 +-
8 files changed, 504 insertions(+), 19 deletions(-)
---
diff --git a/app/core/gimpimage.c b/app/core/gimpimage.c
index 58749c9..b71e2f8 100644
--- a/app/core/gimpimage.c
+++ b/app/core/gimpimage.c
@@ -2492,9 +2492,12 @@ gimp_image_get_xcf_version (GimpImage *image,
if (gimp_image_get_precision (image) != GIMP_PRECISION_U8_GAMMA)
version = MAX (7, version);
- /* need version 8 for zlib compression */
+ /* need version 8 for zlib compression, and version 12 for delta-encoded
+ * zlib compression; we always use delta encoding when zlib compression is
+ * requested.
+ */
if (zlib_compression)
- version = MAX (8, version);
+ version = MAX (12, version);
/* if version is 10 (lots of new layer modes), go to version 11 with
* 64 bit offsets right away
@@ -2531,6 +2534,7 @@ gimp_image_get_xcf_version (GimpImage *image,
case 9:
case 10:
case 11:
+ case 12:
if (gimp_version) *gimp_version = 210;
if (version_string) *version_string = "GIMP 2.10";
break;
diff --git a/app/xcf/Makefile.am b/app/xcf/Makefile.am
index 65a3dc8..a95a87c 100644
--- a/app/xcf/Makefile.am
+++ b/app/xcf/Makefile.am
@@ -25,5 +25,7 @@ libappxcf_a_SOURCES = \
xcf-save.h \
xcf-seek.c \
xcf-seek.h \
+ xcf-utils.c \
+ xcf-utils.h \
xcf-write.c \
xcf-write.h
diff --git a/app/xcf/xcf-load.c b/app/xcf/xcf-load.c
index 5cc9304..e7efab9 100644
--- a/app/xcf/xcf-load.c
+++ b/app/xcf/xcf-load.c
@@ -70,6 +70,7 @@
#include "xcf-load.h"
#include "xcf-read.h"
#include "xcf-seek.h"
+#include "xcf-utils.h"
#include "gimp-log.h"
#include "gimp-intl.h"
@@ -684,10 +685,11 @@ xcf_load_image_props (XcfInfo *info,
xcf_read_int8 (info, (guint8 *) &compression, 1);
- if ((compression != COMPRESS_NONE) &&
- (compression != COMPRESS_RLE) &&
- (compression != COMPRESS_ZLIB) &&
- (compression != COMPRESS_FRACTAL))
+ if ((compression != COMPRESS_NONE) &&
+ (compression != COMPRESS_RLE) &&
+ (compression != COMPRESS_ZLIB) &&
+ (compression != COMPRESS_FRACTAL) &&
+ (compression != COMPRESS_ZLIB_DELTA))
{
gimp_message (info->gimp, G_OBJECT (info->progress),
GIMP_MESSAGE_ERROR,
@@ -1985,6 +1987,7 @@ xcf_load_level (XcfInfo *info,
fail = TRUE;
break;
case COMPRESS_ZLIB:
+ case COMPRESS_ZLIB_DELTA:
if (!xcf_load_tile_zlib (info, buffer, &rect, format,
offset2 - offset))
fail = TRUE;
@@ -2198,6 +2201,7 @@ xcf_load_tile_zlib (XcfInfo *info,
guchar *tile_data = g_alloca (tile_size);
gsize bytes_read;
guchar *xcfdata;
+ guint32 order;
/* Workaround for bug #357809: avoid crashing on g_malloc() and skip
* this tile (return TRUE without storing data) as if it did not
@@ -2205,8 +2209,24 @@ xcf_load_tile_zlib (XcfInfo *info,
* skip the whole hierarchy while there may still be some valid
* tiles in the file.
*/
- if (data_length <= 0)
- return TRUE;
+ if (data_length <= 0 ||
+ (info->compression == COMPRESS_ZLIB_DELTA && data_length <= 4))
+ {
+ gimp_message_literal (info->gimp, G_OBJECT (info->progress),
+ GIMP_MESSAGE_WARNING,
+ _("Invalid tile data length in XCF file.\n"
+ "Skipping tile."));
+
+ return TRUE;
+ }
+
+ if (info->compression == COMPRESS_ZLIB_DELTA)
+ {
+ /* read the derivative order of a delta-encoded tile. */
+ xcf_read_int32 (info, &order, 1);
+
+ data_length -= 4;
+ }
xcfdata = g_alloca (data_length);
@@ -2252,22 +2272,33 @@ xcf_load_tile_zlib (XcfInfo *info,
}
else if (status == Z_BUF_ERROR)
{
- g_printerr ("xcf: decompressed tile bigger than the expected size.");
+ g_printerr ("xcf: decompressed tile bigger than the expected size.\n");
inflateEnd (&strm);
return FALSE;
}
else if (status != Z_OK)
{
- g_printerr ("xcf: tile decompression failed: %s", zError (status));
+ g_printerr ("xcf: tile decompression failed: %s\n", zError (status));
inflateEnd (&strm);
return FALSE;
}
}
+ inflateEnd (&strm);
+
+ if (info->compression == COMPRESS_ZLIB_DELTA)
+ {
+ /* integrate the data, to get the original back. */
+ if (! xcf_data_integrate (tile_data, tile_data, tile_size / bpp, format,
+ order, 0))
+ {
+ g_printerr ("xcf: tile integration failed\n");
+ return FALSE;
+ }
+ }
+
gegl_buffer_set (buffer, tile_rect, 0, format, tile_data,
GEGL_AUTO_ROWSTRIDE);
-
- inflateEnd (&strm);
return TRUE;
}
diff --git a/app/xcf/xcf-private.h b/app/xcf/xcf-private.h
index aa80876..ac64d1d 100644
--- a/app/xcf/xcf-private.h
+++ b/app/xcf/xcf-private.h
@@ -69,8 +69,9 @@ typedef enum
{
COMPRESS_NONE = 0,
COMPRESS_RLE = 1,
- COMPRESS_ZLIB = 2, /* unused */
- COMPRESS_FRACTAL = 3 /* unused */
+ COMPRESS_ZLIB = 2,
+ COMPRESS_FRACTAL = 3, /* unused */
+ COMPRESS_ZLIB_DELTA = 4
} XcfCompressionType;
typedef enum
diff --git a/app/xcf/xcf-save.c b/app/xcf/xcf-save.c
index 82c384c..3959dee 100644
--- a/app/xcf/xcf-save.c
+++ b/app/xcf/xcf-save.c
@@ -66,11 +66,24 @@
#include "xcf-read.h"
#include "xcf-save.h"
#include "xcf-seek.h"
+#include "xcf-utils.h"
#include "xcf-write.h"
+#include "gimp-log.h"
#include "gimp-intl.h"
+/* the ratio between the test data size and the tile data size, for delta-
+ * encoded zlib compressed tiles. see `xcf_save_tile_zlib()`.
+ */
+#define XCF_SAVE_ZLIB_DELTA_TEST_SIZE_RATIO (1.0 / 8.0)
+
+/* the maximal derivative order that may be used for delta-encoded zlib
+ * compressed tiles. see `xcf_save_tile_zlib()`.
+ */
+#define XCF_SAVE_ZLIB_DELTA_MAX_ORDER 4
+
+
static gboolean xcf_save_image_props (XcfInfo *info,
GimpImage *image,
GError **error);
@@ -1573,6 +1586,7 @@ xcf_save_level (XcfInfo *info,
rlebuf, error));
break;
case COMPRESS_ZLIB:
+ case COMPRESS_ZLIB_DELTA:
xcf_check_error (xcf_save_tile_zlib (info, buffer, &rect, format,
error));
break;
@@ -1761,6 +1775,126 @@ xcf_save_tile_zlib (XcfInfo *info,
gegl_buffer_get (buffer, tile_rect, 1.0, format, tile_data,
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+ if (info->compression == COMPRESS_ZLIB_DELTA)
+ {
+ GeglRectangle test_rect;
+ gint test_size;
+ guint32 order;
+ gint prev_compressed_size = G_MAXINT;
+
+ /* we repeatedly differentiate a small portion of the tile data,
+ * referred to as the test data, and compress it, with the intention of
+ * finding the derivative order which minimizes the compressed data size.
+ */
+
+ test_rect = *tile_rect;
+ test_rect.height *= XCF_SAVE_ZLIB_DELTA_TEST_SIZE_RATIO;
+
+ test_size = bpp * test_rect.width * test_rect.height;
+
+ for (order = 0; order <= XCF_SAVE_ZLIB_DELTA_MAX_ORDER; order++)
+ {
+ gint compressed_size = 0;
+
+ /* do a dummy compression that isn't actually written anywhere, to
+ * find the size of the compressed test data.
+ */
+
+ /* allocate deflate state */
+ strm.zalloc = Z_NULL;
+ strm.zfree = Z_NULL;
+ strm.opaque = Z_NULL;
+
+ status = deflateInit (&strm, Z_DEFAULT_COMPRESSION);
+ if (status != Z_OK)
+ return FALSE;
+
+ strm.next_in = tile_data;
+ strm.avail_in = test_size;
+ strm.next_out = buf;
+ strm.avail_out = tile_size;
+
+ action = Z_NO_FLUSH;
+
+ while ((status == Z_OK || status == Z_BUF_ERROR) &&
+ compressed_size < prev_compressed_size)
+ {
+ if (strm.avail_in == 0)
+ {
+ /* Finish the encoding. */
+ action = Z_FINISH;
+ }
+
+ status = deflate (&strm, action);
+
+ if (status == Z_STREAM_END || status == Z_BUF_ERROR)
+ {
+ compressed_size += tile_size - strm.avail_out;
+
+ /* Reset next_out and avail_out. */
+ strm.next_out = buf;
+ strm.avail_out = tile_size;
+ }
+ else if (status != Z_OK)
+ {
+ g_printerr ("xcf: tile compression failed: %s\n",
+ zError (status));
+ deflateEnd (&strm);
+ return FALSE;
+ }
+ }
+
+ deflateEnd (&strm);
+
+ /* if the compressed data size has increased, compared to the
+ * previous iteration, stop looking and use the derivative order of
+ * the previous iteration.
+ */
+ if (compressed_size >= prev_compressed_size)
+ {
+ order--;
+
+ break;
+ }
+
+ /* otherwise, try to differentiate the data, so that we compress the
+ * next-order derivative on the next iteration. if this fails, or if
+ * we reached the max order, break and use the current order.
+ */
+ if (order == XCF_SAVE_ZLIB_DELTA_MAX_ORDER ||
+ ! xcf_data_differentiate (tile_data, tile_data, test_size / bpp,
+ format, order, order + 1))
+ {
+ break;
+ }
+
+ prev_compressed_size = compressed_size;
+ }
+
+ /* we've overwritten the first test_size bytes of the tile data; reread
+ * them.
+ */
+ gegl_buffer_get (buffer, &test_rect, 1.0, format, tile_data,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ /* calculate the derivative of the determined order of the tile data; if
+ * this fails, use the 0-th derivative (i.e., the original data).
+ */
+ if (! xcf_data_differentiate (tile_data, tile_data, tile_size / bpp,
+ format, 0, order))
+ {
+ order = 0;
+
+ gegl_buffer_get (buffer, tile_rect, 1.0, format, tile_data,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+ }
+
+ /* write the derivative order before the actual data */
+ xcf_write_int32_check_error (info, &order, 1);
+
+ GIMP_LOG (XCF, "delta encoding order: %d", order);
+ }
+
/* allocate deflate state */
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
@@ -1799,7 +1933,7 @@ xcf_save_tile_zlib (XcfInfo *info,
}
else if (status != Z_OK)
{
- g_printerr ("xcf: tile compression failed: %s", zError (status));
+ g_printerr ("xcf: tile compression failed: %s\n", zError (status));
deflateEnd (&strm);
return FALSE;
}
diff --git a/app/xcf/xcf-utils.c b/app/xcf/xcf-utils.c
new file mode 100644
index 0000000..6c0ec07
--- /dev/null
+++ b/app/xcf/xcf-utils.c
@@ -0,0 +1,276 @@
+/* GIMP - The GNU 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <cairo.h>
+#include <gegl.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include "libgimpbase/gimpbase.h"
+#include "libgimpmath/gimpmath.h"
+
+#include "core/core-types.h"
+
+#include "gegl/gimp-babl.h"
+
+#include "xcf-utils.h"
+
+
+/* uncomment to disable delta encoding of floating-point data */
+/* #define XCF_DATA_DIFFERENTIATE_NO_FLOAT */
+
+/* the maximal allowable error for individual components of delta-encoded
+ * floating-point data
+ */
+#define XCF_DATA_DIFFERENTIATE_MAX_FLOAT_ERROR 1e-16
+
+
+/* calculates the (discrete) derivative of a sequence of pixels. the input
+ * sequence, given by `src`, shall be a derivative of order `from_order` of
+ * some original sequence; the output sequence, written to `dest`, is the
+ * derivative of order `to_order`, which shall be greater than or equal to
+ * `from_order`, of the said original sequence.
+ *
+ * `dest` and `src` may be equal, but may not otherwise partially overlap.
+ *
+ * returns TRUE if successful, or FALSE otherwise. upon failure, the contents
+ * of `dest` are unspecified.
+ */
+gboolean
+xcf_data_differentiate (void *dest,
+ const void *src,
+ gint n_pixels,
+ const Babl *format,
+ gint from_order,
+ gint to_order)
+{
+
+ gint n_components;
+ gint l;
+ gint i;
+ gint c;
+
+ g_return_val_if_fail (dest != NULL, FALSE);
+ g_return_val_if_fail (src != NULL, FALSE);
+ g_return_val_if_fail (n_pixels >= 0, FALSE);
+ g_return_val_if_fail (format != NULL, FALSE);
+ g_return_val_if_fail (from_order >= 0 && from_order <= to_order, FALSE);
+
+ if (to_order >= n_pixels)
+ return FALSE;
+
+ n_components = babl_format_get_n_components (format);
+
+ #define XCF_DATA_DIFFERENTIATE(type, verify, max_error) \
+ do \
+ { \
+ type *d = dest; \
+ const type *s = src; \
+ const type *orig = src; \
+ gint size = n_pixels * n_components * sizeof (type); \
+ \
+ if (from_order == to_order || n_pixels == 0) \
+ { \
+ if (dest != src) \
+ memcpy (dest, src, size); \
+ \
+ break; \
+ } \
+ \
+ if (dest != src) \
+ memcpy (dest, src, (from_order + 1) * n_components * sizeof (type)); \
+ \
+ if (verify && orig == dest) \
+ { \
+ orig = g_alloca (size); \
+ \
+ memcpy ((void *) orig, src, size); \
+ } \
+ \
+ for (l = from_order; l < to_order; l++) \
+ { \
+ for (i = n_pixels - 1; i > l; i--) \
+ { \
+ for (c = 0; c < n_components; c++) \
+ { \
+ d[n_components * i + c] = s[n_components * i + c] - \
+ s[n_components * (i - 1) + c]; \
+ } \
+ } \
+ \
+ s = d; \
+ } \
+ \
+ if (verify) \
+ { \
+ type reconstructed[n_pixels * n_components]; \
+ \
+ if (! xcf_data_integrate (reconstructed, dest, n_pixels, format, \
+ to_order, from_order)) \
+ return FALSE; \
+ \
+ for (i = 0; i < n_pixels * n_components; i++) \
+ { \
+ gdouble error; \
+ \
+ error = fabs ((gdouble) reconstructed[i] - \
+ (gdouble) orig[i]); \
+ \
+ if (error > max_error) \
+ return FALSE; \
+ } \
+ } \
+ } \
+ while (0)
+
+ switch (gimp_babl_format_get_component_type (format))
+ {
+ case GIMP_COMPONENT_TYPE_U8:
+ XCF_DATA_DIFFERENTIATE (guint8, FALSE, 0);
+ break;
+
+ case GIMP_COMPONENT_TYPE_U16:
+ XCF_DATA_DIFFERENTIATE (guint16, FALSE, 0);
+ break;
+
+ case GIMP_COMPONENT_TYPE_U32:
+ XCF_DATA_DIFFERENTIATE (guint32, FALSE, 0);
+ break;
+
+#ifndef XCF_DATA_DIFFERENTIATE_NO_FLOAT
+ case GIMP_COMPONENT_TYPE_FLOAT:
+ XCF_DATA_DIFFERENTIATE (gfloat, TRUE,
+ XCF_DATA_DIFFERENTIATE_MAX_FLOAT_ERROR);
+ break;
+
+ case GIMP_COMPONENT_TYPE_DOUBLE:
+ XCF_DATA_DIFFERENTIATE (gdouble, TRUE,
+ XCF_DATA_DIFFERENTIATE_MAX_FLOAT_ERROR);
+ break;
+#endif
+
+ default:
+ return FALSE;
+ }
+
+ #undef XCF_DATA_DIFFERENTIATE
+
+ return TRUE;
+}
+
+/* calculates the (discrete) integral of a sequence of pixels. the input
+ * sequence, given by `src`, shall be a derivative of order `from_order` of
+ * some original sequence; the output sequence, written to `dest`, is the
+ * derivative of order `to_order`, which shall be less than or equal to
+ * `from_order`, of the said original sequence.
+ *
+ * `dest` and `src` may be equal, but may not otherwise partially overlap.
+ *
+ * returns TRUE if successful, or FALSE otherwise. upon failure, the contents
+ * of `dest` are unspecified.
+ */
+gboolean
+xcf_data_integrate (void *dest,
+ const void *src,
+ gint n_pixels,
+ const Babl *format,
+ gint from_order,
+ gint to_order)
+{
+
+ gint n_components;
+ gint l;
+ gint i;
+ gint c;
+
+ g_return_val_if_fail (dest != NULL, FALSE);
+ g_return_val_if_fail (src != NULL, FALSE);
+ g_return_val_if_fail (n_pixels >= 0, FALSE);
+ g_return_val_if_fail (format != NULL, FALSE);
+ g_return_val_if_fail (to_order >= 0 && to_order <= from_order, FALSE);
+
+ if (from_order >= n_pixels)
+ return FALSE;
+
+ n_components = babl_format_get_n_components (format);
+
+ #define XCF_DATA_INTEGRATE(type) \
+ do \
+ { \
+ type *d = dest; \
+ const type *s = src; \
+ \
+ if (from_order == to_order || n_pixels == 0) \
+ { \
+ if (dest != src) \
+ memcpy (dest, src, n_pixels * n_components * sizeof (type)); \
+ \
+ break; \
+ } \
+ \
+ if (dest != src) \
+ memcpy (dest, src, from_order * n_components * sizeof (type)); \
+ \
+ for (l = from_order; l > to_order; l--) \
+ { \
+ for (i = l; i < n_pixels; i++) \
+ { \
+ for (c = 0; c < n_components; c++) \
+ { \
+ d[n_components * i + c] = s[n_components * i + c] + \
+ d[n_components * (i - 1) + c]; \
+ } \
+ } \
+ \
+ s = d; \
+ } \
+ } \
+ while (0)
+
+ switch (gimp_babl_format_get_component_type (format))
+ {
+ case GIMP_COMPONENT_TYPE_U8:
+ XCF_DATA_INTEGRATE (guint8);
+ break;
+
+ case GIMP_COMPONENT_TYPE_U16:
+ XCF_DATA_INTEGRATE (guint16);
+ break;
+
+ case GIMP_COMPONENT_TYPE_U32:
+ XCF_DATA_INTEGRATE (guint32);
+ break;
+
+ case GIMP_COMPONENT_TYPE_FLOAT:
+ XCF_DATA_INTEGRATE (gfloat);
+ break;
+
+ case GIMP_COMPONENT_TYPE_DOUBLE:
+ XCF_DATA_INTEGRATE (gdouble);
+ break;
+
+ default:
+ return FALSE;
+ }
+
+ #undef XCF_DATA_INTEGRATE
+
+ return TRUE;
+}
diff --git a/app/xcf/xcf-utils.h b/app/xcf/xcf-utils.h
new file mode 100644
index 0000000..eb0846c
--- /dev/null
+++ b/app/xcf/xcf-utils.h
@@ -0,0 +1,36 @@
+/* GIMP - The GNU 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __XCF_UTILS_H__
+#define __XCF_UTILS_H__
+
+
+gboolean xcf_data_differentiate (void *dest,
+ const void *src,
+ gint n_pixels,
+ const Babl *format,
+ gint from_order,
+ gint to_order);
+gboolean xcf_data_integrate (void *dest,
+ const void *src,
+ gint n_pixels,
+ const Babl *format,
+ gint from_order,
+ gint to_order);
+
+
+#endif /* __XCF_UTILS_H__ */
diff --git a/app/xcf/xcf.c b/app/xcf/xcf.c
index 135a92d..e1e427f 100644
--- a/app/xcf/xcf.c
+++ b/app/xcf/xcf.c
@@ -78,7 +78,8 @@ static GimpXcfLoaderFunc * const xcf_loaders[] =
xcf_load_image, /* version 8 */
xcf_load_image, /* version 9 */
xcf_load_image, /* version 10 */
- xcf_load_image /* version 11 */
+ xcf_load_image, /* version 11 */
+ xcf_load_image /* version 12 */
};
@@ -364,13 +365,13 @@ xcf_save_stream (Gimp *gimp,
info.file = output_file;
if (gimp_image_get_xcf_compression (image))
- info.compression = COMPRESS_ZLIB;
+ info.compression = COMPRESS_ZLIB_DELTA;
else
info.compression = COMPRESS_RLE;
info.file_version = gimp_image_get_xcf_version (image,
- info.compression ==
- COMPRESS_ZLIB,
+ info.compression >=
+ COMPRESS_ZLIB_DELTA,
NULL, NULL);
if (info.file_version >= 11)
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]