LCOV - code coverage report
Current view: top level - shumate - shumate-vector-sprite-sheet.c (source / functions) Hit Total Coverage
Test: Code coverage Lines: 119 123 96.7 %
Date: 2024-05-11 21:41:31 Functions: 12 12 100.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 76 106 71.7 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * Copyright (C) 2022 James Westman <james@jwestman.net>
       3                 :            :  *
       4                 :            :  * This library is free software; you can redistribute it and/or
       5                 :            :  * modify it under the terms of the GNU Lesser General Public
       6                 :            :  * License as published by the Free Software Foundation; either
       7                 :            :  * version 2.1 of the License, or (at your option) any later version.
       8                 :            :  *
       9                 :            :  * This library is distributed in the hope that it will be useful,
      10                 :            :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      11                 :            :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      12                 :            :  * Lesser General Public License for more details.
      13                 :            :  *
      14                 :            :  * You should have received a copy of the GNU Lesser General Public
      15                 :            :  * License along with this library; if not, see <https://www.gnu.org/licenses/>.
      16                 :            :  */
      17                 :            : 
      18                 :            : #include "shumate-vector-sprite-sheet.h"
      19                 :            : #include "shumate-vector-renderer.h"
      20                 :            : 
      21                 :            : #ifdef SHUMATE_HAS_VECTOR_RENDERER
      22                 :            : #include <json-glib/json-glib.h>
      23                 :            : #include "vector/shumate-vector-utils-private.h"
      24                 :            : #endif
      25                 :            : 
      26                 :            : /**
      27                 :            :  * ShumateVectorSpriteSheet:
      28                 :            :  *
      29                 :            :  * A collection of [class@VectorSprite]s.
      30                 :            :  *
      31                 :            :  * Sprites are used as icons in symbols or as the pattern for a fill layer.
      32                 :            :  *
      33                 :            :  * Most MapLibre stylesheets provide their spritesheet as a PNG image and a JSON
      34                 :            :  * description of the sprites. This spritesheet can be added using
      35                 :            :  * [method@VectorSpriteSheet.add_page]. Sprites can also be added individually
      36                 :            :  * using [method@VectorSpriteSheet.add_sprite].
      37                 :            :  *
      38                 :            :  * Some map styles rely on application code to provide some or all of their
      39                 :            :  * sprites. This is supported using a fallback function, which can be set using
      40                 :            :  * [method@VectorSpriteSheet.set_fallback]. This function can generate sprites
      41                 :            :  * on demand. For example, it could load a symbolic icon from the [class@Gtk.IconTheme]
      42                 :            :  * or render a custom highway shield.
      43                 :            :  *
      44                 :            :  * ## HiDPI support
      45                 :            :  *
      46                 :            :  * Map styles should provide a double resolution spritesheet for high DPI
      47                 :            :  * displays. That spritesheet can be added as a separate page.
      48                 :            :  * The [class@VectorSpriteSheet] will pick the best sprites for the display's
      49                 :            :  * scale factor.
      50                 :            :  *
      51                 :            :  * If a fallback function is set, it receives the requested scale factor
      52                 :            :  * as an argument. It should use this to generate the sprite at the correct size.
      53                 :            :  * For example, if the scale factor is 2, the image should be twice as large
      54                 :            :  * (but the *sprite's* width and height should be the same).
      55                 :            :  *
      56                 :            :  * ## Thread Safety
      57                 :            :  *
      58                 :            :  * [class@VectorSpriteSheet] is thread safe.
      59                 :            :  *
      60                 :            :  * Since: 1.1
      61                 :            :  */
      62                 :            : 
      63                 :            : /**
      64                 :            :  * ShumateVectorSpriteFallbackFunc:
      65                 :            :  * @sprite_sheet: the [class@VectorSpriteSheet]
      66                 :            :  * @name: the name of the sprite to generate
      67                 :            :  * @scale: the scale factor of the sprite
      68                 :            :  * @user_data: user data passed to [method@VectorSpriteSheet.set_fallback]
      69                 :            :  *
      70                 :            :  * A function to generate sprites for a [class@VectorSpriteSheet].
      71                 :            :  *
      72                 :            :  * See [method@VectorSpriteSheet.set_fallback].
      73                 :            :  *
      74                 :            :  * Returns: (transfer full) (nullable): a [class@VectorSprite] or %NULL
      75                 :            :  *
      76                 :            :  * Since: 1.1
      77                 :            :  */
      78                 :            : 
      79                 :            : #define FALLBACK_QUEUE_CAPACITY 100
      80                 :            : 
      81                 :            : struct _ShumateVectorSpriteSheet
      82                 :            : {
      83                 :            :   GObject parent_instance;
      84                 :            : 
      85                 :            :   GRecMutex mutex;
      86                 :            : 
      87                 :            :   GHashTable *sprite_arrays;
      88                 :            : 
      89                 :            :   ShumateVectorSpriteFallbackFunc *fallback;
      90                 :            :   gpointer fallback_user_data;
      91                 :            :   GDestroyNotify fallback_destroy;
      92                 :            :   GHashTable *fallback_sprites;
      93                 :            :   GQueue *fallback_queue;
      94                 :            : };
      95                 :            : 
      96   [ +  +  +  - ]:        736 : G_DEFINE_FINAL_TYPE (ShumateVectorSpriteSheet, shumate_vector_sprite_sheet, G_TYPE_OBJECT)
      97                 :            : 
      98                 :            : 
      99                 :            : /**
     100                 :            :  * shumate_vector_sprite_sheet_new:
     101                 :            :  *
     102                 :            :  * Creates a new, empty [class@VectorSpriteSheet].
     103                 :            :  *
     104                 :            :  * Returns: (transfer full): a new [class@VectorSpriteSheet]
     105                 :            :  *
     106                 :            :  * Since: 1.1
     107                 :            :  */
     108                 :            : ShumateVectorSpriteSheet *
     109                 :         15 : shumate_vector_sprite_sheet_new (void)
     110                 :            : {
     111                 :         15 :   return g_object_new (SHUMATE_TYPE_VECTOR_SPRITE_SHEET, NULL);
     112                 :            : }
     113                 :            : 
     114                 :            : static void
     115                 :         15 : shumate_vector_sprite_sheet_finalize (GObject *object)
     116                 :            : {
     117                 :         15 :   ShumateVectorSpriteSheet *self = (ShumateVectorSpriteSheet *)object;
     118                 :            : 
     119         [ +  - ]:         15 :   g_clear_pointer (&self->sprite_arrays, g_hash_table_unref);
     120                 :            : 
     121         [ +  + ]:         15 :   if (self->fallback_destroy != NULL)
     122                 :          3 :     self->fallback_destroy (self->fallback_user_data);
     123         [ +  + ]:         15 :   g_clear_pointer (&self->fallback_sprites, g_hash_table_unref);
     124         [ +  + ]:         15 :   if (self->fallback_queue != NULL)
     125                 :          3 :     g_queue_free_full (self->fallback_queue, g_free);
     126                 :            : 
     127                 :         15 :   g_rec_mutex_clear (&self->mutex);
     128                 :            : 
     129                 :         15 :   G_OBJECT_CLASS (shumate_vector_sprite_sheet_parent_class)->finalize (object);
     130                 :         15 : }
     131                 :            : 
     132                 :            : static void
     133                 :         10 : shumate_vector_sprite_sheet_class_init (ShumateVectorSpriteSheetClass *klass)
     134                 :            : {
     135                 :         10 :   GObjectClass *object_class = G_OBJECT_CLASS (klass);
     136                 :            : 
     137                 :         10 :   object_class->finalize = shumate_vector_sprite_sheet_finalize;
     138                 :            : }
     139                 :            : 
     140                 :            : static void
     141                 :         15 : shumate_vector_sprite_sheet_init (ShumateVectorSpriteSheet *self)
     142                 :            : {
     143                 :         15 :   g_rec_mutex_init (&self->mutex);
     144                 :         15 :   self->sprite_arrays = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify)g_ptr_array_unref);
     145                 :         15 : }
     146                 :            : 
     147                 :            : /**
     148                 :            :  * shumate_vector_sprite_sheet_add_sprite:
     149                 :            :  * @self: a [class@VectorSpriteSheet]
     150                 :            :  * @name: the name of the sprite
     151                 :            :  * @sprite: a [class@VectorSprite]
     152                 :            :  *
     153                 :            :  * Adds a sprite to the spritesheet.
     154                 :            :  *
     155                 :            :  * Since: 1.1
     156                 :            :  */
     157                 :            : void
     158                 :         15 : shumate_vector_sprite_sheet_add_sprite (ShumateVectorSpriteSheet *self,
     159                 :            :                                         const char               *name,
     160                 :            :                                         ShumateVectorSprite      *sprite)
     161                 :            : {
     162                 :         15 :   g_autoptr(GRecMutexLocker) locker = NULL;
     163                 :         15 :   GPtrArray *array = NULL;
     164                 :            : 
     165         [ +  - ]:         15 :   g_return_if_fail (SHUMATE_IS_VECTOR_SPRITE_SHEET (self));
     166         [ -  + ]:         15 :   g_return_if_fail (name != NULL);
     167         [ -  + ]:         15 :   g_return_if_fail (SHUMATE_IS_VECTOR_SPRITE (sprite));
     168                 :            : 
     169                 :         15 :   locker = g_rec_mutex_locker_new (&self->mutex);
     170                 :            : 
     171                 :         15 :   array = g_hash_table_lookup (self->sprite_arrays, name);
     172         [ +  + ]:         15 :   if (array == NULL)
     173                 :            :     {
     174                 :         12 :       array = g_ptr_array_new_with_free_func (g_object_unref);
     175         [ -  + ]:         24 :       g_hash_table_insert (self->sprite_arrays, g_strdup (name), array);
     176                 :            :     }
     177                 :            : 
     178                 :         15 :   g_ptr_array_add (array, g_object_ref (sprite));
     179                 :            : }
     180                 :            : 
     181                 :            : /**
     182                 :            :  * shumate_vector_sprite_sheet_add_page:
     183                 :            :  * @self: a [class@VectorSpriteSheet]
     184                 :            :  * @texture: a [class@Gdk.Texture]
     185                 :            :  * @json: a JSON string
     186                 :            :  * @default_scale: the default scale factor of the page
     187                 :            :  * @error: return location for a [struct@GLib.Error], or %NULL
     188                 :            :  *
     189                 :            :  * Adds a page to the spritesheet.
     190                 :            :  *
     191                 :            :  * See <https://maplibre.org/maplibre-gl-js-docs/style-spec/sprite/> for
     192                 :            :  * details about the spritesheet format. Most stylesheets provide these files
     193                 :            :  * along with the main style JSON.
     194                 :            :  *
     195                 :            :  * Map styles should provide a double resolution spritesheet for high DPI
     196                 :            :  * displays. That spritesheet should be added as its own page, with a
     197                 :            :  * @default_scale of 2.
     198                 :            :  *
     199                 :            :  * Returns: %TRUE if the page was added successfully, %FALSE otherwise
     200                 :            :  *
     201                 :            :  * Since: 1.1
     202                 :            :  */
     203                 :            : gboolean
     204                 :         15 : shumate_vector_sprite_sheet_add_page (ShumateVectorSpriteSheet *self,
     205                 :            :                                       GdkTexture               *texture,
     206                 :            :                                       const char               *json,
     207                 :            :                                       double                    default_scale,
     208                 :            :                                       GError                  **error)
     209                 :            : {
     210         [ +  - ]:         15 :   g_return_val_if_fail (SHUMATE_IS_VECTOR_SPRITE_SHEET (self), FALSE);
     211   [ +  -  +  -  :         15 :   g_return_val_if_fail (GDK_IS_TEXTURE (texture), FALSE);
             +  -  -  + ]
     212         [ -  + ]:         15 :   g_return_val_if_fail (json != NULL, FALSE);
     213                 :            : 
     214                 :            :   /* No mutex lock is needed for this function because it only references @self
     215                 :            :      via shumate_vector_sprite_sheet_add_sprite(), which has its own lock. */
     216                 :            : 
     217                 :            : #ifdef SHUMATE_HAS_VECTOR_RENDERER
     218                 :         30 :   g_autoptr(JsonNode) json_node = NULL;
     219                 :         15 :   JsonObject *sprites;
     220                 :         15 :   JsonObjectIter iter;
     221                 :         15 :   const char *sprite_name;
     222                 :         15 :   JsonNode *sprite_node;
     223                 :            : 
     224                 :         15 :   json_node = json_from_string (json, error);
     225         [ -  + ]:         15 :   if (json_node == NULL)
     226                 :            :     return FALSE;
     227                 :            : 
     228         [ -  + ]:         15 :   if (!shumate_vector_json_get_object (json_node, &sprites, error))
     229                 :            :     return FALSE;
     230                 :            : 
     231                 :         15 :   json_object_iter_init (&iter, sprites);
     232         [ +  + ]:         30 :   while (json_object_iter_next (&iter, &sprite_name, &sprite_node))
     233                 :            :     {
     234                 :         15 :       JsonObject *sprite_object;
     235                 :         15 :       g_autoptr(ShumateVectorSprite) sprite = NULL;
     236                 :         15 :       int x, y, width, height, pixel_ratio;
     237                 :            : 
     238         [ +  - ]:         15 :       if (!shumate_vector_json_get_object (sprite_node, &sprite_object, error))
     239                 :            :         return FALSE;
     240                 :            : 
     241                 :         15 :       x = json_object_get_int_member_with_default (sprite_object, "x", 0);
     242                 :         15 :       y = json_object_get_int_member_with_default (sprite_object, "y", 0);
     243                 :         15 :       width = json_object_get_int_member_with_default (sprite_object, "width", 0);
     244                 :         15 :       height = json_object_get_int_member_with_default (sprite_object, "height", 0);
     245                 :         15 :       pixel_ratio = json_object_get_int_member_with_default (sprite_object, "pixelRatio", default_scale);
     246                 :            : 
     247   [ +  -  -  + ]:         15 :       if (x < 0 || y < 0 || width <= 0 || height <= 0)
     248                 :            :         {
     249                 :          0 :           g_set_error (error,
     250                 :            :                        SHUMATE_STYLE_ERROR,
     251                 :            :                        SHUMATE_STYLE_ERROR_MALFORMED_STYLE,
     252                 :            :                        "Invalid dimensions for sprite '%s'", sprite_name);
     253                 :          0 :           return FALSE;
     254                 :            :         }
     255                 :            : 
     256                 :         30 :       sprite = shumate_vector_sprite_new_full (
     257                 :            :         GDK_PAINTABLE (texture),
     258                 :            :         width / pixel_ratio,
     259                 :            :         height / pixel_ratio,
     260                 :            :         pixel_ratio,
     261                 :         15 :         &(GdkRectangle){ x, y, width, height }
     262                 :            :       );
     263                 :            : 
     264         [ +  - ]:         15 :       shumate_vector_sprite_sheet_add_sprite (self, sprite_name, sprite);
     265                 :            :     }
     266                 :            : 
     267                 :            :   return TRUE;
     268                 :            : #else
     269                 :            :   g_set_error (error,
     270                 :            :                SHUMATE_STYLE_ERROR,
     271                 :            :                SHUMATE_STYLE_ERROR_SUPPORT_OMITTED,
     272                 :            :                "Libshumate was compiled without support for vector tiles.");
     273                 :            :   return FALSE;
     274                 :            : #endif
     275                 :            : }
     276                 :            : 
     277                 :            : static ShumateVectorSprite *
     278                 :        342 : search_sprites (GPtrArray *sprites,
     279                 :            :                 double     scale,
     280                 :            :                 gboolean   higher,
     281                 :            :                 gboolean   lower)
     282                 :            : {
     283         [ +  + ]:        342 :   double best_scale = higher ? G_MAXDOUBLE : G_MINDOUBLE;
     284                 :            :   ShumateVectorSprite *best_sprite = NULL;
     285                 :            : 
     286         [ +  + ]:        375 :   for (int i = 0; i < sprites->len; i++)
     287                 :            :     {
     288                 :        360 :       ShumateVectorSprite *sprite = g_ptr_array_index (sprites, i);
     289                 :        360 :       double sprite_scale = shumate_vector_sprite_get_scale_factor (sprite);
     290                 :            : 
     291         [ +  + ]:        360 :       if (sprite_scale > scale)
     292                 :            :         {
     293   [ +  +  +  + ]:         12 :           if (higher && sprite_scale < best_scale)
     294                 :            :             {
     295                 :            :               best_scale = sprite_scale;
     296                 :            :               best_sprite = sprite;
     297                 :            :             }
     298                 :            :           else
     299                 :          9 :             continue;
     300                 :            :         }
     301         [ +  + ]:        348 :       else if (sprite_scale < scale)
     302                 :            :         {
     303   [ +  +  -  + ]:         21 :           if (lower && sprite_scale > best_scale)
     304                 :            :             {
     305                 :            :               best_scale = sprite_scale;
     306                 :            :               best_sprite = sprite;
     307                 :            :             }
     308                 :            :           else
     309                 :         15 :             continue;
     310                 :            :         }
     311                 :            :       else
     312                 :        327 :         return g_object_ref (sprite);
     313                 :            :     }
     314                 :            : 
     315         [ +  + ]:         15 :   if (best_sprite != NULL)
     316                 :          6 :     return g_object_ref (best_sprite);
     317                 :            :   else
     318                 :            :     return NULL;
     319                 :            : }
     320                 :            : 
     321                 :            : 
     322                 :            : /**
     323                 :            :  * shumate_vector_sprite_sheet_get_sprite:
     324                 :            :  * @self: a [class@VectorSpriteSheet]
     325                 :            :  * @name: an icon name
     326                 :            :  * @scale: the scale factor of the icon
     327                 :            :  *
     328                 :            :  * Gets a sprite from the spritesheet.
     329                 :            :  *
     330                 :            :  * The returned sprite might not be at the requested scale factor if an exact
     331                 :            :  * match is not found.
     332                 :            :  *
     333                 :            :  * Returns: (transfer full) (nullable): a [class@VectorSprite], or %NULL if the
     334                 :            :  * icon does not exist.
     335                 :            :  *
     336                 :            :  * Since: 1.1
     337                 :            :  */
     338                 :            : ShumateVectorSprite *
     339                 :        657 : shumate_vector_sprite_sheet_get_sprite (ShumateVectorSpriteSheet *self,
     340                 :            :                                         const char               *name,
     341                 :            :                                         double                    scale)
     342                 :            : {
     343                 :       1314 :   g_autoptr(GRecMutexLocker) locker = NULL;
     344                 :        657 :   ShumateVectorSprite *sprite;
     345                 :        657 :   GPtrArray *sprite_array;
     346                 :            : 
     347         [ +  - ]:        657 :   g_return_val_if_fail (SHUMATE_IS_VECTOR_SPRITE_SHEET (self), NULL);
     348         [ -  + ]:        657 :   g_return_val_if_fail (name != NULL, NULL);
     349                 :            : 
     350                 :        657 :   locker = g_rec_mutex_locker_new (&self->mutex);
     351                 :            : 
     352                 :        657 :   sprite_array = g_hash_table_lookup (self->sprite_arrays, name);
     353         [ +  + ]:        657 :   if (sprite_array != NULL)
     354                 :            :     {
     355                 :            :       /* Search the exact scale, then higher scales, then lower scales */
     356         [ +  + ]:        333 :       if ((sprite = search_sprites (sprite_array, scale, FALSE, FALSE)) != NULL)
     357                 :            :         return sprite;
     358         [ +  + ]:          6 :       if ((sprite = search_sprites (sprite_array, scale, TRUE, FALSE)) != NULL)
     359                 :            :         return sprite;
     360         [ -  + ]:          3 :       if ((sprite = search_sprites (sprite_array, scale, FALSE, TRUE)) != NULL)
     361                 :            :         return sprite;
     362                 :            :     }
     363                 :            : 
     364         [ +  + ]:        324 :   if (self->fallback)
     365                 :            :     {
     366         [ +  + ]:        318 :       if (g_hash_table_lookup_extended (self->fallback_sprites, name, NULL, (gpointer *)&sprite))
     367                 :            :         {
     368         [ +  + ]:          6 :           if (sprite != NULL)
     369                 :          3 :             return g_object_ref (sprite);
     370                 :            :           else
     371                 :            :             return NULL;
     372                 :            :         }
     373                 :            : 
     374                 :        312 :       sprite = self->fallback (self, name, scale, self->fallback_user_data);
     375                 :            : 
     376         [ -  + ]:        624 :       g_hash_table_insert (self->fallback_sprites, g_strdup (name), sprite);
     377         [ -  + ]:        624 :       g_queue_push_tail (self->fallback_queue, g_strdup (name));
     378         [ +  + ]:        312 :       if (g_queue_get_length (self->fallback_queue) > FALLBACK_QUEUE_CAPACITY)
     379                 :            :         {
     380                 :         24 :           g_autofree char *old_name = g_queue_pop_head (self->fallback_queue);
     381                 :         12 :           g_hash_table_remove (self->fallback_sprites, old_name);
     382                 :            :         }
     383                 :            : 
     384         [ +  + ]:        312 :       if (sprite != NULL)
     385                 :        309 :         return g_object_ref (sprite);
     386                 :            :     }
     387                 :            : 
     388                 :            :   return NULL;
     389                 :            : }
     390                 :            : 
     391                 :            : 
     392                 :            : static void
     393                 :        312 : object_unref0 (gpointer object)
     394                 :            : {
     395         [ +  + ]:        312 :   if (object != NULL)
     396                 :        309 :     g_object_unref (object);
     397                 :        312 : }
     398                 :            : 
     399                 :            : 
     400                 :            : /**
     401                 :            :  * shumate_vector_sprite_sheet_set_fallback:
     402                 :            :  * @self: a [class@VectorSpriteSheet]
     403                 :            :  * @fallback: (nullable): a [callback@ShumateVectorSpriteFallbackFunc] or %NULL
     404                 :            :  * @user_data: user data to pass to @fallback
     405                 :            :  * @notify: a [callback@GLib.DestroyNotify] for @user_data
     406                 :            :  *
     407                 :            :  * Sets a fallback function to generate sprites.
     408                 :            :  *
     409                 :            :  * The fallback function is called when a texture is not found in the sprite
     410                 :            :  * sheet. It receives the icon name and scale factor, and should return a
     411                 :            :  * [class@VectorSprite], or %NULL if the icon could not be generated.
     412                 :            :  * It may be called in a different thread, and it may be called multiple times
     413                 :            :  * for the same icon name.
     414                 :            :  *
     415                 :            :  * If a previous fallback function was set, it will be replaced and any sprites
     416                 :            :  * it generated will be cleared.
     417                 :            :  *
     418                 :            :  * @fallback may be %NULL to clear the fallback function.
     419                 :            :  *
     420                 :            :  * Since: 1.1
     421                 :            :  */
     422                 :            : void
     423                 :          3 : shumate_vector_sprite_sheet_set_fallback (ShumateVectorSpriteSheet        *self,
     424                 :            :                                           ShumateVectorSpriteFallbackFunc  fallback,
     425                 :            :                                           gpointer                         user_data,
     426                 :            :                                           GDestroyNotify                   notify)
     427                 :            : {
     428                 :          3 :   g_autoptr(GRecMutexLocker) locker = NULL;
     429                 :            : 
     430         [ +  - ]:          3 :   g_return_if_fail (SHUMATE_IS_VECTOR_SPRITE_SHEET (self));
     431         [ -  + ]:          3 :   g_return_if_fail (!(fallback == NULL && user_data != NULL));
     432                 :            : 
     433                 :          3 :   locker = g_rec_mutex_locker_new (&self->mutex);
     434                 :            : 
     435                 :            :   /* Clear any previous fallback func */
     436         [ -  + ]:          3 :   if (self->fallback_destroy != NULL)
     437                 :          0 :     self->fallback_destroy (self->fallback_user_data);
     438                 :            : 
     439                 :          3 :   self->fallback = NULL;
     440                 :          3 :   self->fallback_user_data = NULL;
     441                 :          3 :   self->fallback_destroy = NULL;
     442         [ -  + ]:          3 :   g_clear_pointer (&self->fallback_sprites, g_hash_table_unref);
     443         [ -  + ]:          3 :   if (self->fallback_queue != NULL)
     444                 :          0 :     g_queue_free_full (self->fallback_queue, g_free);
     445                 :            : 
     446         [ +  - ]:          3 :   if (fallback != NULL)
     447                 :            :     {
     448                 :          3 :       self->fallback = fallback;
     449                 :          3 :       self->fallback_user_data = user_data;
     450                 :          3 :       self->fallback_destroy = notify;
     451                 :            : 
     452                 :          3 :       self->fallback_sprites = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, object_unref0);
     453                 :          3 :       self->fallback_queue = g_queue_new ();
     454                 :            :     }
     455                 :            : }

Generated by: LCOV version 1.14