[gimp/wip/xcf-delta-compression: 857/859] app: add delta-encoded zlib compression mode for XCF tiles



commit abf24a4b49aca1eb0c07ab49c98382cc4d16c579
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 delta encoding is always lossless: both for integer
    formats, and for floating point formats, whose raw
    reperesenatation is manipulated using integer arithmetic.
    
    Add XCF version 14, which enables the new compression mode.  Use
    version 14, and the new mode, for all zlib-compressed XCFs.

 app/core/gimpimage.c  |    8 ++-
 app/xcf/xcf-load.c    |   46 +++++++++--
 app/xcf/xcf-private.h |    5 +-
 app/xcf/xcf-save.c    |  189 ++++++++++++++++++++++++++++++++++++++------
 app/xcf/xcf-utils.c   |  211 +++++++++++++++++++++++++++++++++++++++++++++++++
 app/xcf/xcf-utils.h   |   17 ++++-
 app/xcf/xcf.c         |    7 +-
 7 files changed, 442 insertions(+), 41 deletions(-)
---
diff --git a/app/core/gimpimage.c b/app/core/gimpimage.c
index c0e413c..f422854 100644
--- a/app/core/gimpimage.c
+++ b/app/core/gimpimage.c
@@ -2468,9 +2468,12 @@ gimp_image_get_xcf_version (GimpImage    *image,
   if (gimp_image_get_precision (image) > GIMP_PRECISION_U8_GAMMA)
     version = MAX (12, version);
 
-  /* need version 8 for zlib compression */
+  /* need version 8 for zlib compression, and version 14 for delta-encoded
+   * zlib compression; we always use delta encoding when zlib compression is
+   * requested.
+   */
   if (zlib_compression)
-    version = MAX (8, version);
+    version = MAX (14, version);
 
   /* if version is 10 (lots of new layer modes), go to version 11 with
    * 64 bit offsets right away
@@ -2509,6 +2512,7 @@ gimp_image_get_xcf_version (GimpImage    *image,
     case 11:
     case 12:
     case 13:
+    case 14:
       if (gimp_version)   *gimp_version   = 210;
       if (version_string) *version_string = "GIMP 2.10";
       break;
diff --git a/app/xcf/xcf-load.c b/app/xcf/xcf-load.c
index 93d6b2f..707c780 100644
--- a/app/xcf/xcf-load.c
+++ b/app/xcf/xcf-load.c
@@ -685,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,
@@ -2004,6 +2005,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;
@@ -2244,6 +2246,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
@@ -2251,8 +2254,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);
 
@@ -2298,13 +2317,13 @@ 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;
         }
@@ -2320,6 +2339,17 @@ xcf_load_tile_zlib (XcfInfo       *info,
                             tile_size / bpp * n_components);
         }
 
+      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);
     }
diff --git a/app/xcf/xcf-private.h b/app/xcf/xcf-private.h
index 4b18fc8..18a804f 100644
--- a/app/xcf/xcf-private.h
+++ b/app/xcf/xcf-private.h
@@ -70,8 +70,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 b0e034c..c4b1a5e 100644
--- a/app/xcf/xcf-save.c
+++ b/app/xcf/xcf-save.c
@@ -66,11 +66,25 @@
 #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.  bigger values result in better and slower
+ * compression.  see xcf_save_tile_zlib().
+ */
+#define XCF_SAVE_ZLIB_DELTA_TEST_SIZE_RATIO (1.0 / 2.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       2
+
+
 static gboolean xcf_save_image_props   (XcfInfo           *info,
                                         GimpImage         *image,
                                         GError           **error);
@@ -112,6 +126,14 @@ static gboolean xcf_save_tile_rle      (XcfInfo           *info,
                                         const Babl        *format,
                                         guchar            *rlebuf,
                                         GError           **error);
+static gboolean xcf_save_data_zlib     (XcfInfo           *info,
+                                        const Babl        *format,
+                                        const guchar      *data,
+                                        gint               size,
+                                        gboolean           write,
+                                        gint               max_compressed_size,
+                                        gint              *compressed_size,
+                                        GError           **error);
 static gboolean xcf_save_tile_zlib     (XcfInfo           *info,
                                         GeglBuffer        *buffer,
                                         GeglRectangle     *tile_rect,
@@ -1590,6 +1612,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;
@@ -1787,31 +1810,36 @@ xcf_save_tile_rle (XcfInfo        *info,
 }
 
 static gboolean
-xcf_save_tile_zlib (XcfInfo        *info,
-                    GeglBuffer     *buffer,
-                    GeglRectangle  *tile_rect,
-                    const Babl     *format,
-                    GError        **error)
+xcf_save_data_zlib (XcfInfo       *info,
+                    const Babl    *format,
+                    const guchar  *data,
+                    gint           size,
+                    gboolean       write,
+                    gint           max_compressed_size,
+                    gint          *compressed_size,
+                    GError       **error)
 {
-  gint      bpp       = babl_format_get_bytes_per_pixel (format);
-  gint      tile_size = bpp * tile_rect->width * tile_rect->height;
-  guchar   *tile_data = g_alloca (tile_size);
   /* The buffer for compressed data. */
-  guchar   *buf       = g_alloca (tile_size);
-  GError   *tmp_error = NULL;
+  guchar   *buf            = g_alloca (size);
   z_stream  strm;
   int       action;
   int       status;
-
-  gegl_buffer_get (buffer, tile_rect, 1.0, format, tile_data,
-                   GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+  gint      remaining_size = max_compressed_size;
+  GError   *tmp_error      = NULL;
 
   if (info->file_version >= 12)
     {
-      gint n_components = babl_format_get_n_components (format);
+      gint    bpp          = babl_format_get_bytes_per_pixel (format);
+      gint    n_components = babl_format_get_n_components (format);
+      guchar *be_data      = g_alloca (size);
 
-      xcf_write_to_be (bpp / n_components, tile_data,
-                       tile_size / bpp * n_components);
+      /* don't overwrite 'data'; the caller expects it not to change */
+      memcpy (be_data, data, size);
+
+      xcf_write_to_be (bpp / n_components, be_data,
+                       size / bpp * n_components);
+
+      data = be_data;
     }
 
   /* allocate deflate state */
@@ -1823,14 +1851,17 @@ xcf_save_tile_zlib (XcfInfo        *info,
   if (status != Z_OK)
     return FALSE;
 
-  strm.next_in   = tile_data;
-  strm.avail_in  = tile_size;
+  strm.next_in   = (guchar *) data;
+  strm.avail_in  = size;
   strm.next_out  = buf;
-  strm.avail_out = tile_size;
+  strm.avail_out = size;
 
   action = Z_NO_FLUSH;
 
-  while (status == Z_OK || status == Z_BUF_ERROR)
+  if (compressed_size)
+    *compressed_size = 0;
+
+  while ((status == Z_OK || status == Z_BUF_ERROR) && remaining_size > 0)
     {
       if (strm.avail_in == 0)
         {
@@ -1842,27 +1873,137 @@ xcf_save_tile_zlib (XcfInfo        *info,
 
       if (status == Z_STREAM_END || status == Z_BUF_ERROR)
         {
-          size_t write_size = tile_size - strm.avail_out;
+          gint write_size = size - strm.avail_out;
 
-          xcf_write_int8_check_error (info, buf, write_size);
+          if (compressed_size)
+            *compressed_size += write_size;
+
+          if (write)
+            xcf_write_int8_check_error (info, buf, write_size);
 
           /* Reset next_out and avail_out. */
           strm.next_out  = buf;
-          strm.avail_out = tile_size;
+          strm.avail_out = size;
+
+          remaining_size -= write_size;
         }
       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;
         }
     }
 
   deflateEnd (&strm);
+
   return TRUE;
 }
 
 static gboolean
+xcf_save_tile_zlib (XcfInfo        *info,
+                    GeglBuffer     *buffer,
+                    GeglRectangle  *tile_rect,
+                    const Babl     *format,
+                    GError        **error)
+{
+  gint      bpp       = babl_format_get_bytes_per_pixel (format);
+  gint      tile_size = bpp * tile_rect->width * tile_rect->height;
+  guchar   *tile_data = g_alloca (tile_size);
+  GError   *tmp_error = NULL;
+
+  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_rect.height  = MAX (test_rect.height, 1);
+
+      test_size = bpp * test_rect.width * test_rect.height;
+
+      g_assert (test_size <= tile_size);
+
+      for (order = 0; order <= XCF_SAVE_ZLIB_DELTA_MAX_ORDER; order++)
+        {
+          gint compressed_size;
+
+          /* do a dummy compression, that isn't actually written anywhere, to
+           * find the size of the compressed test data.
+           */
+          if (! xcf_save_data_zlib (info, format, tile_data, test_size, FALSE,
+                                    prev_compressed_size, &compressed_size,
+                                    error))
+            {
+              return FALSE;
+            }
+
+          /* 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);
+    }
+
+  return xcf_save_data_zlib (info, format, tile_data, tile_size, TRUE,
+                             G_MAXINT, NULL, error);
+}
+
+static gboolean
 xcf_save_parasite (XcfInfo       *info,
                    GimpParasite  *parasite,
                    GError       **error)
diff --git a/app/xcf/xcf-utils.c b/app/xcf/xcf-utils.c
index 6ef0b72..2075a3c 100644
--- a/app/xcf/xcf-utils.c
+++ b/app/xcf/xcf-utils.c
@@ -17,10 +17,19 @@
 
 #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"
 
 
@@ -51,3 +60,205 @@ xcf_data_is_zero (const void *data,
 
   return TRUE;
 }
+
+/* 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 bpp;
+  gint n_components;
+  gint o;
+  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 == 0 && n_pixels == 0)
+    return TRUE;
+  else if (to_order >= n_pixels)
+    return FALSE;
+
+  bpp          = babl_format_get_bytes_per_pixel (format);
+  n_components = babl_format_get_n_components    (format);
+
+  if (from_order == to_order)
+    {
+      if (dest != src)
+        memcpy (dest, src, n_pixels * bpp);
+
+      return TRUE;
+    }
+
+  #define XCF_DATA_DIFFERENTIATE(type)                                         \
+    do                                                                         \
+      {                                                                        \
+        type       *d = dest;                                                  \
+        const type *s = src;                                                   \
+                                                                               \
+        if (dest != src)                                                       \
+          memcpy (dest, src, (from_order + 1) * bpp);                          \
+                                                                               \
+        for (o = from_order; o < to_order; o++)                                \
+          {                                                                    \
+            for (i = n_pixels - 1; i > o; 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;                                                             \
+          }                                                                    \
+      }                                                                        \
+    while (0)
+
+  switch (gimp_babl_format_get_component_type (format))
+    {
+    case GIMP_COMPONENT_TYPE_U8:
+      XCF_DATA_DIFFERENTIATE (guint8);
+      break;
+
+    case GIMP_COMPONENT_TYPE_U16:
+    case GIMP_COMPONENT_TYPE_HALF:
+      XCF_DATA_DIFFERENTIATE (guint16);
+      break;
+
+    case GIMP_COMPONENT_TYPE_U32:
+    case GIMP_COMPONENT_TYPE_FLOAT:
+      XCF_DATA_DIFFERENTIATE (guint32);
+      break;
+
+    case GIMP_COMPONENT_TYPE_DOUBLE:
+      XCF_DATA_DIFFERENTIATE (guint64);
+      break;
+
+    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 bpp;
+  gint n_components;
+  gint o;
+  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 == 0 && n_pixels == 0)
+    return TRUE;
+  else if (from_order >= n_pixels)
+    return FALSE;
+
+  bpp          = babl_format_get_bytes_per_pixel (format);
+  n_components = babl_format_get_n_components    (format);
+
+  if (from_order == to_order)
+    {
+      if (dest != src)
+        memcpy (dest, src, n_pixels * bpp);
+
+      return TRUE;
+    }
+
+  #define XCF_DATA_INTEGRATE(type)                                             \
+    do                                                                         \
+      {                                                                        \
+        type       *d = dest;                                                  \
+        const type *s = src;                                                   \
+                                                                               \
+        if (dest != src)                                                       \
+          memcpy (dest, src, from_order * bpp);                                \
+                                                                               \
+        for (o = from_order; o > to_order; o--)                                \
+          {                                                                    \
+            for (i = o; 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:
+    case GIMP_COMPONENT_TYPE_HALF:
+      XCF_DATA_INTEGRATE (guint16);
+      break;
+
+    case GIMP_COMPONENT_TYPE_U32:
+    case GIMP_COMPONENT_TYPE_FLOAT:
+      XCF_DATA_INTEGRATE (guint32);
+      break;
+
+    case GIMP_COMPONENT_TYPE_DOUBLE:
+      XCF_DATA_INTEGRATE (guint64);
+      break;
+
+    default:
+      return FALSE;
+    }
+
+  #undef XCF_DATA_INTEGRATE
+
+  return TRUE;
+}
diff --git a/app/xcf/xcf-utils.h b/app/xcf/xcf-utils.h
index 07b6856..f5c74e1 100644
--- a/app/xcf/xcf-utils.h
+++ b/app/xcf/xcf-utils.h
@@ -19,8 +19,21 @@
 #define __XCF_UTILS_H__
 
 
-gboolean   xcf_data_is_zero (const void *data,
-                             gint        size);
+gboolean   xcf_data_is_zero       (const void *data,
+                                   gint        size);
+
+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 16cee0b..c9f8998 100644
--- a/app/xcf/xcf.c
+++ b/app/xcf/xcf.c
@@ -80,7 +80,8 @@ static GimpXcfLoaderFunc * const xcf_loaders[] =
   xcf_load_image,   /* version 10 */
   xcf_load_image,   /* version 11 */
   xcf_load_image,   /* version 12 */
-  xcf_load_image    /* version 13 */
+  xcf_load_image,   /* version 13 */
+  xcf_load_image    /* version 14 */
 };
 
 
@@ -367,13 +368,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,
+                                                  COMPRESS_ZLIB_DELTA,
                                                   NULL, NULL);
 
   if (info.file_version >= 11)


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]