summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenjamin Otte <otte.benjamin@googlemail.com>2023-03-11 00:26:18 +0000
committerBenjamin Otte <otte.benjamin@googlemail.com>2023-03-11 00:26:18 +0000
commit97d53b1e867715440df199731eb20d4935cb674a (patch)
tree814684e9924a908542f53d18622a07cdfb791f78
parent50d772788149f5a37fc521766b5d33a87336e95a (diff)
parent7c960034716db8846d0c06defa574260b98f7ac3 (diff)
downloadgtk+-97d53b1e867715440df199731eb20d4935cb674a.tar.gz
Merge branch 'wip/otte/rendernode-export' into 'main'
rendernode: Register SVG serializer See merge request GNOME/gtk!5637
-rw-r--r--demos/node-editor/node-editor-window.c190
-rw-r--r--gsk/gskrendernodeimpl.c134
2 files changed, 295 insertions, 29 deletions
diff --git a/demos/node-editor/node-editor-window.c b/demos/node-editor/node-editor-window.c
index c3b876ac5e..3fe3d818c7 100644
--- a/demos/node-editor/node-editor-window.c
+++ b/demos/node-editor/node-editor-window.c
@@ -32,6 +32,11 @@
#include "gsk/vulkan/gskvulkanrenderer.h"
#endif
+#include <cairo.h>
+#ifdef CAIRO_HAS_SVG_SURFACE
+#include <cairo-svg.h>
+#endif
+
typedef struct
{
gsize start_chars;
@@ -643,23 +648,34 @@ save_cb (GtkWidget *button,
g_object_unref (dialog);
}
-static GdkTexture *
-create_texture (NodeEditorWindow *self)
+static GskRenderNode *
+create_node (NodeEditorWindow *self)
{
GdkPaintable *paintable;
GtkSnapshot *snapshot;
- GskRenderer *renderer;
GskRenderNode *node;
- GdkTexture *texture;
paintable = gtk_picture_get_paintable (GTK_PICTURE (self->picture));
if (paintable == NULL ||
gdk_paintable_get_intrinsic_width (paintable) <= 0 ||
gdk_paintable_get_intrinsic_height (paintable) <= 0)
return NULL;
+
snapshot = gtk_snapshot_new ();
gdk_paintable_snapshot (paintable, snapshot, gdk_paintable_get_intrinsic_width (paintable), gdk_paintable_get_intrinsic_height (paintable));
node = gtk_snapshot_free_to_node (snapshot);
+
+ return node;
+}
+
+static GdkTexture *
+create_texture (NodeEditorWindow *self)
+{
+ GskRenderer *renderer;
+ GskRenderNode *node;
+ GdkTexture *texture;
+
+ node = create_node (self);
if (node == NULL)
return NULL;
@@ -670,6 +686,58 @@ create_texture (NodeEditorWindow *self)
return texture;
}
+#ifdef CAIRO_HAS_SVG_SURFACE
+static cairo_status_t
+cairo_serializer_write (gpointer user_data,
+ const unsigned char *data,
+ unsigned int length)
+{
+ g_byte_array_append (user_data, data, length);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static GBytes *
+create_svg (GskRenderNode *node,
+ GError **error)
+{
+ cairo_surface_t *surface;
+ cairo_t *cr;
+ graphene_rect_t bounds;
+ GByteArray *array;
+
+ gsk_render_node_get_bounds (node, &bounds);
+ array = g_byte_array_new ();
+
+ surface = cairo_svg_surface_create_for_stream (cairo_serializer_write,
+ array,
+ bounds.size.width,
+ bounds.size.height);
+ cairo_svg_surface_set_document_unit (surface, CAIRO_SVG_UNIT_PX);
+ cairo_surface_set_device_offset (surface, -bounds.origin.x, -bounds.origin.y);
+
+ cr = cairo_create (surface);
+ gsk_render_node_draw (node, cr);
+ cairo_destroy (cr);
+
+ cairo_surface_finish (surface);
+ if (cairo_surface_status (surface) == CAIRO_STATUS_SUCCESS)
+ {
+ cairo_surface_destroy (surface);
+ return g_byte_array_free_to_bytes (array);
+ }
+ else
+ {
+ g_set_error (error,
+ G_IO_ERROR, G_IO_ERROR_FAILED,
+ "%s", cairo_status_to_string (cairo_surface_status (surface)));
+ cairo_surface_destroy (surface);
+ g_byte_array_unref (array);
+ return NULL;
+ }
+}
+#endif
+
static GdkTexture *
create_cairo_texture (NodeEditorWindow *self)
{
@@ -702,50 +770,140 @@ create_cairo_texture (NodeEditorWindow *self)
}
static void
-export_image_response_cb (GObject *source,
+export_image_saved_cb (GObject *source,
+ GAsyncResult *result,
+ void *user_data)
+{
+ GError *error = NULL;
+
+ if (!g_file_replace_contents_finish (G_FILE (source), result, NULL, &error))
+ {
+ GtkAlertDialog *alert;
+
+ alert = gtk_alert_dialog_new ("Exporting to image failed");
+ gtk_alert_dialog_set_detail (alert, error->message);
+ gtk_alert_dialog_show (alert, NULL);
+ g_object_unref (alert);
+ g_clear_error (&error);
+ }
+}
+
+static void
+export_image_response_cb (GObject *source,
GAsyncResult *result,
- void *user_data)
+ void *user_data)
{
GtkFileDialog *dialog = GTK_FILE_DIALOG (source);
- GdkTexture *texture = user_data;
+ GskRenderNode *node = user_data;
GFile *file;
+ char *uri;
+ GBytes *bytes;
file = gtk_file_dialog_save_finish (dialog, result, NULL);
- if (file)
+ if (file == NULL)
+ {
+ gsk_render_node_unref (node);
+ return;
+ }
+
+ uri = g_file_get_uri (file);
+#ifdef CAIRO_HAS_SVG_SURFACE
+ if (g_str_has_suffix (uri, "svg"))
{
- if (!gdk_texture_save_to_png (texture, g_file_peek_path (file)))
+ GError *error = NULL;
+
+ bytes = create_svg (node, &error);
+ if (bytes == NULL)
{
GtkAlertDialog *alert;
alert = gtk_alert_dialog_new ("Exporting to image failed");
- gtk_alert_dialog_show (alert, GTK_WINDOW (gtk_window_get_transient_for (GTK_WINDOW (dialog))));
+ gtk_alert_dialog_set_detail (alert, error->message);
+ gtk_alert_dialog_show (alert, NULL);
g_object_unref (alert);
+ g_clear_error (&error);
}
+ }
+ else
+#endif
+ {
+ GdkTexture *texture;
+ GskRenderer *renderer;
- g_object_unref (file);
+ renderer = gsk_gl_renderer_new ();
+ if (!gsk_renderer_realize (renderer, NULL, NULL))
+ {
+ g_object_unref (renderer);
+ renderer = gsk_cairo_renderer_new ();
+ if (!gsk_renderer_realize (renderer, NULL, NULL))
+ {
+ g_assert_not_reached ();
+ }
+ }
+ texture = gsk_renderer_render_texture (renderer, node, NULL);
+ gsk_renderer_unrealize (renderer);
+ g_object_unref (renderer);
+
+ if (g_str_has_suffix (uri, "tiff"))
+ bytes = gdk_texture_save_to_tiff_bytes (texture);
+ else
+ bytes = gdk_texture_save_to_png_bytes (texture);
+ g_object_unref (texture);
}
+ g_free (uri);
- g_object_unref (texture);
+ if (bytes)
+ {
+ g_file_replace_contents_bytes_async (file,
+ bytes,
+ NULL,
+ FALSE,
+ 0,
+ NULL,
+ export_image_saved_cb,
+ NULL);
+ g_bytes_unref (bytes);
+ }
+ gsk_render_node_unref (node);
+ g_object_unref (file);
}
static void
export_image_cb (GtkWidget *button,
NodeEditorWindow *self)
{
- GdkTexture *texture;
+ GskRenderNode *node;
GtkFileDialog *dialog;
+ GtkFileFilter *filter;
+ GListStore *filters;
- texture = create_texture (self);
- if (texture == NULL)
+ node = create_node (self);
+ if (node == NULL)
return;
+ filters = g_list_store_new (GTK_TYPE_FILE_FILTER);
+ filter = gtk_file_filter_new ();
+ gtk_file_filter_add_mime_type (filter, "image/png");
+ g_list_store_append (filters, filter);
+ g_object_unref (filter);
+ filter = gtk_file_filter_new ();
+ gtk_file_filter_add_mime_type (filter, "image/svg+xml");
+ g_list_store_append (filters, filter);
+ g_object_unref (filter);
+ filter = gtk_file_filter_new ();
+ gtk_file_filter_add_mime_type (filter, "image/tiff");
+ g_list_store_append (filters, filter);
+ g_object_unref (filter);
+
dialog = gtk_file_dialog_new ();
gtk_file_dialog_set_title (dialog, "");
gtk_file_dialog_set_initial_name (dialog, "example.png");
+ gtk_file_dialog_set_filters (dialog, G_LIST_MODEL (filters));
gtk_file_dialog_save (dialog,
GTK_WINDOW (gtk_widget_get_root (GTK_WIDGET (button))),
NULL,
- export_image_response_cb, texture);
+ export_image_response_cb, node);
+ g_object_unref (filters);
g_object_unref (dialog);
}
diff --git a/gsk/gskrendernodeimpl.c b/gsk/gskrendernodeimpl.c
index 5d1f19da5b..8e0b0a8415 100644
--- a/gsk/gskrendernodeimpl.c
+++ b/gsk/gskrendernodeimpl.c
@@ -21,8 +21,10 @@
#include "gskrendernodeprivate.h"
#include "gskcairoblurprivate.h"
+#include "gskcairorenderer.h"
#include "gskdebugprivate.h"
#include "gskdiffprivate.h"
+#include "gl/gskglrenderer.h"
#include "gskrendererprivate.h"
#include "gskroundedrectprivate.h"
#include "gsktransformprivate.h"
@@ -31,6 +33,10 @@
#include "gdk/gdkmemoryformatprivate.h"
#include "gdk/gdkprivate.h"
+#include <cairo.h>
+#ifdef CAIRO_HAS_SVG_SURFACE
+#include <cairo-svg.h>
+#endif
#include <hb-ot.h>
/* maximal number of rectangles we keep in a diff region before we throw
@@ -6227,9 +6233,9 @@ gsk_render_node_init_types_once (void)
}
static void
-gsk_render_node_content_serializer_finish (GObject *source,
- GAsyncResult *result,
- gpointer serializer)
+gsk_render_node_serialize_bytes_finish (GObject *source,
+ GAsyncResult *result,
+ gpointer serializer)
{
GOutputStream *stream = G_OUTPUT_STREAM (source);
GError *error = NULL;
@@ -6241,16 +6247,11 @@ gsk_render_node_content_serializer_finish (GObject *source,
}
static void
-gsk_render_node_content_serializer (GdkContentSerializer *serializer)
+gsk_render_node_serialize_bytes (GdkContentSerializer *serializer,
+ GBytes *bytes)
{
GInputStream *input;
- const GValue *value;
- GskRenderNode *node;
- GBytes *bytes;
- value = gdk_content_serializer_get_value (serializer);
- node = gsk_value_get_render_node (value);
- bytes = gsk_render_node_serialize (node);
input = g_memory_input_stream_new_from_bytes (bytes);
g_output_stream_splice_async (gdk_content_serializer_get_output_stream (serializer),
@@ -6258,16 +6259,111 @@ gsk_render_node_content_serializer (GdkContentSerializer *serializer)
G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE,
gdk_content_serializer_get_priority (serializer),
gdk_content_serializer_get_cancellable (serializer),
- gsk_render_node_content_serializer_finish,
+ gsk_render_node_serialize_bytes_finish,
serializer);
g_object_unref (input);
g_bytes_unref (bytes);
}
+#ifdef CAIRO_HAS_SVG_SURFACE
+static cairo_status_t
+gsk_render_node_cairo_serializer_write (gpointer user_data,
+ const unsigned char *data,
+ unsigned int length)
+{
+ g_byte_array_append (user_data, data, length);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+gsk_render_node_svg_serializer (GdkContentSerializer *serializer)
+{
+ GskRenderNode *node;
+ cairo_surface_t *surface;
+ cairo_t *cr;
+ graphene_rect_t bounds;
+ GByteArray *array;
+
+ node = gsk_value_get_render_node (gdk_content_serializer_get_value (serializer));
+ gsk_render_node_get_bounds (node, &bounds);
+ array = g_byte_array_new ();
+
+ surface = cairo_svg_surface_create_for_stream (gsk_render_node_cairo_serializer_write,
+ array,
+ bounds.size.width,
+ bounds.size.height);
+ cairo_svg_surface_set_document_unit (surface, CAIRO_SVG_UNIT_PX);
+ cairo_surface_set_device_offset (surface, -bounds.origin.x, -bounds.origin.y);
+
+ cr = cairo_create (surface);
+ gsk_render_node_draw (node, cr);
+ cairo_destroy (cr);
+
+ cairo_surface_finish (surface);
+ if (cairo_surface_status (surface) == CAIRO_STATUS_SUCCESS)
+ {
+ gsk_render_node_serialize_bytes (serializer, g_byte_array_free_to_bytes (array));
+ }
+ else
+ {
+ GError *error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_FAILED,
+ cairo_status_to_string (cairo_surface_status (surface)));
+ gdk_content_serializer_return_error (serializer, error);
+ g_byte_array_unref (array);
+ }
+ cairo_surface_destroy (surface);
+}
+#endif
+
+static void
+gsk_render_node_png_serializer (GdkContentSerializer *serializer)
+{
+ GskRenderNode *node;
+ GdkTexture *texture;
+ GskRenderer *renderer;
+ GBytes *bytes;
+
+ node = gsk_value_get_render_node (gdk_content_serializer_get_value (serializer));
+
+ renderer = gsk_gl_renderer_new ();
+ if (!gsk_renderer_realize (renderer, NULL, NULL))
+ {
+ g_object_unref (renderer);
+ renderer = gsk_cairo_renderer_new ();
+ if (!gsk_renderer_realize (renderer, NULL, NULL))
+ {
+ g_assert_not_reached ();
+ }
+ }
+ texture = gsk_renderer_render_texture (renderer, node, NULL);
+ gsk_renderer_unrealize (renderer);
+ g_object_unref (renderer);
+
+ bytes = gdk_texture_save_to_png_bytes (texture);
+ g_object_unref (texture);
+
+ gsk_render_node_serialize_bytes (serializer, bytes);
+}
+
+static void
+gsk_render_node_content_serializer (GdkContentSerializer *serializer)
+{
+ const GValue *value;
+ GskRenderNode *node;
+ GBytes *bytes;
+
+ value = gdk_content_serializer_get_value (serializer);
+ node = gsk_value_get_render_node (value);
+ bytes = gsk_render_node_serialize (node);
+
+ gsk_render_node_serialize_bytes (serializer, bytes);
+}
+
static void
gsk_render_node_content_deserializer_finish (GObject *source,
- GAsyncResult *result,
- gpointer deserializer)
+ GAsyncResult *result,
+ gpointer deserializer)
{
GOutputStream *stream = G_OUTPUT_STREAM (source);
GError *error = NULL;
@@ -6331,6 +6427,18 @@ gsk_render_node_init_content_serializers (void)
gsk_render_node_content_serializer,
NULL,
NULL);
+#ifdef CAIRO_HAS_SVG_SURFACE
+ gdk_content_register_serializer (GSK_TYPE_RENDER_NODE,
+ "image/svg+xml",
+ gsk_render_node_svg_serializer,
+ NULL,
+ NULL);
+#endif
+ gdk_content_register_serializer (GSK_TYPE_RENDER_NODE,
+ "image/png",
+ gsk_render_node_png_serializer,
+ NULL,
+ NULL);
gdk_content_register_deserializer ("application/x-gtk-render-node",
GSK_TYPE_RENDER_NODE,