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,2007 Øyvind Kolås <pippin@gimp.org>
17  */
18 #include "config.h"
19 #include <glib.h>
20 #include <glib-object.h>
21 #include <string.h>
22 
23 #include "gegl-buffer.h"
24 #include "gegl-buffer-private.h"
25 #include "gegl-tile-handler-empty.h"
26 
G_DEFINE_TYPE(GeglTileHandlerEmpty,gegl_tile_handler_empty,GEGL_TYPE_TILE_HANDLER)27 G_DEFINE_TYPE (GeglTileHandlerEmpty, gegl_tile_handler_empty,
28                GEGL_TYPE_TILE_HANDLER)
29 
30 static void
31 finalize (GObject *object)
32 {
33   GeglTileHandlerEmpty *empty = GEGL_TILE_HANDLER_EMPTY (object);
34 
35   if (empty->tile)
36     gegl_tile_unref (empty->tile);
37 
38   G_OBJECT_CLASS (gegl_tile_handler_empty_parent_class)->finalize (object);
39 }
40 
41 static GeglTile *
get_tile(GeglTileSource * gegl_tile_source,gint x,gint y,gint z)42 get_tile (GeglTileSource *gegl_tile_source,
43           gint            x,
44           gint            y,
45           gint            z)
46 {
47   GeglTileSource       *source = ((GeglTileHandler *) gegl_tile_source)->source;
48   GeglTileHandlerEmpty *empty  = (GeglTileHandlerEmpty *) gegl_tile_source;
49   GeglTile             *tile   = NULL;
50 
51   if (source)
52     tile = gegl_tile_source_get_tile (source, x, y, z);
53   if (tile)
54     return tile;
55 
56   if (!empty->tile)
57     {
58       gint tile_size = gegl_tile_backend_get_tile_size (empty->backend);
59       empty->tile    = gegl_tile_handler_empty_new_tile (tile_size);
60     }
61 
62   tile = gegl_tile_handler_dup_tile (GEGL_TILE_HANDLER (empty),
63                                      empty->tile, x, y, z);
64 
65   /* if empty tiles don't have to be zero-initialized, mark them as fully
66    * damaged, so that their data is not unnecessarily initialized when
67    * uncloned.
68    *
69    * we currently only do this for level-0 tiles, since it simplifies the rest
70    * of the code.
71    */
72   if (z == 0 && ! empty->initialized)
73     tile->damage = ~0ull;
74 
75   /* no need to store the tile, since we'll just create another empty tile on-
76    * demand if it's dropped.
77    */
78   gegl_tile_mark_as_stored (tile);
79 
80   return tile;
81 }
82 
83 static gpointer
gegl_tile_handler_empty_command(GeglTileSource * buffer,GeglTileCommand command,gint x,gint y,gint z,gpointer data)84 gegl_tile_handler_empty_command (GeglTileSource  *buffer,
85                                  GeglTileCommand  command,
86                                  gint             x,
87                                  gint             y,
88                                  gint             z,
89                                  gpointer         data)
90 {
91   if (command == GEGL_TILE_GET)
92     return get_tile (buffer, x, y, z);
93 
94   return gegl_tile_handler_source_command (buffer, command, x, y, z, data);
95 }
96 
97 static void
gegl_tile_handler_empty_class_init(GeglTileHandlerEmptyClass * klass)98 gegl_tile_handler_empty_class_init (GeglTileHandlerEmptyClass *klass)
99 {
100   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
101 
102   gobject_class->finalize = finalize;
103 }
104 
105 static void
gegl_tile_handler_empty_init(GeglTileHandlerEmpty * self)106 gegl_tile_handler_empty_init (GeglTileHandlerEmpty *self)
107 {
108   ((GeglTileSource *) self)->command = gegl_tile_handler_empty_command;
109 }
110 
111 GeglTileHandler *
gegl_tile_handler_empty_new(GeglTileBackend * backend,gboolean initialized)112 gegl_tile_handler_empty_new (GeglTileBackend *backend,
113                              gboolean         initialized)
114 {
115   GeglTileHandlerEmpty *empty = g_object_new (GEGL_TYPE_TILE_HANDLER_EMPTY, NULL);
116 
117   empty->backend     = backend;
118   empty->tile        = NULL;
119   empty->initialized = initialized;
120 
121   return (void*)empty;
122 }
123 
124 GeglTile *
gegl_tile_handler_empty_new_tile(gint tile_size)125 gegl_tile_handler_empty_new_tile (gint tile_size)
126 {
127   static GeglTile   *common_tile = NULL;
128   static const gint  common_empty_size = sizeof (gdouble) * 4 * 128 * 128;
129 
130   GeglTile *tile;
131 
132   if (tile_size > common_empty_size)
133     {
134       /* The tile size is too big to use the shared buffer */
135       tile = gegl_tile_new (tile_size);
136 
137       memset (gegl_tile_get_data (tile), 0x00, tile_size);
138       tile->is_zero_tile = TRUE;
139     }
140   else
141     {
142       if (!g_atomic_pointer_get (&common_tile) &&
143           g_once_init_enter (&common_tile))
144         {
145           GeglTile *allocated_tile = gegl_tile_new_bare ();
146           guchar *allocated_buffer = gegl_malloc (common_empty_size);
147           memset (allocated_buffer, 0x00, common_empty_size);
148 
149           allocated_tile->data           = allocated_buffer;
150           allocated_tile->destroy_notify = NULL;
151           allocated_tile->size           = common_empty_size;
152           allocated_tile->is_zero_tile   = TRUE;
153           allocated_tile->is_global_tile = TRUE;
154 
155           /* avoid counting duplicates of the empty tile towards the total
156            * cache size, both since this is unnecessary, and since they may
157            * have different sizes, which is inconsistent with the duplicate-
158            * tracking cache logic.
159            */
160           (*gegl_tile_n_cached_clones (allocated_tile))++;
161 
162           g_once_init_leave (&common_tile, allocated_tile);
163         }
164 
165       tile = gegl_tile_dup (common_tile);
166 
167       tile->size = tile_size;
168     }
169 
170   return tile;
171 }
172