[gimp] Bug 735026: Enable zlib compression in XCF for GIMP 2.10



commit 128baab2b601c5001c174100dd572a838daac8a1
Author: Jehan <jehan girinstud io>
Date:   Mon Sep 15 15:33:22 2014 +0200

    Bug 735026: Enable zlib compression in XCF for GIMP 2.10
    
    XCF file format bumped to version 8 when compressing with zlib.

 app/xcf/xcf-load.c |  100 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 app/xcf/xcf-save.c |   81 +++++++++++++++++++++++++++++++++++++++++-
 app/xcf/xcf.c      |    5 ++-
 3 files changed, 181 insertions(+), 5 deletions(-)
---
diff --git a/app/xcf/xcf-load.c b/app/xcf/xcf-load.c
index ffe9f69..b36b240 100644
--- a/app/xcf/xcf-load.c
+++ b/app/xcf/xcf-load.c
@@ -18,6 +18,7 @@
 #include "config.h"
 
 #include <string.h>
+#include <zlib.h>
 
 #include <cairo.h>
 #include <gegl.h>
@@ -114,6 +115,11 @@ static gboolean        xcf_load_tile_rle      (XcfInfo       *info,
                                                GeglRectangle *tile_rect,
                                                const Babl    *format,
                                                gint           data_length);
+static gboolean        xcf_load_tile_zlib     (XcfInfo       *info,
+                                               GeglBuffer    *buffer,
+                                               GeglRectangle *tile_rect,
+                                               const Babl    *format,
+                                               gint           data_length);
 static GimpParasite  * xcf_load_parasite      (XcfInfo       *info);
 static gboolean        xcf_load_old_paths     (XcfInfo       *info,
                                                GimpImage     *image);
@@ -1780,8 +1786,9 @@ xcf_load_level (XcfInfo    *info,
             fail = TRUE;
           break;
         case COMPRESS_ZLIB:
-          g_warning ("xcf: zlib compression unimplemented");
-          fail = TRUE;
+          if (!xcf_load_tile_zlib (info, buffer, &rect, format,
+                                   offset2 - offset))
+            fail = TRUE;
           break;
         case COMPRESS_FRACTAL:
           g_warning ("xcf: fractal compression unimplemented");
@@ -1975,6 +1982,95 @@ xcf_load_tile_rle (XcfInfo       *info,
   return FALSE;
 }
 
+static gboolean
+xcf_load_tile_zlib (XcfInfo       *info,
+                    GeglBuffer    *buffer,
+                    GeglRectangle *tile_rect,
+                    const Babl    *format,
+                    gint           data_length)
+{
+  z_stream  strm;
+  int       action;
+  int       status;
+  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);
+  gsize     bytes_read;
+  guchar   *xcfdata;
+
+  /* Workaround for bug #357809: avoid crashing on g_malloc() and skip
+   * this tile (return TRUE without storing data) as if it did not
+   * contain any data.  It is better than returning FALSE, which would
+   * skip the whole hierarchy while there may still be some valid
+   * tiles in the file.
+   */
+  if (data_length <= 0)
+    return TRUE;
+
+  xcfdata = g_alloca (data_length);
+
+  /* we have to read directly instead of xcf_read_* because we may be
+   * reading past the end of the file here
+   */
+  g_input_stream_read_all (info->input, xcfdata, data_length,
+                           &bytes_read, NULL, NULL);
+
+  if (bytes_read == 0)
+    return TRUE;
+
+  info->cp      += bytes_read;
+
+  strm.next_out  = tile_data;
+  strm.avail_out = tile_size;
+
+  strm.zalloc    = Z_NULL;
+  strm.zfree     = Z_NULL;
+  strm.opaque    = Z_NULL;
+  strm.next_in   = xcfdata;
+  strm.avail_in  = bytes_read;
+
+  /* Initialize the stream decompression. */
+  status = inflateInit (&strm);
+  if (status != Z_OK)
+    return FALSE;
+
+  action = Z_NO_FLUSH;
+
+  while (status == Z_OK)
+    {
+      if (strm.avail_in == 0)
+        {
+          action = Z_FINISH;
+        }
+
+      status = inflate (&strm, action);
+
+      if (status == Z_STREAM_END)
+        {
+          /* All the data was successfully decoded. */
+          break;
+        }
+      else if (status == Z_BUF_ERROR)
+        {
+          g_printerr ("xcf: decompressed tile bigger than the expected size.");
+          inflateEnd (&strm);
+          return FALSE;
+        }
+      else if (status != Z_OK)
+        {
+          g_printerr ("xcf: tile decompression failed: %s", zError (status));
+          inflateEnd (&strm);
+          return FALSE;
+        }
+    }
+
+  gegl_buffer_set (buffer, tile_rect, 0, format, tile_data,
+                   GEGL_AUTO_ROWSTRIDE);
+
+  inflateEnd (&strm);
+  return TRUE;
+}
+
 static GimpParasite *
 xcf_load_parasite (XcfInfo *info)
 {
diff --git a/app/xcf/xcf-save.c b/app/xcf/xcf-save.c
index 7fe9df7..5a6de8a 100644
--- a/app/xcf/xcf-save.c
+++ b/app/xcf/xcf-save.c
@@ -18,6 +18,7 @@
 #include "config.h"
 
 #include <string.h>
+#include <zlib.h>
 
 #include <cairo.h>
 #include <gegl.h>
@@ -109,6 +110,11 @@ static gboolean xcf_save_tile_rle      (XcfInfo           *info,
                                         const Babl        *format,
                                         guchar            *rlebuf,
                                         GError           **error);
+static gboolean xcf_save_tile_zlib     (XcfInfo           *info,
+                                        GeglBuffer        *buffer,
+                                        GeglRectangle     *tile_rect,
+                                        const Babl        *format,
+                                        GError           **error);
 static gboolean xcf_save_parasite      (XcfInfo           *info,
                                         GimpParasite      *parasite,
                                         GError           **error);
@@ -223,6 +229,10 @@ xcf_save_choose_format (XcfInfo   *info,
   if (gimp_image_get_precision (image) != GIMP_PRECISION_U8_GAMMA)
     save_version = MAX (7, save_version);
 
+  /* need version 8 for zlib compression */
+  if (info->compression == COMPRESS_ZLIB)
+    save_version = MAX (8, save_version);
+
   info->file_version = save_version;
 }
 
@@ -1506,7 +1516,8 @@ xcf_save_level (XcfInfo     *info,
                                               rlebuf, error));
           break;
         case COMPRESS_ZLIB:
