[gegl] buffer, tests, perf: add gegl-compression



commit f4b7ae5337eaff424103a6c0cefdca21d164dd85
Author: Ell <ell_se yahoo com>
Date:   Mon Dec 17 04:40:31 2018 -0500

    buffer, tests, perf: add gegl-compression
    
    gegl-compression is a simple compression framework, meant for
    (losslessly) compressing image data.  It exposes a set of
    compression algorithms, which can be used to compress/decompress
    data.  We're going to use gegl-compression to compress tile data
    stored in the swap.
    
    Add correctness and performance tests, which test all the available
    algorithms.

 gegl/buffer/Makefile.am         |   2 +
 gegl/buffer/gegl-compression.c  | 137 +++++++++++++++++++++++++++++++
 gegl/buffer/gegl-compression.h  |  73 +++++++++++++++++
 perf/Makefile.am                |  14 +++-
 perf/test-compression.c         | 162 +++++++++++++++++++++++++++++++++++++
 tests/simple/Makefile.am        |   1 +
 tests/simple/test-compression.c | 174 ++++++++++++++++++++++++++++++++++++++++
 7 files changed, 561 insertions(+), 2 deletions(-)
---
diff --git a/gegl/buffer/Makefile.am b/gegl/buffer/Makefile.am
index 95634420c..3d9ada9e2 100644
--- a/gegl/buffer/Makefile.am
+++ b/gegl/buffer/Makefile.am
@@ -42,6 +42,7 @@ libbuffer_la_SOURCES = \
        gegl-buffer-load.c      \
     gegl-buffer-save.c         \
     gegl-buffer-swap.c         \
+    gegl-compression.c         \
     gegl-sampler.c             \
     gegl-sampler-cubic.c       \
     gegl-sampler-linear.c      \
@@ -73,6 +74,7 @@ libbuffer_la_SOURCES = \
     gegl-buffer-formats.h      \
     gegl-buffer-swap.h         \
     gegl-buffer-swap-private.h \
+    gegl-compression.h         \
     gegl-sampler.h             \
     gegl-sampler-cubic.h       \
     gegl-sampler-linear.h      \
