[libshumate] vector: Add ShumateVectorStyle



commit 5d2d232a719ceb867eaa940a03e04d428e1875c7
Author: James Westman <james jwestman net>
Date:   Mon Aug 23 01:23:22 2021 -0500

    vector: Add ShumateVectorStyle
    
    Here begins the epic vector tiles merge request...
    
    ShumateVectorStyle is the main entry point of the vector renderer. By
    setting the `style` property of a ShumateNetworkTileSource, tiles will
    be interpreted as protobuf/Mapbox Vector Tile formatted files rather
    than raster images.
    
    So far, though, none of that is implemented and ShumateVectorStyle
    contains a dummy renderer.

 demos/shumate-demo-window.c           |  13 +++
 shumate/meson.build                   |   2 +
 shumate/shumate-network-tile-source.c | 201 +++++++++++++++++++++++++++-----
 shumate/shumate-network-tile-source.h |  14 +++
 shumate/shumate-vector-style.c        | 208 ++++++++++++++++++++++++++++++++++
 shumate/shumate-vector-style.h        |  33 ++++++
 shumate/shumate.h                     |   2 +
 7 files changed, 443 insertions(+), 30 deletions(-)
---
diff --git a/demos/shumate-demo-window.c b/demos/shumate-demo-window.c
index 88b6e5c..311e42e 100644
--- a/demos/shumate-demo-window.c
+++ b/demos/shumate-demo-window.c
@@ -138,6 +138,8 @@ shumate_demo_window_init (ShumateDemoWindow *self)
 {
   ShumateViewport *viewport;
   GtkExpression *expression;
+  g_autoptr(ShumateVectorStyle) style = NULL;
+  ShumateMapSource *map_source = NULL;
 
   gtk_widget_init_template (GTK_WIDGET (self));
 
@@ -148,6 +150,17 @@ shumate_demo_window_init (ShumateDemoWindow *self)
   gtk_drop_down_set_expression (self->layers_dropdown, expression);
   gtk_drop_down_set_model (self->layers_dropdown, G_LIST_MODEL (self->registry));
 
+  style = shumate_vector_style_create ("{}", NULL);
+
+  map_source = SHUMATE_MAP_SOURCE (shumate_network_tile_source_new_vector_full (
+    "vector-tiles",
+    "Vector Tiles",
+    "© OpenStreetMap contributors", NULL, 0, 14, 512,
+    SHUMATE_MAP_PROJECTION_MERCATOR,
+    "https://jwestman.pages.gitlab.gnome.org/vector-tile-test-data/world_overview/#Z#/#X#/#Y#.pbf";,
+    style
+  ));
+  shumate_map_source_registry_add (self->registry, map_source);
 
   viewport = shumate_map_get_viewport (self->map);
 
diff --git a/shumate/meson.build b/shumate/meson.build
index 4c9542a..0326fd4 100644
--- a/shumate/meson.build
+++ b/shumate/meson.build
@@ -17,6 +17,7 @@ libshumate_public_h = [
   'shumate-point.h',
   'shumate-scale.h',
   'shumate-tile.h',
+  'shumate-vector-style.h',
   'shumate-viewport.h',
   'shumate.h',
 ]
@@ -46,6 +47,7 @@ libshumate_sources = [
   'shumate-point.c',
   'shumate-scale.c',
   'shumate-tile.c',
+  'shumate-vector-style.c',
   'shumate-viewport.c',
 ]
 
diff --git a/shumate/shumate-network-tile-source.c b/shumate/shumate-network-tile-source.c
index a25d090..1a18a16 100644
--- a/shumate/shumate-network-tile-source.c
+++ b/shumate/shumate-network-tile-source.c
@@ -56,6 +56,7 @@ enum
   PROP_MAX_CONNS,
   PROP_USER_AGENT,
   PROP_FILE_CACHE,
+  PROP_STYLE,
   N_PROPERTIES,
 };
 
@@ -69,6 +70,7 @@ typedef struct
   SoupSession *soup_session;
   int max_conns;
   ShumateFileCache *file_cache;
+  ShumateVectorStyle *style;
 } ShumateNetworkTileSourcePrivate;
 
 G_DEFINE_TYPE_WITH_PRIVATE (ShumateNetworkTileSource, shumate_network_tile_source, SHUMATE_TYPE_MAP_SOURCE);
