diff options
author | Benjamin Otte <otte.benjamin@googlemail.com> | 2023-03-11 00:26:18 +0000 |
---|---|---|
committer | Benjamin Otte <otte.benjamin@googlemail.com> | 2023-03-11 00:26:18 +0000 |
commit | 97d53b1e867715440df199731eb20d4935cb674a (patch) | |
tree | 814684e9924a908542f53d18622a07cdfb791f78 | |
parent | 50d772788149f5a37fc521766b5d33a87336e95a (diff) | |
parent | 7c960034716db8846d0c06defa574260b98f7ac3 (diff) | |
download | gtk+-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.c | 190 | ||||
-rw-r--r-- | gsk/gskrendernodeimpl.c | 134 |
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, |