[libshumate] vector: Add sprite and fill-pattern support



commit 645f061963d1ec0da9353aa383344a42393146b0
Author: James Westman <james jwestman net>
Date:   Tue Jun 7 22:09:09 2022 -0500

    vector: Add sprite and fill-pattern support

 demos/map-style.json                               |   9 +
 demos/org.gnome.Shumate.Demo.gresources.xml        |   2 +
 demos/shumate-demo-window.c                        |  17 ++
 demos/sprites.json                                 |   9 +
 demos/sprites.svg                                  |  57 +++++
 shumate/meson.build                                |   2 +
 shumate/shumate-vector-renderer.c                  |  44 ++++
 shumate/shumate-vector-renderer.h                  |   4 +
 shumate/vector/shumate-vector-fill-layer.c         |  35 ++-
 .../vector/shumate-vector-render-scope-private.h   |   3 +
 .../vector/shumate-vector-sprite-sheet-private.h   |  35 +++
 shumate/vector/shumate-vector-sprite-sheet.c       | 237 +++++++++++++++++++++
 12 files changed, 452 insertions(+), 2 deletions(-)
---
diff --git a/demos/map-style.json b/demos/map-style.json
index 3fed433..f620544 100644
--- a/demos/map-style.json
+++ b/demos/map-style.json
@@ -32,6 +32,15 @@
         }
       }
     },
+    {
+      "id": "water-pattern",
+      "type": "fill",
+      "source-layer": "water",
+      "paint": {
+        "fill-pattern": "ocean",
+        "fill-opacity": 0.1
+      }
+    },
     {
       "id": "country_boundary",
       "type": "line",
diff --git a/demos/org.gnome.Shumate.Demo.gresources.xml b/demos/org.gnome.Shumate.Demo.gresources.xml
index 1ebd6cf..d839b5b 100644
--- a/demos/org.gnome.Shumate.Demo.gresources.xml
+++ b/demos/org.gnome.Shumate.Demo.gresources.xml
@@ -8,5 +8,7 @@
   </gresource>
   <gresource prefix="/org/gnome/Shumate/Demo/styles">
     <file preprocess="json-stripblanks">map-style.json</file>
+    <file preprocess="json-stripblanks">sprites.json</file>
+    <file>sprites.svg</file>
   </gresource>
 </gresources>
diff --git a/demos/shumate-demo-window.c b/demos/shumate-demo-window.c
index 1b1feaf..4067fb1 100644
--- a/demos/shumate-demo-window.c
+++ b/demos/shumate-demo-window.c
@@ -162,6 +162,23 @@ shumate_demo_window_init (ShumateDemoWindow *self)
         }
       else
         {
+          g_autoptr(GdkPixbuf) sprites_pixbuf = NULL;
+          g_autoptr(GBytes) sprites_json = NULL;
+
+          sprites_json = g_resources_lookup_data ("/org/gnome/Shumate/Demo/styles/sprites.json", 
G_RESOURCE_LOOKUP_FLAGS_NONE, NULL);
+          sprites_pixbuf = gdk_pixbuf_new_from_resource ("/org/gnome/Shumate/Demo/styles/sprites.svg", NULL);
+
+          shumate_vector_renderer_set_sprite_sheet_data (renderer,
+                                                         sprites_pixbuf,
+                                                         g_bytes_get_data (sprites_json, NULL),
+                                                         &error);
+
+          if (error)
+            {
+              g_warning ("Failed to create spritesheet for vector map style: %s", error->message);
+              g_clear_error (&error);
+            }
+
           shumate_map_source_set_license (SHUMATE_MAP_SOURCE (renderer), "© OpenStreetMap contributors");
           shumate_map_source_registry_add (self->registry, SHUMATE_MAP_SOURCE (renderer));
         }
