[gegl] buffer: add gegl_tile_backend_command(); pre-0.4.10 compatibility



commit 30047e65723ebb44fcde9c6b5f60ceecb43b0895
Author: Ell <ell_se yahoo com>
Date:   Sun Aug 19 19:19:37 2018 -0400

    buffer: add gegl_tile_backend_command(); pre-0.4.10 compatibility
    
    Tile backends currently assert in their command handlers that the
    input command is between 0 and GEGL_TILE_LAST_COMMAND (this is true
    for the built-in backends, but we can assume it's also true for
    custom backends.)  This prevents us from adding new tile commands
    without breaking the ABI.
    
    Instead, add a new gegl_tile_backend_command() function, which acts
    as a default command handler for tile backends, and to which their
    handlers should forward unhandled commands (this function currently
    simply performs the range check for the command -- however, against
    the GEGL_TILE_LAST_COMMAND value of the runtime GEGL -- and returns
    NULL; we can add differet default behaviors for different commands
    as necessary.)
    
    In order to remain backward compatible with tile backends compiled
    against older versions of GEGL, which still contain the above
    assertion, we replace the subclass's command handler with a thunk
    upon construction, which tests whether the original handler
    forwards unhandled commands to gegl_tile_backend_command(), and re-
    replaces the handler with either the original handler if it does,
    or a compatibility shim, which only forwards pre-0.4.10 commands to
    the original handler, if it doesn't.

 gegl/buffer/gegl-buffer-backend.h |   6 ++-
 gegl/buffer/gegl-buffer-types.h   |  20 +++----
 gegl/buffer/gegl-tile-backend.c   | 108 ++++++++++++++++++++++++++++++++++++++
 gegl/buffer/gegl-tile-backend.h   |  21 ++++++++
 4 files changed, 145 insertions(+), 10 deletions(-)
---
diff --git a/gegl/buffer/gegl-buffer-backend.h b/gegl/buffer/gegl-buffer-backend.h
index b2cee230f..f7a415f58 100644
--- a/gegl/buffer/gegl-buffer-backend.h
+++ b/gegl/buffer/gegl-buffer-backend.h
@@ -46,7 +46,11 @@ typedef enum
   GEGL_TILE_FLUSH,
   GEGL_TILE_REFETCH,
   GEGL_TILE_REINIT,
-  GEGL_TILE_COPY,
+
+  _GEGL_TILE_LAST_0_4_8_COMMAND,
+
+  GEGL_TILE_COPY = _GEGL_TILE_LAST_0_4_8_COMMAND,
+
   GEGL_TILE_LAST_COMMAND
 } GeglTileCommand;
 
diff --git a/gegl/buffer/gegl-buffer-types.h b/gegl/buffer/gegl-buffer-types.h
index c6d8333c0..9d55db2e2 100644
--- a/gegl/buffer/gegl-buffer-types.h
+++ b/gegl/buffer/gegl-buffer-types.h
@@ -30,18 +30,20 @@
 
 struct _GeglTileBackendPrivate
 {
-  gint        tile_width;
-  gint        tile_height;
-  const Babl *format;    /* defaults to the babl format "R'G'B'A u8" */
-  gint        px_size;   /* size of a single pixel in bytes */
-  gint        tile_size; /* size of an entire tile in bytes */
+  gint                   tile_width;
+  gint                   tile_height;
+  const Babl            *format;    /* defaults to the babl format "R'G'B'A u8" */
+  gint                   px_size;   /* size of a single pixel in bytes */
+  gint                   tile_size; /* size of an entire tile in bytes */
 
-  gboolean    flush_on_destroy;
+  gboolean               flush_on_destroy;
 
-  GeglRectangle extent;
+  GeglRectangle          extent;
 
-  gpointer    storage;
-  gboolean    shared;
+  gpointer               storage;
+  gboolean               shared;
+
+  GeglTileSourceCommand  command;
 };
 
 typedef struct _GeglTileHandlerChain      GeglTileHandlerChain;
diff --git a/gegl/buffer/gegl-tile-backend.c b/gegl/buffer/gegl-tile-backend.c
index 03c5a854b..720fa5ffc 100644
--- a/gegl/buffer/gegl-tile-backend.c
+++ b/gegl/buffer/gegl-tile-backend.c
@@ -115,10 +115,93 @@ set_property (GObject      *gobject,
     }
 }
 
