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