1 /* This file is part of GEGL.
2  *
3  * This library is free software; you can redistribute it and/or
4  * modify it under the terms of the GNU Lesser General Public
5  * License as published by the Free Software Foundation; either
6  * version 3 of the License, or (at your option) any later version.
7  *
8  * This library is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * Lesser General Public License for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public
14  * License along with GEGL; if not, see <https://www.gnu.org/licenses/>.
15  *
16  * Copyright 2006, 2007 Øyvind Kolås <pippin@gimp.org>
17  */
18 #include "config.h"
19 
20 #include <string.h>
21 
22 #include <glib-object.h>
23 #include <glib/gstdio.h>
24 
25 #include "gegl-buffer.h"
26 #include "gegl-buffer-types.h"
27 #include "gegl-buffer-private.h"
28 #include "gegl-tile-source.h"
29 #include "gegl-tile-backend.h"
30 #include "gegl-buffer-config.h"
31 
32 G_DEFINE_TYPE_WITH_PRIVATE (GeglTileBackend, gegl_tile_backend,
33                             GEGL_TYPE_TILE_SOURCE)
34 
35 #define parent_class gegl_tile_backend_parent_class
36 
37 enum
38 {
39   PROP_0,
40   PROP_TILE_WIDTH,
41   PROP_TILE_HEIGHT,
42   PROP_PX_SIZE,
43   PROP_TILE_SIZE,
44   PROP_FORMAT,
45   PROP_FLUSH_ON_DESTROY
46 };
47 
48 static void
get_property(GObject * gobject,guint property_id,GValue * value,GParamSpec * pspec)49 get_property (GObject    *gobject,
50               guint       property_id,
51               GValue     *value,
52               GParamSpec *pspec)
53 {
54   GeglTileBackend *backend = GEGL_TILE_BACKEND (gobject);
55 
56   switch (property_id)
57     {
58       case PROP_TILE_WIDTH:
59         g_value_set_int (value, backend->priv->tile_width);
60         break;
61 
62       case PROP_TILE_HEIGHT:
63         g_value_set_int (value, backend->priv->tile_height);
64         break;
65 
66       case PROP_TILE_SIZE:
67         g_value_set_int (value, backend->priv->tile_size);
68         break;
69 
70       case PROP_PX_SIZE:
71         g_value_set_int (value, backend->priv->px_size);
72         break;
73 
74       case PROP_FORMAT:
75         g_value_set_pointer (value, (void*)backend->priv->format);
76         break;
77 
78       case PROP_FLUSH_ON_DESTROY:
79         g_value_set_boolean (value, backend->priv->flush_on_destroy);
80         break;
81 
82       default:
83         G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, property_id, pspec);
84         break;
85     }
86 }
87 
88 static void
set_property(GObject * gobject,guint property_id,const GValue * value,GParamSpec * pspec)89 set_property (GObject      *gobject,
90               guint         property_id,
91               const GValue *value,
92               GParamSpec   *pspec)
93 {
94   GeglTileBackend *backend = GEGL_TILE_BACKEND (gobject);
95 
96   switch (property_id)
97     {
98       case PROP_TILE_WIDTH:
99         backend->priv->tile_width = g_value_get_int (value);
100         return;
101 
102       case PROP_TILE_HEIGHT:
103         backend->priv->tile_height = g_value_get_int (value);
104         return;
105 
106       case PROP_FORMAT:
107         backend->priv->format = g_value_get_pointer (value);
108         break;
109 
110       case PROP_FLUSH_ON_DESTROY:
111         backend->priv->flush_on_destroy = g_value_get_boolean (value);
112         break;
113 
114       default:
115         G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, property_id, pspec);
116         break;
117     }
118 }
119 
120 /* before 0.4.10, tile backends used to assert that
121  * '0 <= command < GEGL_TILE_LAST_COMMAND' in their command handlers, which
122  * prevented us from adding new tile commands without breaking the abi, since
123  * GEGL_TILE_LAST_COMMAND is a compile-time constant.  tile backends are now
124  * expected to forward unhandled commands to gegl_tile_backend_command()
125  * instead.
126  *
127  * in order to keep supporting tile backends that were compiled against 0.4.8
128  * or earlier, we replace the backend's command handler with a thunk
129  * (tile_command_check_0_4_8()) upon construction, which tests whether the
130  * backend forwards unhandled commands to gegl_tile_backend_command(), and
131  * subsequently replaces the command handler with either the original command
132  * handler if it does, or a compatibility shim (tile_command()) if it doesn't.
133  */
134 
135 /* this is the actual default command handler, called by
136  * gegl_tile_backend_command().  currently, it simply validates the command
137  * range, and returns NULL to any command.  we can add special behavior for
138  * different commands as needed.
139  */
140 static inline gpointer
_gegl_tile_backend_command(GeglTileBackend * backend,GeglTileCommand command,gint x,gint y,gint z,gpointer data)141 _gegl_tile_backend_command (GeglTileBackend *backend,
142                             GeglTileCommand  command,
143                             gint             x,
144                             gint             y,
145                             gint             z,
146                             gpointer         data)
147 {
148   g_return_val_if_fail (command >= 0 && command < GEGL_TILE_LAST_COMMAND, NULL);
149 
150   return NULL;
151 }
152 
153 /* this is a compatibility shim for backends compiled against 0.4.8 or earlier.
154  * it forwards commands that were present in these versions to the original
155  * handler, and newer commands to the default handler.
156  */
157 static gpointer
tile_command(GeglTileSource * source,GeglTileCommand command,gint x,gint y,gint z,gpointer data)158 tile_command (GeglTileSource  *source,
159               GeglTileCommand  command,
160               gint             x,
161               gint             y,
162               gint             z,
163               gpointer         data)
164 {
165   GeglTileBackend *backend = GEGL_TILE_BACKEND (source);
166 
167   if (G_LIKELY (command < _GEGL_TILE_LAST_0_4_8_COMMAND))
168     return backend->priv->command (source, command, x, y, z, data);
169 
170   return _gegl_tile_backend_command (backend, command, x, y, z, data);
171 }
172 
173 /* this is a thunk, testing whether the backend forwards unhandled commands to
174  * gegl_tile_backend_command().  if it does, we replace the thunk by the
175  * original handler; if it doesn't, we assume the backend can't handle post-
176  * 0.4.8 commands, and replace the thunk with the compatibility shim.
177  */
178 static gpointer
tile_command_check_0_4_8(GeglTileSource * source,GeglTileCommand command,gint x,gint y,gint z,gpointer data)179 tile_command_check_0_4_8 (GeglTileSource  *source,
180                           GeglTileCommand  command,
181                           gint             x,
182                           gint             y,
183                           gint             z,
184                           gpointer         data)
185 {
186   GeglTileBackend *backend = GEGL_TILE_BACKEND (source);
187 
188   /* start by replacing the thunk by the compatibility shim */
189   source->command = tile_command;
190 
191   /* pass the original handler a dummy command.  we use GEGL_TILE_IS_CACHED,
192    * since backends shouldn't handle this command.  if the handler forwards the
193    * command to gegl_tile_backend_command(), it will replace the shim by the
194    * original handler.
195    */
196   backend->priv->command (source, GEGL_TILE_IS_CACHED, 0, 0, 0, NULL);
197 
198   /* forward the command to either the shim or the original handler */
199   return source->command (source, command, x, y, z, data);
200 }
201 
202 static void
constructed(GObject * object)203 constructed (GObject *object)
204 {
205   GeglTileBackend *backend = GEGL_TILE_BACKEND (object);
206   GeglTileSource  *source  = GEGL_TILE_SOURCE (object);
207 
208   G_OBJECT_CLASS (parent_class)->constructed (object);
209 
210   g_assert (backend->priv->tile_width > 0 && backend->priv->tile_height > 0);
211   g_assert (backend->priv->format);
212 
213   backend->priv->px_size = babl_format_get_bytes_per_pixel (backend->priv->format);
214   backend->priv->tile_size = backend->priv->tile_width * backend->priv->tile_height * backend->priv->px_size;
215 
216   backend->priv->command = source->command;
217   source->command        = tile_command_check_0_4_8;
218 }
219 
220 static void
gegl_tile_backend_class_init(GeglTileBackendClass * klass)221 gegl_tile_backend_class_init (GeglTileBackendClass *klass)
222 {
223   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
224 
225   gobject_class->set_property = set_property;
226   gobject_class->get_property = get_property;
227   gobject_class->constructed  = constructed;
228 
229   g_object_class_install_property (gobject_class, PROP_TILE_WIDTH,
230                                    g_param_spec_int ("tile-width", "tile-width",
231                                                      "Tile width in pixels",
232                                                      0, G_MAXINT, 0,
233                                                      G_PARAM_READWRITE |
234                                                      G_PARAM_CONSTRUCT_ONLY |
235                                                      G_PARAM_STATIC_STRINGS));
236   g_object_class_install_property (gobject_class, PROP_TILE_HEIGHT,
237                                    g_param_spec_int ("tile-height", "tile-height",
238                                                      "Tile height in pixels",
239                                                      0, G_MAXINT, 0,
240                                                      G_PARAM_READWRITE |
241                                                      G_PARAM_CONSTRUCT_ONLY |
242                                                      G_PARAM_STATIC_STRINGS));
243   g_object_class_install_property (gobject_class, PROP_TILE_SIZE,
244                                    g_param_spec_int ("tile-size", "tile-size",
245                                                      "Size of the tiles linear buffer in bytes",
246                                                      0, G_MAXINT, 0,
247                                                      G_PARAM_READABLE |
248                                                      G_PARAM_STATIC_STRINGS));
249   g_object_class_install_property (gobject_class, PROP_PX_SIZE,
250                                    g_param_spec_int ("px-size", "px-size",
251                                                      "Size of a single pixel in bytes",
252                                                      0, G_MAXINT, 0,
253                                                      G_PARAM_READABLE |
254                                                      G_PARAM_STATIC_STRINGS));
255   g_object_class_install_property (gobject_class, PROP_FORMAT,
256                                    g_param_spec_pointer ("format", "format",
257                                                          "babl format",
258                                                          G_PARAM_READWRITE |
259                                                          G_PARAM_CONSTRUCT_ONLY |
260                                                          G_PARAM_STATIC_STRINGS));
261   g_object_class_install_property (gobject_class, PROP_FLUSH_ON_DESTROY,
262                                    g_param_spec_boolean ("flush-on-destroy", "flush-on-destroy",
263                                                          "Cache tiles will be flushed before the backend is destroyed",
264                                                          TRUE,
265                                                          G_PARAM_READWRITE |
266                                                          G_PARAM_STATIC_STRINGS));
267 }
268 
269 static void
gegl_tile_backend_init(GeglTileBackend * self)270 gegl_tile_backend_init (GeglTileBackend *self)
271 {
272   self->priv = gegl_tile_backend_get_instance_private (self);
273   self->priv->shared = FALSE;
274   self->priv->flush_on_destroy = TRUE;
275 }
276 
277 
278 gint
gegl_tile_backend_get_tile_size(GeglTileBackend * tile_backend)279 gegl_tile_backend_get_tile_size (GeglTileBackend *tile_backend)
280 {
281   return tile_backend->priv->tile_size;
282 }
283 
284 gint
gegl_tile_backend_get_tile_width(GeglTileBackend * tile_backend)285 gegl_tile_backend_get_tile_width (GeglTileBackend *tile_backend)
286 {
287   return tile_backend->priv->tile_width;
288 }
289 
290 gint
gegl_tile_backend_get_tile_height(GeglTileBackend * tile_backend)291 gegl_tile_backend_get_tile_height (GeglTileBackend *tile_backend)
292 {
293   return tile_backend->priv->tile_height;
294 }
295 
296 const Babl *
gegl_tile_backend_get_format(GeglTileBackend * tile_backend)297 gegl_tile_backend_get_format (GeglTileBackend *tile_backend)
298 {
299   return tile_backend->priv->format;
300 }
301 
302 
303 void
gegl_tile_backend_set_extent(GeglTileBackend * tile_backend,const GeglRectangle * rectangle)304 gegl_tile_backend_set_extent (GeglTileBackend     *tile_backend,
305                               const GeglRectangle *rectangle)
306 {
307   tile_backend->priv->extent = *rectangle;
308 }
309 
310 GeglRectangle
gegl_tile_backend_get_extent(GeglTileBackend * tile_backend)311 gegl_tile_backend_get_extent (GeglTileBackend *tile_backend)
312 {
313   return tile_backend->priv->extent;
314 }
315 
316 GeglTileSource *
gegl_tile_backend_peek_storage(GeglTileBackend * backend)317 gegl_tile_backend_peek_storage (GeglTileBackend *backend)
318 {
319   return backend->priv->storage;
320 }
321 
322 void
gegl_tile_backend_set_flush_on_destroy(GeglTileBackend * tile_backend,gboolean flush_on_destroy)323 gegl_tile_backend_set_flush_on_destroy (GeglTileBackend *tile_backend,
324                                         gboolean         flush_on_destroy)
325 {
326   tile_backend->priv->flush_on_destroy = flush_on_destroy;
327 }
328 
329 gboolean
gegl_tile_backend_get_flush_on_destroy(GeglTileBackend * tile_backend)330 gegl_tile_backend_get_flush_on_destroy (GeglTileBackend *tile_backend)
331 {
332   return tile_backend->priv->flush_on_destroy;
333 }
334 
335 gpointer
gegl_tile_backend_command(GeglTileBackend * backend,GeglTileCommand command,gint x,gint y,gint z,gpointer data)336 gegl_tile_backend_command (GeglTileBackend *backend,
337                            GeglTileCommand  command,
338                            gint             x,
339                            gint             y,
340                            gint             z,
341                            gpointer         data)
342 {
343   /* we've been called during the tile_command() test, which means the backend
344    * is post-0.4.8 compatible.  replace the shim with the original handler.
345    */
346   if (backend->priv->command)
347     {
348       GeglTileSource *source = GEGL_TILE_SOURCE (backend);
349 
350       source->command        = backend->priv->command;
351       backend->priv->command = NULL;
352     }
353 
354   return _gegl_tile_backend_command (backend, command, x, y, z, data);
355 }
356 
357 void
gegl_tile_backend_unlink_swap(gchar * path)358 gegl_tile_backend_unlink_swap (gchar *path)
359 {
360   gchar *dirname = g_path_get_dirname (path);
361 
362   /* Ensure we delete only files in our known swap directory for safety. */
363   if (g_file_test (path, G_FILE_TEST_EXISTS) &&
364       g_strcmp0 (dirname, gegl_buffer_config()->swap) == 0)
365     g_unlink (path);
366 
367   g_free (dirname);
368 }
369