-          g_error ("xcf: zlib compression unimplemented");
+          xcf_check_error (xcf_save_tile_zlib (info, buffer, &rect, format,
+                                               error));
           break;
         case COMPRESS_FRACTAL:
           g_error ("xcf: fractal compression unimplemented");
@@ -1681,6 +1692,74 @@ xcf_save_tile_rle (XcfInfo        *info,
 }
 
 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);
+  /* The buffer for compressed data. */
+  guchar   *buf       = g_alloca (tile_size);
+  GError   *tmp_error = NULL;
+  z_stream  strm;
+  int       action;
+  int       status;
+
+  gegl_buffer_get (buffer, tile_rect, 1.0, format, tile_data,
+                   GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+  /* 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  = tile_size;
+  strm.next_out  = buf;
+  strm.avail_out = tile_size;
+
+  action = Z_NO_FLUSH;
+
+  while (status == Z_OK || status == Z_BUF_ERROR)
+    {
+      if (strm.avail_in == 0)
+        {
+          /* Finish the encoding. */
+          action = Z_FINISH;
+        }
+
+      status = deflate (&strm, action);
+
+      if (status == Z_STREAM_END || status == Z_BUF_ERROR)
+        {
+          size_t write_size = tile_size - strm.avail_out;
+
+          xcf_write_int8_check_error (info, buf, write_size);
+
+          /* 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", zError (status));
+          deflateEnd (&strm);
+          return FALSE;
+        }
+    }
+
+  deflateEnd (&strm);
+  return TRUE;
+}
+
+static gboolean
 xcf_save_parasite (XcfInfo       *info,
                    GimpParasite  *parasite,
                    GError       **error)
diff --git a/app/xcf/xcf.c b/app/xcf/xcf.c
index c675a86..110be91 100644
--- a/app/xcf/xcf.c
+++ b/app/xcf/xcf.c
@@ -74,7 +74,8 @@ static GimpXcfLoaderFunc * const xcf_loaders[] =
   xcf_load_image,   /* version 4 */
   xcf_load_image,   /* version 5 */
   xcf_load_image,   /* version 6 */
-  xcf_load_image    /* version 7 */
+  xcf_load_image,   /* version 7 */
+  xcf_load_image    /* version 8 */
 };
 
 
@@ -390,7 +391,7 @@ xcf_save_invoker (GimpProcedure         *procedure,
       info.seekable    = G_SEEKABLE (info.output);
       info.progress    = progress;
       info.filename    = filename;
-      info.compression = COMPRESS_RLE;
+      info.compression = COMPRESS_ZLIB;
 
       if (progress)
         gimp_progress_start (progress, FALSE, _("Saving '%s'"), filename);


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