diff --git a/demos/sprites.json b/demos/sprites.json
new file mode 100644
index 0000000..469fefb
--- /dev/null
+++ b/demos/sprites.json
@@ -0,0 +1,9 @@
+{
+  "ocean": {
+    "height": 8,
+    "pixelRatio": 1,
+    "width": 16,
+    "x": 0,
+    "y": 0
+  }
+}
\ No newline at end of file
diff --git a/demos/sprites.svg b/demos/sprites.svg
new file mode 100644
index 0000000..c8328cd
--- /dev/null
+++ b/demos/sprites.svg
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   width="16"
+   height="8"
+   viewBox="0 0 16 8"
+   version="1.1"
+   id="svg5"
+   inkscape:version="1.2 (dc2aedaf03, 2022-05-15)"
+   sodipodi:docname="sprites.svg"
+   inkscape:export-filename="sprites.png"
+   inkscape:export-xdpi="96"
+   inkscape:export-ydpi="96"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape";
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd";
+   xmlns="http://www.w3.org/2000/svg";
+   xmlns:svg="http://www.w3.org/2000/svg";>
+  <sodipodi:namedview
+     id="namedview7"
+     pagecolor="#505050"
+     bordercolor="#eeeeee"
+     borderopacity="1"
+     inkscape:showpageshadow="0"
+     inkscape:pageopacity="0"
+     inkscape:pagecheckerboard="0"
+     inkscape:deskcolor="#505050"
+     inkscape:document-units="px"
+     showgrid="true"
+     inkscape:zoom="24.4375"
+     inkscape:cx="9.8005115"
+     inkscape:cy="5.2378517"
+     inkscape:window-width="1920"
+     inkscape:window-height="1011"
+     inkscape:window-x="0"
+     inkscape:window-y="0"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="layer1">
+    <inkscape:grid
+       type="xygrid"
+       id="grid1050"
+       empspacing="4"
+       originx="0"
+       originy="0" />
+  </sodipodi:namedview>
+  <defs
+     id="defs2" />
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1">
+    <path
+       id="path1104"
+       style="color:#000000;fill:#fefefe;stroke-linecap:square;-inkscape-stroke:none"
+       d="M 7.5,1 C 7.5,1.8343462 6.790313,2.7024995 5.4335938,3.3808594 4.0768734,4.0592192 2.1415758,4.5 
0,4.5 v 1 C 2.2766854,5.5 4.3421614,5.0447404 5.8808594,4.2753906 6.7921632,3.8197391 7.5394361,3.2357286 
8,2.5507812 8.460564,3.2357286 9.2078367,3.8197391 10.119141,4.2753906 11.657839,5.0447404 13.723315,5.5 
16,5.5 v -1 C 13.858424,4.5 11.923127,4.0592192 10.566406,3.3808594 9.2096868,2.7024995 8.5,1.8343462 8.5,1 
Z" />
+  </g>
+</svg>
diff --git a/shumate/meson.build b/shumate/meson.build
index 94a5257..1e02f8a 100644
--- a/shumate/meson.build
+++ b/shumate/meson.build
@@ -43,6 +43,7 @@ libshumate_private_h = [
   'vector/shumate-vector-layer-private.h',
   'vector/shumate-vector-line-layer-private.h',
   'vector/shumate-vector-render-scope-private.h',
+  'vector/shumate-vector-sprite-sheet-private.h',
   'vector/shumate-vector-symbol-private.h',
   'vector/shumate-vector-symbol-container-private.h',
   'vector/shumate-vector-symbol-info-private.h',
@@ -170,6 +171,7 @@ if get_option('vector_renderer')
     'vector/shumate-vector-layer.c',
     'vector/shumate-vector-line-layer.c',
     'vector/shumate-vector-render-scope.c',
+    'vector/shumate-vector-sprite-sheet.c',
     'vector/shumate-vector-symbol.c',
     'vector/shumate-vector-symbol-container.c',
     'vector/shumate-vector-symbol-info.c',
diff --git a/shumate/shumate-vector-renderer.c b/shumate/shumate-vector-renderer.c
index ab1cd49..aa8d2ab 100644
--- a/shumate/shumate-vector-renderer.c
+++ b/shumate/shumate-vector-renderer.c
@@ -42,6 +42,10 @@ struct _ShumateVectorRenderer
   ShumateDataSource *data_source;
   GPtrArray *tiles;
 
+#ifdef SHUMATE_HAS_VECTOR_RENDERER
+  ShumateVectorSpriteSheet *sprites;
+#endif
+
   char *style_json;
 
   GPtrArray *layers;
@@ -132,6 +136,9 @@ shumate_vector_renderer_finalize (GObject *object)
   g_clear_pointer (&self->source_name, g_free);
   g_clear_object (&self->data_source);
   g_clear_pointer (&self->tiles, g_ptr_array_unref);
+#ifdef SHUMATE_HAS_VECTOR_RENDERER
+  g_clear_object (&self->sprites);
+#endif
 
   G_OBJECT_CLASS (shumate_vector_renderer_parent_class)->finalize (object);
 }
