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 this library; if not, see <https://www.gnu.org/licenses/>.
15 *
16 * Copyright 2006-2008 Øyvind Kolås <pippin@gimp.org>
17 */
18
19 #include "config.h"
20
21 #include <math.h>
22 #include <string.h>
23 #include <errno.h>
24 #include <stdlib.h>
25
26 #include <sys/types.h>
27 #ifdef HAVE_UNISTD_H
28 #include <unistd.h>
29 #endif
30
31 #include <glib-object.h>
32 #include <glib/gstdio.h>
33
34 #include "gegl-debug.h"
35 #include "gegl-buffer.h"
36 #include "gegl-buffer-types.h"
37 #include "gegl-buffer-config.h"
38 #include "gegl-buffer-private.h"
39 #include "gegl-rectangle.h"
40 #include "gegl-tile-handler-cache.h"
41 #include "gegl-tile-handler-private.h"
42 #include "gegl-tile-storage.h"
43 #include "gegl-tile-backend-file.h"
44 #include "gegl-tile-backend-swap.h"
45 #include "gegl-tile-backend-ram.h"
46 #include "gegl-buffer-formats.h"
47
48 #ifdef GEGL_ENABLE_DEBUG
49 #define DEBUG_ALLOCATIONS (gegl_debug_flags & GEGL_DEBUG_BUFFER_ALLOC)
50 #endif
51
52 #ifdef HAVE_EXECINFO_H
53 #include <execinfo.h>
54 #endif
55
56
57 G_DEFINE_TYPE (GeglBuffer, gegl_buffer, GEGL_TYPE_TILE_HANDLER)
58
59 static GObjectClass * parent_class = NULL;
60
61 enum
62 {
63 PROP_0,
64 PROP_X,
65 PROP_Y,
66 PROP_WIDTH,
67 PROP_HEIGHT,
68 PROP_SHIFT_X,
69 PROP_SHIFT_Y,
70 PROP_ABYSS_X,
71 PROP_ABYSS_Y,
72 PROP_ABYSS_WIDTH,
73 PROP_ABYSS_HEIGHT,
74 PROP_TILE_WIDTH,
75 PROP_TILE_HEIGHT,
76 PROP_FORMAT,
77 PROP_PX_SIZE,
78 PROP_PIXELS,
79 PROP_PATH,
80 PROP_BACKEND,
81 PROP_INITIALIZED
82 };
83
84 enum
85 {
86 CHANGED,
87 LAST_SIGNAL
88 };
89
90 static guint gegl_buffer_signals[LAST_SIGNAL] = { 0 };
91
92 static void
gegl_buffer_get_property(GObject * gobject,guint property_id,GValue * value,GParamSpec * pspec)93 gegl_buffer_get_property (GObject *gobject,
94 guint property_id,
95 GValue *value,
96 GParamSpec *pspec)
97 {
98 GeglBuffer *buffer = GEGL_BUFFER (gobject);
99
100 switch (property_id)
101 {
102 case PROP_WIDTH:
103 g_value_set_int (value, buffer->extent.width);
104 break;
105
106 case PROP_HEIGHT:
107 g_value_set_int (value, buffer->extent.height);
108 break;
109 case PROP_TILE_WIDTH:
110 g_value_set_int (value, buffer->tile_width);
111 break;
112
113 case PROP_TILE_HEIGHT:
114 g_value_set_int (value, buffer->tile_height);
115 break;
116
117 case PROP_PATH:
118 g_value_set_string (value, buffer->path);
119 break;
120
121 case PROP_PIXELS:
122 g_value_set_int (value, buffer->extent.width * buffer->extent.height);
123 break;
124
125 case PROP_PX_SIZE:
126 g_value_set_int (value, buffer->tile_storage->px_size);
127 break;
128
129 case PROP_FORMAT:
130 g_value_set_pointer (value, (gpointer) gegl_buffer_get_format (buffer));
131 break;
132
133 case PROP_BACKEND:
134 g_value_set_object (value, buffer->backend);
135 break;
136
137 case PROP_X:
138 g_value_set_int (value, buffer->extent.x);
139 break;
140
141 case PROP_Y:
142 g_value_set_int (value, buffer->extent.y);
143 break;
144
145 case PROP_SHIFT_X:
146 g_value_set_int (value, buffer->shift_x);
147 break;
148
149 case PROP_SHIFT_Y:
150 g_value_set_int (value, buffer->shift_y);
151 break;
152
153 case PROP_ABYSS_X:
154 g_value_set_int (value, buffer->abyss.x);
155 break;
156
157 case PROP_ABYSS_Y:
158 g_value_set_int (value, buffer->abyss.y);
159 break;
160
161 case PROP_ABYSS_WIDTH:
162 g_value_set_int (value, buffer->abyss.width);
163 break;
164
165 case PROP_ABYSS_HEIGHT:
166 g_value_set_int (value, buffer->abyss.height);
167 break;
168
169 case PROP_INITIALIZED:
170 g_value_set_boolean (value, buffer->initialized);
171 break;
172
173 default:
174 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, property_id, pspec);
175 break;
176 }
177 }
178
179 static void
gegl_buffer_set_property(GObject * gobject,guint property_id,const GValue * value,GParamSpec * pspec)180 gegl_buffer_set_property (GObject *gobject,
181 guint property_id,
182 const GValue *value,
183 GParamSpec *pspec)
184 {
185 GeglBuffer *buffer = GEGL_BUFFER (gobject);
186
187 switch (property_id)
188 {
189 case PROP_X:
190 buffer->extent.x = g_value_get_int (value);
191 break;
192
193 case PROP_Y:
194 buffer->extent.y = g_value_get_int (value);
195 break;
196
197 case PROP_WIDTH:
198 buffer->extent.width = g_value_get_int (value);
199 break;
200
201 case PROP_HEIGHT:
202 buffer->extent.height = g_value_get_int (value);
203 break;
204
205 case PROP_TILE_HEIGHT:
206 buffer->tile_height = g_value_get_int (value);
207 break;
208
209 case PROP_TILE_WIDTH:
210 buffer->tile_width = g_value_get_int (value);
211 break;
212
213 case PROP_PATH:
214 if (buffer->path)
215 g_free (buffer->path);
216 buffer->path = g_value_dup_string (value);
217 break;
218
219 case PROP_SHIFT_X:
220 buffer->shift_x = g_value_get_int (value);
221 break;
222
223 case PROP_SHIFT_Y:
224 buffer->shift_y = g_value_get_int (value);
225 break;
226
227 case PROP_ABYSS_X:
228 buffer->abyss.x = g_value_get_int (value);
229 break;
230
231 case PROP_ABYSS_Y:
232 buffer->abyss.y = g_value_get_int (value);
233 break;
234
235 case PROP_ABYSS_WIDTH:
236 buffer->abyss.width = g_value_get_int (value);
237 break;
238
239 case PROP_ABYSS_HEIGHT:
240 buffer->abyss.height = g_value_get_int (value);
241 break;
242
243 case PROP_FORMAT:
244 /* Do not set to NULL even if asked to do so by a non-overridden
245 * value, this is needed since a default value can not be specified
246 * for a gpointer paramspec
247 */
248 if (g_value_get_pointer (value))
249 {
250 const Babl *format = g_value_get_pointer (value);
251 /* XXX: need to check if the internal format matches, should
252 * perhaps do different things here depending on whether
253 * we are during construction or not
254 */
255 if (buffer->soft_format)
256 {
257 gegl_buffer_set_format (buffer, format);
258 }
259 else
260 {
261 buffer->format = format;
262 }
263 }
264 break;
265
266 case PROP_BACKEND:
267 if (buffer->backend)
268 g_object_unref (buffer->backend);
269 buffer->backend = g_value_dup_object (value);
270 break;
271
272 case PROP_INITIALIZED:
273 buffer->initialized = g_value_get_boolean (value);
274 break;
275
276 default:
277 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, property_id, pspec);
278 break;
279 }
280 }
281
282 #ifdef GEGL_ENABLE_DEBUG
283 static GMutex allocated_buffers_mutex;
284 static GList *allocated_buffers_list = NULL;
285 #endif
286 static volatile gint allocated_buffers = 0;
287 static volatile gint de_allocated_buffers = 0;
288
289
290
291 /* this should only be possible if this buffer matches all the buffers down to
292 * storage, all of those parent buffers would change size as well, no tiles
293 * would be voided as a result of changing the extent.
294 */
295 gboolean
gegl_buffer_set_extent(GeglBuffer * buffer,const GeglRectangle * extent)296 gegl_buffer_set_extent (GeglBuffer *buffer,
297 const GeglRectangle *extent)
298 {
299 g_return_val_if_fail (GEGL_IS_BUFFER (buffer), FALSE);
300
301 buffer->extent = *extent;
302
303 if (buffer->backend)
304 gegl_tile_backend_set_extent (buffer->backend, &buffer->extent);
305
306 if (buffer->abyss_tracks_extent)
307 buffer->abyss = *extent;
308
309 return TRUE;
310 }
311
312 gboolean
gegl_buffer_set_abyss(GeglBuffer * buffer,const GeglRectangle * abyss)313 gegl_buffer_set_abyss (GeglBuffer *buffer,
314 const GeglRectangle *abyss)
315 {
316 g_return_val_if_fail (GEGL_IS_BUFFER (buffer), FALSE);
317
318 buffer->abyss = *abyss;
319
320 return TRUE;
321 }
322
323 void
gegl_buffer_stats(void)324 gegl_buffer_stats (void)
325 {
326 g_warning ("Buffer statistics: allocated:%i deallocated:%i balance:%i",
327 allocated_buffers, de_allocated_buffers, allocated_buffers - de_allocated_buffers);
328 }
329
330 gint
gegl_buffer_leaks(void)331 gegl_buffer_leaks (void)
332 {
333 #ifdef GEGL_ENABLE_DEBUG
334 if (DEBUG_ALLOCATIONS)
335 {
336 GList *leaked_buffer = NULL;
337
338 g_mutex_lock (&allocated_buffers_mutex);
339
340 for (leaked_buffer = allocated_buffers_list;
341 leaked_buffer != NULL;
342 leaked_buffer = leaked_buffer->next)
343 {
344 GeglBuffer *buffer = GEGL_BUFFER (leaked_buffer->data);
345
346 #ifdef HAVE_EXECINFO_H
347 g_printerr ("\n"
348 "Leaked buffer allocation stack trace:\n");
349 backtrace_symbols_fd (buffer->alloc_stack_trace, buffer->alloc_stack_size, fileno (stderr));
350 putc ('\n', stderr);
351 #endif
352 }
353
354 g_list_free (allocated_buffers_list);
355 allocated_buffers_list = NULL;
356
357 g_mutex_unlock (&allocated_buffers_mutex);
358 }
359 #endif
360
361 return allocated_buffers - de_allocated_buffers;
362 }
363
364 void
_gegl_buffer_drop_hot_tile(GeglBuffer * buffer)365 _gegl_buffer_drop_hot_tile (GeglBuffer *buffer)
366 {
367 GeglTileStorage *storage = buffer->tile_storage;
368 GeglTile *tile;
369
370 tile = gegl_tile_storage_steal_hot_tile (storage);
371
372 if (tile)
373 gegl_tile_unref (tile);
374 }
375
376 static void
gegl_buffer_dispose(GObject * object)377 gegl_buffer_dispose (GObject *object)
378 {
379 GeglBuffer *buffer = GEGL_BUFFER (object);
380 GeglTileHandler *handler = GEGL_TILE_HANDLER (object);
381
382 if (gegl_buffer_ext_flush)
383 gegl_buffer_ext_flush (buffer, NULL);
384
385 if (GEGL_IS_TILE_STORAGE (handler->source))
386 {
387 GeglTileBackend *backend = gegl_buffer_backend (buffer);
388
389 if (gegl_tile_backend_get_flush_on_destroy (backend))
390 gegl_buffer_flush (buffer);
391 }
392
393 _gegl_buffer_drop_hot_tile (buffer);
394
395 if (buffer->backend)
396 {
397 g_object_unref (buffer->backend);
398 buffer->backend = NULL;
399 }
400
401 G_OBJECT_CLASS (parent_class)->dispose (object);
402 }
403
404 static void
gegl_buffer_finalize(GObject * object)405 gegl_buffer_finalize (GObject *object)
406 {
407 #ifdef GEGL_ENABLE_DEBUG
408 if (DEBUG_ALLOCATIONS)
409 {
410 g_mutex_lock (&allocated_buffers_mutex);
411 allocated_buffers_list = g_list_remove (allocated_buffers_list, object);
412 g_mutex_unlock (&allocated_buffers_mutex);
413 g_free (GEGL_BUFFER (object)->alloc_stack_trace);
414 }
415 #endif
416
417 g_free (GEGL_BUFFER (object)->path);
418 g_atomic_int_inc (&de_allocated_buffers);
419 G_OBJECT_CLASS (parent_class)->finalize (object);
420 }
421
422
423 GeglTileBackend *
gegl_buffer_backend2(GeglBuffer * buffer)424 gegl_buffer_backend2 (GeglBuffer *buffer)
425 {
426 GeglTileSource *tmp = GEGL_TILE_SOURCE (buffer);
427
428 while (tmp && !GEGL_IS_TILE_BACKEND (tmp))
429 tmp = GEGL_TILE_HANDLER (tmp)->source;
430
431 return (GeglTileBackend *) tmp;
432 }
433
434 GeglTileBackend *
gegl_buffer_backend(GeglBuffer * buffer)435 gegl_buffer_backend (GeglBuffer *buffer)
436 {
437 GeglTileBackend *tmp;
438
439 if (G_LIKELY (buffer->backend))
440 return buffer->backend;
441
442 tmp = gegl_buffer_backend2 (buffer);
443
444 if (tmp)
445 buffer->backend = g_object_ref (tmp);
446
447 return tmp;
448 }
449
450 static GeglTileStorage *
gegl_buffer_tile_storage(GeglBuffer * buffer)451 gegl_buffer_tile_storage (GeglBuffer *buffer)
452 {
453 GeglTileSource *tmp = GEGL_TILE_SOURCE (buffer);
454
455 while (tmp && !GEGL_IS_TILE_STORAGE (tmp))
456 tmp = GEGL_TILE_HANDLER (tmp)->source;
457
458 g_warn_if_fail (tmp);
459
460 return (GeglTileStorage *) tmp;
461 }
462
463 static void
gegl_buffer_storage_changed(GeglTileStorage * storage,const GeglRectangle * rect,gpointer userdata)464 gegl_buffer_storage_changed (GeglTileStorage *storage,
465 const GeglRectangle *rect,
466 gpointer userdata)
467 {
468 gegl_buffer_emit_changed_signal (GEGL_BUFFER (userdata), rect);
469 }
470
471 static GObject *
gegl_buffer_constructor(GType type,guint n_params,GObjectConstructParam * params)472 gegl_buffer_constructor (GType type,
473 guint n_params,
474 GObjectConstructParam *params)
475 {
476 GObject *object;
477 GeglBuffer *buffer;
478 GeglTileBackend *backend;
479 GeglTileHandler *handler;
480 GeglTileSource *source;
481
482 object = G_OBJECT_CLASS (parent_class)->constructor (type, n_params, params);
483
484 buffer = GEGL_BUFFER (object);
485 handler = GEGL_TILE_HANDLER (object);
486 source = handler->source;
487 backend = gegl_buffer_backend (buffer);
488
489 if (source)
490 {
491 if (GEGL_IS_TILE_STORAGE (source))
492 {
493 GeglTileStorage *src_storage = GEGL_TILE_STORAGE (source);
494 buffer->format = src_storage->format;
495 buffer->tile_width = src_storage->tile_width;
496 buffer->tile_height = src_storage->tile_height;
497 }
498 else if (GEGL_IS_BUFFER (source))
499 {
500 GeglBuffer *src_buffer = GEGL_BUFFER (source);
501 buffer->format = src_buffer->format;
502 buffer->soft_format = src_buffer->soft_format;
503 buffer->tile_width = src_buffer->tile_width;
504 buffer->tile_height = src_buffer->tile_height;
505 }
506 }
507 else
508 {
509 if (buffer->backend)
510 {
511 backend = buffer->backend;
512
513 buffer->format = gegl_tile_backend_get_format (backend);
514 buffer->tile_width = gegl_tile_backend_get_tile_width (backend);
515 buffer->tile_height = gegl_tile_backend_get_tile_height (backend);
516
517 if (buffer->path)
518 g_free (buffer->path);
519
520 if (GEGL_IS_TILE_BACKEND_FILE (backend))
521 g_object_get (backend, "path", &buffer->path, NULL);
522 else
523 buffer->path = NULL;
524 }
525 else
526 {
527 gboolean use_ram = FALSE;
528 const char *maybe_path = NULL;
529
530 if (!buffer->format)
531 {
532 g_warning ("Buffer constructed without format, assuming RGBA float");
533 buffer->format = babl_format ("RGBA float");
534 }
535
536 /* make a new backend & storage */
537
538 if (buffer->path)
539 maybe_path = buffer->path;
540 else
541 maybe_path = gegl_buffer_config ()->swap;
542
543 if (maybe_path)
544 use_ram = g_ascii_strcasecmp (maybe_path, "ram") == 0;
545 else
546 use_ram = TRUE;
547
548 if (use_ram == TRUE)
549 {
550 backend = g_object_new (GEGL_TYPE_TILE_BACKEND_RAM,
551 "tile-width", buffer->tile_width,
552 "tile-height", buffer->tile_height,
553 "format", buffer->format,
554 NULL);
555 }
556 else if (buffer->path)
557 {
558 backend = g_object_new (GEGL_TYPE_TILE_BACKEND_FILE,
559 "tile-width", buffer->tile_width,
560 "tile-height", buffer->tile_height,
561 "format", buffer->format,
562 "path", buffer->path,
563 NULL);
564
565 /* Re-inherit values in case path pointed to an existing buffer */
566 buffer->format = gegl_tile_backend_get_format (backend);
567 buffer->tile_width = gegl_tile_backend_get_tile_width (backend);
568 buffer->tile_height = gegl_tile_backend_get_tile_height (backend);
569
570 if (buffer->extent.width == -1 || buffer->extent.height == -1)
571 buffer->extent = gegl_tile_backend_get_extent (backend);
572 }
573 else
574 {
575 backend = g_object_new (GEGL_TYPE_TILE_BACKEND_SWAP,
576 "tile-width", buffer->tile_width,
577 "tile-height", buffer->tile_height,
578 "format", buffer->format,
579 NULL);
580 }
581
582 buffer->backend = backend;
583 }
584
585 source = GEGL_TILE_SOURCE (gegl_tile_storage_new (backend,
586 buffer->initialized));
587 gegl_tile_handler_set_source ((GeglTileHandler*)(buffer), source);
588 g_object_unref (source);
589 }
590
591 /* Connect to the changed signal of source, this is used by some backends
592 * (e.g. File) to notify of outside changes to the buffer.
593 */
594 if (GEGL_IS_TILE_STORAGE (source))
595 {
596 g_signal_connect (source, "changed",
597 G_CALLBACK (gegl_buffer_storage_changed),
598 buffer);
599 }
600
601 g_assert (backend);
602
603 if (buffer->extent.width == -1 ||
604 buffer->extent.height == -1) /* no specified extents,
605 inheriting from source */
606 {
607 if (GEGL_IS_BUFFER (source))
608 {
609 buffer->extent.x = GEGL_BUFFER (source)->extent.x - buffer->shift_x;
610 buffer->extent.y = GEGL_BUFFER (source)->extent.y - buffer->shift_y;
611 buffer->extent.width = GEGL_BUFFER (source)->extent.width;
612 buffer->extent.height = GEGL_BUFFER (source)->extent.height;
613 }
614 else
615 {
616 buffer->extent.x = 0;
617 buffer->extent.y = 0;
618 buffer->extent.width = 0;
619 buffer->extent.height = 0;
620 }
621 }
622
623 buffer->abyss_tracks_extent = FALSE;
624
625 if (buffer->abyss.width == 0 &&
626 buffer->abyss.height == 0 &&
627 buffer->abyss.x == 0 &&
628 buffer->abyss.y == 0) /* 0 sized extent == inherit buffer extent
629 */
630 {
631 buffer->abyss.x = buffer->extent.x;
632 buffer->abyss.y = buffer->extent.y;
633 buffer->abyss.width = buffer->extent.width;
634 buffer->abyss.height = buffer->extent.height;
635 buffer->abyss_tracks_extent = TRUE;
636 }
637 else if (buffer->abyss.width == 0 &&
638 buffer->abyss.height == 0)
639 {
640 g_warning ("peculiar abyss dimensions: %i,%i %ix%i",
641 buffer->abyss.x,
642 buffer->abyss.y,
643 buffer->abyss.width,
644 buffer->abyss.height);
645 }
646 else if (buffer->abyss.width == -1 ||
647 buffer->abyss.height == -1)
648 {
649 buffer->abyss.x = GEGL_BUFFER (source)->abyss.x - buffer->shift_x;
650 buffer->abyss.y = GEGL_BUFFER (source)->abyss.y - buffer->shift_y;
651 buffer->abyss.width = GEGL_BUFFER (source)->abyss.width;
652 buffer->abyss.height = GEGL_BUFFER (source)->abyss.height;
653 }
654
655 /* intersect our own abyss with parent's abyss if it exists
656 */
657 if (GEGL_IS_BUFFER (source))
658 {
659 GeglBuffer *source_buf = GEGL_BUFFER (source);
660 GeglRectangle parent;
661 GeglRectangle request;
662 GeglRectangle self;
663
664 parent.x = source_buf->abyss.x - buffer->shift_x;
665 parent.y = source_buf->abyss.y - buffer->shift_y;
666 parent.width = source_buf->abyss.width;
667 parent.height = source_buf->abyss.height;
668
669 request.x = buffer->abyss.x;
670 request.y = buffer->abyss.y;
671 request.width = buffer->abyss.width;
672 request.height = buffer->abyss.height;
673
674 gegl_rectangle_intersect (&self, &parent, &request);
675
676 /* Don't have the abyss track the extent if the intersection is
677 * not the entire extent. Otherwise, setting the extent identical
678 * to itself could suddenly make the abyss bigger. */
679 if (buffer->abyss_tracks_extent &&
680 (buffer->extent.x != self.x ||
681 buffer->extent.y != self.y ||
682 buffer->extent.width != self.width ||
683 buffer->extent.height != self.height) )
684 {
685 buffer->abyss_tracks_extent = FALSE;
686 }
687
688 buffer->abyss.x = self.x;
689 buffer->abyss.y = self.y;
690 buffer->abyss.width = self.width;
691 buffer->abyss.height = self.height;
692
693 /* compute our own total shift <- this should probably happen
694 * approximatly first */
695 buffer->shift_x += source_buf->shift_x;
696 buffer->shift_y += source_buf->shift_y;
697 }
698
699 buffer->tile_storage = gegl_buffer_tile_storage (buffer);
700
701 _gegl_tile_handler_set_tile_storage (handler, buffer->tile_storage);
702 _gegl_tile_handler_set_cache (handler, buffer->tile_storage->cache);
703
704 /* intialize the soft format to be equivalent to the actual
705 * format, unless the soft format was copied from a source buffer
706 */
707 if (! buffer->soft_format)
708 buffer->soft_format = buffer->format;
709
710 g_assert (buffer->tile_width == buffer->tile_storage->tile_width);
711 g_assert (buffer->tile_height == buffer->tile_storage->tile_height);
712
713 return object;
714 }
715
716 static GeglTile *
gegl_buffer_get_tile_int(GeglTileSource * source,gint x,gint y,gint z)717 gegl_buffer_get_tile_int (GeglTileSource *source,
718 gint x,
719 gint y,
720 gint z)
721 {
722 GeglTileHandler *handler = (GeglTileHandler*) (source);
723 GeglTile *tile = NULL;
724
725 source = handler->source;
726
727 if (source)
728 tile = gegl_tile_source_get_tile (source, x, y, z);
729 else
730 g_assert (0);
731
732 if (tile)
733 {
734 GeglBuffer *buffer = (GeglBuffer*) handler;
735
736 /* storing information in tile, to enable the dispose function of the
737 * tile instance to "hook" back to the storage with correct
738 * coordinates.
739 */
740 {
741 if (!tile->tile_storage)
742 {
743 gegl_tile_lock (tile);
744 tile->tile_storage = buffer->tile_storage;
745 gegl_tile_unlock (tile);
746 tile->rev--;
747 }
748 tile->x = x;
749 tile->y = y;
750 tile->z = z;
751 }
752 }
753
754 return tile;
755 }
756
757
758 static gpointer
gegl_buffer_command(GeglTileSource * source,GeglTileCommand command,gint x,gint y,gint z,gpointer data)759 gegl_buffer_command (GeglTileSource *source,
760 GeglTileCommand command,
761 gint x,
762 gint y,
763 gint z,
764 gpointer data)
765 {
766 GeglTileHandler *handler = GEGL_TILE_HANDLER (source);
767
768 switch (command)
769 {
770 case GEGL_TILE_GET:
771 return gegl_buffer_get_tile_int (source, x, y, z);
772 default:
773 return gegl_tile_handler_source_command (handler, command, x, y, z, data);
774 }
775 }
776
777 static void
gegl_buffer_class_init(GeglBufferClass * class)778 gegl_buffer_class_init (GeglBufferClass *class)
779 {
780 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
781
782 parent_class = g_type_class_peek_parent (class);
783
784 gobject_class->dispose = gegl_buffer_dispose;
785 gobject_class->finalize = gegl_buffer_finalize;
786 gobject_class->constructor = gegl_buffer_constructor;
787 gobject_class->set_property = gegl_buffer_set_property;
788 gobject_class->get_property = gegl_buffer_get_property;
789
790 g_object_class_install_property (gobject_class, PROP_PX_SIZE,
791 g_param_spec_int ("px-size", "pixel-size", "size of a single pixel in bytes.",
792 0, G_MAXINT, 0,
793 G_PARAM_READABLE |
794 G_PARAM_STATIC_STRINGS));
795 g_object_class_install_property (gobject_class, PROP_PIXELS,
796 g_param_spec_int ("pixels", "pixels", "total amount of pixels in image (width x height)",
797 0, G_MAXINT, 0,
798 G_PARAM_READABLE |
799 G_PARAM_STATIC_STRINGS));
800 g_object_class_install_property (gobject_class, PROP_WIDTH,
801 g_param_spec_int ("width", "width", "pixel width of buffer",
802 -1, G_MAXINT, -1,
803 G_PARAM_READWRITE |
804 G_PARAM_CONSTRUCT |
805 G_PARAM_STATIC_STRINGS));
806 g_object_class_install_property (gobject_class, PROP_HEIGHT,
807 g_param_spec_int ("height", "height", "pixel height of buffer",
808 -1, G_MAXINT, -1,
809 G_PARAM_READWRITE |
810 G_PARAM_CONSTRUCT |
811 G_PARAM_STATIC_STRINGS));
812 g_object_class_install_property (gobject_class, PROP_X,
813 g_param_spec_int ("x", "x", "local origin's offset relative to source origin",
814 G_MININT / 2, G_MAXINT / 2, 0,
815 G_PARAM_READWRITE |
816 G_PARAM_CONSTRUCT |
817 G_PARAM_STATIC_STRINGS));
818 g_object_class_install_property (gobject_class, PROP_Y,
819 g_param_spec_int ("y", "y", "local origin's offset relative to source origin",
820 G_MININT / 2, G_MAXINT / 2, 0,
821 G_PARAM_READWRITE |
822 G_PARAM_CONSTRUCT |
823 G_PARAM_STATIC_STRINGS));
824 g_object_class_install_property (gobject_class, PROP_ABYSS_WIDTH,
825 g_param_spec_int ("abyss-width", "abyss-width", "pixel width of abyss",
826 -1, G_MAXINT, 0,
827 G_PARAM_READWRITE |
828 G_PARAM_CONSTRUCT_ONLY |
829 G_PARAM_STATIC_STRINGS));
830 g_object_class_install_property (gobject_class, PROP_ABYSS_HEIGHT,
831 g_param_spec_int ("abyss-height", "abyss-height", "pixel height of abyss",
832 -1, G_MAXINT, 0,
833 G_PARAM_READWRITE |
834 G_PARAM_CONSTRUCT_ONLY |
835 G_PARAM_STATIC_STRINGS));
836 g_object_class_install_property (gobject_class, PROP_ABYSS_X,
837 g_param_spec_int ("abyss-x", "abyss-x", "",
838 G_MININT / 2, G_MAXINT / 2, 0,
839 G_PARAM_READWRITE |
840 G_PARAM_CONSTRUCT_ONLY |
841 G_PARAM_STATIC_STRINGS));
842 g_object_class_install_property (gobject_class, PROP_ABYSS_Y,
843 g_param_spec_int ("abyss-y", "abyss-y", "",
844 G_MININT / 2, G_MAXINT / 2, 0,
845 G_PARAM_READWRITE |
846 G_PARAM_CONSTRUCT_ONLY |
847 G_PARAM_STATIC_STRINGS));
848 g_object_class_install_property (gobject_class, PROP_SHIFT_X,
849 g_param_spec_int ("shift-x", "shift-x", "",
850 G_MININT / 2, G_MAXINT / 2, 0,
851 G_PARAM_READWRITE |
852 G_PARAM_CONSTRUCT_ONLY |
853 G_PARAM_STATIC_STRINGS));
854 g_object_class_install_property (gobject_class, PROP_SHIFT_Y,
855 g_param_spec_int ("shift-y", "shift-y", "",
856 G_MININT / 2, G_MAXINT / 2, 0,
857 G_PARAM_READWRITE |
858 G_PARAM_CONSTRUCT_ONLY |
859 G_PARAM_STATIC_STRINGS));
860 g_object_class_install_property (gobject_class, PROP_FORMAT,
861 g_param_spec_pointer ("format", "format", "babl format",
862 G_PARAM_READWRITE |
863 G_PARAM_CONSTRUCT |
864 G_PARAM_STATIC_STRINGS));
865
866 g_object_class_install_property (gobject_class, PROP_BACKEND,
867 g_param_spec_object ("backend", "backend", "A custom tile-backend instance to use",
868 GEGL_TYPE_TILE_BACKEND,
869 G_PARAM_READWRITE |
870 G_PARAM_CONSTRUCT_ONLY |
871 G_PARAM_STATIC_STRINGS));
872
873 g_object_class_install_property (gobject_class, PROP_TILE_HEIGHT,
874 g_param_spec_int ("tile-height", "tile-height", "height of a tile",
875 -1, G_MAXINT, gegl_buffer_config()->tile_height,
876 G_PARAM_READWRITE |
877 G_PARAM_CONSTRUCT_ONLY |
878 G_PARAM_STATIC_STRINGS));
879
880 g_object_class_install_property (gobject_class, PROP_TILE_WIDTH,
881 g_param_spec_int ("tile-width", "tile-width", "width of a tile",
882 -1, G_MAXINT, gegl_buffer_config()->tile_width,
883 G_PARAM_READWRITE |
884 G_PARAM_CONSTRUCT_ONLY |
885 G_PARAM_STATIC_STRINGS));
886
887 g_object_class_install_property (gobject_class, PROP_PATH,
888 g_param_spec_string ("path", "Path",
889 "URI to where the buffer is stored",
890 NULL,
891 G_PARAM_READWRITE |
892 G_PARAM_CONSTRUCT_ONLY |
893 G_PARAM_STATIC_STRINGS));
894
895 g_object_class_install_property (gobject_class, PROP_INITIALIZED,
896 g_param_spec_boolean ("initialized",
897 NULL, NULL,
898 TRUE,
899 G_PARAM_READWRITE |
900 G_PARAM_CONSTRUCT_ONLY |
901 G_PARAM_STATIC_STRINGS));
902
903 gegl_buffer_signals[CHANGED] =
904 g_signal_new ("changed",
905 G_TYPE_FROM_CLASS (gobject_class),
906 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
907 0, NULL, NULL, NULL,
908 G_TYPE_NONE, 1,
909 GEGL_TYPE_RECTANGLE);
910
911 }
912
913 #ifdef GEGL_ENABLE_DEBUG
914 #define MAX_N_FUNCTIONS 100
915 static void
gegl_buffer_set_alloc_stack(GeglBuffer * buffer)916 gegl_buffer_set_alloc_stack (GeglBuffer *buffer)
917 {
918 #ifdef HAVE_EXECINFO_H
919 if (DEBUG_ALLOCATIONS)
920 {
921 buffer->alloc_stack_trace = g_new (gpointer, MAX_N_FUNCTIONS);
922 buffer->alloc_stack_size = backtrace (buffer->alloc_stack_trace, MAX_N_FUNCTIONS);
923 }
924 #endif
925 }
926 #endif
927
928 void gegl_bt (void);
gegl_bt(void)929 void gegl_bt (void)
930 {
931 #ifdef GEGL_ENABLE_DEBUG
932 #ifndef HAVE_EXECINFO_H
933 g_print ("backtrace() not available for this platform\n");
934 #else
935 gpointer functions[MAX_N_FUNCTIONS];
936 int n_functions = 0;
937
938 n_functions = backtrace (functions, MAX_N_FUNCTIONS);
939 backtrace_symbols_fd (functions, n_functions, fileno (stderr));
940 putc ('\n', stderr);
941 #endif
942 #endif
943 }
944
945 static void
gegl_buffer_init(GeglBuffer * buffer)946 gegl_buffer_init (GeglBuffer *buffer)
947 {
948 buffer->tile_width = 128;
949 buffer->tile_height = 64;
950
951 ((GeglTileSource*)buffer)->command = gegl_buffer_command;
952
953 g_atomic_int_inc (&allocated_buffers);
954
955 #ifdef GEGL_ENABLE_DEBUG
956 if (DEBUG_ALLOCATIONS)
957 {
958 gegl_buffer_set_alloc_stack (buffer);
959 g_mutex_lock (&allocated_buffers_mutex);
960 allocated_buffers_list = g_list_prepend (allocated_buffers_list, buffer);
961 g_mutex_unlock (&allocated_buffers_mutex);
962 }
963 #endif
964 }
965
966
967 const GeglRectangle *
gegl_buffer_get_extent(GeglBuffer * buffer)968 gegl_buffer_get_extent (GeglBuffer *buffer)
969 {
970 g_return_val_if_fail (GEGL_IS_BUFFER (buffer), NULL);
971
972 return &(buffer->extent);
973 }
974
975 const GeglRectangle *
gegl_buffer_get_abyss(GeglBuffer * buffer)976 gegl_buffer_get_abyss (GeglBuffer *buffer)
977 {
978 g_return_val_if_fail (GEGL_IS_BUFFER (buffer), NULL);
979
980 return &(buffer->abyss);
981 }
982
983
984 GeglBuffer *
gegl_buffer_new_ram(const GeglRectangle * extent,const Babl * format)985 gegl_buffer_new_ram (const GeglRectangle *extent,
986 const Babl *format)
987 {
988 GeglRectangle empty={0,0,0,0};
989
990 if (extent == NULL)
991 extent = ∅
992
993 if (format == NULL)
994 format = gegl_babl_rgba_linear_float ();
995
996 return g_object_new (GEGL_TYPE_BUFFER,
997 "x", extent->x,
998 "y", extent->y,
999 "width", extent->width,
1000 "height", extent->height,
1001 "format", format,
1002 "path", "RAM",
1003 NULL);
1004 }
1005
1006 GeglBuffer *
gegl_buffer_new(const GeglRectangle * extent,const Babl * format)1007 gegl_buffer_new (const GeglRectangle *extent,
1008 const Babl *format)
1009 {
1010 GeglRectangle empty={0,0,0,0};
1011
1012 if (extent == NULL)
1013 extent = ∅
1014
1015 if (format == NULL)
1016 format = gegl_babl_rgba_linear_float ();
1017
1018 return g_object_new (GEGL_TYPE_BUFFER,
1019 "x", extent->x,
1020 "y", extent->y,
1021 "width", extent->width,
1022 "height", extent->height,
1023 "format", format,
1024 NULL);
1025 }
1026
1027 GeglBuffer *
gegl_buffer_new_for_backend(const GeglRectangle * extent,GeglTileBackend * backend)1028 gegl_buffer_new_for_backend (const GeglRectangle *extent,
1029 GeglTileBackend *backend)
1030 {
1031 GeglRectangle rect = { 0, 0, 0, 0 };
1032 const Babl *format;
1033
1034 /* if no extent is passed in inherit from backend */
1035 if (extent == NULL)
1036 {
1037 extent = ▭
1038 rect = gegl_tile_backend_get_extent (backend);
1039 /* if backend didnt have a rect, make it an infinite plane */
1040 if (gegl_rectangle_is_empty (extent))
1041 rect = gegl_rectangle_infinite_plane ();
1042 }
1043
1044 /* use the format of the backend */
1045 format = gegl_tile_backend_get_format (backend);
1046
1047 return g_object_new (GEGL_TYPE_BUFFER,
1048 "x", extent->x,
1049 "y", extent->y,
1050 "width", extent->width,
1051 "height", extent->height,
1052 "format", format,
1053 "backend", backend,
1054 NULL);
1055 }
1056
1057 void
gegl_buffer_add_handler(GeglBuffer * buffer,gpointer handler)1058 gegl_buffer_add_handler (GeglBuffer *buffer,
1059 gpointer handler)
1060 {
1061 gegl_tile_storage_add_handler (buffer->tile_storage, handler);
1062 }
1063
1064 void
gegl_buffer_remove_handler(GeglBuffer * buffer,gpointer handler)1065 gegl_buffer_remove_handler (GeglBuffer *buffer,
1066 gpointer handler)
1067 {
1068 gegl_tile_storage_remove_handler (buffer->tile_storage, handler);
1069 }
1070
1071 /* FIXME: this function needs optimizing, perhaps keep a pool
1072 * of GeglBuffer shells that can be adapted to the needs
1073 * on runtime, and recycling them through a hashtable?
1074 */
1075 GeglBuffer*
gegl_buffer_create_sub_buffer(GeglBuffer * buffer,const GeglRectangle * extent)1076 gegl_buffer_create_sub_buffer (GeglBuffer *buffer,
1077 const GeglRectangle *extent)
1078 {
1079 g_return_val_if_fail (GEGL_IS_BUFFER (buffer), NULL);
1080
1081 if (extent == NULL)
1082 extent = gegl_buffer_get_extent (buffer);
1083
1084 if (!extent || extent->width < 0 || extent->height < 0)
1085 {
1086 g_warning ("avoiding creating buffer of size: %ix%i returning an empty buffer instead.\n", extent->width, extent->height);
1087 return g_object_new (GEGL_TYPE_BUFFER,
1088 "source", buffer,
1089 "x", extent->x,
1090 "y", extent->y,
1091 "width", 0,
1092 "height", 0,
1093 NULL);
1094 }
1095
1096 return g_object_new (GEGL_TYPE_BUFFER,
1097 "source", buffer,
1098 "x", extent->x,
1099 "y", extent->y,
1100 "width", extent->width,
1101 "height", extent->height,
1102 NULL);
1103 }
1104
1105 const Babl *
gegl_buffer_get_format(GeglBuffer * buffer)1106 gegl_buffer_get_format (GeglBuffer *buffer)
1107 {
1108 g_return_val_if_fail (buffer, NULL);
1109
1110 if (buffer->soft_format)
1111 return buffer->soft_format;
1112 return buffer->format;
1113 }
1114
1115 const Babl *
gegl_buffer_set_format(GeglBuffer * buffer,const Babl * format)1116 gegl_buffer_set_format (GeglBuffer *buffer,
1117 const Babl *format)
1118 {
1119 if (format == NULL)
1120 {
1121 buffer->soft_format = buffer->format;
1122 return buffer->soft_format;
1123 }
1124 if (babl_format_get_bytes_per_pixel (format) ==
1125 babl_format_get_bytes_per_pixel (buffer->format))
1126 {
1127 buffer->soft_format = format;
1128 return buffer->soft_format;
1129 }
1130 g_warning ("tried to set format of different bpp on buffer\n");
1131 return NULL;
1132 }
1133
1134 gboolean
gegl_buffer_is_shared(GeglBuffer * buffer)1135 gegl_buffer_is_shared (GeglBuffer *buffer)
1136 {
1137 GeglTileBackend *backend = gegl_buffer_backend (buffer);
1138
1139 return backend->priv->shared;
1140 }
1141
1142 #ifndef GEGL_BUFFER_DISABLE_LOCKS
1143 gboolean
gegl_buffer_try_lock(GeglBuffer * buffer)1144 gegl_buffer_try_lock (GeglBuffer *buffer)
1145 {
1146 gboolean ret = TRUE;
1147
1148 if (gegl_buffer_is_shared (buffer))
1149 {
1150 GeglTileBackend *backend = gegl_buffer_backend (buffer);
1151
1152 g_rec_mutex_lock (&buffer->tile_storage->mutex);
1153 if (buffer->lock_count > 0)
1154 buffer->lock_count++;
1155 else if (gegl_tile_backend_file_try_lock (GEGL_TILE_BACKEND_FILE (backend)))
1156 buffer->lock_count++;
1157 else
1158 ret = FALSE;
1159 g_rec_mutex_unlock (&buffer->tile_storage->mutex);
1160 }
1161
1162 return ret;
1163 }
1164
1165 /* this locking is for synchronising shared buffers */
1166 gboolean
gegl_buffer_lock(GeglBuffer * buffer)1167 gegl_buffer_lock (GeglBuffer *buffer)
1168 {
1169 while (gegl_buffer_try_lock (buffer)==FALSE)
1170 {
1171 g_print ("waiting to aquire buffer..");
1172 g_usleep (100000);
1173 }
1174 return TRUE;
1175 }
1176
1177 gboolean
gegl_buffer_unlock(GeglBuffer * buffer)1178 gegl_buffer_unlock (GeglBuffer *buffer)
1179 {
1180 gboolean ret = TRUE;
1181
1182 if (gegl_buffer_is_shared (buffer))
1183 {
1184 GeglTileBackend *backend = gegl_buffer_backend (buffer);
1185
1186 g_rec_mutex_lock (&buffer->tile_storage->mutex);
1187
1188 buffer->lock_count--;
1189 g_assert (buffer->lock_count >= 0);
1190
1191 if (buffer->lock_count == 0)
1192 ret = gegl_tile_backend_file_unlock (GEGL_TILE_BACKEND_FILE (backend));
1193
1194 g_rec_mutex_unlock (&buffer->tile_storage->mutex);
1195 }
1196
1197 return ret;
1198 }
1199 #endif
1200
1201 gboolean
gegl_buffer_share_storage(GeglBuffer * buffer1,GeglBuffer * buffer2)1202 gegl_buffer_share_storage (GeglBuffer *buffer1,
1203 GeglBuffer *buffer2)
1204 {
1205 g_return_val_if_fail (GEGL_IS_BUFFER (buffer1), FALSE);
1206 g_return_val_if_fail (GEGL_IS_BUFFER (buffer2), FALSE);
1207
1208 return buffer1->tile_storage == buffer2->tile_storage;
1209 }
1210
1211 void
gegl_buffer_emit_changed_signal(GeglBuffer * buffer,const GeglRectangle * rect)1212 gegl_buffer_emit_changed_signal (GeglBuffer *buffer,
1213 const GeglRectangle *rect)
1214 {
1215 if (buffer->changed_signal_connections)
1216 {
1217 GeglRectangle copy;
1218
1219 if (rect == NULL)
1220 copy = *gegl_buffer_get_extent (buffer);
1221 else
1222 copy = *rect;
1223
1224 if (buffer->changed_signal_freeze_count == 0)
1225 {
1226 g_signal_emit (buffer, gegl_buffer_signals[CHANGED], 0, ©, NULL);
1227 }
1228 else
1229 {
1230 g_rec_mutex_lock (&buffer->tile_storage->mutex);
1231
1232 gegl_rectangle_bounding_box (&buffer->changed_signal_accumulator,
1233 &buffer->changed_signal_accumulator,
1234 ©);
1235
1236 g_rec_mutex_unlock (&buffer->tile_storage->mutex);
1237 }
1238 }
1239 }
1240
gegl_buffer_signal_connect(GeglBuffer * buffer,const char * detailed_signal,GCallback c_handler,gpointer data)1241 glong gegl_buffer_signal_connect (GeglBuffer *buffer,
1242 const char *detailed_signal,
1243 GCallback c_handler,
1244 gpointer data)
1245 {
1246 buffer->changed_signal_connections++;
1247 return g_signal_connect(buffer, detailed_signal, c_handler, data);
1248 }
1249
1250 void
gegl_buffer_freeze_changed(GeglBuffer * buffer)1251 gegl_buffer_freeze_changed (GeglBuffer *buffer)
1252 {
1253 g_return_if_fail (GEGL_IS_BUFFER (buffer));
1254
1255 if (buffer->changed_signal_freeze_count++ == 0)
1256 {
1257 buffer->changed_signal_accumulator.x = 0;
1258 buffer->changed_signal_accumulator.y = 0;
1259 buffer->changed_signal_accumulator.width = 0;
1260 buffer->changed_signal_accumulator.height = 0;
1261 }
1262 }
1263
1264 void
gegl_buffer_thaw_changed(GeglBuffer * buffer)1265 gegl_buffer_thaw_changed (GeglBuffer *buffer)
1266 {
1267 g_return_if_fail (GEGL_IS_BUFFER (buffer));
1268 g_return_if_fail (buffer->changed_signal_freeze_count > 0);
1269
1270 if (--buffer->changed_signal_freeze_count == 0 &&
1271 ! gegl_rectangle_is_empty (&buffer->changed_signal_accumulator))
1272 {
1273 gegl_buffer_emit_changed_signal (buffer,
1274 &buffer->changed_signal_accumulator);
1275 }
1276 }
1277
1278 GeglTile *
gegl_buffer_get_tile(GeglBuffer * buffer,gint x,gint y,gint z)1279 gegl_buffer_get_tile (GeglBuffer *buffer,
1280 gint x,
1281 gint y,
1282 gint z)
1283 {
1284 GeglTileSource *source = (GeglTileSource*)buffer;
1285 GeglTile *tile;
1286
1287 g_assert (source);
1288 {
1289 GeglTileStorage *tile_storage = buffer->tile_storage;
1290 g_assert (tile_storage);
1291
1292 g_rec_mutex_lock (&tile_storage->mutex);
1293
1294 tile = gegl_tile_source_command (source, GEGL_TILE_GET,
1295 x, y, z, NULL);
1296
1297 g_rec_mutex_unlock (&tile_storage->mutex);
1298 }
1299
1300 return tile;
1301 }
1302
1303 void (*gegl_tile_handler_cache_ext_flush) (void *cache, const GeglRectangle *rect)=NULL;
1304 void (*gegl_buffer_ext_flush) (GeglBuffer *buffer, const GeglRectangle *rect)=NULL;
1305 void (*gegl_buffer_ext_invalidate) (GeglBuffer *buffer, const GeglRectangle *rect)=NULL;
1306