Commit 30047e65 authored by Ell's avatar Ell
Browse files

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.
parent 64021786
......@@ -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;
......
......@@ -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;
......
......@@ -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)
{
......
......@@ -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;
/**
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment