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