[gegl] gegl-compression: add RLE algorithms



commit d87873cdc2d5d404788566878731af55e8816800
Author: Ell <ell_se yahoo com>
Date:   Mon Dec 17 04:49:42 2018 -0500

    gegl-compression: add RLE algorithms
    
    Add a set of RLE compression algorithms: "rle1", "rle2", "rle4",
    and "rle8".  The numeric suffix specifies the unit of compression,
    in bits -- the algorithms independently compress the respective
    units of each pixel.  That is, "rle1" RLE-compresses the first bit
    of each pixel, then the second bit of each pixel, and so on, while
    "rle8" RLE-compresses the first byte of each pixel, then the second
    byte of each pixel, and so on.  Smaller units yield better, but
    slower, compression.

 gegl/buffer/Makefile.am            |   2 +
 gegl/buffer/gegl-compression-rle.c | 642 +++++++++++++++++++++++++++++++++++++
 gegl/buffer/gegl-compression-rle.h |  30 ++
 gegl/buffer/gegl-compression.c     |   2 +
 4 files changed, 676 insertions(+)
---
diff --git a/gegl/buffer/Makefile.am b/gegl/buffer/Makefile.am
index 235174cf8..ae95964c7 100644
--- a/gegl/buffer/Makefile.am
+++ b/gegl/buffer/Makefile.am
@@ -44,6 +44,7 @@ libbuffer_la_SOURCES = \
     gegl-buffer-swap.c         \
     gegl-compression.c         \
     gegl-compression-nop.c     \
+    gegl-compression-rle.c     \
     gegl-sampler.c             \
     gegl-sampler-cubic.c       \
     gegl-sampler-linear.c      \
@@ -77,6 +78,7 @@ libbuffer_la_SOURCES = \
     gegl-buffer-swap-private.h \
     gegl-compression.h         \
     gegl-compression-nop.h     \
+    gegl-compression-rle.h     \
     gegl-sampler.h             \
     gegl-sampler-cubic.h       \
     gegl-sampler-linear.h      \