diff --git a/gegl/buffer/gegl-compression.c b/gegl/buffer/gegl-compression.c
new file mode 100644
index 000000000..5e932991c
--- /dev/null
+++ b/gegl/buffer/gegl-compression.c
@@ -0,0 +1,137 @@
+/* This file is part of GEGL
+ *
+ * GEGL is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * GEGL 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GEGL; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "gegl-compression.h"
+
+
+/*  local variables  */
+
+GHashTable *algorithms;
+
+
+/*  public functions  */
+
+void
+gegl_compression_init (void)
+{
+  g_return_if_fail (algorithms == NULL);
+
+  algorithms = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+}
+
+void
+gegl_compression_cleanup (void)
+{
+  g_clear_pointer (&algorithms, g_hash_table_unref);
+}
+
+void
+gegl_compression_register (const gchar           *name,
+                           const GeglCompression *compression)
+{
+  g_return_if_fail (name != NULL);
+  g_return_if_fail (compression != NULL);
+  g_return_if_fail (compression->compress != NULL);
+  g_return_if_fail (compression->decompress != NULL);
+
+  g_hash_table_insert (algorithms, g_strdup (name), (gpointer) compression);
+}
+
+static gint
+gegl_compression_list_compare (gconstpointer x,
+                               gconstpointer y)
+{
+  return strcmp (*(const gchar **) x, *(const gchar **) y);
+}
+
+const gchar **
+gegl_compression_list (void)
+{
+  const gchar    **names;
+  GHashTableIter   iter;
+  gint             i;
+
+  names = g_new (const gchar *, g_hash_table_size (algorithms) + 1);
+
+  g_hash_table_iter_init (&iter, algorithms);
+
+  i = 0;
+
+  while (g_hash_table_iter_next (&iter, (gpointer *) &names[i], NULL))
+    i++;
+
+  names[i] = NULL;
+
+  qsort (names, i, sizeof (names[0]), gegl_compression_list_compare);
+
+  return names;
+}
+
+const GeglCompression *
+gegl_compression (const gchar *name)
+{
+  g_return_val_if_fail (name != NULL, NULL);
+
+  return g_hash_table_lookup (algorithms, name);
+}
+
+gboolean
+gegl_compression_compress (const GeglCompression *compression,
+                           const Babl            *format,
+                           gconstpointer          data,
+                           gint                   n,
+                           gpointer               compressed,
+                           gint                  *compressed_size,
+                           gint                   max_compressed_size)
+{
+  g_return_val_if_fail (compression != NULL, FALSE);
+  g_return_val_if_fail (format != NULL, FALSE);
+  g_return_val_if_fail (data != NULL || n == 0, FALSE);
+  g_return_val_if_fail (n >= 0, FALSE);
+  g_return_val_if_fail (compressed != NULL || max_compressed_size == 0, FALSE);
+  g_return_val_if_fail (compressed_size != NULL, FALSE);
+  g_return_val_if_fail (max_compressed_size >= 0, FALSE);
+
+  return compression->compress (compression,
+                                format, data, n,
+                                compressed, compressed_size,
+                                max_compressed_size);
+}
+
+gboolean
+gegl_compression_decompress (const GeglCompression *compression,
+                             const Babl            *format,
+                             gpointer               data,
+                             gint                   n,
+                             gconstpointer          compressed,
+                             gint                   compressed_size)
+{
+  g_return_val_if_fail (compression != NULL, FALSE);
+  g_return_val_if_fail (format != NULL, FALSE);
+  g_return_val_if_fail (data != NULL || n == 0, FALSE);
+  g_return_val_if_fail (n >= 0, FALSE);
+  g_return_val_if_fail (compressed != NULL || compressed_size == 0, FALSE);
+  g_return_val_if_fail (compressed_size >= 0, FALSE);
+
+  return compression->decompress (compression,
+                                  format, data, n,
+                                  compressed, compressed_size);
+}
diff --git a/gegl/buffer/gegl-compression.h b/gegl/buffer/gegl-compression.h
new file mode 100644
index 000000000..901b00ee1
--- /dev/null
+++ b/gegl/buffer/gegl-compression.h
@@ -0,0 +1,73 @@
+/* This file is part of GEGL
+ *
+ * GEGL is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * GEGL 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GEGL; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GEGL_COMPRESSION_H__
+#define __GEGL_COMPRESSION_H__
+
+
+#include <glib.h>
+#include <babl/babl.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GeglCompression GeglCompression;
+
+typedef gboolean (* GeglCompressionCompressFunc)   (const GeglCompression *compression,
+                                                    const Babl            *format,
+                                                    gconstpointer          data,
+                                                    gint                   n,
+                                                    gpointer               compressed,
+                                                    gint                  *compressed_size,
+                                                    gint                   max_compressed_size);
+typedef gboolean (* GeglCompressionDecompressFunc) (const GeglCompression *compression,
+                                                    const Babl            *format,
+                                                    gpointer               data,
+                                                    gint                   n,
+                                                    gconstpointer          compressed,
+                                                    gint                   compressed_size);
+
+struct _GeglCompression
+{
+  GeglCompressionCompressFunc   compress;
+  GeglCompressionDecompressFunc decompress;
+};
+
+void                     gegl_compression_init       (void);
+void                     gegl_compression_cleanup    (void);
+
+void                     gegl_compression_register   (const gchar           *name,
+                                                      const GeglCompression *compression);
+const gchar           ** gegl_compression_list       (void);
+
+const GeglCompression  * gegl_compression            (const gchar           *name);
+
+gboolean                 gegl_compression_compress   (const GeglCompression *compression,
+                                                      const Babl            *format,
+                                                      gconstpointer          data,
+                                                      gint                   n,
+                                                      gpointer               compressed,
+                                                      gint                  *compressed_size,
+                                                      gint                   max_compressed_size);
+gboolean                 gegl_compression_decompress (const GeglCompression *compression,
+                                                      const Babl            *format,
+                                                      gpointer               data,
+                                                      gint                   n,
+                                                      gconstpointer          compressed,
+                                                      gint                   compressed_size);
+
+G_END_DECLS
+
+#endif
diff --git a/perf/Makefile.am b/perf/Makefile.am
index e0cf50559..ec040d544 100644
--- a/perf/Makefile.am
+++ b/perf/Makefile.am
@@ -11,7 +11,8 @@ noinst_PROGRAMS = \
        test-rotate \
        test-saturation \
        test-scale \
-       test-translate
+       test-translate \
+       test-compression
 
 AM_CPPFLAGS = \
        -I$(top_srcdir)/ \
@@ -35,7 +36,15 @@ LDADD = $(common_ldadd) $(DEP_LIBS) $(BABL_LIBS) $(MATH_LIB)
 perf-report: check
 
 check:
-       for a in $(noinst_PROGRAMS);do GEGL_PATH=../operations ./$$a;done;true
+       $(AM_V_at)                         \
+       for a in $(noinst_PROGRAMS); do    \
+         echo;                            \
+         echo $$a:;                       \
+         GEGL_PATH=../operations          \
+         ABS_TOP_SRCDIR=$(abs_top_srcdir) \
+         ./$$a;                           \
+       done;                              \
+       true
 
 test_rotate_SOURCES = test-rotate.c
 test_saturation_SOURCES = test-saturation.c
@@ -49,6 +58,7 @@ test_init_SOURCES = test-init.c
 test_unsharpmask_SOURCES = test-unsharpmask.c
 test_gegl_buffer_access_SOURCES = test-gegl-buffer-access.c
 test_samplers_SOURCES = test-samplers.c
+test_compression_SOURCES = test-compression.c
 
 EXTRA_DIST = Makefile-retrospect Makefile-tests create-report.rb test-common.h
 
diff --git a/perf/test-compression.c b/perf/test-compression.c
new file mode 100644
index 000000000..69aee8bb4
--- /dev/null
+++ b/perf/test-compression.c
@@ -0,0 +1,162 @@
+/*
+ * 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 <https://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2018 Ell
+ */
+
+#include "test-common.h"
+#include "buffer/gegl-compression.h"
+
+#define SUCCESS  0
+#define FAILURE -1
+
+static gpointer
+load_png (const gchar *path,
+          const Babl  *format,
+          gint        *n)
+{
+  GeglNode   *node;
+  GeglNode   *node_source;
+  GeglNode   *node_sink;
+  GeglBuffer *buffer = NULL;
+  gpointer    data;
+
+  node = gegl_node_new ();
+
+  node_source = gegl_node_new_child (node,
+                                     "operation", "gegl:load",
+                                     "path",      path,
+                                     NULL);
+  node_sink   = gegl_node_new_child (node,
+                                     "operation", "gegl:buffer-sink",
+                                     "buffer",    &buffer,
+                                     NULL);
+
+  gegl_node_link (node_source, node_sink);
+
+  gegl_node_process (node_sink);
+
+  g_object_unref (node);
+
+  *n   = gegl_buffer_get_width (buffer) * gegl_buffer_get_height (buffer);
+  data = g_malloc (*n * babl_format_get_bytes_per_pixel (format));
+
+  gegl_buffer_get (buffer, NULL, 1.0, format, data,
+                   GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+  g_object_unref (buffer);
+
+  return data;
+}
+
+gint
+main (gint    argc,
+      gchar **argv)
+{
+  const Babl   *format;
+  gint          bpp;
+  gchar        *path;
+  gpointer      data;
+  gint          n;
+  gint          size;
+  guint8       *compressed;
+  gint          max_compressed_size;
+  guint8       *decompressed;
+  const gchar **algorithms;
+  gint          i;
+  gint          result = FAILURE;
+
+  gegl_init (&argc, &argv);
+
+  format = babl_format ("R'G'B'A u8");
+  bpp    = babl_format_get_bytes_per_pixel (format);
+
+  path = g_build_filename (g_getenv ("ABS_TOP_SRCDIR"),
+                           "tests", "compositions", "data", "car-stack.png",
+                           NULL);
+
+  data = load_png (path, format, &n);
+  size = n * bpp;
+
+  g_free (path);
+
+  max_compressed_size = 2 * n * bpp;
+  compressed          = g_malloc (max_compressed_size);
+  decompressed        = g_malloc (size);
+
+  algorithms = gegl_compression_list ();
+
+  for (i = 0; algorithms[i]; i++)
+    {
+      const GeglCompression *compression = gegl_compression (algorithms[i]);
+      gchar                 *id;
+      gint                   compressed_size;
+      gint                   j;
+
+      id = g_strdup_printf ("%s compress", algorithms[i]);
+      test_start ();
+
+      for (j = 0; j < ITERATIONS && converged < BAIL_COUNT; j++)
+        {
+          test_start_iter();
+
+          if (! gegl_compression_compress (compression, format,
+                                           data, n,
+                                           compressed, &compressed_size,
+                                           max_compressed_size))
+            {
+              goto end;
+            }
+
+          test_end_iter();
+        }
+
+      test_end (id, (gdouble) size * ITERATIONS);
+      g_free (id);
+
+      id = g_strdup_printf ("%s decompress", algorithms[i]);
+      test_start ();
+
+      for (j = 0; j < ITERATIONS && converged < BAIL_COUNT; j++)
+        {
+          test_start_iter();
+
+          if (! gegl_compression_decompress (compression, format,
+                                             decompressed, n,
+                                             compressed, compressed_size))
+            {
+              goto end;
+            }
+
+          test_end_iter();
+        }
+
+      test_end (id, (gdouble) size * ITERATIONS);
+      g_free (id);
+    }
+
+  result = SUCCESS;
+
+end:
+  g_free (algorithms);
+
+  g_free (compressed);
+  g_free (decompressed);
+
+  g_free (data);
+
+  gegl_exit ();
+
+  return result;
+}
diff --git a/tests/simple/Makefile.am b/tests/simple/Makefile.am
index 866464715..f23061520 100644
--- a/tests/simple/Makefile.am
+++ b/tests/simple/Makefile.am
@@ -10,6 +10,7 @@ noinst_PROGRAMS =                     \
        test-buffer-tile-voiding        \
        test-buffer-unaligned-access    \
        test-change-processor-rect      \
+       test-compression                \
        test-convert-format             \
        test-color-op                   \
        test-empty-tile                 \
diff --git a/tests/simple/test-compression.c b/tests/simple/test-compression.c
new file mode 100644
index 000000000..93baefd3b
--- /dev/null
+++ b/tests/simple/test-compression.c
@@ -0,0 +1,174 @@
+/*
+ * 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 <https://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2018 Ell
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include "gegl.h"
+#include "buffer/gegl-compression.h"
+
+#define SUCCESS  0
+#define FAILURE -1
+
+static gpointer
+load_png (const gchar *path,
+          const Babl  *format,
+          gint        *n)
+{
+  GeglNode   *node;
+  GeglNode   *node_source;
+  GeglNode   *node_sink;
+  GeglBuffer *buffer = NULL;
+  gpointer    data;
+
+  node = gegl_node_new ();
+
+  node_source = gegl_node_new_child (node,
+                                     "operation", "gegl:load",
+                                     "path",      path,
+                                     NULL);
+  node_sink   = gegl_node_new_child (node,
+                                     "operation", "gegl:buffer-sink",
+                                     "buffer",    &buffer,
+                                     NULL);
+
+  gegl_node_link (node_source, node_sink);
+
+  gegl_node_process (node_sink);
+
+  g_object_unref (node);
+
+  *n   = gegl_buffer_get_width (buffer) * gegl_buffer_get_height (buffer);
+  data = g_malloc (*n * babl_format_get_bytes_per_pixel (format));
+
+  gegl_buffer_get (buffer, NULL, 1.0, format, data,
+                   GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+  g_object_unref (buffer);
+
+  return data;
+}
+
+gint
+main (gint    argc,
+      gchar **argv)
+{
+  const Babl   *format;
+  gint          bpp;
+  gchar        *path;
+  gpointer      data;
+  gint          n;
+  gint          size;
+  guint8       *compressed;
+  gint          max_compressed_size;
+  guint8       *decompressed;
+  const gchar   signature[] = "test-gegl-compression";
+  const gchar **algorithms;
+  gint          i;
+  gint          result = SUCCESS;
+
+  gegl_init (&argc, &argv);
+
+  format = babl_format ("R'G'B'A u8");
+  bpp    = babl_format_get_bytes_per_pixel (format);
+
+  path = g_build_filename (g_getenv ("ABS_TOP_SRCDIR"),
+                           "tests", "compositions", "data", "car-stack.png",
+                           NULL);
+
+  data = load_png (path, format, &n);
+  size = n * bpp;
+
+  g_free (path);
+
+  max_compressed_size = 2 * n * bpp;
+  compressed          = g_malloc (max_compressed_size + sizeof (signature));
+  decompressed        = g_malloc (size);
+
+  algorithms = gegl_compression_list ();
+
+  for (i = 0; algorithms[i]; i++)
+    {
+      const GeglCompression *compression = gegl_compression (algorithms[i]);
+      gint                   compressed_size;
+      gint                   trunc_size;
+
+      printf ("%s: ", algorithms[i]);
+      fflush (stdout);
+
+      memset (compressed,   0, max_compressed_size);
+      memset (decompressed, 0, size);
+
+      if (! gegl_compression_compress (compression, format,
+                                       data, n,
+                                       compressed, &compressed_size,
+                                       max_compressed_size))
+        {
+          goto fail;
+        }
+
+      if (! gegl_compression_decompress (compression, format,
+                                         decompressed, n,
+                                         compressed, compressed_size))
+        {
+          goto fail;
+        }
+
+      if (memcmp (data, decompressed, size))
+        goto fail;
+
+      printf ("pass (%d%%)\n", (100 * compressed_size + size / 2) / size);
+
+      printf ("%s (trunc.): ", algorithms[i]);
+      fflush (stdout);
+
+      trunc_size = compressed_size / 2;
+
+      memcpy (compressed + trunc_size, signature, sizeof (signature));
+
+      if (gegl_compression_compress (compression, format,
+                                     data, n,
+                                     compressed, &compressed_size,
+                                     trunc_size))
+        {
+          goto fail;
+        }
+
+      if (memcmp (compressed + trunc_size, signature, sizeof (signature)))
+        goto fail;
+
+      printf ("pass\n");
+
+      continue;
+
+fail:
+      printf ("FAIL\n");
+    }
+
+  g_free (algorithms);
+
+  g_free (compressed);
+  g_free (decompressed);
+
+  g_free (data);
+
+  gegl_exit ();
+
+  return result;
+}


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