@@ -424,6 +431,42 @@ shumate_vector_renderer_get_style_json (ShumateVectorRenderer *self)
 }
 
 
+/**
+ * shumate_vector_renderer_set_sprite_sheet_data:
+ * @self: a [class@VectorRenderer]
+ * @sprites_pixbuf: a [class@Gdk.Pixbuf]
+ * @sprites_json: a JSON string
+ * @error: return location for a #GError, or %NULL
+ *
+ * Sets the sprite sheet used by the style JSON to render icons and textures.
+ *
+ * See <https://maplibre.org/maplibre-gl-js-docs/style-spec/sprite/> for
+ * details about the spritesheet format. Most stylesheets provide these files
+ * along with the main style JSON.
+ */
+void
+shumate_vector_renderer_set_sprite_sheet_data (ShumateVectorRenderer  *self,
+                                               GdkPixbuf              *sprites_pixbuf,
+                                               const char             *sprites_json,
+                                               GError                **error)
+{
+  g_return_if_fail (SHUMATE_IS_VECTOR_RENDERER (self));
+  g_return_if_fail (GDK_IS_PIXBUF (sprites_pixbuf));
+  g_return_if_fail (sprites_json != NULL);
+
+#ifdef SHUMATE_HAS_VECTOR_RENDERER
+  g_clear_object (&self->sprites);
+  self->sprites = shumate_vector_sprite_sheet_new (sprites_pixbuf, sprites_json, NULL, error);
+#else
+  g_set_error (error,
+               SHUMATE_STYLE_ERROR,
+               SHUMATE_STYLE_ERROR_SUPPORT_OMITTED,
+               "Libshumate was compiled without support for vector tiles.");
+  return FALSE;
+#endif
+}
+
+
 #ifdef SHUMATE_HAS_VECTOR_RENDERER
 static GdkTexture *
 texture_new_for_surface (cairo_surface_t *surface)
@@ -540,6 +583,7 @@ render (ShumateVectorRenderer *self,
   scope.target_size = texture_size;
   scope.zoom_level = zoom_level;
   scope.symbols = symbols;
+  scope.sprites = self->sprites;
 
   surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, texture_size, texture_size);
   scope.cr = cairo_create (surface);
diff --git a/shumate/shumate-vector-renderer.h b/shumate/shumate-vector-renderer.h
index 1e62f2b..fddabd0 100644
--- a/shumate/shumate-vector-renderer.h
+++ b/shumate/shumate-vector-renderer.h
@@ -35,6 +35,10 @@ ShumateVectorRenderer *shumate_vector_renderer_new (const char  *id,
                                                     const char  *style_json,
                                                     GError     **error);
 