diff --git a/gegl/buffer/gegl-compression-rle.c b/gegl/buffer/gegl-compression-rle.c
new file mode 100644
index 000000000..053db07ee
--- /dev/null
+++ b/gegl/buffer/gegl-compression-rle.c
@@ -0,0 +1,642 @@
+/* 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 <string.h>
+
+#include "gegl-compression.h"
+#include "gegl-compression-rle.h"
+
+
+#define RLE_COMPRESS_VERBATIM_UNROLL    4
+#define RLE_COMPRESS_REPEAT_UNROLL     16
+#define RLE_DECOMPRESS_VERBATIM_UNROLL  3
+#define RLE_DECOMPRESS_REPEAT_UNROLL   16
+
+
+#ifdef RLE_BITS
+
+
+#ifdef RLE_COMPRESS_PASS
+
+
+#define gegl_compression_rle_compress_pass      \
+  RLE_CAT (gegl_compression_rle_compress,       \
+           RLE_CAT (_pass_, RLE_COMPRESS_PASS))
+
+
+/*  private functions  */
+
+static gboolean __attribute__ ((noinline))
+gegl_compression_rle_compress_pass (const guint8 *data,
+                                    gint          n,
+                                    gint          shift,
+                                    gint          bpp,
+                                    guint8       *compressed,
+                                    gint         *compressed_size,
+                                    gint          max_compressed_size)
+{
+  typedef enum
+  {
+    STATE_UNKNOWN,
+    STATE_VERBATIM,
+    STATE_REPEAT
+  } State;
+
+  State  state;
+  guint8 val;
+  guint8 last_val;
+  gint   count;
+  gint   size;
+
+#if RLE_BITS == 8
+  #define PACK()        \
+    G_STMT_START        \
+      {                 \
+        last_val = val; \
+                        \
+        val = *data;    \
+                        \
+        data += bpp;    \
+                        \
+        n--;            \
+      }                 \
+    G_STMT_END
+#else
+  guint8 mask;
+
+  shift = 8 - (shift + 1) * RLE_BITS;
+  mask  = ((1 << RLE_BITS) - 1) << shift;
+
+  #define PACK()                          \
+    G_STMT_START                          \
+      {                                   \
+        gint v = 0;                       \
+        gint i;                           \
+                                          \
+        last_val = val;                   \
+                                          \
+        for (i = 0; i < 8; i += RLE_BITS) \
+          {                               \
+            v |= (*data & mask) << i;     \
+                                          \
+            data += bpp;                  \
+          }                               \
+                                          \
+        val = v >> shift;                 \
+                                          \
+        n--;                              \
+      }                                   \
+    G_STMT_END
+#endif
+
+  size = 0;
+
+  state = STATE_UNKNOWN;
+  count = 0;
+
+  while (TRUE)
+    {
+      switch (state)
+        {
+        case STATE_UNKNOWN:
+          {
+            if (count == 0)
+              {
+                if (! n)
+                  goto end;
+
+                PACK ();
+              }
+
+            if (! n)
+              {
+                RLE_WRITE (compressed, 0, size, max_compressed_size);
+                RLE_WRITE (compressed, val, size, max_compressed_size);
+
+                goto end;
+              }
+
+            PACK ();
+
+            if (val != last_val)
+              {
+                RLE_WRITE (compressed, 0, size, max_compressed_size);
+                RLE_WRITE (compressed, last_val, size, max_compressed_size);
+
+                state = STATE_VERBATIM;
+                count = 1;
+              }
+            else
+              {
+                state = STATE_REPEAT;
+                count = 2;
+              }
+          }
+          break;
+
+        case STATE_VERBATIM:
+          {
+            gint next_count = 1;
+
+            state = STATE_UNKNOWN;
+
+            #define INNER_LOOP()                                          \
+              G_STMT_START                                                \
+                {                                                         \
+                  RLE_WRITE (compressed, val, size, max_compressed_size); \
+                  count++;                                                \
+                                                                          \
+                  if (! n)                                                \
+                    {                                                     \
+                      next_count = 0;                                     \
+                                                                          \
+                      goto verbatim_end;                                  \
+                    }                                                     \
+                                                                          \
+                  PACK ();                                                \
+                                                                          \
+                  if (val == last_val)                                    \
+                    {                                                     \
+                      if (! n || count >= 126)                            \
+                        {                                                 \
+                          count--;                                        \
+                          size--;                                         \
+                                                                          \
+                          state      = STATE_REPEAT;                      \
+                          next_count = 2;                                 \
+                                                                          \
+                          goto verbatim_end;                              \
+                        }                                                 \
+                      else                                                \
+                        {                                                 \
+                          PACK ();                                        \
+                                                                          \
+                          if (val == last_val)                            \
+                            {                                             \
+                              count--;                                    \
+                              size--;                                     \
+                                                                          \
+                              state      = STATE_REPEAT;                  \
+                              next_count = 3;                             \
+                                                                          \
+                              goto verbatim_end;                          \
+                            }                                             \
+                          else                                            \
+                            {                                             \
+                              RLE_WRITE (compressed, last_val,            \
+                                         size, max_compressed_size);      \
+                              count++;                                    \
+                            }                                             \
+                        }                                                 \
+                    }                                                     \
+                }                                                         \
+              G_STMT_END
+
+            while (count <= 128 - 2 * RLE_COMPRESS_VERBATIM_UNROLL)
+              {
+                gint i;
+
+                for (i = 0; i < RLE_COMPRESS_VERBATIM_UNROLL; i++)
+                  INNER_LOOP ();
+              }
+
+            while (count < 128)
+              INNER_LOOP ();
+
+            #undef INNER_LOOP
+
+verbatim_end:
+            compressed[size - count - 1] = count - 1;
+
+            count = next_count;
+          }
+          break;
+
+        case STATE_REPEAT:
+          {
+            gint next_count = 0;
+
+            state = STATE_UNKNOWN;
+
+            #define INNER_LOOP()       \
+              G_STMT_START             \
+                {                      \
+                  PACK ();             \
+                                       \
+                  if (val != last_val) \
+                    {                  \
+                      next_count = 1;  \
+                                       \
+                      goto repeat_end; \
+                    }                  \
+                                       \
+                  count++;             \
+                }                      \
+              G_STMT_END
+
+            while (n     >= RLE_COMPRESS_REPEAT_UNROLL &&
+                   count <= (1 << 16) - RLE_COMPRESS_REPEAT_UNROLL)
+              {
+                gint i;
+
+                for (i = 0; i < RLE_COMPRESS_REPEAT_UNROLL; i++)
+                  INNER_LOOP ();
+              }
+
+            while (n && count < (1 << 16))
+              INNER_LOOP ();
+
+            #undef INNER_LOOP
+
+repeat_end:
+            if (count < 128)
+              {
+                RLE_WRITE (compressed, 255 - count, size, max_compressed_size);
+              }
+            else
+              {
+                count--;
+
+                RLE_WRITE (compressed, 255,          size, max_compressed_size);
+                RLE_WRITE (compressed, count >> 8,   size, max_compressed_size);
+                RLE_WRITE (compressed, count & 0xff, size, max_compressed_size);
+              }
+
+            RLE_WRITE (compressed, last_val, size, max_compressed_size);
+
+            count = next_count;
+          }
+          break;
+        }
+    }
+
+  #undef PACK
+
+end:
+  *compressed_size = size;
+
+  return TRUE;
+}
+
+
+#undef gegl_compression_rle_compress_pass
+
+#undef RLE_COMPRESS_PASS
+#undef RLE_WRITE
+
+
+#elif defined (RLE_DECOMPRESS_PASS)
+
+
+#define gegl_compression_rle_decompress_pass      \
+  RLE_CAT (gegl_compression_rle_decompress,       \
+           RLE_CAT (_pass_, RLE_DECOMPRESS_PASS))
+
+
+/*  private functions  */
+
+static void __attribute__ ((noinline))
+gegl_compression_rle_decompress_pass (guint8        *data,
+                                      gint           n,
+                                      gint           bpp,
+                                      const guint8  *compressed,
+                                      const guint8 **next_compressed)
+{
+#if RLE_BITS == 8
+  #define UNPACK(val)  \
+    G_STMT_START       \
+      {                \
+        *data = (val); \
+                       \
+        data += bpp;   \
+      }                \
+    G_STMT_END
+#else
+  #define UNPACK(val)                                 \
+    G_STMT_START                                      \
+      {                                               \
+        guint8 v = (val);                             \
+        gint   i;                                     \
+                                                      \
+        for (i = 0; i < 8 / RLE_BITS; i++)            \
+          {                                           \
+            *data   = (RLE_READ (data) << RLE_BITS) | \
+                      (v & ((1 << RLE_BITS) - 1));    \
+            v     >>= RLE_BITS;                       \
+                                                      \
+            data += bpp;                              \
+          }                                           \
+      }                                               \
+    G_STMT_END
+#endif
+
+  while (n)
+    {
+      gint count = *compressed++;
+      gint state = count & 0x80;
+
+      if (state == 0)
+        {
+          count++;
+
+          n -= count;
+
+          for (;
+               count >= RLE_DECOMPRESS_VERBATIM_UNROLL;
+               count -= RLE_DECOMPRESS_VERBATIM_UNROLL)
+            {
+              gint i;
+
+              for (i = 0; i < RLE_DECOMPRESS_VERBATIM_UNROLL; i++)
+                UNPACK (*compressed++);
+            }
+
+          for (; count; count--)
+            UNPACK (*compressed++);
+        }
+      else
+        {
+          guint8 val;
+
+          count = 255 - count;
+
+          if (! count)
+            {
+              count   = *compressed++;
+              count <<= 8;
+              count  |= *compressed++;
+
+              count++;
+            }
+
+          n -= count;
+
+          val = *compressed++;
+
+          for (;
+               count >= RLE_DECOMPRESS_REPEAT_UNROLL;
+               count -= RLE_DECOMPRESS_REPEAT_UNROLL)
+            {
+              gint i;
+
+              for (i = 0; i < RLE_DECOMPRESS_REPEAT_UNROLL; i++)
+                UNPACK (val);
+            }
+
+          for (; count; count--)
+            UNPACK (val);
+        }
+    }
+
+  #undef UNPACK
+
+  *next_compressed = compressed;
+}
+
+
+#undef gegl_compression_rle_decompress_pass
+
+#undef RLE_DECOMPRESS_PASS
+#undef RLE_READ
+
+
+#else /* ! RLE_COMPRESS_PASS && ! RLE_DECOMPRESS_PASS */
+
+
+#define RLE_CAT(x, y)   RLE_CAT_I (x, y)
+#define RLE_CAT_I(x, y) x ## y
+
+
+#define gegl_compression_rle \
+  RLE_CAT (gegl_compression_rle, RLE_BITS)
+#define gegl_compression_rle_compress \
+  RLE_CAT (gegl_compression_rle_compress, RLE_BITS)
+#define gegl_compression_rle_compress_pass_nobounds \
+  RLE_CAT (gegl_compression_rle_compress, _pass_nobounds)
+#define gegl_compression_rle_compress_pass_bounds \
+  RLE_CAT (gegl_compression_rle_compress, _pass_bounds)
+#define gegl_compression_rle_decompress \
+  RLE_CAT (gegl_compression_rle_decompress, RLE_BITS)
+#define gegl_compression_rle_decompress_pass_noinit \
+  RLE_CAT (gegl_compression_rle_decompress, _pass_noinit)
+#define gegl_compression_rle_decompress_pass_init \
+  RLE_CAT (gegl_compression_rle_decompress, _pass_init)
+
+
+/*  local function prototypes  */
+
+static gboolean   gegl_compression_rle_compress   (const GeglCompression *compression,
+                                                   const Babl            *format,
+                                                   gconstpointer          data,
+                                                   gint                   n,
+                                                   gpointer               compressed,
+                                                   gint                  *compressed_size,
+                                                   gint                   max_compressed_size);
+static gboolean   gegl_compression_rle_decompress (const GeglCompression *compression,
+                                                   const Babl            *format,
+                                                   gpointer               data,
+                                                   gint                   n,
+                                                   gconstpointer          compressed,
+                                                   gint                   compressed_size);
+
+
+/*  local variables  */
+
+static const GeglCompression gegl_compression_rle =
+{
+  .compress   = gegl_compression_rle_compress,
+  .decompress = gegl_compression_rle_decompress
+};
+
+
+/*  private functions  */
+
+#define RLE_COMPRESS_PASS nobounds
+#define RLE_WRITE(dest, val, i, n) \
+  ((dest)[(i)++] = (val))
+#include "gegl-compression-rle.c"
+
+#define RLE_COMPRESS_PASS bounds
+#define RLE_WRITE(dest, val, i, n) \
+  G_STMT_START                     \
+    {                              \
+      gint j = (i)++;              \
+                                   \
+      if (j == (n))                \
+        return FALSE;              \
+                                   \
+      (dest)[j] = (val);           \
+    }                              \
+  G_STMT_END
+#include "gegl-compression-rle.c"
+
+static gboolean
+gegl_compression_rle_compress (const GeglCompression *compression,
+                               const Babl            *format,
+                               gconstpointer          data,
+                               gint                   n,
+                               gpointer               compressed,
+                               gint                  *compressed_size,
+                               gint                   max_compressed_size)
+{
+  const guint8 *data8               = data;
+  guint8       *compressed8         = compressed;
+  gint          bpp                 = babl_format_get_bytes_per_pixel (format);
+  gint          m                   = 8 / RLE_BITS;
+  gint          rem_compressed_size = max_compressed_size;
+  gint          i;
+
+  for (i = 0; i < m * bpp; i++)
+    {
+      gint max_pass_compressed_size;
+      gint pass_compressed_size;
+
+      max_pass_compressed_size = n / m + (n / m + 127) / 128;
+
+      if (max_pass_compressed_size <= rem_compressed_size)
+        {
+          gegl_compression_rle_compress_pass_nobounds (
+            data8 + i / m, n / m, i % m, bpp,
+            compressed8, &pass_compressed_size, rem_compressed_size);
+        }
+      else
+        {
+          if (! gegl_compression_rle_compress_pass_bounds (
+                  data8 + i / m, n / m, i % m, bpp,
+                  compressed8, &pass_compressed_size, rem_compressed_size))
+            {
+              return FALSE;
+            }
+        }
+
+      compressed8         += pass_compressed_size;
+      rem_compressed_size -= pass_compressed_size;
+    }
+
+  if (m > 1)
+    {
+      gint rem = (n % m) * bpp;
+
+      if (rem > rem_compressed_size)
+        return FALSE;
+
+      memcpy (compressed8, data8 + n * bpp - rem, rem);
+
+      compressed8         += rem;
+      rem_compressed_size -= rem;
+    }
+
+  *compressed_size = max_compressed_size - rem_compressed_size;
+
+  return TRUE;
+}
+
+#define RLE_DECOMPRESS_PASS noinit
+#define RLE_READ(src) 0
+#include "gegl-compression-rle.c"
+
+#define RLE_DECOMPRESS_PASS init
+#define RLE_READ(src) (*(src))
+#include "gegl-compression-rle.c"
+
+static gboolean
+gegl_compression_rle_decompress (const GeglCompression *compression,
+                                 const Babl            *format,
+                                 gpointer               data,
+                                 gint                   n,
+                                 gconstpointer          compressed,
+                                 gint                   compressed_size)
+{
+  guint8       *data8       = data;
+  const guint8 *compressed8 = compressed;
+  gint          bpp         = babl_format_get_bytes_per_pixel (format);
+  gint          m           = 8 / RLE_BITS;
+  gint          i;
+
+  for (i = 0; i < m * bpp; i++)
+    {
+      if (i % m)
+        {
+          gegl_compression_rle_decompress_pass_init   (data8 + i / m, n / m,
+                                                       bpp,
+                                                       compressed8,
+                                                       &compressed8);
+        }
+      else
+        {
+          gegl_compression_rle_decompress_pass_noinit (data8 + i / m, n / m,
+                                                       bpp,
+                                                       compressed8,
+                                                       &compressed8);
+        }
+    }
+
+  if (m > 1)
+    {
+      gint rem = (n % m) * bpp;
+
+      memcpy (data8 + n * bpp - rem, compressed8, rem);
+    }
+
+  return TRUE;
+}
+
+
+#undef gegl_compression_rle
+#undef gegl_compression_rle_compress
+#undef gegl_compression_rle_compress_pass_bounds
+#undef gegl_compression_rle_compress_pass_nobounds
+#undef gegl_compression_rle_decompress
+#undef gegl_compression_rle_decompress_pass_noinit
+#undef gegl_compression_rle_decompress_pass_init
+
+#undef RLE_BITS
+
+
+#endif /* ! RLE_COMPRESS_PASS && ! RLE_DECOMPRESS_PASS */
+
+
+#else /* ! RLE_BITS */
+
+
+#define RLE_BITS 1
+#include "gegl-compression-rle.c"
+
+#define RLE_BITS 2
+#include "gegl-compression-rle.c"
+
+#define RLE_BITS 4
+#include "gegl-compression-rle.c"
+
+#define RLE_BITS 8
+#include "gegl-compression-rle.c"
+
+
+/*  public functions  */
+
+void
+gegl_compression_rle_init (void)
+{
+  gegl_compression_register ("rle1", &gegl_compression_rle1);
+  gegl_compression_register ("rle2", &gegl_compression_rle2);
+  gegl_compression_register ("rle4", &gegl_compression_rle4);
+  gegl_compression_register ("rle8", &gegl_compression_rle8);
+}
+
+
+#endif /* ! RLE_BITS */
diff --git a/gegl/buffer/gegl-compression-rle.h b/gegl/buffer/gegl-compression-rle.h
new file mode 100644
index 000000000..d53c15b5f
--- /dev/null
+++ b/gegl/buffer/gegl-compression-rle.h
@@ -0,0 +1,30 @@
+/* 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_RLE_H__
+#define __GEGL_COMPRESSION_RLE_H__
+
+
+#include <glib.h>
+#include <babl/babl.h>
+
+G_BEGIN_DECLS
+
+void   gegl_compression_rle_init (void);
+
+G_END_DECLS
+
+#endif
diff --git a/gegl/buffer/gegl-compression.c b/gegl/buffer/gegl-compression.c
index 51e89177d..0c2abe2a8 100644
--- a/gegl/buffer/gegl-compression.c
+++ b/gegl/buffer/gegl-compression.c
@@ -21,6 +21,7 @@
 
 #include "gegl-compression.h"
 #include "gegl-compression-nop.h"
+#include "gegl-compression-rle.h"
 
 
 /*  local variables  */
@@ -38,6 +39,7 @@ gegl_compression_init (void)
   algorithms = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
 
   gegl_compression_nop_init ();
+  gegl_compression_rle_init ();
 }
 
 void


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