GCC Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 54.1% 251 / 0 / 464
Functions: 45.7% 16 / 0 / 35
Branches: 33.4% 99 / 0 / 296

shumate/shumate-vector-renderer.c
Line Branch Exec Source
1 /*
2 * Copyright (C) 2021 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-renderer-private.h"
19 #include "shumate-tile-downloader.h"
20 #include "shumate-tile-private.h"
21 #include "shumate-profiling-private.h"
22 #include "shumate-vector-reader.h"
23 #include "shumate-vector-reader-iter.h"
24 #include "shumate-vector-value-private.h"
25
26 /**
27 * ShumateVectorRenderer:
28 *
29 * A [class@MapSource] that renders tiles from a given vector data source.
30 */
31
32 #include <json-glib/json-glib.h>
33 #include <cairo/cairo.h>
34
35 #include "vector/shumate-vector-render-scope-private.h"
36 #include "vector/shumate-vector-symbol-info-private.h"
37 #include "vector/shumate-vector-utils-private.h"
38 #include "vector/shumate-vector-layer-private.h"
39 #include "vector/shumate-vector-index-private.h"
40
41 struct _ShumateVectorRenderer
42 {
43 ShumateMapSource parent_instance;
44
45 char *source_name;
46 ShumateDataSource *data_source;
47
48 ShumateVectorSpriteSheet *sprites;
49 GMutex sprites_mutex;
50
51 GHashTable *global_state;
52 GHashTable *default_global_state;
53 GMutex global_state_mutex;
54
55 GThreadPool *thread_pool;
56
57 char *style_json;
58
59 GPtrArray *layers;
60
61 ShumateVectorIndexDescription *index_description;
62 };
63
64
65 static gboolean begin_render (ShumateVectorRenderer *self,
66 GTask *task,
67 GBytes *tile_data,
68 ShumateGridPosition *source_position);
69
70 static void shumate_vector_renderer_initable_iface_init (GInitableIface *iface);
71
72
4/6
shumate_vector_renderer_class_intern_init:
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 5 times.
shumate_vector_renderer_get_type:
✓ Branch 2 → 3 taken 5 times.
✓ Branch 2 → 7 taken 22 times.
✓ Branch 4 → 5 taken 5 times.
✗ Branch 4 → 7 not taken.
37 G_DEFINE_TYPE_WITH_CODE (ShumateVectorRenderer, shumate_vector_renderer, SHUMATE_TYPE_MAP_SOURCE,
73 G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, shumate_vector_renderer_initable_iface_init))
74
75 enum {
76 PROP_0,
77 PROP_STYLE_JSON,
78 PROP_SPRITE_SHEET,
79 N_PROPS
80 };
81
82 static GParamSpec *properties [N_PROPS];
83
84
85 /**
86 * shumate_vector_renderer_new:
87 * @id: an ID for the map source
88 * @style_json: a vector style
89 * @error: return location for a #GError, or %NULL
90 *
91 * Creates a new [class@VectorRenderer] from the given JSON style.
92 *
93 * The stylesheet should contain a list of tile sources. Tiles will be
94 * downloaded using [class@TileDownloader]s.
95 *
96 * See the [MapLibre Style Specification](https://maplibre.org/maplibre-gl-js-docs/style-spec/)
97 * for details on @style_json, but be aware that libshumate does not support
98 * every feature of the specification.
99 *
100 * Returns: (transfer full): a newly constructed [class@VectorRenderer], or %NULL if @error is set
101 */
102 ShumateVectorRenderer *
103 6 shumate_vector_renderer_new (const char *id,
104 const char *style_json,
105 GError **error)
106 {
107
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 6 times.
6 g_return_val_if_fail (id != NULL, NULL);
108
1/2
✓ Branch 4 → 5 taken 6 times.
✗ Branch 4 → 7 not taken.
6 g_return_val_if_fail (style_json != NULL, NULL);
109
110 6 return g_initable_new (SHUMATE_TYPE_VECTOR_RENDERER, NULL, error,
111 "id", id,
112 "style-json", style_json,
113 NULL);
114 }
115
116
117 /**
118 * shumate_vector_renderer_is_supported:
119 *
120 * Checks whether libshumate was compiled with vector tile support. Previous
121 * versions of libshumate had a build-time option to disable vector tiles,
122 * but as of 1.6 they are always enabled.
123 *
124 * Returns: %TRUE
125 */
126 gboolean
127 ✗ shumate_vector_renderer_is_supported (void)
128 {
129 ✗ return TRUE;
130 }
131
132
133 static void
134 6 shumate_vector_renderer_finalize (GObject *object)
135 {
136 6 ShumateVectorRenderer *self = (ShumateVectorRenderer *)object;
137
138
1/2
✓ Branch 2 → 3 taken 6 times.
✗ Branch 2 → 4 not taken.
6 g_clear_pointer (&self->layers, g_ptr_array_unref);
139
1/2
✓ Branch 4 → 5 taken 6 times.
✗ Branch 4 → 6 not taken.
6 g_clear_pointer (&self->style_json, g_free);
140
1/2
✓ Branch 6 → 7 taken 6 times.
✗ Branch 6 → 8 not taken.
6 g_clear_pointer (&self->source_name, g_free);
141
1/2
✓ Branch 8 → 9 taken 6 times.
✗ Branch 8 → 10 not taken.
6 g_clear_object (&self->data_source);
142
2/2
✓ Branch 10 → 11 taken 2 times.
✓ Branch 10 → 12 taken 4 times.
6 g_clear_object (&self->sprites);
143
1/2
✓ Branch 12 → 13 taken 6 times.
✗ Branch 12 → 14 not taken.
6 g_clear_pointer (&self->index_description, shumate_vector_index_description_free);
144
145
1/2
✗ Branch 14 → 15 not taken.
✓ Branch 14 → 16 taken 6 times.
6 if (self->thread_pool)
146 ✗ g_thread_pool_free (self->thread_pool, FALSE, FALSE);
147
148 6 g_mutex_clear (&self->sprites_mutex);
149
150
1/2
✓ Branch 17 → 18 taken 6 times.
✗ Branch 17 → 19 not taken.
6 g_clear_pointer (&self->global_state, g_hash_table_unref);
151
1/2
✓ Branch 19 → 20 taken 6 times.
✗ Branch 19 → 21 not taken.
6 g_clear_pointer (&self->default_global_state, g_hash_table_unref);
152 6 g_mutex_clear (&self->global_state_mutex);
153
154 6 G_OBJECT_CLASS (shumate_vector_renderer_parent_class)->finalize (object);
155 6 }
156
157 static void
158 ✗ shumate_vector_renderer_get_property (GObject *object,
159 guint prop_id,
160 GValue *value,
161 GParamSpec *pspec)
162 {
163 ✗ ShumateVectorRenderer *self = SHUMATE_VECTOR_RENDERER (object);
164
165 ✗ switch (prop_id)
166 {
167 ✗ case PROP_STYLE_JSON:
168 ✗ g_value_set_string (value, self->style_json);
169 ✗ break;
170 ✗ case PROP_SPRITE_SHEET:
171 ✗ g_value_set_object (value, shumate_vector_renderer_get_sprite_sheet (self));
172 ✗ break;
173 ✗ default:
174 ✗ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
175 }
176 ✗ }
177
178 static void
179 6 shumate_vector_renderer_set_property (GObject *object,
180 guint prop_id,
181 const GValue *value,
182 GParamSpec *pspec)
183 {
184 6 ShumateVectorRenderer *self = SHUMATE_VECTOR_RENDERER (object);
185
186
1/3
✓ Branch 2 → 3 taken 6 times.
✗ Branch 2 → 15 not taken.
✗ Branch 2 → 18 not taken.
6 switch (prop_id)
187 {
188 6 case PROP_STYLE_JSON:
189 /* Property is construct only, so it should only be set once */
190
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 6 times.
6 g_assert (self->style_json == NULL);
191
1/2
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 6 times.
6 self->style_json = g_strdup (g_value_get_string (value));
192 6 break;
193 ✗ case PROP_SPRITE_SHEET:
194 ✗ shumate_vector_renderer_set_sprite_sheet (self, g_value_get_object (value));
195 ✗ break;
196 ✗ default:
197 ✗ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
198 }
199 6 }
200
201 static void shumate_vector_renderer_fill_tile_async (ShumateMapSource *map_source,
202 ShumateTile *tile,
203 GCancellable *cancellable,
204 GAsyncReadyCallback callback,
205 gpointer user_data);
206
207 static gboolean shumate_vector_renderer_fill_tile_finish (ShumateMapSource *map_source,
208 GAsyncResult *result,
209 GError **error);
210
211 static void
212 5 shumate_vector_renderer_class_init (ShumateVectorRendererClass *klass)
213 {
214 5 GObjectClass *object_class = G_OBJECT_CLASS (klass);
215 5 ShumateMapSourceClass *map_source_class = SHUMATE_MAP_SOURCE_CLASS (klass);
216
217 5 object_class->finalize = shumate_vector_renderer_finalize;
218 5 object_class->get_property = shumate_vector_renderer_get_property;
219 5 object_class->set_property = shumate_vector_renderer_set_property;
220
221 5 map_source_class->fill_tile_async = shumate_vector_renderer_fill_tile_async;
222 5 map_source_class->fill_tile_finish = shumate_vector_renderer_fill_tile_finish;
223
224 /**
225 * ShumateVectorRenderer:style-json:
226 *
227 * A map style, in [Mapbox Style Specification](https://docs.mapbox.com/mapbox-gl-js/style-spec/)
228 * format.
229 *
230 * Note that not all features of the specification are supported.
231 */
232 10 properties[PROP_STYLE_JSON] =
233 5 g_param_spec_string ("style-json", NULL, NULL,
234 NULL,
235 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
236
237 /**
238 * ShumateVectorRenderer:sprite-sheet:
239 *
240 * The sprite sheet used to render icons and textures.
241 *
242 * Since: 1.1
243 */
244 10 properties[PROP_SPRITE_SHEET] =
245 5 g_param_spec_object ("sprite-sheet", NULL, NULL,
246 SHUMATE_TYPE_VECTOR_SPRITE_SHEET,
247 G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
248
249 5 g_object_class_install_properties (object_class, N_PROPS, properties);
250 5 }
251
252
253 static gboolean
254 6 shumate_vector_renderer_initable_init (GInitable *initable,
255 GCancellable *cancellable,
256 GError **error)
257 {
258 6 SHUMATE_PROFILE_START ();
259
260 6 ShumateVectorRenderer *self = (ShumateVectorRenderer *)initable;
261 6 g_autoptr(JsonNode) node = NULL;
262 6 JsonNode *layers_node;
263 6 JsonNode *sources_node;
264 6 JsonNode *state_node;
265 6 JsonObject *object;
266 6 const char *style_name;
267
268
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 6 times.
6 g_return_val_if_fail (SHUMATE_IS_VECTOR_RENDERER (self), FALSE);
269
1/2
✓ Branch 6 → 7 taken 6 times.
✗ Branch 6 → 9 not taken.
6 g_return_val_if_fail (self->style_json != NULL, FALSE);
270
271
1/2
✓ Branch 8 → 10 taken 6 times.
✗ Branch 8 → 158 not taken.
6 if (!(node = json_from_string (self->style_json, error)))
272 return FALSE;
273
274
1/2
✓ Branch 11 → 12 taken 6 times.
✗ Branch 11 → 157 not taken.
6 if (!shumate_vector_json_get_object (node, &object, error))
275 return FALSE;
276
277
1/2
✓ Branch 13 → 14 taken 6 times.
✗ Branch 13 → 157 not taken.
6 if (!shumate_vector_json_get_string_member (object, "name", &style_name, error))
278 return FALSE;
279
1/2
✗ Branch 14 → 15 not taken.
✓ Branch 14 → 16 taken 6 times.
6 if (style_name != NULL)
280 ✗ shumate_map_source_set_name (SHUMATE_MAP_SOURCE (self), style_name);
281
282
1/2
✗ Branch 17 → 18 not taken.
✓ Branch 17 → 21 taken 6 times.
6 if ((sources_node = json_object_get_member (object, "sources")) == NULL)
283 {
284 ✗ g_set_error (error,
285 SHUMATE_STYLE_ERROR,
286 SHUMATE_STYLE_ERROR_UNSUPPORTED,
287 "a data source is required");
288 ✗ return FALSE;
289 }
290 else
291 {
292 6 JsonObject *sources;
293 6 JsonObjectIter iter;
294 6 const char *source_name;
295 6 JsonNode *source_node;
296 6 int minzoom = 30, maxzoom = 0;
297
298
1/2
✓ Branch 22 → 23 taken 6 times.
✗ Branch 22 → 84 not taken.
6 if (!shumate_vector_json_get_object (sources_node, &sources, error))
299 ✗ return FALSE;
300
301
1/2
✗ Branch 24 → 25 not taken.
✓ Branch 24 → 27 taken 6 times.
6 if (json_object_get_size (sources) > 1)
302 {
303 /* TODO: Support multiple data sources */
304 ✗ g_set_error (error,
305 SHUMATE_STYLE_ERROR,
306 SHUMATE_STYLE_ERROR_UNSUPPORTED,
307 "ShumateVectorRenderer does not currently support multiple data sources");
308 ✗ return FALSE;
309 }
310
1/2
✗ Branch 28 → 29 not taken.
✓ Branch 28 → 32 taken 6 times.
6 else if (json_object_get_size (object) == 0)
311 {
312 ✗ g_set_error (error,
313 SHUMATE_STYLE_ERROR,
314 SHUMATE_STYLE_ERROR_UNSUPPORTED,
315 "a data source is required");
316 ✗ return FALSE;
317 }
318
319 6 json_object_iter_init (&iter, sources);
320
2/2
✓ Branch 76 → 33 taken 6 times.
✓ Branch 76 → 77 taken 6 times.
12 while (json_object_iter_next (&iter, &source_name, &source_node))
321 {
322 6 JsonObject *source_object;
323 6 const char *source_type;
324 6 const char *url;
325 6 JsonArray *tiles;
326 6 const char *url_template;
327 6 ShumateDataSource *data_source;
328
329
1/2
✓ Branch 34 → 35 taken 6 times.
✗ Branch 34 → 73 not taken.
6 if (!shumate_vector_json_get_object (source_node, &source_object, error))
330 ✗ return FALSE;
331
332
1/2
✓ Branch 36 → 37 taken 6 times.
✗ Branch 36 → 73 not taken.
6 if (!shumate_vector_json_get_string_member (source_object, "type", &source_type, error)
333
1/2
✓ Branch 38 → 39 taken 6 times.
✗ Branch 38 → 73 not taken.
6 || !shumate_vector_json_get_string_member (source_object, "url", &url, error)
334
1/2
✓ Branch 40 → 41 taken 6 times.
✗ Branch 40 → 73 not taken.
6 || !shumate_vector_json_get_array_member (source_object, "tiles", &tiles, error))
335 return FALSE;
336
337
1/2
✗ Branch 42 → 43 not taken.
✓ Branch 42 → 45 taken 6 times.
6 if (g_strcmp0 (source_type, "vector") != 0)
338 {
339 ✗ g_set_error (error,
340 SHUMATE_STYLE_ERROR,
341 SHUMATE_STYLE_ERROR_UNSUPPORTED,
342 "ShumateVectorRenderer currently only supports vector sources.");
343 ✗ return FALSE;
344 }
345
346
1/2
✗ Branch 45 → 46 not taken.
✓ Branch 45 → 49 taken 6 times.
6 if (url != NULL)
347 {
348 ✗ g_set_error (error,
349 SHUMATE_STYLE_ERROR,
350 SHUMATE_STYLE_ERROR_UNSUPPORTED,
351 "ShumateVectorRenderer does not currently support TileJSON links. "
352 "Please embed the TileJSON data directly into the style.");
353 ✗ return FALSE;
354 }
355
356
2/4
✓ Branch 49 → 50 taken 6 times.
✗ Branch 49 → 52 not taken.
✗ Branch 51 → 52 not taken.
✓ Branch 51 → 55 taken 6 times.
6 if (tiles == NULL || json_array_get_length (tiles) == 0)
357 {
358 ✗ g_set_error (error,
359 SHUMATE_STYLE_ERROR,
360 SHUMATE_STYLE_ERROR_MALFORMED_STYLE,
361 "Expected 'tiles' array to contain at least one element");
362 ✗ return FALSE;
363 }
364
365
1/2
✓ Branch 57 → 58 taken 6 times.
✗ Branch 57 → 73 not taken.
6 if (!shumate_vector_json_get_string (json_array_get_element (tiles, 0),
366 &url_template,
367 error))
368 return FALSE;
369
370 6 minzoom = json_object_get_int_member_with_default (source_object, "minzoom", 0);
371 6 maxzoom = json_object_get_int_member_with_default (source_object, "maxzoom", 30);
372
373 6 data_source = SHUMATE_DATA_SOURCE (shumate_tile_downloader_new (url_template));
374 6 shumate_data_source_set_min_zoom_level (data_source, minzoom);
375 6 shumate_data_source_set_max_zoom_level (data_source, maxzoom);
376
377
1/2
✗ Branch 63 → 64 not taken.
✓ Branch 63 → 65 taken 6 times.
6 self->source_name = g_strdup (source_name);
378 6 self->data_source = data_source;
379 }
380
381 6 maxzoom = MAX (maxzoom, 18);
382
383
1/2
✓ Branch 77 → 78 taken 6 times.
✗ Branch 77 → 80 not taken.
6 if (minzoom < maxzoom)
384 {
385 6 shumate_map_source_set_min_zoom_level (SHUMATE_MAP_SOURCE (self), minzoom);
386 6 shumate_map_source_set_max_zoom_level (SHUMATE_MAP_SOURCE (self), maxzoom);
387 }
388 }
389
390 6 self->layers = g_ptr_array_new_with_free_func (g_object_unref);
391
1/2
✓ Branch 82 → 85 taken 6 times.
✗ Branch 82 → 108 not taken.
6 if ((layers_node = json_object_get_member (object, "layers")))
392 {
393 6 JsonArray *layers;
394
395
1/2
✓ Branch 86 → 87 taken 6 times.
✗ Branch 86 → 107 not taken.
6 if (!shumate_vector_json_get_array (layers_node, &layers, error))
396 ✗ return FALSE;
397
398
2/2
✓ Branch 105 → 89 taken 24 times.
✓ Branch 105 → 106 taken 6 times.
30 for (int i = 0, n = json_array_get_length (layers); i < n; i ++)
399 {
400 24 JsonNode *layer_node = json_array_get_element (layers, i);
401 24 JsonObject *layer_obj;
402 24 const char *layer_id;
403 24 ShumateVectorLayer *layer;
404 24 ShumateVectorExpression *filter;
405
406
1/2
✓ Branch 91 → 92 taken 24 times.
✗ Branch 91 → 104 not taken.
24 if (!shumate_vector_json_get_object (layer_node, &layer_obj, error))
407 ✗ return FALSE;
408
409
1/2
✓ Branch 93 → 94 taken 24 times.
✗ Branch 93 → 104 not taken.
24 if (!shumate_vector_json_get_string_member (layer_obj, "id", &layer_id, error))
410 return FALSE;
411
412
1/2
✗ Branch 95 → 96 not taken.
✓ Branch 95 → 97 taken 24 times.
24 if (!(layer = shumate_vector_layer_create_from_json (layer_obj, error)))
413 {
414 ✗ g_prefix_error (error, "layer '%s': ", layer_id);
415 ✗ return FALSE;
416 }
417
418 24 g_ptr_array_add (self->layers, layer);
419 24 filter = shumate_vector_layer_get_filter (layer);
420
1/2
✗ Branch 99 → 100 not taken.
✓ Branch 99 → 102 taken 24 times.
24 if (filter != NULL)
421 ✗ shumate_vector_expression_collect_indexes (filter, shumate_vector_layer_get_source_layer (layer), self->index_description);
422 }
423 }
424
425
1/2
✓ Branch 109 → 110 taken 6 times.
✗ Branch 109 → 155 not taken.
6 if ((state_node = json_object_get_member (object, "state")))
426 {
427 6 JsonObject *state_values;
428 6 JsonObjectIter iter;
429 6 JsonNode *state_value_node;
430 6 const char *key;
431
432
1/2
✗ Branch 111 → 112 not taken.
✓ Branch 111 → 113 taken 6 times.
6 if (!shumate_vector_json_get_object (state_node, &state_values, error))
433 {
434 ✗ g_prefix_error (error, "state: ");
435 ✗ return FALSE;
436 }
437
438 6 self->default_global_state = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify)shumate_vector_value_free);
439 6 self->global_state = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify)shumate_vector_value_free);
440
441 6 json_object_iter_init (&iter, state_values);
442
2/2
✓ Branch 151 → 116 taken 12 times.
✓ Branch 151 → 152 taken 6 times.
18 while (json_object_iter_next (&iter, &key, &state_value_node))
443 {
444 12 JsonObject *state_value_object;
445 12 g_auto(ShumateVectorValue) value = SHUMATE_VECTOR_VALUE_INIT;
446
447
1/2
✗ Branch 117 → 118 not taken.
✓ Branch 117 → 119 taken 12 times.
12 if (!shumate_vector_json_get_object (state_value_node, &state_value_object, error))
448 {
449 ✗ g_prefix_error (error, "state '%s': ", key);
450 ✗ return FALSE;
451 }
452
453
1/2
✗ Branch 121 → 122 not taken.
✓ Branch 121 → 124 taken 12 times.
12 if (!shumate_vector_value_set_from_json_literal (&value, json_object_get_member (state_value_object, "default"), error))
454 {
455 ✗ g_prefix_error (error, "state '%s': ", key);
456 ✗ return FALSE;
457 }
458
459
1/2
✗ Branch 125 → 126 not taken.
✓ Branch 125 → 127 taken 12 times.
24 g_hash_table_insert (self->default_global_state, g_strdup (key), shumate_vector_value_dup (&value));
460
1/2
✗ Branch 135 → 136 not taken.
✓ Branch 135 → 137 taken 12 times.
24 g_hash_table_insert (self->global_state, g_strdup (key), shumate_vector_value_dup (&value));
461 }
462 }
463
464 /* According to the style spec, this is not configurable for vector tiles */
465 6 shumate_map_source_set_tile_size (SHUMATE_MAP_SOURCE (self), 512);
466
467 6 return TRUE;
468 }
469
470
471 static void
472 5 shumate_vector_renderer_initable_iface_init (GInitableIface *iface)
473 {
474 5 iface->init = shumate_vector_renderer_initable_init;
475 5 }
476
477
478 /* An item in the thread pool queue to render a tile from received data. */
479 typedef struct {
480 GTask *task;
481 GCancellable *cancellable;
482 gulong cancellable_handle;
483 GBytes *data;
484 ShumateGridPosition source_position;
485
486 GdkPaintable *paintable;
487 GPtrArray *symbols;
488 } RenderJob;
489
490
491 static void
492 ✗ render_job_unref (RenderJob *job)
493 {
494 ✗ if (job->cancellable_handle != 0)
495 ✗ g_cancellable_disconnect (g_task_get_cancellable (job->task), job->cancellable_handle);
496 ✗ g_clear_object (&job->cancellable);
497 ✗ g_clear_object (&job->task);
498 ✗ g_clear_pointer (&job->data, g_bytes_unref);
499 ✗ g_clear_object (&job->paintable);
500 ✗ g_clear_pointer (&job->symbols, g_ptr_array_unref);
501 ✗ g_free (job);
502 ✗ }
503
504 /* The data associated with a shumate_vector_renderer_fill_tile_async() task. */
505 typedef struct {
506 ShumateTile *tile;
507 RenderJob *current_job;
508 ShumateDataSourceRequest *req;
509 guint8 completed : 1;
510 } TaskData;
511
512 static void
513 ✗ task_data_free (TaskData *data)
514 {
515 ✗ g_clear_object (&data->tile);
516 ✗ g_clear_object (&data->req);
517 ✗ g_free (data);
518 ✗ }
519
520
521 static void
522 6 shumate_vector_renderer_init (ShumateVectorRenderer *self)
523 {
524 6 g_mutex_init (&self->sprites_mutex);
525 6 g_mutex_init (&self->global_state_mutex);
526 6 self->index_description = shumate_vector_index_description_new ();
527 6 }
528
529
530 /**
531 * shumate_vector_renderer_get_style_json:
532 * @self: a [class@VectorStyle]
533 *
534 * Gets the JSON string from which this vector style was loaded.
535 *
536 * Returns: (nullable): the style JSON, or %NULL if none is set
537 */
538 const char *
539 ✗ shumate_vector_renderer_get_style_json (ShumateVectorRenderer *self)
540 {
541 ✗ g_return_val_if_fail (SHUMATE_IS_VECTOR_RENDERER (self), NULL);
542
543 ✗ return self->style_json;
544 }
545
546
547 /**
548 * shumate_vector_renderer_set_sprite_sheet_data:
549 * @self: a [class@VectorRenderer]
550 * @sprites_pixbuf: a [class@GdkPixbuf.Pixbuf]
551 * @sprites_json: a JSON string
552 * @error: return location for a #GError, or %NULL
553 *
554 * Sets the sprite sheet used by the style JSON to render icons and textures.
555 *
556 * The existing [property@VectorRenderer:sprite-sheet] property will be replaced
557 * with a new instance of [class@VectorSpriteSheet].
558 *
559 * Returns: whether the sprite sheet was loaded successfully
560 *
561 * Deprecated: 1.1: Use the methods of [property@VectorRenderer:sprite-sheet] instead.
562 */
563 gboolean
564 ✗ shumate_vector_renderer_set_sprite_sheet_data (ShumateVectorRenderer *self,
565 GdkPixbuf *sprites_pixbuf,
566 const char *sprites_json,
567 GError **error)
568 {
569 ✗ g_autoptr(ShumateVectorSpriteSheet) sprites = NULL;
570 ✗ g_autoptr(GdkTexture) texture = NULL;
571
572 ✗ g_return_val_if_fail (SHUMATE_IS_VECTOR_RENDERER (self), FALSE);
573 ✗ g_return_val_if_fail (GDK_IS_PIXBUF (sprites_pixbuf), FALSE);
574 ✗ g_return_val_if_fail (sprites_json != NULL, FALSE);
575
576 ✗ sprites = shumate_vector_sprite_sheet_new ();
577
578 ✗ texture = gdk_texture_new_for_pixbuf (sprites_pixbuf);
579 ✗ if (!shumate_vector_sprite_sheet_add_page (sprites, texture, sprites_json, 1, error))
580 return FALSE;
581
582 ✗ shumate_vector_renderer_set_sprite_sheet (self, sprites);
583 ✗ return TRUE;
584 }
585
586
587 /**
588 * shumate_vector_renderer_get_sprite_sheet:
589 * @self: a [class@VectorRenderer]
590 *
591 * Gets the sprite sheet used to render icons and textures.
592 *
593 * Returns: (transfer none): the [class@VectorSpriteSheet]
594 *
595 * Since: 1.1
596 */
597 ShumateVectorSpriteSheet *
598 ✗ shumate_vector_renderer_get_sprite_sheet (ShumateVectorRenderer *self)
599 {
600 ✗ g_autoptr(GMutexLocker) locker = NULL;
601
602 ✗ g_return_val_if_fail (SHUMATE_IS_VECTOR_RENDERER (self), FALSE);
603
604 ✗ locker = g_mutex_locker_new (&self->sprites_mutex);
605
606 ✗ if (self->sprites == NULL)
607 ✗ self->sprites = shumate_vector_sprite_sheet_new ();
608
609 ✗ return self->sprites;
610 }
611
612
613 /**
614 * shumate_vector_renderer_set_sprite_sheet:
615 * @self: a [class@VectorRenderer]
616 * @sprites: a [class@VectorSpriteSheet]
617 *
618 * Sets the sprite sheet used to render icons and textures.
619 *
620 * Since: 1.1
621 */
622 void
623 ✗ shumate_vector_renderer_set_sprite_sheet (ShumateVectorRenderer *self,
624 ShumateVectorSpriteSheet *sprites)
625 {
626 ✗ g_autoptr(GMutexLocker) locker = NULL;
627
628 ✗ g_return_if_fail (SHUMATE_IS_VECTOR_RENDERER (self));
629 ✗ g_return_if_fail (SHUMATE_IS_VECTOR_SPRITE_SHEET (sprites));
630
631 ✗ locker = g_mutex_locker_new (&self->sprites_mutex);
632
633 ✗ if (g_set_object (&self->sprites, sprites))
634 ✗ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SPRITE_SHEET]);
635 }
636
637
638 /**
639 * shumate_vector_renderer_set_data_source:
640 * @self: a [class@VectorRenderer]
641 * @name: the name of the data source
642 * @data_source: a [class@DataSource]
643 *
644 * Adds a data source to the renderer.
645 *
646 * Currently, [class@VectorRenderer] only supports one data source
647 * and throws an error if the style does not contain exactly one
648 * data source. However, support for multiple sources may be added
649 * in the future, so this method accepts a name parameter. If the
650 * name does not match the one expected by the style, this method
651 * will have no effect.
652 *
653 * Since: 1.2
654 */
655 void
656 ✗ shumate_vector_renderer_set_data_source (ShumateVectorRenderer *self,
657 const char *name,
658 ShumateDataSource *data_source)
659 {
660 ✗ g_return_if_fail (SHUMATE_IS_VECTOR_RENDERER (self));
661 ✗ g_return_if_fail (name != NULL);
662 ✗ g_return_if_fail (SHUMATE_IS_DATA_SOURCE (data_source));
663
664 ✗ if (g_strcmp0 (name, self->source_name) == 0)
665 {
666 ✗ if (g_set_object (&self->data_source, data_source))
667 ✗ g_signal_emit_by_name (self, "modified");
668 }
669 }
670
671
672 /**
673 * shumate_vector_renderer_set_global_state:
674 * @self: a [class@VectorRenderer]
675 * @key: the state key
676 * @value: the state value
677 *
678 * Sets a global state value.
679 *
680 * Global state can be accessed in the stylesheet through the "global-state" expression operator.
681 * This allows styles to provide options that can be configured without changing the style JSON.
682 *
683 * Previously rendered tiles are not affected by changes to global state and must be re-rendered.
684 *
685 * Since: 1.6
686 */
687 void
688 2 shumate_vector_renderer_set_global_state (ShumateVectorRenderer *self,
689 const char *key,
690 ShumateVectorValue *value)
691 {
692 4 g_autoptr(GMutexLocker) locker = NULL;
693 2 ShumateVectorValue *val;
694 2 ShumateVectorValue *current_value;
695
696
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 2 times.
2 g_return_if_fail (SHUMATE_IS_VECTOR_RENDERER (self));
697
1/2
✓ Branch 5 → 6 taken 2 times.
✗ Branch 5 → 8 not taken.
2 g_return_if_fail (key != NULL);
698
699 2 locker = g_mutex_locker_new (&self->global_state_mutex);
700
701
1/2
✗ Branch 7 → 9 not taken.
✓ Branch 7 → 11 taken 2 times.
2 if (self->global_state == NULL)
702 ✗ self->global_state = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify)shumate_vector_value_free);
703
704 2 current_value = g_hash_table_lookup (self->global_state, key);
705
1/2
✗ Branch 13 → 14 not taken.
✓ Branch 13 → 15 taken 2 times.
2 if (shumate_vector_value_equal (current_value, value))
706 ✗ return;
707
708 2 val = g_new0 (ShumateVectorValue, 1);
709 2 shumate_vector_value_copy (value, val);
710
1/2
✗ Branch 17 → 18 not taken.
✓ Branch 17 → 20 taken 2 times.
4 g_hash_table_insert (self->global_state, g_strdup (key), val);
711
712 2 g_signal_emit_by_name (self, "modified");
713 }
714
715
716 /**
717 * shumate_vector_renderer_get_global_state:
718 * @self: a [class@VectorRenderer]
719 * @key: the state key
720 *
721 * Gets a global state value.
722 *
723 * If the value has not been set with [method@VectorRenderer.set_global_state], the default
724 * defined in the style will be returned, or %NULL if no default is defined. Note that an
725 * explicitly set null value will return a [struct@VectorValue] of type null, while an
726 * undefined value will return %NULL.
727 *
728 * Returns: (transfer none): the state value, or %NULL if none is set
729 * Since: 1.6
730 */
731 ShumateVectorValue *
732 8 shumate_vector_renderer_get_global_state (ShumateVectorRenderer *self,
733 const char *key)
734 {
735 16 g_autoptr(GMutexLocker) locker = NULL;
736 8 ShumateVectorValue *value;
737
738
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 8 times.
8 g_return_val_if_fail (SHUMATE_IS_VECTOR_RENDERER (self), NULL);
739
1/2
✓ Branch 5 → 6 taken 8 times.
✗ Branch 5 → 8 not taken.
8 g_return_val_if_fail (key != NULL, NULL);
740
741 8 locker = g_mutex_locker_new (&self->global_state_mutex);
742
743
1/2
✓ Branch 7 → 9 taken 8 times.
✗ Branch 7 → 13 not taken.
8 if (self->global_state == NULL)
744 return NULL;
745
746 8 value = g_hash_table_lookup (self->global_state, key);
747
2/2
✓ Branch 10 → 11 taken 2 times.
✓ Branch 10 → 13 taken 6 times.
8 if (value != NULL)
748 return value;
749
750
1/2
✓ Branch 11 → 12 taken 2 times.
✗ Branch 11 → 13 not taken.
2 if (self->default_global_state == NULL)
751 return NULL;
752
753 2 return g_hash_table_lookup (self->default_global_state, key);
754 }
755
756
757 /**
758 * shumate_vector_renderer_reset_global_state:
759 * @self: a [class@VectorRenderer]
760 * @key: the state key
761 *
762 * Resets a global state value to the default defined in the style, or to null
763 * if no default is defined.
764 *
765 * Since: 1.6
766 */
767 void
768 2 shumate_vector_renderer_reset_global_state (ShumateVectorRenderer *self,
769 const char *key)
770 {
771 4 g_autoptr(GMutexLocker) locker = NULL;
772 2 ShumateVectorValue *default_value;
773 2 ShumateVectorValue *current_value;
774
775
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 2 times.
2 g_return_if_fail (SHUMATE_IS_VECTOR_RENDERER (self));
776
1/2
✓ Branch 5 → 6 taken 2 times.
✗ Branch 5 → 8 not taken.
2 g_return_if_fail (key != NULL);
777
778 2 locker = g_mutex_locker_new (&self->global_state_mutex);
779
780
1/2
✓ Branch 7 → 9 taken 2 times.
✗ Branch 7 → 29 not taken.
2 if (self->global_state == NULL)
781 return;
782
783
1/2
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 13 taken 2 times.
2 if (self->default_global_state == NULL)
784 {
785 ✗ if (g_hash_table_remove (self->global_state, key))
786 ✗ g_signal_emit_by_name (self, "modified");
787 return;
788 }
789
790 2 default_value = g_hash_table_lookup (self->default_global_state, key);
791
1/2
✓ Branch 14 → 15 taken 2 times.
✗ Branch 14 → 25 not taken.
2 if (default_value != NULL)
792 {
793 2 current_value = g_hash_table_lookup (self->global_state, key);
794
1/2
✓ Branch 17 → 18 taken 2 times.
✗ Branch 17 → 29 not taken.
2 if (shumate_vector_value_equal (current_value, default_value))
795 return;
796
797
1/2
✗ Branch 19 → 20 not taken.
✓ Branch 19 → 22 taken 2 times.
4 g_hash_table_insert (self->global_state, g_strdup (key), shumate_vector_value_dup (default_value));
798
799 2 g_signal_emit_by_name (self, "modified");
800 }
801 else
802 {
803 ✗ if (g_hash_table_remove (self->global_state, key))
804 ✗ g_signal_emit_by_name (self, "modified");
805 }
806 }
807
808
809 static GdkTexture *
810 2 texture_new_for_surface (cairo_surface_t *surface)
811 {
812 4 g_autoptr(GBytes) bytes = NULL;
813 2 GdkTexture *texture;
814
815
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 2 times.
2 g_return_val_if_fail (cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_IMAGE, NULL);
816
1/2
✓ Branch 6 → 7 taken 2 times.
✗ Branch 6 → 9 not taken.
2 g_return_val_if_fail (cairo_image_surface_get_width (surface) > 0, NULL);
817
1/2
✓ Branch 8 → 10 taken 2 times.
✗ Branch 8 → 20 not taken.
2 g_return_val_if_fail (cairo_image_surface_get_height (surface) > 0, NULL);
818
819 2 bytes = g_bytes_new_with_free_func (cairo_image_surface_get_data (surface),
820 2 (gsize) cairo_image_surface_get_height (surface)
821 2 * (gsize) cairo_image_surface_get_stride (surface),
822 (GDestroyNotify) cairo_surface_destroy,
823 2 cairo_surface_reference (surface));
824
825 2 texture = gdk_memory_texture_new (cairo_image_surface_get_width (surface),
826 cairo_image_surface_get_height (surface),
827 GDK_MEMORY_B8G8R8A8_PREMULTIPLIED,
828 bytes,
829 2 cairo_image_surface_get_stride (surface));
830
831
1/2
✓ Branch 19 → 21 taken 2 times.
✗ Branch 19 → 22 not taken.
2 return texture;
832 }
833
834
835 static void
836 ✗ get_source_coordinates (ShumateVectorRenderer *self,
837 int *x,
838 int *y,
839 int *zoom_level)
840 {
841 /* Figure out which tile from the data source should be used to render the
842 * given tile (which will be different if we're overzooming). */
843
844 ✗ int maxzoom = shumate_data_source_get_max_zoom_level (self->data_source);
845 ✗ if (*zoom_level > maxzoom)
846 {
847 ✗ *x >>= (*zoom_level - maxzoom);
848 ✗ *y >>= (*zoom_level - maxzoom);
849 ✗ *zoom_level = maxzoom;
850 }
851 ✗ }
852
853 static void
854 ✗ on_request_notify (ShumateDataSourceRequest *req,
855 GParamSpec *pspec,
856 gpointer user_data)
857 {
858 ✗ GTask *task = user_data;
859 ✗ GBytes *data;
860 ✗ ShumateVectorRenderer *self = g_task_get_source_object (task);
861
862 ✗ if ((data = shumate_data_source_request_get_data (req)))
863 {
864 ✗ begin_render (
865 self,
866 task,
867 data,
868 ✗ &SHUMATE_GRID_POSITION_INIT (
869 shumate_data_source_request_get_x (req),
870 shumate_data_source_request_get_y (req),
871 shumate_data_source_request_get_zoom_level (req)
872 )
873 );
874 }
875 ✗ }
876
877
878 static void
879 ✗ return_from_task (GTask *task, ShumateDataSourceRequest *req)
880 {
881 ✗ GError *error;
882 ✗ TaskData *data = g_task_get_task_data (task);
883
884 ✗ shumate_tile_set_state (data->tile, SHUMATE_STATE_DONE);
885
886 ✗ if ((error = shumate_data_source_request_get_error (req)))
887 ✗ g_task_return_error (task, g_error_copy (error));
888 else
889 ✗ g_task_return_boolean (task, TRUE);
890 ✗ }
891
892 static void
893 ✗ on_request_notify_completed (ShumateDataSourceRequest *req,
894 GParamSpec *pspec,
895 gpointer user_data)
896 {
897 ✗ g_autoptr(GTask) task = user_data;
898 ✗ TaskData *data = g_task_get_task_data (task);
899
900 ✗ if (data->current_job != NULL)
901 ✗ data->completed = TRUE;
902 else
903 ✗ return_from_task (task, req);
904 ✗ }
905
906 static void
907 ✗ shumate_vector_renderer_fill_tile_async (ShumateMapSource *map_source,
908 ShumateTile *tile,
909 GCancellable *cancellable,
910 GAsyncReadyCallback callback,
911 gpointer user_data)
912 {
913 ✗ ShumateVectorRenderer *self = (ShumateVectorRenderer *)map_source;
914 ✗ g_autoptr(GTask) task = NULL;
915 ✗ int x, y, zoom_level;
916 ✗ g_autoptr(ShumateDataSourceRequest) req = NULL;
917 ✗ GBytes *data;
918 ✗ TaskData *task_data;
919
920 ✗ g_return_if_fail (SHUMATE_IS_VECTOR_RENDERER (self));
921 ✗ g_return_if_fail (SHUMATE_IS_TILE (tile));
922 ✗ g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
923
924 ✗ task = g_task_new (self, cancellable, callback, user_data);
925 ✗ g_task_set_source_tag (task, shumate_vector_renderer_fill_tile_async);
926
927 ✗ task_data = g_new0 (TaskData, 1);
928 ✗ task_data->tile = g_object_ref (tile);
929 ✗ g_task_set_task_data (task, task_data, (GDestroyNotify)task_data_free);
930
931 ✗ x = shumate_tile_get_x (tile);
932 ✗ y = shumate_tile_get_y (tile);
933 ✗ zoom_level = shumate_tile_get_zoom_level (tile);
934
935 ✗ get_source_coordinates (self, &x, &y, &zoom_level);
936
937 ✗ req = shumate_data_source_start_request (self->data_source, x, y, zoom_level, cancellable);
938 ✗ task_data->req = g_object_ref (req);
939
940 ✗ if ((data = shumate_data_source_request_get_data (req)))
941 {
942 ✗ begin_render (
943 self,
944 task,
945 data,
946 ✗ &SHUMATE_GRID_POSITION_INIT (x, y, zoom_level)
947 );
948 }
949
950 ✗ if (shumate_data_source_request_is_completed (req))
951 ✗ on_request_notify_completed (req, NULL, g_steal_pointer (&task));
952 else
953 {
954 ✗ g_signal_connect_object (req, "notify::data", (GCallback)on_request_notify, task, 0);
955 ✗ g_signal_connect_object (req, "notify::completed", (GCallback)on_request_notify_completed, g_object_ref (task), 0);
956 }
957 }
958
959 static gboolean
960 ✗ shumate_vector_renderer_fill_tile_finish (ShumateMapSource *map_source,
961 GAsyncResult *result,
962 GError **error)
963 {
964 ✗ ShumateVectorRenderer *self = (ShumateVectorRenderer *)map_source;
965
966 ✗ g_return_val_if_fail (SHUMATE_IS_VECTOR_RENDERER (self), FALSE);
967 ✗ g_return_val_if_fail (g_task_is_valid (result, self), FALSE);
968
969 ✗ return g_task_propagate_boolean (G_TASK (result), error);
970 }
971
972 void
973 2 shumate_vector_renderer_render (ShumateVectorRenderer *self,
974 ShumateTile *tile,
975 GBytes *tile_data,
976 ShumateGridPosition *source_position,
977 GdkPaintable **paintable,
978 GPtrArray **symbols)
979 {
980 2 SHUMATE_PROFILE_START ();
981
982 2 ShumateVectorRenderScope scope;
983 2 cairo_surface_t *surface;
984 4 g_autoptr(GPtrArray) symbol_list = g_ptr_array_new_with_free_func ((GDestroyNotify)shumate_vector_symbol_info_unref);
985 2 int texture_size;
986
1/2
✓ Branch 70 → 71 taken 2 times.
✗ Branch 70 → 72 not taken.
2 g_autofree char *profile_desc = NULL;
987 2 g_autoptr(ShumateVectorSpriteSheet) sprites = NULL;
988
1/2
✓ Branch 67 → 68 taken 2 times.
✗ Branch 67 → 69 not taken.
2 g_autoptr(ShumateVectorReader) reader = NULL;
989
1/2
✓ Branch 65 → 66 taken 2 times.
✗ Branch 65 → 67 not taken.
2 g_autoptr(GHashTable) global_state = NULL;
990
991
1/2
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 2 times.
2 g_assert (SHUMATE_IS_VECTOR_RENDERER (self));
992
1/2
✓ Branch 8 → 9 taken 2 times.
✗ Branch 8 → 11 not taken.
2 g_assert (SHUMATE_IS_TILE (tile));
993
994 2 g_mutex_lock (&self->sprites_mutex);
995
1/2
✓ Branch 10 → 12 taken 2 times.
✗ Branch 10 → 14 not taken.
2 if (self->sprites == NULL)
996 2 self->sprites = shumate_vector_sprite_sheet_new ();
997 2 sprites = g_object_ref (self->sprites);
998 2 g_mutex_unlock (&self->sprites_mutex);
999
1000 2 g_mutex_lock (&self->global_state_mutex);
1001
1/2
✓ Branch 17 → 18 taken 2 times.
✗ Branch 17 → 34 not taken.
2 if (self->global_state != NULL)
1002 {
1003 2 GHashTableIter iter;
1004 2 const char *key;
1005 2 ShumateVectorValue *value;
1006
1007 2 global_state = g_hash_table_new_similar (self->global_state);
1008
1009 2 g_hash_table_iter_init (&iter, self->global_state);
1010
2/2
✓ Branch 32 → 21 taken 4 times.
✓ Branch 32 → 33 taken 2 times.
6 while (g_hash_table_iter_next (&iter, (gpointer *)&key, (gpointer *)&value))
1011
1/2
✗ Branch 22 → 23 not taken.
✓ Branch 22 → 24 taken 4 times.
8 g_hash_table_insert (global_state, g_strdup (key), shumate_vector_value_dup (value));
1012 }
1013 2 g_mutex_unlock (&self->global_state_mutex);
1014
1015 2 texture_size = shumate_tile_get_size (tile);
1016 2 scope.scale_factor = shumate_tile_get_scale_factor (tile);
1017 2 scope.target_size = texture_size;
1018 2 scope.tile_x = shumate_tile_get_x (tile);
1019 2 scope.tile_y = shumate_tile_get_y (tile);
1020 2 scope.zoom_level = shumate_tile_get_zoom_level (tile);
1021 2 scope.symbols = symbol_list;
1022 2 scope.sprites = sprites;
1023 2 scope.index = NULL;
1024 2 scope.index_description = self->index_description;
1025 2 scope.global_state = global_state;
1026
1027
1/2
✗ Branch 40 → 41 not taken.
✓ Branch 40 → 42 taken 2 times.
2 if (scope.zoom_level > source_position->zoom)
1028 {
1029 ✗ float s = 1 << ((int)scope.zoom_level - source_position->zoom);
1030 ✗ scope.overzoom_x = (scope.tile_x - (source_position->x << ((int)scope.zoom_level - source_position->zoom))) / s;
1031 ✗ scope.overzoom_y = (scope.tile_y - (source_position->y << ((int)scope.zoom_level - source_position->zoom))) / s;
1032 ✗ scope.overzoom_scale = s;
1033 }
1034 else
1035 {
1036 2 scope.overzoom_x = 0;
1037 2 scope.overzoom_y = 0;
1038 2 scope.overzoom_scale = 1;
1039 }
1040
1041 4 surface = cairo_image_surface_create (
1042 CAIRO_FORMAT_ARGB32,
1043 texture_size * scope.scale_factor,
1044 2 texture_size * scope.scale_factor
1045 );
1046 2 scope.cr = cairo_create (surface);
1047 2 cairo_scale (scope.cr, scope.scale_factor, scope.scale_factor);
1048
1049 2 reader = shumate_vector_reader_new (tile_data);
1050 2 scope.reader = shumate_vector_reader_iterate (reader);
1051
1052
1/2
✓ Branch 48 → 49 taken 2 times.
✗ Branch 48 → 53 not taken.
2 if (scope.reader != NULL)
1053
2/2
✓ Branch 52 → 50 taken 8 times.
✓ Branch 52 → 53 taken 2 times.
10 for (scope.layer_idx = 0; scope.layer_idx < self->layers->len; scope.layer_idx ++)
1054 8 shumate_vector_layer_render ((ShumateVectorLayer *)self->layers->pdata[scope.layer_idx], &scope);
1055
1056 2 *paintable = GDK_PAINTABLE (texture_new_for_surface (surface));
1057 2 *symbols = g_ptr_array_ref (scope.symbols);
1058
1059 2 cairo_destroy (scope.cr);
1060 2 cairo_surface_destroy (surface);
1061
1/2
✓ Branch 57 → 58 taken 2 times.
✗ Branch 57 → 59 not taken.
2 g_clear_object (&scope.reader);
1062
1063
1/2
✗ Branch 59 → 60 not taken.
✓ Branch 59 → 61 taken 2 times.
2 g_clear_pointer (&scope.index, shumate_vector_index_free);
1064
1065 2 profile_desc = g_strdup_printf ("(%d, %d) @ %f", scope.tile_x, scope.tile_y, scope.zoom_level);
1066
1/2
✓ Branch 63 → 64 taken 2 times.
✗ Branch 63 → 65 not taken.
2 SHUMATE_PROFILE_END (profile_desc);
1067 2 }
1068
1069 static gboolean
1070 ✗ render_job_finish (RenderJob *job)
1071 {
1072 ✗ TaskData *data = g_task_get_task_data (job->task);
1073
1074 ✗ if (!g_cancellable_is_cancelled (job->cancellable))
1075 {
1076 /* Note: The order of these is important, because ShumateMapLayer relies on notify::paintable to refresh everything
1077 (since symbols isn't a property) */
1078 ✗ shumate_tile_set_symbols (data->tile, job->symbols);
1079 ✗ shumate_tile_set_paintable (data->tile, job->paintable);
1080 }
1081
1082 ✗ if (data->current_job == job)
1083 {
1084 ✗ data->current_job = NULL;
1085
1086 ✗ if (data->completed)
1087 ✗ return_from_task (job->task, data->req);
1088 }
1089
1090 ✗ render_job_unref (job);
1091 ✗ return G_SOURCE_REMOVE;
1092 }
1093
1094 static void
1095 ✗ thread_func (RenderJob *job)
1096 {
1097 ✗ ShumateVectorRenderer *self = g_task_get_source_object (job->task);
1098 ✗ TaskData *data = g_task_get_task_data (job->task);
1099
1100 ✗ if (!g_cancellable_is_cancelled (job->cancellable))
1101 {
1102 ✗ shumate_vector_renderer_render (
1103 self,
1104 data->tile,
1105 job->data,
1106 &job->source_position,
1107 &job->paintable,
1108 &job->symbols
1109 );
1110 }
1111
1112 ✗ g_idle_add ((GSourceFunc)render_job_finish, job);
1113 ✗ }
1114
1115 static void
1116 ✗ chain_cancel (GCancellable *source,
1117 GCancellable *dest)
1118 {
1119 ✗ g_cancellable_cancel (dest);
1120 ✗ }
1121
1122 static gboolean
1123 ✗ begin_render (ShumateVectorRenderer *self,
1124 GTask *task,
1125 GBytes *tile_data,
1126 ShumateGridPosition *source_position)
1127 {
1128 ✗ g_autoptr(GError) error = NULL;
1129 ✗ RenderJob *job = NULL;
1130 ✗ TaskData *data = (TaskData *)g_task_get_task_data (task);
1131
1132 ✗ if (data->current_job != NULL)
1133 ✗ g_cancellable_cancel (data->current_job->cancellable);
1134
1135 ✗ job = g_new0 (RenderJob, 1);
1136 ✗ job->cancellable = g_cancellable_new ();
1137 ✗ job->task = g_object_ref (task);
1138 ✗ job->data = g_bytes_ref (tile_data);
1139 ✗ job->source_position = *source_position;
1140 ✗ data->current_job = job;
1141
1142 /* If the input cancellable is cancelled, stop the render job. */
1143 ✗ if (g_task_get_cancellable (task) != NULL)
1144 {
1145 ✗ job->cancellable_handle = g_cancellable_connect (
1146 g_task_get_cancellable (task),
1147 G_CALLBACK (chain_cancel),
1148 job, NULL
1149 );
1150 }
1151
1152 ✗ if (self->thread_pool == NULL)
1153 {
1154 ✗ self->thread_pool = g_thread_pool_new_full (
1155 (GFunc)thread_func,
1156 NULL,
1157 (GDestroyNotify)render_job_unref,
1158 ✗ g_get_num_processors () - 1,
1159 FALSE,
1160 &error
1161 );
1162 ✗ if (self->thread_pool == NULL)
1163 {
1164 ✗ g_critical ("Failed to create thread pool: %s", error->message);
1165 ✗ return FALSE;
1166 }
1167 }
1168
1169 ✗ if (!g_thread_pool_push (self->thread_pool, job, &error))
1170 {
1171 ✗ g_critical ("Failed to push job to thread pool: %s", error->message);
1172 ✗ return FALSE;
1173 }
1174
1175 return TRUE;
1176 }
1177
1178
1179 /**
1180 * shumate_style_error_quark:
1181 *
1182 * Returns: a #GQuark
1183 */
1184
2/2
✓ Branch 2 → 3 taken 3 times.
✓ Branch 2 → 5 taken 42 times.
45 G_DEFINE_QUARK (shumate-style-error-quark, shumate_style_error);
1185