+/* before 0.4.10, tile backends used to assert that
+ * '0 <= command < GEGL_TILE_LAST_COMMAND' in their command handlers, which
+ * prevented us from adding new tile commands without breaking the abi, since
+ * GEGL_TILE_LAST_COMMAND is a compile-time constant.  tile backends are now
+ * expected to forward unhandled commands to gegl_tile_backend_command()
+ * instead.
+ *
+ * in order to keep supporting tile backends that were compiled against 0.4.8
+ * or earlier, we replace the backend's command handler with a thunk
+ * (tile_command_check_0_4_8()) upon construction, which tests whether the
+ * backend forwards unhandled commands to gegl_tile_backend_command(), and
+ * subsequently replaces the command handler with either the original command
+ * handler if it does, or a compatibility shim (tile_command()) if it doesn't.
+ */
+
+/* this is the actual default command handler, called by
+ * gegl_tile_backend_command().  currently, it simply validates the command
+ * range, and returns NULL to any command.  we can add special behavior for
+ * different commands as needed.
+ */
+static inline gpointer
+_gegl_tile_backend_command (GeglTileBackend *backend,
+                            GeglTileCommand  command,
+                            gint             x,
+                            gint             y,
+                            gint             z,
+                            gpointer         data)
+{
+  g_return_val_if_fail (command >= 0 && command < GEGL_TILE_LAST_COMMAND, NULL);
+
+  return NULL;
+}
+
+/* this is a compatibility shim for backends compiled against 0.4.8 or earlier.
+ * it forwards commands that were present in these versions to the original
+ * handler, and newer commands to the default handler.
+ */
+static gpointer
+tile_command (GeglTileSource  *source,
+              GeglTileCommand  command,
+              gint             x,
+              gint             y,
+              gint             z,
+              gpointer         data)
+{
+  GeglTileBackend *backend = GEGL_TILE_BACKEND (source);
+
+  if (command < _GEGL_TILE_LAST_0_4_8_COMMAND)
+    return backend->priv->command (source, command, x, y, z, data);
+
+  return _gegl_tile_backend_command (backend, command, x, y, z, data);
+}
+
+/* this is a thunk, testing whether the backend forwards unhandled commands to
+ * gegl_tile_backend_command().  if it does, we replace the thunk by the
+ * original handler; if it doesn't, we assume the backend can't handle post-
+ * 0.4.8 commands, and replace the thunk with the compatibility shim.
+ */
+static gpointer
+tile_command_check_0_4_8 (GeglTileSource  *source,
+                          GeglTileCommand  command,
+                          gint             x,
+                          gint             y,
+                          gint             z,
+                          gpointer         data)
+{
+  GeglTileBackend *backend = GEGL_TILE_BACKEND (source);
+
+  /* start by replacing the thunk by the compatibility shim */
+  source->command = tile_command;
+
+  /* pass the original handler a dummy command.  we use GEGL_TILE_IS_CACHED,
+   * since backends shouldn't handle this command.  if the handler forwards the
+   * command to gegl_tile_backend_command(), it will replace the shim by the
+   * original handler.
+   */
+  backend->priv->command (source, GEGL_TILE_IS_CACHED, 0, 0, 0, NULL);
+
+  /* forward the command to either the shim or the original handler */
+  return source->command (source, command, x, y, z, data);
+}
+
 static void
 constructed (GObject *object)
 {
   GeglTileBackend *backend = GEGL_TILE_BACKEND (object);
+  GeglTileSource  *source  = GEGL_TILE_SOURCE (object);
 
   G_OBJECT_CLASS (parent_class)->constructed (object);
 
@@ -127,6 +210,9 @@ constructed (GObject *object)
 
   backend->priv->px_size = babl_format_get_bytes_per_pixel (backend->priv->format);
   backend->priv->tile_size = backend->priv->tile_width * backend->priv->tile_height * backend->priv->px_size;
+
+  backend->priv->command = source->command;
+  source->command        = tile_command_check_0_4_8;
 }
 
 static void
@@ -242,6 +328,28 @@ gegl_tile_backend_get_flush_on_destroy (GeglTileBackend *tile_backend)
   return tile_backend->priv->flush_on_destroy;
 }
 
+gpointer
+gegl_tile_backend_command (GeglTileBackend *backend,
+                           GeglTileCommand  command,
+                           gint             x,
+                           gint             y,
+                           gint             z,
+                           gpointer         data)
+{
+  /* we've been called during the tile_command() test, which means the backend
+   * is post-0.4.8 compatible.  replace the shim with the original handler.
+   */
+  if (backend->priv->command)
+    {
+      GeglTileSource *source = GEGL_TILE_SOURCE (backend);
+
+      source->command        = backend->priv->command;
+      backend->priv->command = NULL;
+    }
+
+  return _gegl_tile_backend_command (backend, command, x, y, z, data);
+}
+
 void
 gegl_tile_backend_unlink_swap (gchar *path)
 {
diff --git a/gegl/buffer/gegl-tile-backend.h b/gegl/buffer/gegl-tile-backend.h
index 3f0224993..3d1f806a2 100644
--- a/gegl/buffer/gegl-tile-backend.h
+++ b/gegl/buffer/gegl-tile-backend.h
@@ -121,6 +121,27 @@ void gegl_tile_backend_set_flush_on_destroy (GeglTileBackend *tile_backend,
 
 gboolean gegl_tile_backend_get_flush_on_destroy (GeglTileBackend *tile_backend);
 
+/**
+ * gegl_tile_backend_command:
+ * @backend: a #GeglTileBackend
+ * @command: the tile command
+ * @x: x coordinate
+ * @y: y coordinate
+ * @z: tile zoom level
+ * @data: user data
+ *
+ * The default tile-backend command handler.  Tile backends should forward
+ * commands they don't handle themselves to this function.
+ *
+ * Returns: Command result.
+ */
+gpointer gegl_tile_backend_command (GeglTileBackend *backend,
+                                    GeglTileCommand  command,
+                                    gint             x,
+                                    gint             y,
+                                    gint             z,
+                                    gpointer         data);
+
 GType gegl_tile_backend_get_type (void) G_GNUC_CONST;
 
 /**


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