@@ -136,6 +138,10 @@ shumate_network_tile_source_get_property (GObject *object,
       g_value_set_object (value, priv->file_cache);
       break;
 
+    case PROP_STYLE:
+      g_value_set_object (value, priv->style);
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     }
@@ -149,6 +155,7 @@ shumate_network_tile_source_set_property (GObject *object,
     GParamSpec *pspec)
 {
   ShumateNetworkTileSource *tile_source = SHUMATE_NETWORK_TILE_SOURCE (object);
+  ShumateNetworkTileSourcePrivate *priv = shumate_network_tile_source_get_instance_private (tile_source);
 
   switch (prop_id)
     {
@@ -172,6 +179,10 @@ shumate_network_tile_source_set_property (GObject *object,
       shumate_network_tile_source_set_user_agent (tile_source, g_value_get_string (value));
       break;
 
+    case PROP_STYLE:
+      g_set_object (&priv->style, g_value_get_object (value));
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     }
@@ -300,6 +311,18 @@ shumate_network_tile_source_class_init (ShumateNetworkTileSourceClass *klass)
                         SHUMATE_TYPE_FILE_CACHE,
                         G_PARAM_READABLE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
 
+  /**
+   * ShumateNetworkTileSource:style:
+   *
+   * The style for rendering vector tiles.
+   */
+  obj_properties[PROP_STYLE] =
+    g_param_spec_object("style",
+                        "Style",
+                        "Style",
+                        SHUMATE_TYPE_VECTOR_STYLE,
+                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
   g_object_class_install_properties (object_class, N_PROPERTIES, obj_properties);
 }
 
@@ -375,6 +398,53 @@ shumate_network_tile_source_new_full (const char *id,
 }
 
 
+/**
+ * shumate_network_tile_source_new_vector_full:
+ * @id: the map source's id
+ * @name: the map source's name
+ * @license: the map source's license
+ * @license_uri: the map source's license URI
+ * @min_zoom: the map source's minimum zoom level
+ * @max_zoom: the map source's maximum zoom level
+ * @tile_size: the map source's tile size (in pixels)
+ * @projection: the map source's projection
+ * @uri_format: the URI to fetch the tiles from, see #shumate_network_tile_source_set_uri_format
+ * @style: the style with which to render tiles
+ *
+ * Creates a [class@NetworkTileSource] that renders tiles with a [class@VectorStyle].
+ *
+ * Returns: a constructed [class@NetworkTileSource] object
+ */
+ShumateNetworkTileSource *
+shumate_network_tile_source_new_vector_full (const char *id,
+    const char *name,
+    const char *license,
+    const char *license_uri,
+    guint min_zoom,
+    guint max_zoom,
+    guint tile_size,
+    ShumateMapProjection projection,
+    const char *uri_format,
+    ShumateVectorStyle *style)
+{
+  ShumateNetworkTileSource *source;
+
+  source = g_object_new (SHUMATE_TYPE_NETWORK_TILE_SOURCE,
+        "id", id,
+        "name", name,
+        "license", license,
+        "license-uri", license_uri,
+        "min-zoom-level", min_zoom,
+        "max-zoom-level", max_zoom,
+        "tile-size", tile_size,
+        "projection", projection,
+        "uri-format", uri_format,
+        "style", style,
+        NULL);
+  return source;
+}
+
+
 /**
  * shumate_network_tile_source_get_uri_format:
  * @tile_source: the #ShumateNetworkTileSource
@@ -593,6 +663,24 @@ shumate_network_tile_source_set_user_agent (
 }
 
 
+/**
+ * shumate_network_tile_source_get_style:
+ * @self: a [class@NetworkTileSource]
+ *
+ * Gets the vector style used to render vector tiles, or %NULL if the source
+ * uses raster tiles.
+ *
+ * Returns: (transfer none): the source's vector rendering style
+ */
+ShumateVectorStyle *
+shumate_network_tile_source_get_style (ShumateNetworkTileSource *self)
+{
+  ShumateNetworkTileSourcePrivate *priv = shumate_network_tile_source_get_instance_private (self);
+  g_return_val_if_fail (SHUMATE_IS_NETWORK_TILE_SOURCE (self), NULL);
+  return priv->style;
+}
+
+
 #define SIZE 8
 static char *
 get_tile_uri (ShumateNetworkTileSource *tile_source,
@@ -646,11 +734,12 @@ get_tile_uri (ShumateNetworkTileSource *tile_source,
 }
 
 static void on_file_cache_get_tile (GObject *source_object, GAsyncResult *res, gpointer user_data);
-static void on_pixbuf_created_from_cache (GObject *source_object, GAsyncResult *res, gpointer user_data);
+static void on_tile_rendered_from_cache (GObject *source_object, GAsyncResult *res, gpointer user_data);
 static void fetch_from_network (GTask *task);
 static void on_message_sent (GObject *source_object, GAsyncResult *res, gpointer user_data);
 static void on_message_read (GObject *source_object, GAsyncResult *res, gpointer user_data);
 static void on_pixbuf_created (GObject *source_object, GAsyncResult *res, gpointer user_data);
+static void on_tile_rendered (GObject *source_object, GAsyncResult *res, gpointer user_data);
 
 typedef struct {
   ShumateNetworkTileSource *self;
@@ -692,6 +781,77 @@ get_modified_time_string (GDateTime *modified_time)
 }
 
 
+static void
+render_tile_async (ShumateNetworkTileSource *self,
+                   ShumateTile *tile,
+                   GBytes *bytes,
+                   GCancellable *cancellable,
+                   GAsyncReadyCallback callback,
+                   gpointer user_data)
+{
+  ShumateNetworkTileSourcePrivate *priv = shumate_network_tile_source_get_instance_private (self);
+
+  g_autoptr(GTask) task = g_task_new (self, cancellable, callback, user_data);
+  g_task_set_source_tag (task, render_tile_async);
+
+  if (priv->style)
+    {
+      g_autoptr(GdkTexture) texture = NULL;
+      g_autoptr(GError) error = NULL;
+
+      texture = shumate_vector_style_render (priv->style, shumate_tile_get_size (tile));
+      if (error != NULL)
+        {
+          g_task_return_error (task, g_steal_pointer (&error));
+          return;
+        }
+
+      shumate_tile_set_texture (tile, texture);
+      shumate_tile_set_fade_in (tile, TRUE);
+
+      g_task_return_boolean (task, TRUE);
+    }
+  else
+    {
+      g_autoptr(GInputStream) input_stream = g_memory_input_stream_new_from_bytes (bytes);
+
+      g_task_set_task_data (task, g_object_ref (tile), g_object_unref);
+
+      gdk_pixbuf_new_from_stream_async (input_stream, cancellable, on_pixbuf_created, g_object_ref (task));
+    }
+}
+
+static void
+on_pixbuf_created (GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+  g_autoptr(GTask) task = user_data;
+  g_autoptr(GError) error = NULL;
+  g_autoptr(GdkPixbuf) pixbuf = NULL;
+  g_autoptr(GdkTexture) texture = NULL;
+  ShumateTile *tile = SHUMATE_TILE (g_task_get_task_data (task));
+
+  pixbuf = gdk_pixbuf_new_from_stream_finish (res, &error);
+  if (error != NULL)
+    {
+      g_task_return_error (task, g_steal_pointer (&error));
+      return;
+    }
+
+  texture = gdk_texture_new_for_pixbuf (pixbuf);
+  shumate_tile_set_texture (tile, texture);
+  shumate_tile_set_fade_in (tile, TRUE);
+
+  g_task_return_boolean (task, TRUE);
+}
+
+gboolean
+render_tile_finish (ShumateNetworkTileSource *self,
+                    GAsyncResult *result,
+                    GError **error)
+{
+  return g_task_propagate_boolean (G_TASK (result), error);
+}
+
 static void
 fill_tile_async (ShumateMapSource *self,
                  ShumateTile *tile,
@@ -740,23 +900,17 @@ on_file_cache_get_tile (GObject *source_object, GAsyncResult *res, gpointer user
                                                     &data->etag, &data->modtime, res, NULL);
 
   if (data->bytes != NULL)
-    {
-      g_autoptr(GInputStream) input_stream = g_memory_input_stream_new_from_bytes (data->bytes);
-
-      /* When on_pixbuf_created_from_cache() is called, it will call
-       * fetch_from_network() if needed */
-      gdk_pixbuf_new_from_stream_async (input_stream, cancellable, on_pixbuf_created_from_cache, 
g_object_ref (task));
-    }
+    /* When on_pixbuf_created_from_cache() is called, it will call
+     * fetch_from_network() if needed */
+    render_tile_async (data->self, data->tile, data->bytes, cancellable, on_tile_rendered_from_cache, 
g_object_ref (task));
   else
-    {
-      fetch_from_network (task);
-    }
+    fetch_from_network (task);
 }
 
 /* Fill the tile from the pixbuf, created from the cache. Then, if the cache
  * data is potentially out of date, fetch from the network. */
 static void
-on_pixbuf_created_from_cache (GObject *source_object, GAsyncResult *res, gpointer user_data)
+on_tile_rendered_from_cache (GObject *source_object, GAsyncResult *res, gpointer user_data)
 {
   g_autoptr(GTask) task = user_data;
   FillTileData *data = g_task_get_task_data (task);
@@ -764,18 +918,12 @@ on_pixbuf_created_from_cache (GObject *source_object, GAsyncResult *res, gpointe
   g_autoptr(GdkPixbuf) pixbuf = NULL;
   g_autoptr(GError) error = NULL;
 
-  pixbuf = gdk_pixbuf_new_from_stream_finish (res, &error);
-
-  if (error != NULL)
+  if (!render_tile_finish (data->self, res, &error))
     {
       g_task_return_error (task, g_steal_pointer (&error));
       return;
     }
 
-  texture = gdk_texture_new_for_pixbuf (pixbuf);
-  shumate_tile_set_texture (data->tile, texture);
-  shumate_tile_set_fade_in (data->tile, TRUE);
-
   if (data->bytes != NULL && !tile_is_expired (data->modtime))
     {
       shumate_tile_set_state (data->tile, SHUMATE_STATE_DONE);
@@ -921,9 +1069,7 @@ on_message_read (GObject *source_object, GAsyncResult *res, gpointer user_data)
   GOutputStream *output_stream = G_OUTPUT_STREAM (source_object);
   g_autoptr(GTask) task = user_data;
   FillTileData *data = g_task_get_task_data (task);
-  GCancellable *cancellable = g_task_get_cancellable (task);
   g_autoptr(GError) error = NULL;
-  g_autoptr(GInputStream) input_stream = NULL;
 
   g_output_stream_splice_finish (output_stream, res, &error);
   if (error != NULL)
@@ -935,15 +1081,14 @@ on_message_read (GObject *source_object, GAsyncResult *res, gpointer user_data)
   g_bytes_unref (data->bytes);
   data->bytes = g_memory_output_stream_steal_as_bytes (G_MEMORY_OUTPUT_STREAM (output_stream));
 
-  input_stream = g_memory_input_stream_new_from_bytes (data->bytes);
-  gdk_pixbuf_new_from_stream_async (input_stream, cancellable, on_pixbuf_created, g_object_ref (task));
+  render_tile_async (data->self, data->tile, data->bytes, NULL, on_tile_rendered, g_object_ref (task));
 }
 
 /* Fill the tile from the pixbuf, created from the network response. Begin
  * storing the data in the cache (but don't wait for that to finish). Then
  * return. */
 static void
-on_pixbuf_created (GObject *source_object, GAsyncResult *res, gpointer user_data)
+on_tile_rendered (GObject *source_object, GAsyncResult *res, gpointer user_data)
 {
   g_autoptr(GTask) task = user_data;
   FillTileData *data = g_task_get_task_data (task);
@@ -953,8 +1098,7 @@ on_pixbuf_created (GObject *source_object, GAsyncResult *res, gpointer user_data
   g_autoptr(GdkPixbuf) pixbuf = NULL;
   g_autoptr(GdkTexture) texture = NULL;
 
-  pixbuf = gdk_pixbuf_new_from_stream_finish (res, &error);
-  if (error != NULL)
+  if (!render_tile_finish (data->self, res, &error))
     {
       g_task_return_error (task, g_steal_pointer (&error));
       return;
@@ -962,11 +1106,8 @@ on_pixbuf_created (GObject *source_object, GAsyncResult *res, gpointer user_data
 
   shumate_file_cache_store_tile_async (priv->file_cache, data->tile, data->bytes, data->etag, cancellable, 
NULL, NULL);
 
-  texture = gdk_texture_new_for_pixbuf (pixbuf);
-  shumate_tile_set_texture (data->tile, texture);
-  shumate_tile_set_fade_in (data->tile, TRUE);
-
   shumate_tile_set_state (data->tile, SHUMATE_STATE_DONE);
+
   g_task_return_boolean (task, TRUE);
 }
 
diff --git a/shumate/shumate-network-tile-source.h b/shumate/shumate-network-tile-source.h
index 0332a97..062ec11 100644
--- a/shumate/shumate-network-tile-source.h
+++ b/shumate/shumate-network-tile-source.h
@@ -25,6 +25,7 @@
 #ifndef _SHUMATE_NETWORK_TILE_SOURCE_H_
 #define _SHUMATE_NETWORK_TILE_SOURCE_H_
 
+#include <shumate/shumate-vector-style.h>
 #include <shumate/shumate-map-source.h>
 
 G_BEGIN_DECLS
@@ -77,6 +78,17 @@ ShumateNetworkTileSource *shumate_network_tile_source_new_full (const char *id,
     ShumateMapProjection projection,
     const char *uri_format);
 
+ShumateNetworkTileSource *shumate_network_tile_source_new_vector_full (const char *id,
+    const char *name,
+    const char *license,
+    const char *license_uri,
+    guint min_zoom,
+    guint max_zoom,
+    guint tile_size,
+    ShumateMapProjection projection,
+    const char *uri_format,
+    ShumateVectorStyle *style);
+
 const char *shumate_network_tile_source_get_uri_format (ShumateNetworkTileSource *tile_source);
 void shumate_network_tile_source_set_uri_format (ShumateNetworkTileSource *tile_source,
     const char *uri_format);
@@ -96,6 +108,8 @@ void shumate_network_tile_source_set_max_conns (ShumateNetworkTileSource *tile_s
 void shumate_network_tile_source_set_user_agent (ShumateNetworkTileSource *tile_source,
     const char *user_agent);
 
+ShumateVectorStyle *shumate_network_tile_source_get_style (ShumateNetworkTileSource *self);
+
 G_END_DECLS
 
 #endif /* _SHUMATE_NETWORK_TILE_SOURCE_H_ */
diff --git a/shumate/shumate-vector-style.c b/shumate/shumate-vector-style.c
new file mode 100644
index 0000000..841cde9
--- /dev/null
+++ b/shumate/shumate-vector-style.c
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2021 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 <cairo/cairo.h>
+
+#include "shumate-vector-style.h"
+
+struct _ShumateVectorStyle
+{
+  GObject parent_instance;
+
+  char *style_json;
+};
+
+G_DEFINE_TYPE (ShumateVectorStyle, shumate_vector_style, G_TYPE_OBJECT)
+
+enum {
+  PROP_0,
+  PROP_STYLE_JSON,
+  N_PROPS
+};
+
+static GParamSpec *properties [N_PROPS];
+
+
+/**
+ * shumate_vector_style_create:
+ * @style_json: a JSON string
+ * @error: return location for a [class@GLib.Error], or %NULL
+ *
+ * Creates a vector style from a JSON definition.
+ *
+ * Returns: (transfer full): a new [class@VectorStyle] object, or %NULL if the
+ * style could not be parsed
+ */
+ShumateVectorStyle *
+shumate_vector_style_create (const char *style_json, GError **error)
+{
+  g_return_val_if_fail (style_json != NULL, NULL);
+
+  return g_object_new (SHUMATE_TYPE_VECTOR_STYLE,
+                       "style-json", style_json,
+                       NULL);
+}
+
+
+static void
+shumate_vector_style_finalize (GObject *object)
+{
+  ShumateVectorStyle *self = (ShumateVectorStyle *)object;
+
+  g_clear_pointer (&self->style_json, g_free);
+
+  G_OBJECT_CLASS (shumate_vector_style_parent_class)->finalize (object);
+}
+
+static void
+shumate_vector_style_get_property (GObject    *object,
+                                   guint       prop_id,
+                                   GValue     *value,
+                                   GParamSpec *pspec)
+{
+  ShumateVectorStyle *self = SHUMATE_VECTOR_STYLE (object);
+
+  switch (prop_id)
+    {
+    case PROP_STYLE_JSON:
+      g_value_set_string (value, self->style_json);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+shumate_vector_style_set_property (GObject      *object,
+                                   guint         prop_id,
+                                   const GValue *value,
+                                   GParamSpec   *pspec)
+{
+  ShumateVectorStyle *self = SHUMATE_VECTOR_STYLE (object);
+
+  switch (prop_id)
+    {
+    case PROP_STYLE_JSON:
+      /* Property is construct only, so it should only be set once */
+      g_assert (self->style_json == NULL);
+      self->style_json = g_strdup (g_value_get_string (value));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+shumate_vector_style_class_init (ShumateVectorStyleClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = shumate_vector_style_finalize;
+  object_class->get_property = shumate_vector_style_get_property;
+  object_class->set_property = shumate_vector_style_set_property;
+
+  properties[PROP_STYLE_JSON] =
+    g_param_spec_string ("style-json",
+                         "Style JSON",
+                         "Style JSON",
+                         NULL,
+                         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 void
+shumate_vector_style_init (ShumateVectorStyle *self)
+{
+}
+
+
+/**
+ * shumate_vector_style_get_style_json:
+ * @self: a [class@VectorStyle]
+ *
+ * Gets the JSON string from which this vector style was loaded.
+ *
+ * Returns: (nullable): the style JSON, or %NULL if none is set
+ */
+const char *
+shumate_vector_style_get_style_json (ShumateVectorStyle *self)
+{
+  g_return_val_if_fail (SHUMATE_IS_VECTOR_STYLE (self), NULL);
+
+  return self->style_json;
+}
+
+
+static GdkTexture *
+texture_new_for_surface (cairo_surface_t *surface)
+{
+  g_autoptr(GBytes) bytes = NULL;
+  GdkTexture *texture;
+
+  g_return_val_if_fail (cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_IMAGE, NULL);
+  g_return_val_if_fail (cairo_image_surface_get_width (surface) > 0, NULL);
+  g_return_val_if_fail (cairo_image_surface_get_height (surface) > 0, NULL);
+
+  bytes = g_bytes_new_with_free_func (cairo_image_surface_get_data (surface),
+                                      cairo_image_surface_get_height (surface)
+                                      * cairo_image_surface_get_stride (surface),
+                                      (GDestroyNotify) cairo_surface_destroy,
+                                      cairo_surface_reference (surface));
+
+  texture = gdk_memory_texture_new (cairo_image_surface_get_width (surface),
+                                    cairo_image_surface_get_height (surface),
+                                    GDK_MEMORY_B8G8R8A8_PREMULTIPLIED,
+                                    bytes,
+                                    cairo_image_surface_get_stride (surface));
+
+  return texture;
+}
+
+
+/**
+ * shumate_vector_style_render:
+ * @self: a [class@VectorStyle]
+ *
+ * Renders a tile to a texture using this style.
+ *
+ * Returns: (transfer full): a [class@Gdk.Texture] containing the rendered tile
+ */
+GdkTexture *
+shumate_vector_style_render (ShumateVectorStyle *self, int size)
+{
+  GdkTexture *texture;
+  cairo_t *cr;
+  cairo_surface_t *surface;
+
+  g_return_val_if_fail (SHUMATE_IS_VECTOR_STYLE (self), NULL);
+
+  surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, size, size);
+  cr = cairo_create (surface);
+
+  cairo_set_source_rgb (cr, 0, 0, 0);
+  cairo_paint (cr);
+
+  texture = texture_new_for_surface (surface);
+
+  cairo_destroy (cr);
+  cairo_surface_destroy (surface);
+
+  return texture;
+}
+
diff --git a/shumate/shumate-vector-style.h b/shumate/shumate-vector-style.h
new file mode 100644
index 0000000..ac0d66f
--- /dev/null
+++ b/shumate/shumate-vector-style.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2021 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 <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define SHUMATE_TYPE_VECTOR_STYLE (shumate_vector_style_get_type())
+
+G_DECLARE_FINAL_TYPE (ShumateVectorStyle, shumate_vector_style, SHUMATE, VECTOR_STYLE, GObject)
+
+ShumateVectorStyle *shumate_vector_style_create (const char *style_json, GError **error);
+
+GdkTexture *shumate_vector_style_render (ShumateVectorStyle *self, int size);
+
+G_END_DECLS
+
diff --git a/shumate/shumate.h b/shumate/shumate.h
index 0486d1a..2809970 100644
--- a/shumate/shumate.h
+++ b/shumate/shumate.h
@@ -48,6 +48,8 @@
 #include "shumate/shumate-memory-cache.h"
 #include "shumate/shumate-file-cache.h"
 
+#include "shumate/shumate-vector-style.h"
+
 #undef __SHUMATE_SHUMATE_H_INSIDE__
 
 #endif


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