+void shumate_vector_renderer_set_sprite_sheet_data (ShumateVectorRenderer  *self,
+                                                    GdkPixbuf              *sprites_pixbuf,
+                                                    const char             *sprites_json,
+                                                    GError                **error);
 
 /**
  * SHUMATE_STYLE_ERROR:
diff --git a/shumate/vector/shumate-vector-fill-layer.c b/shumate/vector/shumate-vector-fill-layer.c
index 2e49ecd..e1384e0 100644
--- a/shumate/vector/shumate-vector-fill-layer.c
+++ b/shumate/vector/shumate-vector-fill-layer.c
@@ -18,6 +18,7 @@
 #include <gtk/gtk.h>
 #include "shumate-vector-expression-private.h"
 #include "shumate-vector-fill-layer-private.h"
+#include "shumate-vector-sprite-sheet-private.h"
 #include "shumate-vector-utils-private.h"
 #include "shumate-vector-value-private.h"
 
@@ -27,6 +28,7 @@ struct _ShumateVectorFillLayer
 
   ShumateVectorExpression *color;
   ShumateVectorExpression *opacity;
+  ShumateVectorExpression *pattern;
 };
 
 G_DEFINE_TYPE (ShumateVectorFillLayer, shumate_vector_fill_layer, SHUMATE_TYPE_VECTOR_LAYER)
@@ -50,6 +52,9 @@ shumate_vector_fill_layer_create_from_json (JsonObject *object, GError **error)
 
       if (!(layer->opacity = shumate_vector_expression_from_json (json_object_get_member (paint, 
"fill-opacity"), error)))
         return NULL;
+
+      if (!(layer->pattern = shumate_vector_expression_from_json (json_object_get_member (paint, 
"fill-pattern"), error)))
+        return NULL;
     }
 
   return (ShumateVectorLayer *)layer;
@@ -62,14 +67,40 @@ shumate_vector_fill_layer_render (ShumateVectorLayer *layer, ShumateVectorRender
   ShumateVectorFillLayer *self = SHUMATE_VECTOR_FILL_LAYER (layer);
   GdkRGBA color = SHUMATE_VECTOR_COLOR_BLACK;
   double opacity;
+  g_autofree char *pattern;
 
   shumate_vector_expression_eval_color (self->color, scope, &color);
   opacity = shumate_vector_expression_eval_number (self->opacity, scope, 1.0);
+  pattern = shumate_vector_expression_eval_string (self->pattern, scope, NULL);
 
   shumate_vector_render_scope_exec_geometry (scope);
 
-  cairo_set_source_rgba (scope->cr, color.red, color.green, color.blue, color.alpha * opacity);
-  cairo_fill (scope->cr);
+  if (pattern != NULL)
+    {
+      GdkPixbuf *sprite = shumate_vector_sprite_sheet_get_icon (scope->sprites, pattern);
+      if (sprite != NULL)
+        {
+          cairo_pattern_t *cr_pattern;
+          cairo_matrix_t matrix;
+
+          gdk_cairo_set_source_pixbuf (scope->cr, sprite, 0, 0);
+          cr_pattern = cairo_get_source (scope->cr);
+          cairo_matrix_init_scale (&matrix, 1 / scope->scale, 1 / scope->scale);
+          cairo_pattern_set_matrix (cr_pattern, &matrix);
+          cairo_pattern_set_extend (cr_pattern, CAIRO_EXTEND_REPEAT);
+
+          /* Use cairo_paint_with_alpha so that we can set fill-opacity correctly. */
+          cairo_save (scope->cr);
+          cairo_clip (scope->cr);
+          cairo_paint_with_alpha (scope->cr, opacity);
+          cairo_restore (scope->cr);
+        }
+    }
+  else
+    {
+      cairo_set_source_rgba (scope->cr, color.red, color.green, color.blue, color.alpha * opacity);
+      cairo_fill (scope->cr);
+    }
 }
 
 
diff --git a/shumate/vector/shumate-vector-render-scope-private.h 
b/shumate/vector/shumate-vector-render-scope-private.h
index eb121b3..a2797c6 100644
--- a/shumate/vector/shumate-vector-render-scope-private.h
+++ b/shumate/vector/shumate-vector-render-scope-private.h
@@ -20,6 +20,7 @@
 #include <glib-object.h>
 #include <cairo/cairo.h>
 #include "vector_tile.pb-c.h"
+#include "shumate-vector-sprite-sheet-private.h"
 #include "shumate-vector-value-private.h"
 #include "shumate-vector-utils-private.h"
 
