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



commit 7537cea82279866ee1032990237a340eeba9ebe8
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    |  170 ++++++++++++++++++++++++++----
 app/xcf/xcf-utils.c   |  276 +++++++++++++++++++++++++++++++++++++++++++++++++
 app/xcf/xcf-utils.h   |   36 +++++++
 app/xcf/xcf.c         |    7 +-
 8 files changed, 517 insertions(+), 38 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..5970d9f 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);
@@ -112,6 +125,13 @@ static gboolean xcf_save_tile_rle      (XcfInfo           *info,
                                         const Babl        *format,
                                         guchar            *rlebuf,
                                         GError           **error);
+static gboolean xcf_save_data_zlib     (XcfInfo           *info,
+                                        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,
@@ -1573,6 +1593,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;
@@ -1742,24 +1763,21 @@ 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 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;
 
   /* allocate deflate state */
   strm.zalloc = Z_NULL;
@@ -1770,14 +1788,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)
         {
@@ -1789,27 +1810,134 @@ 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;
+
+          if (compressed_size)
+            *compressed_size += write_size;
 
-          xcf_write_int8_check_error (info, buf, 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_size = bpp * test_rect.width * test_rect.height;
+
+      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, 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, 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
new file mode 100644
index 0000000..2fbeea7
--- /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-6
+
+
+/* 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 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 >= 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 (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;                                                             \
+          }                                                                    \
+                                                                               \
+        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 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 >= 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 (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:
+      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..0c30267 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,
+                                                  COMPRESS_ZLIB_DELTA,
                                                   NULL, NULL);
 
   if (info.file_version >= 11)


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