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 = &empty;
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 = &empty;
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 = &rect;
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, &copy, 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                                        &copy);
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