@@ -31,6 +32,8 @@ typedef struct {
 
   GPtrArray *symbols;
 
+  ShumateVectorSpriteSheet *sprites;
+
   VectorTile__Tile *tile;
   VectorTile__Tile__Layer *layer;
   VectorTile__Tile__Feature *feature;
diff --git a/shumate/vector/shumate-vector-sprite-sheet-private.h 
b/shumate/vector/shumate-vector-sprite-sheet-private.h
new file mode 100644
index 0000000..4201605
--- /dev/null
+++ b/shumate/vector/shumate-vector-sprite-sheet-private.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2022 James Westman <james jwestman net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <gdk/gdk.h>
+
+G_BEGIN_DECLS
+
+#define SHUMATE_TYPE_VECTOR_SPRITE_SHEET (shumate_vector_sprite_sheet_get_type())
+G_DECLARE_FINAL_TYPE (ShumateVectorSpriteSheet, shumate_vector_sprite_sheet, SHUMATE, VECTOR_SPRITE_SHEET, 
GObject)
+
+ShumateVectorSpriteSheet *shumate_vector_sprite_sheet_new (GdkPixbuf     *pixbuf,
+                                                           const char    *json,
+                                                           GCancellable  *cancellable,
+                                                           GError       **error);
+
+GdkPixbuf *shumate_vector_sprite_sheet_get_icon (ShumateVectorSpriteSheet *self,
+                                                 const char               *name);
+
+G_END_DECLS
diff --git a/shumate/vector/shumate-vector-sprite-sheet.c b/shumate/vector/shumate-vector-sprite-sheet.c
new file mode 100644
index 0000000..d36506e
--- /dev/null
+++ b/shumate/vector/shumate-vector-sprite-sheet.c
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2022 James Westman <james jwestman net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <json-glib/json-glib.h>
+#include "shumate-vector-sprite-sheet-private.h"
+#include "shumate-vector-utils-private.h"
+
+struct _ShumateVectorSpriteSheet
+{
+  GObject parent_instance;
+
+  char *json;
+  GdkPixbuf *pixbuf;
+  GHashTable *sprites;
+};
+
+enum {
+  PROP_0,
+  PROP_PIXBUF,
+  PROP_JSON,
+  N_PROPS,
+};
+
+static GParamSpec *properties [N_PROPS];
+
+static void shumate_vector_sprite_sheet_initable_iface_init (GInitableIface *iface);
+
+G_DEFINE_FINAL_TYPE_WITH_CODE (ShumateVectorSpriteSheet, shumate_vector_sprite_sheet, G_TYPE_OBJECT,
+                               G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, 
shumate_vector_sprite_sheet_initable_iface_init))
+
+
+typedef struct {
+  int x, y;
+  int width, height;
+  GdkPixbuf *pixbuf;
+} Sprite;
+
+static void
+sprite_free (Sprite *sprite)
+{
+  g_clear_object (&sprite->pixbuf);
+  g_free (sprite);
+}
+
+
+ShumateVectorSpriteSheet *
+shumate_vector_sprite_sheet_new (GdkPixbuf     *pixbuf,
+                                 const char    *json,
+                                 GCancellable  *cancellable,
+                                 GError       **error)
+{
+  return g_initable_new (SHUMATE_TYPE_VECTOR_SPRITE_SHEET, cancellable, error,
+                         "json", json,
+                         "pixbuf", pixbuf,
+                         NULL);
+}
+
+static void
+shumate_vector_sprite_sheet_get_property (GObject    *object,
+                                          guint       prop_id,
+                                          GValue     *value,
+                                          GParamSpec *pspec)
+{
+  ShumateVectorSpriteSheet *self = SHUMATE_VECTOR_SPRITE_SHEET (object);
+
+  switch (prop_id)
+    {
+    case PROP_PIXBUF:
+      g_value_set_object (value, self->pixbuf);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+shumate_vector_sprite_sheet_set_property (GObject      *object,
+                                          guint         prop_id,
+                                          const GValue *value,
+                                          GParamSpec   *pspec)
+{
+  ShumateVectorSpriteSheet *self = SHUMATE_VECTOR_SPRITE_SHEET (object);
+
+  switch (prop_id)
+    {
+    case PROP_PIXBUF:
+      /* Property is construct only, so it should only be set once */
+      g_assert (self->pixbuf == NULL);
+      self->pixbuf = g_value_dup_object (value);
+      break;
+    case PROP_JSON:
+      /* Property is construct only, so it should only be set once */
+      g_assert (self->json == NULL);
+      self->json = g_value_dup_string (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+shumate_vector_sprite_sheet_finalize (GObject *object)
+{
+  ShumateVectorSpriteSheet *self = (ShumateVectorSpriteSheet *)object;
+
+  g_clear_pointer (&self->json, g_free);
+  g_clear_object (&self->pixbuf);
+  g_clear_pointer (&self->sprites, g_hash_table_unref);
+
+  G_OBJECT_CLASS (shumate_vector_sprite_sheet_parent_class)->finalize (object);
+}
+
+static void
+shumate_vector_sprite_sheet_class_init (ShumateVectorSpriteSheetClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = shumate_vector_sprite_sheet_finalize;
+  object_class->get_property = shumate_vector_sprite_sheet_get_property;
+  object_class->set_property = shumate_vector_sprite_sheet_set_property;
+
+  properties[PROP_JSON] =
+    g_param_spec_string ("json",
+                         "JSON",
+                         "JSON",
+                         NULL,
+                         G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY | 
G_PARAM_STATIC_STRINGS);
+
+  properties[PROP_PIXBUF] =
+    g_param_spec_object ("pixbuf",
+                         "Pixbuf",
+                         "Pixbuf",
+                         GDK_TYPE_PIXBUF,
+                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY | 
G_PARAM_STATIC_STRINGS);
+
+  g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static gboolean
+shumate_vector_sprite_sheet_initable_init (GInitable     *initable,
+                                           GCancellable  *cancellable,
+                                           GError       **error)
+{
+  ShumateVectorSpriteSheet *self = (ShumateVectorSpriteSheet *)initable;
+  g_autoptr(JsonNode) json_node = NULL;
+  JsonObject *sprites;
+  JsonObjectIter iter;
+  const char *sprite_name;
+  JsonNode *sprite_node;
+
+  json_node = json_from_string (self->json, error);
+  g_clear_pointer (&self->json, g_free);
+
+  if (json_node == NULL)
+    return FALSE;
+
+  if (!shumate_vector_json_get_object (json_node, &sprites, error))
+    return FALSE;
+
+  json_object_iter_init (&iter, sprites);
+  while (json_object_iter_next (&iter, &sprite_name, &sprite_node))
+    {
+      Sprite *sprite;
+      JsonObject *sprite_object;
+
+      if (!shumate_vector_json_get_object (sprite_node, &sprite_object, error))
+        return FALSE;
+
+      sprite = g_new0 (Sprite, 1);
+      sprite->x = json_object_get_int_member_with_default (sprite_object, "x", 0);
+      sprite->y = json_object_get_int_member_with_default (sprite_object, "y", 0);
+      sprite->width = json_object_get_int_member_with_default (sprite_object, "width", 0);
+      sprite->height = json_object_get_int_member_with_default (sprite_object, "height", 0);
+
+      g_hash_table_insert (self->sprites, g_strdup (sprite_name), sprite);
+    }
+
+  return TRUE;
+}
+
+static void
+shumate_vector_sprite_sheet_initable_iface_init (GInitableIface *iface)
+{
+  iface->init = shumate_vector_sprite_sheet_initable_init;
+}
+
+static void
+shumate_vector_sprite_sheet_init (ShumateVectorSpriteSheet *self)
+{
+  self->sprites = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify)sprite_free);
+}
+
+/*< private >
+ * shumate_vector_sprite_sheet_get_icon:
+ * @self: a [class@VectorSpriteSheet]
+ * @name: an icon name
+ *
+ * Gets an icon's image from the spritesheet.
+ *
+ * Returns: (transfer none) (nullable): a [class@Gdk.Pixbuf], or %NULL if the
+ * icon does not exist.
+ */
+GdkPixbuf *
+shumate_vector_sprite_sheet_get_icon (ShumateVectorSpriteSheet *self,
+                                      const char               *name)
+{
+  Sprite *sprite;
+
+  sprite = g_hash_table_lookup (self->sprites, name);
+  if (sprite == NULL)
+    return NULL;
+
+  if (sprite->pixbuf != NULL)
+    return sprite->pixbuf;
+
+  sprite->pixbuf = gdk_pixbuf_new_subpixbuf (self->pixbuf,
+                                             sprite->x,
+                                             sprite->y,
+                                             sprite->width,
+                                             sprite->height);
+
+  return sprite->pixbuf;
+}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]