Compare commits
25 Commits
main
...
wip/baeder
Author | SHA1 | Date | |
---|---|---|---|
|
2b5c49fb58 | ||
|
c0791ca38e | ||
|
8ca1a46272 | ||
|
97b688702b | ||
|
31e503aa46 | ||
|
5d63bfd285 | ||
|
78859b9e2a | ||
|
d4dd43f93d | ||
|
ed51dd58b5 | ||
|
f3742a37db | ||
|
f1ebc75f58 | ||
|
0ec6abcedb | ||
|
cc1cc27037 | ||
|
b86fd0269e | ||
|
adb6d5f965 | ||
|
982524c860 | ||
|
b56c102f8e | ||
|
a3daaac009 | ||
|
77ace0a39f | ||
|
6c9011d181 | ||
|
e6865e1c21 | ||
|
cc20e07347 | ||
|
e37319193c | ||
|
107e462831 | ||
|
76254a18ad |
@@ -32,7 +32,6 @@ xvfb-run -a -s "-screen 0 1024x768x24" \
|
||||
--timeout-multiplier 2 \
|
||||
--print-errorlogs \
|
||||
--suite=gtk \
|
||||
--no-suite=gtk:gsk \
|
||||
--no-suite=gtk:a11y
|
||||
|
||||
# Save the exit code
|
||||
|
@@ -1,3 +1,4 @@
|
||||
subdir('gtk-demo')
|
||||
subdir('icon-browser')
|
||||
subdir('node-editor')
|
||||
subdir('widget-factory')
|
||||
|
28
demos/node-editor/main.c
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <node-editor-application.h>
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
return g_application_run (G_APPLICATION (node_editor_application_new ()), argc, argv);
|
||||
}
|
17
demos/node-editor/meson.build
Normal file
@@ -0,0 +1,17 @@
|
||||
node_editor_sources = [
|
||||
'main.c',
|
||||
'node-editor-application.c',
|
||||
'node-editor-window.c',
|
||||
]
|
||||
|
||||
node_editor_resources = gnome.compile_resources('node_editor_resources',
|
||||
'node-editor.gresource.xml',
|
||||
source_dir: '.')
|
||||
|
||||
executable('gtk4-node-editor',
|
||||
node_editor_sources, node_editor_resources,
|
||||
dependencies: libgtk_dep,
|
||||
include_directories: confinc,
|
||||
gui_app: true,
|
||||
link_args: extra_demo_ldflags,
|
||||
install: false)
|
114
demos/node-editor/node-editor-application.c
Normal file
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "node-editor-application.h"
|
||||
|
||||
#include "node-editor-window.h"
|
||||
|
||||
struct _NodeEditorApplication
|
||||
{
|
||||
GtkApplication parent;
|
||||
};
|
||||
|
||||
struct _NodeEditorApplicationClass
|
||||
{
|
||||
GtkApplicationClass parent_class;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE(NodeEditorApplication, node_editor_application, GTK_TYPE_APPLICATION);
|
||||
|
||||
static void
|
||||
node_editor_application_init (NodeEditorApplication *app)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
quit_activated (GSimpleAction *action,
|
||||
GVariant *parameter,
|
||||
gpointer data)
|
||||
{
|
||||
g_application_quit (G_APPLICATION (data));
|
||||
}
|
||||
|
||||
static GActionEntry app_entries[] =
|
||||
{
|
||||
{ "quit", quit_activated, NULL, NULL, NULL }
|
||||
};
|
||||
|
||||
static void
|
||||
node_editor_application_startup (GApplication *app)
|
||||
{
|
||||
const gchar *quit_accels[2] = { "<Ctrl>Q", NULL };
|
||||
|
||||
G_APPLICATION_CLASS (node_editor_application_parent_class)->startup (app);
|
||||
|
||||
g_action_map_add_action_entries (G_ACTION_MAP (app),
|
||||
app_entries, G_N_ELEMENTS (app_entries),
|
||||
app);
|
||||
gtk_application_set_accels_for_action (GTK_APPLICATION (app),
|
||||
"app.quit",
|
||||
quit_accels);
|
||||
}
|
||||
|
||||
static void
|
||||
node_editor_application_activate (GApplication *app)
|
||||
{
|
||||
NodeEditorWindow *win;
|
||||
|
||||
win = node_editor_window_new (NODE_EDITOR_APPLICATION (app));
|
||||
gtk_window_present (GTK_WINDOW (win));
|
||||
}
|
||||
|
||||
static void
|
||||
node_editor_application_open (GApplication *app,
|
||||
GFile **files,
|
||||
gint n_files,
|
||||
const gchar *hint)
|
||||
{
|
||||
NodeEditorWindow *win;
|
||||
gint i;
|
||||
|
||||
for (i = 0; i < n_files; i++)
|
||||
{
|
||||
win = node_editor_window_new (NODE_EDITOR_APPLICATION (app));
|
||||
node_editor_window_load (win, files[i]);
|
||||
gtk_window_present (GTK_WINDOW (win));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
node_editor_application_class_init (NodeEditorApplicationClass *class)
|
||||
{
|
||||
GApplicationClass *application_class = G_APPLICATION_CLASS (class);
|
||||
|
||||
application_class->startup = node_editor_application_startup;
|
||||
application_class->activate = node_editor_application_activate;
|
||||
application_class->open = node_editor_application_open;
|
||||
}
|
||||
|
||||
NodeEditorApplication *
|
||||
node_editor_application_new (void)
|
||||
{
|
||||
return g_object_new (NODE_EDITOR_APPLICATION_TYPE,
|
||||
"application-id", "org.gtk.gtk4.NodeEditor",
|
||||
"flags", G_APPLICATION_HANDLES_OPEN,
|
||||
NULL);
|
||||
}
|
38
demos/node-editor/node-editor-application.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __NODE_EDITOR_APPLICATION_H__
|
||||
#define __NODE_EDITOR_APPLICATION_H__
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
|
||||
#define NODE_EDITOR_APPLICATION_TYPE (node_editor_application_get_type ())
|
||||
#define NODE_EDITOR_APPLICATION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NODE_EDITOR_APPLICATION_TYPE, NodeEditorApplication))
|
||||
|
||||
|
||||
typedef struct _NodeEditorApplication NodeEditorApplication;
|
||||
typedef struct _NodeEditorApplicationClass NodeEditorApplicationClass;
|
||||
|
||||
|
||||
GType node_editor_application_get_type (void);
|
||||
NodeEditorApplication *node_editor_application_new (void);
|
||||
|
||||
|
||||
#endif /* __NODE_EDITOR_APPLICATION_H__ */
|
431
demos/node-editor/node-editor-window.c
Normal file
@@ -0,0 +1,431 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "node-editor-window.h"
|
||||
|
||||
#include "gsk/gskrendernodeparserprivate.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
gsize start_chars;
|
||||
gsize end_chars;
|
||||
char *message;
|
||||
} TextViewError;
|
||||
|
||||
struct _NodeEditorWindow
|
||||
{
|
||||
GtkApplicationWindow parent;
|
||||
|
||||
GtkWidget *picture;
|
||||
GtkWidget *text_view;
|
||||
GtkTextBuffer *text_buffer;
|
||||
|
||||
GArray *errors;
|
||||
};
|
||||
|
||||
struct _NodeEditorWindowClass
|
||||
{
|
||||
GtkApplicationWindowClass parent_class;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE(NodeEditorWindow, node_editor_window, GTK_TYPE_APPLICATION_WINDOW);
|
||||
|
||||
static void
|
||||
text_view_error_free (TextViewError *e)
|
||||
{
|
||||
g_free (e->message);
|
||||
}
|
||||
|
||||
static gchar *
|
||||
get_current_text (GtkTextBuffer *buffer)
|
||||
{
|
||||
GtkTextIter start, end;
|
||||
|
||||
gtk_text_buffer_get_start_iter (buffer, &start);
|
||||
gtk_text_buffer_get_end_iter (buffer, &end);
|
||||
gtk_text_buffer_remove_all_tags (buffer, &start, &end);
|
||||
|
||||
return gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
|
||||
}
|
||||
|
||||
static void
|
||||
deserialize_error_func (const GtkCssSection *section,
|
||||
const GError *error,
|
||||
gpointer user_data)
|
||||
{
|
||||
const GtkCssLocation *start_location = gtk_css_section_get_start_location (section);
|
||||
const GtkCssLocation *end_location = gtk_css_section_get_end_location (section);
|
||||
NodeEditorWindow *self = user_data;
|
||||
GtkTextIter start_iter, end_iter;
|
||||
TextViewError text_view_error;
|
||||
|
||||
gtk_text_buffer_get_iter_at_line_offset (self->text_buffer, &start_iter,
|
||||
start_location->lines,
|
||||
start_location->line_chars);
|
||||
gtk_text_buffer_get_iter_at_line_offset (self->text_buffer, &end_iter,
|
||||
end_location->lines,
|
||||
end_location->line_chars);
|
||||
|
||||
gtk_text_buffer_apply_tag_by_name (self->text_buffer, "error",
|
||||
&start_iter, &end_iter);
|
||||
|
||||
text_view_error.start_chars = start_location->chars;
|
||||
text_view_error.end_chars = end_location->chars;
|
||||
text_view_error.message = g_strdup (error->message);
|
||||
g_array_append_val (self->errors, text_view_error);
|
||||
}
|
||||
|
||||
static void
|
||||
text_changed (GtkTextBuffer *buffer,
|
||||
NodeEditorWindow *self)
|
||||
{
|
||||
GskRenderNode *node;
|
||||
char *text;
|
||||
GBytes *bytes;
|
||||
|
||||
g_array_remove_range (self->errors, 0, self->errors->len);
|
||||
text = get_current_text (self->text_buffer);
|
||||
bytes = g_bytes_new_take (text, strlen (text));
|
||||
|
||||
/* If this is too slow, go fix the parser performance */
|
||||
node = gsk_render_node_deserialize (bytes, deserialize_error_func, self);
|
||||
g_bytes_unref (bytes);
|
||||
if (node)
|
||||
{
|
||||
/* XXX: Is this code necessary or can we have API to turn nodes into paintables? */
|
||||
GtkSnapshot *snapshot;
|
||||
GdkPaintable *paintable;
|
||||
graphene_rect_t bounds;
|
||||
|
||||
snapshot = gtk_snapshot_new ();
|
||||
gsk_render_node_get_bounds (node, &bounds);
|
||||
gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (- bounds.origin.x, - bounds.origin.y));
|
||||
gtk_snapshot_append_node (snapshot, node);
|
||||
gsk_render_node_unref (node);
|
||||
paintable = gtk_snapshot_free_to_paintable (snapshot, &bounds.size);
|
||||
gtk_picture_set_paintable (GTK_PICTURE (self->picture), paintable);
|
||||
g_clear_object (&paintable);
|
||||
}
|
||||
else
|
||||
{
|
||||
gtk_picture_set_paintable (GTK_PICTURE (self->picture), NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
text_view_query_tooltip_cb (GtkWidget *widget,
|
||||
int x,
|
||||
int y,
|
||||
gboolean keyboard_tip,
|
||||
GtkTooltip *tooltip,
|
||||
NodeEditorWindow *self)
|
||||
{
|
||||
GtkTextIter iter;
|
||||
guint i;
|
||||
|
||||
if (keyboard_tip)
|
||||
{
|
||||
gint offset;
|
||||
|
||||
g_object_get (self->text_buffer, "cursor-position", &offset, NULL);
|
||||
gtk_text_buffer_get_iter_at_offset (self->text_buffer, &iter, offset);
|
||||
}
|
||||
else
|
||||
{
|
||||
gint bx, by, trailing;
|
||||
|
||||
gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (self->text_view), GTK_TEXT_WINDOW_TEXT,
|
||||
x, y, &bx, &by);
|
||||
gtk_text_view_get_iter_at_position (GTK_TEXT_VIEW (self->text_view), &iter, &trailing, bx, by);
|
||||
}
|
||||
|
||||
for (i = 0; i < self->errors->len; i ++)
|
||||
{
|
||||
const TextViewError *e = &g_array_index (self->errors, TextViewError, i);
|
||||
GtkTextIter start_iter, end_iter;
|
||||
|
||||
gtk_text_buffer_get_iter_at_offset (self->text_buffer, &start_iter, e->start_chars);
|
||||
gtk_text_buffer_get_iter_at_offset (self->text_buffer, &end_iter, e->end_chars);
|
||||
|
||||
if (gtk_text_iter_in_range (&iter, &start_iter, &end_iter))
|
||||
{
|
||||
gtk_tooltip_set_text (tooltip, e->message);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
node_editor_window_load (NodeEditorWindow *self,
|
||||
GFile *file)
|
||||
{
|
||||
GtkTextIter end;
|
||||
GBytes *bytes;
|
||||
|
||||
bytes = g_file_load_bytes (file, NULL, NULL, NULL);
|
||||
if (bytes == NULL)
|
||||
return FALSE;
|
||||
|
||||
if (!g_utf8_validate (g_bytes_get_data (bytes, NULL), g_bytes_get_size (bytes), NULL))
|
||||
{
|
||||
g_bytes_unref (bytes);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gtk_text_buffer_get_end_iter (self->text_buffer, &end);
|
||||
gtk_text_buffer_insert (self->text_buffer,
|
||||
&end,
|
||||
g_bytes_get_data (bytes, NULL),
|
||||
g_bytes_get_size (bytes));
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
open_response_cb (GtkWidget *dialog,
|
||||
gint response,
|
||||
NodeEditorWindow *self)
|
||||
{
|
||||
gtk_widget_hide (dialog);
|
||||
|
||||
if (response == GTK_RESPONSE_ACCEPT)
|
||||
{
|
||||
GFile *file;
|
||||
|
||||
file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
|
||||
node_editor_window_load (self, file);
|
||||
g_object_unref (file);
|
||||
}
|
||||
|
||||
gtk_widget_destroy (dialog);
|
||||
}
|
||||
|
||||
static void
|
||||
open_cb (GtkWidget *button,
|
||||
NodeEditorWindow *self)
|
||||
{
|
||||
GtkWidget *dialog;
|
||||
|
||||
dialog = gtk_file_chooser_dialog_new ("Open node file",
|
||||
GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (button))),
|
||||
GTK_FILE_CHOOSER_ACTION_OPEN,
|
||||
"_Cancel", GTK_RESPONSE_CANCEL,
|
||||
"_Load", GTK_RESPONSE_ACCEPT,
|
||||
NULL);
|
||||
|
||||
gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
|
||||
gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
|
||||
gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), ".");
|
||||
g_signal_connect (dialog, "response", G_CALLBACK (open_response_cb), self);
|
||||
gtk_widget_show (dialog);
|
||||
}
|
||||
|
||||
static void
|
||||
save_response_cb (GtkWidget *dialog,
|
||||
gint response,
|
||||
NodeEditorWindow *self)
|
||||
{
|
||||
gtk_widget_hide (dialog);
|
||||
|
||||
if (response == GTK_RESPONSE_ACCEPT)
|
||||
{
|
||||
char *text, *filename;
|
||||
GError *error = NULL;
|
||||
|
||||
text = get_current_text (self->text_buffer);
|
||||
|
||||
filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
|
||||
if (!g_file_set_contents (filename, text, -1, &error))
|
||||
{
|
||||
GtkWidget *dialog;
|
||||
|
||||
dialog = gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (self))),
|
||||
GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT,
|
||||
GTK_MESSAGE_INFO,
|
||||
GTK_BUTTONS_OK,
|
||||
"Saving failed");
|
||||
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
|
||||
"%s", error->message);
|
||||
g_signal_connect (dialog, "response", G_CALLBACK (gtk_widget_destroy), NULL);
|
||||
gtk_widget_show (dialog);
|
||||
g_error_free (error);
|
||||
}
|
||||
g_free (filename);
|
||||
}
|
||||
|
||||
gtk_widget_destroy (dialog);
|
||||
}
|
||||
|
||||
static void
|
||||
save_cb (GtkWidget *button,
|
||||
NodeEditorWindow *self)
|
||||
{
|
||||
GtkWidget *dialog;
|
||||
|
||||
dialog = gtk_file_chooser_dialog_new ("Save node",
|
||||
GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (button))),
|
||||
GTK_FILE_CHOOSER_ACTION_SAVE,
|
||||
"_Cancel", GTK_RESPONSE_CANCEL,
|
||||
"_Save", GTK_RESPONSE_ACCEPT,
|
||||
NULL);
|
||||
|
||||
gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
|
||||
gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
|
||||
gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog), TRUE);
|
||||
gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), ".");
|
||||
g_signal_connect (dialog, "response", G_CALLBACK (save_response_cb), self);
|
||||
gtk_widget_show (dialog);
|
||||
}
|
||||
|
||||
static GdkTexture *
|
||||
create_texture (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);
|
||||
if (node == NULL)
|
||||
return NULL;
|
||||
|
||||
/* ahem */
|
||||
renderer = GTK_ROOT_GET_IFACE (gtk_widget_get_root (GTK_WIDGET (self)))->get_renderer (gtk_widget_get_root (GTK_WIDGET (self)));
|
||||
texture = gsk_renderer_render_texture (renderer, node, NULL);
|
||||
gsk_render_node_unref (node);
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
static void
|
||||
export_image_response_cb (GtkWidget *dialog,
|
||||
gint response,
|
||||
GdkTexture *texture)
|
||||
{
|
||||
gtk_widget_hide (dialog);
|
||||
|
||||
if (response == GTK_RESPONSE_ACCEPT)
|
||||
{
|
||||
char *filename;
|
||||
|
||||
filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
|
||||
if (!gdk_texture_save_to_png (texture, filename))
|
||||
{
|
||||
GtkWidget *message_dialog;
|
||||
|
||||
message_dialog = gtk_message_dialog_new (GTK_WINDOW (gtk_window_get_transient_for (GTK_WINDOW (dialog))),
|
||||
GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT,
|
||||
GTK_MESSAGE_INFO,
|
||||
GTK_BUTTONS_OK,
|
||||
"Exporting to image failed");
|
||||
g_signal_connect (message_dialog, "response", G_CALLBACK (gtk_widget_destroy), NULL);
|
||||
gtk_widget_show (message_dialog);
|
||||
}
|
||||
g_free (filename);
|
||||
}
|
||||
|
||||
gtk_widget_destroy (dialog);
|
||||
g_object_unref (texture);
|
||||
}
|
||||
|
||||
static void
|
||||
export_image_cb (GtkWidget *button,
|
||||
NodeEditorWindow *self)
|
||||
{
|
||||
GdkTexture *texture;
|
||||
GtkWidget *dialog;
|
||||
|
||||
texture = create_texture (self);
|
||||
if (texture == NULL)
|
||||
return;
|
||||
|
||||
dialog = gtk_file_chooser_dialog_new ("",
|
||||
GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (button))),
|
||||
GTK_FILE_CHOOSER_ACTION_SAVE,
|
||||
"_Cancel", GTK_RESPONSE_CANCEL,
|
||||
"_Save", GTK_RESPONSE_ACCEPT,
|
||||
NULL);
|
||||
|
||||
gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
|
||||
gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
|
||||
gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog), TRUE);
|
||||
g_signal_connect (dialog, "response", G_CALLBACK (export_image_response_cb), texture);
|
||||
gtk_widget_show (dialog);
|
||||
}
|
||||
|
||||
static void
|
||||
node_editor_window_finalize (GObject *object)
|
||||
{
|
||||
NodeEditorWindow *self = (NodeEditorWindow *)object;
|
||||
|
||||
g_array_free (self->errors, TRUE);
|
||||
|
||||
G_OBJECT_CLASS (node_editor_window_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
node_editor_window_class_init (NodeEditorWindowClass *class)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
||||
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
|
||||
|
||||
object_class->finalize = node_editor_window_finalize;
|
||||
|
||||
gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (class),
|
||||
"/org/gtk/gtk4/node-editor/node-editor-window.ui");
|
||||
|
||||
gtk_widget_class_bind_template_child (widget_class, NodeEditorWindow, text_buffer);
|
||||
gtk_widget_class_bind_template_child (widget_class, NodeEditorWindow, text_view);
|
||||
gtk_widget_class_bind_template_child (widget_class, NodeEditorWindow, picture);
|
||||
|
||||
gtk_widget_class_bind_template_callback (widget_class, text_changed);
|
||||
gtk_widget_class_bind_template_callback (widget_class, text_view_query_tooltip_cb);
|
||||
gtk_widget_class_bind_template_callback (widget_class, open_cb);
|
||||
gtk_widget_class_bind_template_callback (widget_class, save_cb);
|
||||
gtk_widget_class_bind_template_callback (widget_class, export_image_cb);
|
||||
}
|
||||
|
||||
static void
|
||||
node_editor_window_init (NodeEditorWindow *self)
|
||||
{
|
||||
gtk_widget_init_template (GTK_WIDGET (self));
|
||||
|
||||
self->errors = g_array_new (FALSE, TRUE, sizeof (TextViewError));
|
||||
g_array_set_clear_func (self->errors, (GDestroyNotify)text_view_error_free);
|
||||
}
|
||||
|
||||
NodeEditorWindow *
|
||||
node_editor_window_new (NodeEditorApplication *application)
|
||||
{
|
||||
return g_object_new (NODE_EDITOR_WINDOW_TYPE,
|
||||
"application", application,
|
||||
NULL);
|
||||
}
|
42
demos/node-editor/node-editor-window.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __NODE_EDITOR_WINDOW_H__
|
||||
#define __NODE_EDITOR_WINDOW_H__
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "node-editor-application.h"
|
||||
|
||||
#define NODE_EDITOR_WINDOW_TYPE (node_editor_window_get_type ())
|
||||
#define NODE_EDITOR_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NODE_EDITOR_WINDOW_TYPE, NodeEditorWindow))
|
||||
|
||||
|
||||
typedef struct _NodeEditorWindow NodeEditorWindow;
|
||||
typedef struct _NodeEditorWindowClass NodeEditorWindowClass;
|
||||
|
||||
|
||||
GType node_editor_window_get_type (void);
|
||||
|
||||
NodeEditorWindow * node_editor_window_new (NodeEditorApplication *application);
|
||||
|
||||
gboolean node_editor_window_load (NodeEditorWindow *self,
|
||||
GFile *file);
|
||||
|
||||
#endif /* __NODE_EDITOR_WINDOW_H__ */
|
99
demos/node-editor/node-editor-window.ui
Normal file
@@ -0,0 +1,99 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<object class="GtkTextTagTable" id="tags">
|
||||
<child type="tag">
|
||||
<object class="GtkTextTag">
|
||||
<property name="name">error</property>
|
||||
<property name="underline">error</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<object class="GtkTextBuffer" id="text_buffer">
|
||||
<property name="tag-table">tags</property>
|
||||
<signal name="changed" handler="text_changed"/>
|
||||
</object>
|
||||
<template class="NodeEditorWindow" parent="GtkApplicationWindow">
|
||||
<style>
|
||||
<class name="devel"/>
|
||||
</style>
|
||||
<property name="title" translatable="yes">GTK Node Editor</property>
|
||||
<property name="default-width">1024</property>
|
||||
<property name="default-height">768</property>
|
||||
<child type="titlebar">
|
||||
<object class="GtkHeaderBar" id="header">
|
||||
<property name="title" translatable="yes">GTK Node Editor</property>
|
||||
<property name="show-title-buttons">1</property>
|
||||
<child type="start">
|
||||
<object class="GtkButton">
|
||||
<property name="icon-name">document-open-symbolic</property>
|
||||
<property name="tooltip-text">Open node file</property>
|
||||
<signal name="clicked" handler="open_cb"/>
|
||||
</object>
|
||||
</child>
|
||||
<child type="start">
|
||||
<object class="GtkButton">
|
||||
<property name="icon-name">document-save-symbolic</property>
|
||||
<property name="tooltip-text">Save to node file</property>
|
||||
<signal name="clicked" handler="save_cb"/>
|
||||
</object>
|
||||
</child>
|
||||
<child type="start">
|
||||
<object class="GtkButton">
|
||||
<property name="icon-name">insert-image-symbolic</property>
|
||||
<property name="tooltip-text">Export to image</property>
|
||||
<signal name="clicked" handler="export_image_cb"/>
|
||||
</object>
|
||||
</child>
|
||||
<child type="title">
|
||||
<object class="GtkLabel">
|
||||
<property name="label" translatable="yes">GTK Node Editor</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkPaned">
|
||||
<property name="shrink-child2">false</property>
|
||||
<property name="position">400</property>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow">
|
||||
<property name="hscrollbar-policy">never</property>
|
||||
<property name="expand">1</property>
|
||||
<child>
|
||||
<object class="GtkTextView" id="text_view">
|
||||
<property name="buffer">text_buffer</property>
|
||||
<property name="wrap-mode">word</property>
|
||||
<property name="monospace">1</property>
|
||||
<property name="has-focus">1</property>
|
||||
<property name="top-margin">6</property>
|
||||
<property name="left-margin">6</property>
|
||||
<property name="right-margin">6</property>
|
||||
<property name="bottom-margin">6</property>
|
||||
<property name="has-tooltip">1</property>
|
||||
<signal name="query-tooltip" handler="text_view_query_tooltip_cb"/>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow">
|
||||
<property name="expand">1</property>
|
||||
<property name="min-content-height">100</property>
|
||||
<property name="min-content-width">100</property>
|
||||
<child>
|
||||
<object class="GtkViewport">
|
||||
<child>
|
||||
<object class="GtkPicture" id="picture">
|
||||
<property name="can-shrink">0</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="valign">center</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</template>
|
||||
</interface>
|
6
demos/node-editor/node-editor.gresource.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<gresources>
|
||||
<gresource prefix="/org/gtk/gtk4/node-editor">
|
||||
<file preprocess="xml-stripblanks">node-editor-window.ui</file>
|
||||
</gresource>
|
||||
</gresources>
|
@@ -116,6 +116,8 @@ typedef struct {
|
||||
|
||||
int use_es;
|
||||
|
||||
int max_debug_label_length;
|
||||
|
||||
GdkGLContextPaintData *paint_data;
|
||||
} GdkGLContextPrivate;
|
||||
|
||||
@@ -457,11 +459,14 @@ gdk_gl_context_push_debug_group_printf (GdkGLContext *context,
|
||||
|
||||
if (priv->use_khr_debug)
|
||||
{
|
||||
int msg_len;
|
||||
|
||||
va_start (args, format);
|
||||
message = g_strdup_vprintf (format, args);
|
||||
va_end (args);
|
||||
|
||||
glPushDebugGroupKHR (GL_DEBUG_SOURCE_APPLICATION, 0, -1, message);
|
||||
msg_len = MIN (priv->max_debug_label_length, strlen (message) - 1);
|
||||
glPushDebugGroupKHR (GL_DEBUG_SOURCE_APPLICATION, 0, msg_len, message);
|
||||
g_free (message);
|
||||
}
|
||||
}
|
||||
@@ -500,11 +505,15 @@ gdk_gl_context_label_object_printf (GdkGLContext *context,
|
||||
|
||||
if (priv->use_khr_debug)
|
||||
{
|
||||
int msg_len;
|
||||
|
||||
va_start (args, format);
|
||||
message = g_strdup_vprintf (format, args);
|
||||
va_end (args);
|
||||
|
||||
glObjectLabel (identifier, name, -1, message);
|
||||
msg_len = MIN (priv->max_debug_label_length, strlen (message) - 1);
|
||||
|
||||
glObjectLabel (identifier, name, msg_len, message);
|
||||
g_free (message);
|
||||
}
|
||||
}
|
||||
@@ -992,7 +1001,10 @@ gdk_gl_context_check_extensions (GdkGLContext *context)
|
||||
display = gdk_draw_context_get_display (GDK_DRAW_CONTEXT (context));
|
||||
|
||||
if (priv->has_khr_debug && GDK_DISPLAY_DEBUG_CHECK (display, GL_DEBUG))
|
||||
priv->use_khr_debug = TRUE;
|
||||
{
|
||||
priv->use_khr_debug = TRUE;
|
||||
glGetIntegerv (GL_MAX_LABEL_LENGTH, &priv->max_debug_label_length);
|
||||
}
|
||||
if (!priv->use_es && GDK_DISPLAY_DEBUG_CHECK (display, GL_TEXTURE_RECT))
|
||||
priv->use_texture_rectangle = TRUE;
|
||||
else if (has_npot)
|
||||
|
@@ -535,8 +535,6 @@ gsk_gl_driver_get_texture_for_texture (GskGLDriver *self,
|
||||
}
|
||||
|
||||
t = create_texture (self, gdk_texture_get_width (texture), gdk_texture_get_height (texture));
|
||||
gdk_gl_context_label_object_printf (self->gl_context, GL_TEXTURE, t->texture_id,
|
||||
"GdkTexture<%p> %d", texture, t->texture_id);
|
||||
|
||||
if (gdk_texture_set_render_data (texture, self, t, gsk_gl_driver_release_texture))
|
||||
t->user = texture;
|
||||
@@ -547,6 +545,9 @@ gsk_gl_driver_get_texture_for_texture (GskGLDriver *self,
|
||||
surface,
|
||||
min_filter,
|
||||
mag_filter);
|
||||
gdk_gl_context_label_object_printf (self->gl_context, GL_TEXTURE, t->texture_id,
|
||||
"GdkTexture<%p> %d", texture, t->texture_id);
|
||||
|
||||
cairo_surface_destroy (surface);
|
||||
|
||||
return t->texture_id;
|
||||
|
@@ -326,6 +326,7 @@ struct _GskGLRenderer
|
||||
Program unblurred_outset_shadow_program;
|
||||
Program border_program;
|
||||
Program cross_fade_program;
|
||||
Program blend_program;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -479,14 +480,14 @@ render_fallback_node (GskGLRenderer *self,
|
||||
texture_id = gsk_gl_driver_create_texture (self->gl_driver,
|
||||
surface_width,
|
||||
surface_height);
|
||||
gdk_gl_context_label_object_printf (self->gl_context, GL_TEXTURE, texture_id,
|
||||
"Fallback %s %d", node->node_class->type_name, texture_id);
|
||||
|
||||
gsk_gl_driver_bind_source_texture (self->gl_driver, texture_id);
|
||||
gsk_gl_driver_init_texture_with_surface (self->gl_driver,
|
||||
texture_id,
|
||||
surface,
|
||||
GL_NEAREST, GL_NEAREST);
|
||||
gdk_gl_context_label_object_printf (self->gl_context, GL_TEXTURE, texture_id,
|
||||
"Fallback %s %d", node->node_class->type_name, texture_id);
|
||||
|
||||
|
||||
cairo_surface_destroy (surface);
|
||||
|
||||
@@ -1431,10 +1432,10 @@ render_outset_shadow_node (GskGLRenderer *self,
|
||||
GskRoundedRect blit_clip;
|
||||
|
||||
texture_id = gsk_gl_driver_create_texture (self->gl_driver, texture_width, texture_height);
|
||||
gdk_gl_context_label_object_printf (self->gl_context, GL_TEXTURE, texture_id,
|
||||
"Outset Shadow Temp %d", texture_id);
|
||||
gsk_gl_driver_bind_source_texture (self->gl_driver, texture_id);
|
||||
gsk_gl_driver_init_texture_empty (self->gl_driver, texture_id);
|
||||
gdk_gl_context_label_object_printf (self->gl_context, GL_TEXTURE, texture_id,
|
||||
"Outset Shadow Temp %d", texture_id);
|
||||
render_target = gsk_gl_driver_create_render_target (self->gl_driver, texture_id, FALSE, FALSE);
|
||||
gdk_gl_context_label_object_printf (self->gl_context, GL_FRAMEBUFFER, render_target,
|
||||
"Outset Shadow FB Temp %d", render_target);
|
||||
@@ -1468,10 +1469,10 @@ render_outset_shadow_node (GskGLRenderer *self,
|
||||
});
|
||||
|
||||
blurred_texture_id = gsk_gl_driver_create_permanent_texture (self->gl_driver, texture_width, texture_height);
|
||||
gdk_gl_context_label_object_printf (self->gl_context, GL_TEXTURE, blurred_texture_id,
|
||||
"Outset Shadow Cache %d", blurred_texture_id);
|
||||
gsk_gl_driver_bind_source_texture (self->gl_driver, blurred_texture_id);
|
||||
gsk_gl_driver_init_texture_empty (self->gl_driver, blurred_texture_id);
|
||||
gdk_gl_context_label_object_printf (self->gl_context, GL_TEXTURE, blurred_texture_id,
|
||||
"Outset Shadow Cache %d", blurred_texture_id);
|
||||
blurred_render_target = gsk_gl_driver_create_render_target (self->gl_driver, blurred_texture_id, TRUE, TRUE);
|
||||
gdk_gl_context_label_object_printf (self->gl_context, GL_FRAMEBUFFER, render_target,
|
||||
"Outset Shadow Cache FB %d", render_target);
|
||||
@@ -1900,6 +1901,54 @@ render_cross_fade_node (GskGLRenderer *self,
|
||||
ops_draw (builder, vertex_data);
|
||||
}
|
||||
|
||||
static inline void
|
||||
render_blend_node (GskGLRenderer *self,
|
||||
GskRenderNode *node,
|
||||
RenderOpBuilder *builder)
|
||||
{
|
||||
GskRenderNode *top_child = gsk_blend_node_get_top_child (node);
|
||||
GskRenderNode *bottom_child = gsk_blend_node_get_bottom_child (node);
|
||||
const float min_x = builder->dx + node->bounds.origin.x;
|
||||
const float min_y = builder->dy + node->bounds.origin.y;
|
||||
const float max_x = min_x + node->bounds.size.width;
|
||||
const float max_y = min_y + node->bounds.size.height;
|
||||
int top_texture_id;
|
||||
int bottom_texture_id;
|
||||
gboolean is_offscreen1, is_offscreen2;
|
||||
RenderOp op;
|
||||
const GskQuadVertex vertex_data[GL_N_VERTICES] = {
|
||||
{ { min_x, min_y }, { 0, 1 }, },
|
||||
{ { min_x, max_y }, { 0, 0 }, },
|
||||
{ { max_x, min_y }, { 1, 1 }, },
|
||||
|
||||
{ { max_x, max_y }, { 1, 0 }, },
|
||||
{ { min_x, max_y }, { 0, 0 }, },
|
||||
{ { max_x, min_y }, { 1, 1 }, },
|
||||
};
|
||||
|
||||
/* TODO: We create 2 textures here as big as the blend node, but both the
|
||||
* start and the end node might be a lot smaller than that. */
|
||||
add_offscreen_ops (self, builder,
|
||||
&node->bounds,
|
||||
bottom_child,
|
||||
&bottom_texture_id, &is_offscreen1,
|
||||
FORCE_OFFSCREEN | RESET_CLIP);
|
||||
|
||||
add_offscreen_ops (self, builder,
|
||||
&node->bounds,
|
||||
top_child,
|
||||
&top_texture_id, &is_offscreen2,
|
||||
FORCE_OFFSCREEN | RESET_CLIP);
|
||||
|
||||
ops_set_program (builder, &self->blend_program);
|
||||
ops_set_texture (builder, bottom_texture_id);
|
||||
op.op = OP_CHANGE_BLEND;
|
||||
op.blend.source2 = top_texture_id;
|
||||
op.blend.mode = gsk_blend_node_get_blend_mode (node);
|
||||
ops_add (builder, &op);
|
||||
ops_draw (builder, vertex_data);
|
||||
}
|
||||
|
||||
static inline void
|
||||
apply_viewport_op (const Program *program,
|
||||
const RenderOp *op)
|
||||
@@ -2174,6 +2223,18 @@ apply_cross_fade_op (const Program *program,
|
||||
glUniform1f (program->cross_fade.progress_location, op->cross_fade.progress);
|
||||
}
|
||||
|
||||
static inline void
|
||||
apply_blend_op (const Program *program,
|
||||
const RenderOp *op)
|
||||
{
|
||||
/* End texture id */
|
||||
glUniform1i (program->blend.source2_location, 1);
|
||||
glActiveTexture (GL_TEXTURE0 + 1);
|
||||
glBindTexture (GL_TEXTURE_2D, op->blend.source2);
|
||||
/* progress */
|
||||
glUniform1i (program->blend.mode_location, op->blend.mode);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_gl_renderer_dispose (GObject *gobject)
|
||||
{
|
||||
@@ -2206,6 +2267,7 @@ gsk_gl_renderer_create_programs (GskGLRenderer *self,
|
||||
{ "unblurred outset shadow", "unblurred_outset_shadow.fs.glsl" },
|
||||
{ "border", "border.fs.glsl" },
|
||||
{ "cross fade", "cross_fade.fs.glsl" },
|
||||
{ "blend", "blend.fs.glsl" },
|
||||
};
|
||||
|
||||
builder = gsk_shader_builder_new ();
|
||||
@@ -2336,6 +2398,10 @@ gsk_gl_renderer_create_programs (GskGLRenderer *self,
|
||||
INIT_PROGRAM_UNIFORM_LOCATION (cross_fade, progress);
|
||||
INIT_PROGRAM_UNIFORM_LOCATION (cross_fade, source2);
|
||||
|
||||
/* blend */
|
||||
INIT_PROGRAM_UNIFORM_LOCATION (blend, source2);
|
||||
INIT_PROGRAM_UNIFORM_LOCATION (blend, mode);
|
||||
|
||||
g_object_unref (builder);
|
||||
return TRUE;
|
||||
}
|
||||
@@ -2588,8 +2654,11 @@ gsk_gl_renderer_add_render_ops (GskGLRenderer *self,
|
||||
render_cross_fade_node (self, node, builder);
|
||||
break;
|
||||
|
||||
case GSK_REPEATING_LINEAR_GRADIENT_NODE:
|
||||
case GSK_BLEND_NODE:
|
||||
render_blend_node (self, node, builder);
|
||||
break;
|
||||
|
||||
case GSK_REPEATING_LINEAR_GRADIENT_NODE:
|
||||
case GSK_REPEAT_NODE:
|
||||
case GSK_CAIRO_NODE:
|
||||
default:
|
||||
@@ -2855,6 +2924,11 @@ gsk_gl_renderer_render_ops (GskGLRenderer *self,
|
||||
apply_cross_fade_op (program, op);
|
||||
break;
|
||||
|
||||
case OP_CHANGE_BLEND:
|
||||
g_assert (program == &self->blend_program);
|
||||
apply_blend_op (program, op);
|
||||
break;
|
||||
|
||||
case OP_CHANGE_LINEAR_GRADIENT:
|
||||
apply_linear_gradient_op (program, op);
|
||||
break;
|
||||
|
@@ -11,7 +11,7 @@
|
||||
#include "gskrendernodeprivate.h"
|
||||
|
||||
#define GL_N_VERTICES 6
|
||||
#define GL_N_PROGRAMS 11
|
||||
#define GL_N_PROGRAMS 12
|
||||
|
||||
|
||||
|
||||
@@ -61,6 +61,7 @@ enum {
|
||||
OP_DUMP_FRAMEBUFFER = 23,
|
||||
OP_PUSH_DEBUG_GROUP = 24,
|
||||
OP_POP_DEBUG_GROUP = 25,
|
||||
OP_CHANGE_BLEND = 26,
|
||||
};
|
||||
|
||||
typedef struct
|
||||
@@ -136,6 +137,10 @@ typedef struct
|
||||
int source2_location;
|
||||
int progress_location;
|
||||
} cross_fade;
|
||||
struct {
|
||||
int source2_location;
|
||||
int mode_location;
|
||||
} blend;
|
||||
};
|
||||
|
||||
} Program;
|
||||
@@ -214,6 +219,10 @@ typedef struct
|
||||
float progress;
|
||||
int source2;
|
||||
} cross_fade;
|
||||
struct {
|
||||
int source2;
|
||||
int mode;
|
||||
} blend;
|
||||
struct {
|
||||
char *filename;
|
||||
int width;
|
||||
|
@@ -42,6 +42,7 @@
|
||||
|
||||
#include "gskdebugprivate.h"
|
||||
#include "gskrendererprivate.h"
|
||||
#include "gskrendernodeparserprivate.h"
|
||||
|
||||
#include <graphene-gobject.h>
|
||||
|
||||
@@ -328,19 +329,11 @@ gsk_render_node_diff (GskRenderNode *node1,
|
||||
GBytes *
|
||||
gsk_render_node_serialize (GskRenderNode *node)
|
||||
{
|
||||
GVariant *node_variant, *variant;
|
||||
GBytes *result;
|
||||
char *str;
|
||||
|
||||
node_variant = gsk_render_node_serialize_node (node);
|
||||
|
||||
variant = g_variant_new ("(suuv)",
|
||||
GSK_RENDER_NODE_SERIALIZATION_ID,
|
||||
(guint32) GSK_RENDER_NODE_SERIALIZATION_VERSION,
|
||||
(guint32) gsk_render_node_get_node_type (node),
|
||||
node_variant);
|
||||
|
||||
result = g_variant_get_data_as_bytes (variant);
|
||||
g_variant_unref (variant);
|
||||
str = gsk_render_node_serialize_to_string (node);
|
||||
result = g_bytes_new_take (str, strlen (str));
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -394,39 +387,13 @@ gsk_render_node_write_to_file (GskRenderNode *node,
|
||||
* error.
|
||||
**/
|
||||
GskRenderNode *
|
||||
gsk_render_node_deserialize (GBytes *bytes,
|
||||
GError **error)
|
||||
gsk_render_node_deserialize (GBytes *bytes,
|
||||
GskParseErrorFunc error_func,
|
||||
gpointer user_data)
|
||||
{
|
||||
char *id_string;
|
||||
guint32 version, node_type;
|
||||
GVariant *variant, *node_variant;
|
||||
GskRenderNode *node = NULL;
|
||||
|
||||
variant = g_variant_new_from_bytes (G_VARIANT_TYPE ("(suuv)"), bytes, FALSE);
|
||||
|
||||
g_variant_get (variant, "(suuv)", &id_string, &version, &node_type, &node_variant);
|
||||
|
||||
if (!g_str_equal (id_string, GSK_RENDER_NODE_SERIALIZATION_ID))
|
||||
{
|
||||
g_set_error (error, GSK_SERIALIZATION_ERROR, GSK_SERIALIZATION_UNSUPPORTED_FORMAT,
|
||||
"Data not in GskRenderNode serialization format.");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (version != GSK_RENDER_NODE_SERIALIZATION_VERSION)
|
||||
{
|
||||
g_set_error (error, GSK_SERIALIZATION_ERROR, GSK_SERIALIZATION_UNSUPPORTED_VERSION,
|
||||
"Format version %u not supported.", version);
|
||||
goto out;
|
||||
}
|
||||
|
||||
node = gsk_render_node_deserialize_node (node_type, node_variant, error);
|
||||
|
||||
out:
|
||||
g_free (id_string);
|
||||
g_variant_unref (node_variant);
|
||||
g_variant_unref (variant);
|
||||
node = gsk_render_node_deserialize_from_bytes (bytes, error_func, user_data);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
|
@@ -25,6 +25,7 @@
|
||||
|
||||
#include <gsk/gskroundedrect.h>
|
||||
#include <gsk/gsktypes.h>
|
||||
#include <gtk/css/gtkcss.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
@@ -52,6 +53,10 @@ struct _GskShadow
|
||||
float radius;
|
||||
};
|
||||
|
||||
typedef void (* GskParseErrorFunc) (const GtkCssSection *section,
|
||||
const GError *error,
|
||||
gpointer user_data);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GType gsk_render_node_get_type (void) G_GNUC_CONST;
|
||||
|
||||
@@ -81,8 +86,9 @@ gboolean gsk_render_node_write_to_file (GskRenderNode *
|
||||
const char *filename,
|
||||
GError **error);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskRenderNode * gsk_render_node_deserialize (GBytes *bytes,
|
||||
GError **error);
|
||||
GskRenderNode * gsk_render_node_deserialize (GBytes *bytes,
|
||||
GskParseErrorFunc error_func,
|
||||
gpointer user_data);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskRenderNode * gsk_debug_node_new (GskRenderNode *child,
|
||||
|
1879
gsk/gskrendernodeparser.c
Normal file
12
gsk/gskrendernodeparserprivate.h
Normal file
@@ -0,0 +1,12 @@
|
||||
|
||||
#ifndef __GSK_RENDER_NODE_PARSER_PRIVATE_H__
|
||||
#define __GSK_RENDER_NODE_PARSER_PRIVATE_H__
|
||||
|
||||
#include "gskrendernode.h"
|
||||
|
||||
GskRenderNode * gsk_render_node_deserialize_from_bytes (GBytes *bytes,
|
||||
GskParseErrorFunc error_func,
|
||||
gpointer user_data);
|
||||
char * gsk_render_node_serialize_to_string (GskRenderNode *root);
|
||||
|
||||
#endif
|
@@ -33,9 +33,6 @@ struct _GskRenderNodeClass
|
||||
void (* diff) (GskRenderNode *node1,
|
||||
GskRenderNode *node2,
|
||||
cairo_region_t *region);
|
||||
GVariant * (* serialize) (GskRenderNode *node);
|
||||
GskRenderNode * (* deserialize) (GVariant *variant,
|
||||
GError **error);
|
||||
};
|
||||
|
||||
GskRenderNode * gsk_render_node_new (const GskRenderNodeClass *node_class,
|
||||
@@ -50,11 +47,6 @@ void gsk_render_node_diff_impossible (GskRenderNode *nod
|
||||
GskRenderNode *node2,
|
||||
cairo_region_t *region);
|
||||
|
||||
GVariant * gsk_render_node_serialize_node (GskRenderNode *node);
|
||||
GskRenderNode * gsk_render_node_deserialize_node (GskRenderNodeType type,
|
||||
GVariant *variant,
|
||||
GError **error);
|
||||
|
||||
GskRenderNode * gsk_cairo_node_new_for_surface (const graphene_rect_t *bounds,
|
||||
cairo_surface_t *surface);
|
||||
|
||||
|
@@ -1695,6 +1695,7 @@ gsk_transform_parser_parse (GtkCssParser *parser,
|
||||
if (gtk_css_token_is_ident (token, "none"))
|
||||
{
|
||||
gtk_css_parser_consume_token (parser);
|
||||
|
||||
*out_transform = NULL;
|
||||
return TRUE;
|
||||
}
|
||||
@@ -1902,6 +1903,7 @@ gsk_transform_parse (const char *string,
|
||||
result = FALSE;
|
||||
}
|
||||
gtk_css_parser_unref (parser);
|
||||
|
||||
g_bytes_unref (bytes);
|
||||
|
||||
return result;
|
||||
|
@@ -11,6 +11,7 @@ gsk_private_gl_shaders = [
|
||||
'resources/glsl/unblurred_outset_shadow.fs.glsl',
|
||||
'resources/glsl/border.fs.glsl',
|
||||
'resources/glsl/cross_fade.fs.glsl',
|
||||
'resources/glsl/blend.fs.glsl',
|
||||
'resources/glsl/es2_common.fs.glsl',
|
||||
'resources/glsl/es2_common.vs.glsl',
|
||||
'resources/glsl/gl3_common.fs.glsl',
|
||||
@@ -34,6 +35,7 @@ gsk_private_sources = files([
|
||||
'gskdebug.c',
|
||||
'gskprivate.c',
|
||||
'gskprofiler.c',
|
||||
'gskrendernodeparser.c',
|
||||
'gl/gskshaderbuilder.c',
|
||||
'gl/gskglprofiler.c',
|
||||
'gl/gskglrenderer.c',
|
||||
|
287
gsk/resources/glsl/blend.fs.glsl
Normal file
@@ -0,0 +1,287 @@
|
||||
uniform int u_mode;
|
||||
uniform sampler2D u_source2;
|
||||
|
||||
float
|
||||
combine (float source, float backdrop)
|
||||
{
|
||||
return source + backdrop * (1 - source);
|
||||
}
|
||||
|
||||
vec4
|
||||
composite (vec4 Cs, vec4 Cb, vec3 B)
|
||||
{
|
||||
float ao = Cs.a + Cb.a * (1 - Cs.a);
|
||||
vec3 Co = (Cs.a*(1 - Cb.a)*Cs.rgb + Cs.a*Cb.a*B + (1 - Cs.a)*Cb.a*Cb.rgb) / ao;
|
||||
return vec4(Co, ao);
|
||||
}
|
||||
|
||||
vec4
|
||||
normal (vec4 Cs, vec4 Cb)
|
||||
{
|
||||
return composite (Cs, Cb, Cs.rgb);
|
||||
}
|
||||
|
||||
vec4
|
||||
multiply (vec4 Cs, vec4 Cb)
|
||||
{
|
||||
return composite (Cs, Cb, Cs.rgb * Cb.rgb);
|
||||
}
|
||||
|
||||
vec4
|
||||
difference (vec4 Cs, vec4 Cb)
|
||||
{
|
||||
return composite (Cs, Cb, abs(Cs.rgb - Cb.rgb));
|
||||
}
|
||||
|
||||
vec4
|
||||
screen (vec4 Cs, vec4 Cb)
|
||||
{
|
||||
return composite (Cs, Cb, Cs.rgb + Cb.rgb - Cs.rgb * Cb.rgb);
|
||||
}
|
||||
|
||||
float
|
||||
hard_light (float source, float backdrop)
|
||||
{
|
||||
if (source <= 0.5)
|
||||
return 2 * backdrop * source;
|
||||
else
|
||||
return 2 * (backdrop + source - backdrop * source) - 1;
|
||||
}
|
||||
|
||||
vec4
|
||||
hard_light (vec4 Cs, vec4 Cb)
|
||||
{
|
||||
vec3 B = vec3 (hard_light (Cs.r, Cb.r),
|
||||
hard_light (Cs.g, Cb.g),
|
||||
hard_light (Cs.b, Cb.b));
|
||||
return composite (Cs, Cb, B);
|
||||
}
|
||||
|
||||
float
|
||||
soft_light (float source, float backdrop)
|
||||
{
|
||||
float db;
|
||||
|
||||
if (backdrop <= 0.25)
|
||||
db = ((16 * backdrop - 12) * backdrop + 4) * backdrop;
|
||||
else
|
||||
db = sqrt (backdrop);
|
||||
|
||||
if (source <= 0.5)
|
||||
return backdrop - (1 - 2 * source) * backdrop * (1 - backdrop);
|
||||
else
|
||||
return backdrop + (2 * source - 1) * (db - backdrop);
|
||||
}
|
||||
|
||||
vec4
|
||||
soft_light (vec4 Cs, vec4 Cb)
|
||||
{
|
||||
vec3 B = vec3 (soft_light (Cs.r, Cb.r),
|
||||
soft_light (Cs.g, Cb.g),
|
||||
soft_light (Cs.b, Cb.b));
|
||||
return composite (Cs, Cb, B);
|
||||
}
|
||||
|
||||
vec4
|
||||
overlay (vec4 Cs, vec4 Cb)
|
||||
{
|
||||
vec3 B = vec3 (hard_light (Cb.r, Cs.r),
|
||||
hard_light (Cb.g, Cs.g),
|
||||
hard_light (Cb.b, Cs.b));
|
||||
return composite (Cs, Cb, B);
|
||||
}
|
||||
|
||||
vec4
|
||||
darken (vec4 Cs, vec4 Cb)
|
||||
{
|
||||
vec3 B = min (Cs.rgb, Cb.rgb);
|
||||
return composite (Cs, Cb, B);
|
||||
}
|
||||
|
||||
vec4
|
||||
lighten (vec4 Cs, vec4 Cb)
|
||||
{
|
||||
vec3 B = max (Cs.rgb, Cb.rgb);
|
||||
return composite (Cs, Cb, B);
|
||||
}
|
||||
|
||||
float
|
||||
color_dodge (float source, float backdrop)
|
||||
{
|
||||
return (source == 1.0) ? source : min (backdrop / (1.0 - source), 1.0);
|
||||
}
|
||||
|
||||
vec4
|
||||
color_dodge (vec4 Cs, vec4 Cb)
|
||||
{
|
||||
vec3 B = vec3 (color_dodge (Cs.r, Cb.r),
|
||||
color_dodge (Cs.g, Cb.g),
|
||||
color_dodge (Cs.b, Cb.b));
|
||||
return composite (Cs, Cb, B);
|
||||
}
|
||||
|
||||
|
||||
float
|
||||
color_burn (float source, float backdrop)
|
||||
{
|
||||
return (source == 0.0) ? source : max ((1.0 - ((1.0 - backdrop) / source)), 0.0);
|
||||
}
|
||||
|
||||
vec4
|
||||
color_burn (vec4 Cs, vec4 Cb)
|
||||
{
|
||||
vec3 B = vec3 (color_burn (Cs.r, Cb.r),
|
||||
color_burn (Cs.g, Cb.g),
|
||||
color_burn (Cs.b, Cb.b));
|
||||
return composite (Cs, Cb, B);
|
||||
}
|
||||
|
||||
vec4
|
||||
exclusion (vec4 Cs, vec4 Cb)
|
||||
{
|
||||
vec3 B = Cb.rgb + Cs.rgb - 2.0 * Cb.rgb * Cs.rgb;
|
||||
return composite (Cs, Cb, B);
|
||||
}
|
||||
|
||||
float
|
||||
lum (vec3 c)
|
||||
{
|
||||
return 0.3 * c.r + 0.59 * c.g + 0.11 * c.b;
|
||||
}
|
||||
|
||||
vec3
|
||||
clip_color (vec3 c)
|
||||
{
|
||||
float l = lum (c);
|
||||
float n = min (c.r, min (c.g, c.b));
|
||||
float x = max (c.r, max (c.g, c.b));
|
||||
if (n < 0) c = l + (((c - l) * l) / (l - n));
|
||||
if (x > 1) c = l + (((c - l) * (1 - l)) / (x - l));
|
||||
return c;
|
||||
}
|
||||
|
||||
vec3
|
||||
set_lum (vec3 c, float l)
|
||||
{
|
||||
float d = l - lum (c);
|
||||
return clip_color (vec3 (c.r + d, c.g + d, c.b + d));
|
||||
}
|
||||
|
||||
float
|
||||
sat (vec3 c)
|
||||
{
|
||||
return max (c.r, max (c.g, c.b)) - min (c.r, min (c.g, c.b));
|
||||
}
|
||||
|
||||
vec3
|
||||
set_sat (vec3 c, float s)
|
||||
{
|
||||
float cmin = min (c.r, min (c.g, c.b));
|
||||
float cmax = max (c.r, max (c.g, c.b));
|
||||
vec3 res;
|
||||
|
||||
if (cmax == cmin)
|
||||
res = vec3 (0, 0, 0);
|
||||
else
|
||||
{
|
||||
if (c.r == cmax)
|
||||
{
|
||||
if (c.g == cmin)
|
||||
{
|
||||
res.b = ((c.b - cmin) * s) / (cmax - cmin);
|
||||
res.g = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
res.g = ((c.g - cmin) * s) / (cmax - cmin);
|
||||
res.b = 0;
|
||||
}
|
||||
res.r = s;
|
||||
}
|
||||
else if (c.g == cmax)
|
||||
{
|
||||
if (c.r == cmin)
|
||||
{
|
||||
res.b = ((c.b - cmin) * s) / (cmax - cmin);
|
||||
res.r = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
res.r = ((c.r - cmin) * s) / (cmax - cmin);
|
||||
res.b = 0;
|
||||
}
|
||||
res.g = s;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (c.r == cmin)
|
||||
{
|
||||
res.g = ((c.g - cmin) * s) / (cmax - cmin);
|
||||
res.r = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
res.r = ((c.r - cmin) * s) / (cmax - cmin);
|
||||
res.g = 0;
|
||||
}
|
||||
res.b = s;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
vec4
|
||||
color (vec4 Cs, vec4 Cb)
|
||||
{
|
||||
vec3 B = set_lum (Cs.rgb, lum (Cb.rgb));
|
||||
return composite (Cs, Cb, B);
|
||||
}
|
||||
|
||||
vec4
|
||||
hue (vec4 Cs, vec4 Cb)
|
||||
{
|
||||
vec3 B = set_lum (set_sat (Cs.rgb, sat (Cb.rgb)), lum (Cb.rgb));
|
||||
return composite (Cs, Cb, B);
|
||||
}
|
||||
|
||||
vec4
|
||||
saturation (vec4 Cs, vec4 Cb)
|
||||
{
|
||||
vec3 B = set_lum (set_sat (Cb.rgb, sat (Cs.rgb)), lum (Cb.rgb));
|
||||
return composite (Cs, Cb, B);
|
||||
}
|
||||
|
||||
vec4
|
||||
luminosity (vec4 Cs, vec4 Cb)
|
||||
{
|
||||
vec3 B = set_lum (Cb.rgb, lum (Cs.rgb));
|
||||
return composite (Cs, Cb, B);
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec4 bottom_color = Texture(u_source, vUv);
|
||||
vec4 top_color = Texture(u_source2, vUv);
|
||||
|
||||
vec4 result;
|
||||
switch(u_mode) {
|
||||
case 0: result = normal(bottom_color, top_color); break;
|
||||
case 1: result = multiply(bottom_color, top_color); break;
|
||||
case 2: result = screen(bottom_color, top_color); break;
|
||||
case 3: result = overlay(bottom_color, top_color); break;
|
||||
case 4: result = darken(bottom_color, top_color); break;
|
||||
case 5: result = lighten(bottom_color, top_color); break;
|
||||
case 6: result = color_dodge(bottom_color, top_color); break;
|
||||
case 7: result = color_burn(bottom_color, top_color); break;
|
||||
case 8: result = hard_light(bottom_color, top_color); break;
|
||||
case 9: result = soft_light(bottom_color, top_color); break;
|
||||
case 10: result = difference(bottom_color, top_color); break;
|
||||
case 11: result = exclusion(bottom_color, top_color); break;
|
||||
case 12: result = color(bottom_color, top_color); break;
|
||||
case 13: result = hue(bottom_color, top_color); break;
|
||||
case 14: result = saturation(bottom_color, top_color); break;
|
||||
case 15: result = luminosity(bottom_color, top_color); break;
|
||||
default: discard;
|
||||
}
|
||||
|
||||
setOutputColor(result * u_alpha);
|
||||
}
|
@@ -30,6 +30,29 @@ G_BEGIN_DECLS
|
||||
#define GTK_IS_CSS_PROVIDER_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), GTK_TYPE_CSS_PROVIDER))
|
||||
#define GTK_CSS_PROVIDER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_CSS_PROVIDER, GtkCssProviderClass))
|
||||
|
||||
/**
|
||||
* GtkCssProviderError:
|
||||
* @GTK_CSS_PROVIDER_ERROR_FAILED: Failed.
|
||||
* @GTK_CSS_PROVIDER_ERROR_SYNTAX: Syntax error.
|
||||
* @GTK_CSS_PROVIDER_ERROR_IMPORT: Import error.
|
||||
* @GTK_CSS_PROVIDER_ERROR_NAME: Name error.
|
||||
* @GTK_CSS_PROVIDER_ERROR_DEPRECATED: Deprecation error.
|
||||
* @GTK_CSS_PROVIDER_ERROR_UNKNOWN_VALUE: Unknown value.
|
||||
* @GTK_CSS_PROVIDER_WARN_GENERAL: A general warning.
|
||||
*
|
||||
* Error codes for %GTK_CSS_PROVIDER_ERROR.
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
GTK_CSS_PROVIDER_ERROR_FAILED,
|
||||
GTK_CSS_PROVIDER_ERROR_SYNTAX,
|
||||
GTK_CSS_PROVIDER_ERROR_IMPORT,
|
||||
GTK_CSS_PROVIDER_ERROR_NAME,
|
||||
GTK_CSS_PROVIDER_ERROR_DEPRECATED,
|
||||
GTK_CSS_PROVIDER_ERROR_UNKNOWN_VALUE,
|
||||
GTK_CSS_PROVIDER_WARN_GENERAL,
|
||||
} GtkCssProviderError;
|
||||
|
||||
typedef struct _GtkCssProvider GtkCssProvider;
|
||||
typedef struct _GtkCssProviderClass GtkCssProviderClass;
|
||||
typedef struct _GtkCssProviderPrivate GtkCssProviderPrivate;
|
||||
|
@@ -13,6 +13,19 @@ static GOptionEntry options[] = {
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static void
|
||||
deserialize_error_func (const GtkCssSection *section,
|
||||
const GError *error,
|
||||
gpointer user_data)
|
||||
{
|
||||
char *section_str = gtk_css_section_to_string (section);
|
||||
|
||||
g_warning ("Error at %s: %s", section_str, error->message);
|
||||
|
||||
free (section_str);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
@@ -66,7 +79,7 @@ main(int argc, char **argv)
|
||||
}
|
||||
|
||||
start = g_get_monotonic_time ();
|
||||
node = gsk_render_node_deserialize (bytes, &error);
|
||||
node = gsk_render_node_deserialize (bytes, deserialize_error_func, NULL);
|
||||
end = g_get_monotonic_time ();
|
||||
if (benchmark)
|
||||
{
|
||||
@@ -78,8 +91,6 @@ main(int argc, char **argv)
|
||||
|
||||
if (node == NULL)
|
||||
{
|
||||
g_printerr ("Invalid node file: %s\n", error->message);
|
||||
g_clear_error (&error);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@@ -108,6 +108,18 @@ gtk_node_view_class_init (GtkNodeViewClass *klass)
|
||||
widget_class->snapshot = gtk_node_view_snapshot;
|
||||
}
|
||||
|
||||
static void
|
||||
deserialize_error_func (const GtkCssSection *section,
|
||||
const GError *error,
|
||||
gpointer user_data)
|
||||
{
|
||||
char *section_str = gtk_css_section_to_string (section);
|
||||
|
||||
g_warning ("Error at %s: %s", section_str, error->message);
|
||||
|
||||
free (section_str);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
@@ -152,7 +164,7 @@ main (int argc, char **argv)
|
||||
}
|
||||
|
||||
bytes = g_bytes_new_take (contents, len);
|
||||
GTK_NODE_VIEW (nodeview)->node = gsk_render_node_deserialize (bytes, &error);
|
||||
GTK_NODE_VIEW (nodeview)->node = gsk_render_node_deserialize (bytes, deserialize_error_func, NULL);
|
||||
g_bytes_unref (bytes);
|
||||
|
||||
if (GTK_NODE_VIEW (nodeview)->node == NULL)
|
||||
|
Before Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 120 KiB |
Before Width: | Height: | Size: 121 KiB |
Before Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 75 KiB |
Before Width: | Height: | Size: 76 KiB |
@@ -54,6 +54,18 @@ save_image (cairo_surface_t *surface,
|
||||
g_free (filename);
|
||||
}
|
||||
|
||||
static void
|
||||
deserialize_error_func (const GtkCssSection *section,
|
||||
const GError *error,
|
||||
gpointer user_data)
|
||||
{
|
||||
char *section_str = gtk_css_section_to_string (section);
|
||||
|
||||
g_error ("Error at %s: %s", section_str, error->message);
|
||||
|
||||
free (section_str);
|
||||
}
|
||||
|
||||
/*
|
||||
* Arguments:
|
||||
* 1) .node file to compare
|
||||
@@ -101,7 +113,7 @@ main (int argc, char **argv)
|
||||
}
|
||||
|
||||
bytes = g_bytes_new_take (contents, len);
|
||||
node = gsk_render_node_deserialize (bytes, &error);
|
||||
node = gsk_render_node_deserialize (bytes, deserialize_error_func, NULL);
|
||||
g_bytes_unref (bytes);
|
||||
|
||||
g_assert_no_error (error);
|
||||
|
19
testsuite/gsk/compare/clip-coordinates-3d.node
Normal file
@@ -0,0 +1,19 @@
|
||||
transform {
|
||||
/* adding the perspective line here turns the matrix from a 2D
|
||||
category into a 3D category. It does not have any visual effect. */
|
||||
transform: perspective(200) scale(2);
|
||||
child: container {
|
||||
color {
|
||||
bounds: 0 0 50 50;
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
clip {
|
||||
clip: 10 10 30 30;
|
||||
child: color {
|
||||
bounds: 0 0 50 50;
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
BIN
testsuite/gsk/compare/clip-coordinates-3d.png
Normal file
After Width: | Height: | Size: 269 B |
34
testsuite/gsk/compare/clipped_rounded_clip.node
Normal file
@@ -0,0 +1,34 @@
|
||||
color {
|
||||
color: white;
|
||||
bounds: 50 70 80 90;
|
||||
}
|
||||
|
||||
clip {
|
||||
clip: 60 80 60 70;
|
||||
child: rounded-clip {
|
||||
clip: 20 50 100 100 / 50;
|
||||
child: color {
|
||||
bounds: 20 50 100 100;
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* The actual rounded portion of the clip is irrelevant
|
||||
* for this test case. This just ensures that
|
||||
* offscreen-drawn clips work and have the correct
|
||||
* size, color, etc.
|
||||
*/
|
||||
debug {
|
||||
message: "clipped out area";
|
||||
child: container {
|
||||
color {
|
||||
bounds: 115 80 5 45;
|
||||
color: black;
|
||||
}
|
||||
color {
|
||||
bounds: 60 120 60 30;
|
||||
color: black;
|
||||
}
|
||||
}
|
||||
}
|
BIN
testsuite/gsk/compare/clipped_rounded_clip.png
Normal file
After Width: | Height: | Size: 237 B |
36
testsuite/gsk/compare/opacity_clip.node
Normal file
@@ -0,0 +1,36 @@
|
||||
color {
|
||||
bounds: 40 40 70 70;
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
clip {
|
||||
clip: 50 50 50 50;
|
||||
child: opacity {
|
||||
opacity: 0.4;
|
||||
child: rounded-clip {
|
||||
clip: 50 50 100 100 / 50;
|
||||
child: color {
|
||||
bounds: 50 50 100 100;
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
debug {
|
||||
message: "clipped out area";
|
||||
child: container {
|
||||
color {
|
||||
bounds: 50 70 10 30;
|
||||
color: black;
|
||||
}
|
||||
color {
|
||||
bounds: 55 55 20 20;
|
||||
color: black;
|
||||
}
|
||||
color {
|
||||
bounds: 70 50 30 10;
|
||||
color: black;
|
||||
}
|
||||
}
|
||||
}
|
BIN
testsuite/gsk/compare/opacity_clip.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
8
testsuite/gsk/compare/outset_shadow_offset_both.node
Normal file
@@ -0,0 +1,8 @@
|
||||
outset-shadow {
|
||||
outline: 100 100 100 100;
|
||||
color: black;
|
||||
dx: -100;
|
||||
dy: 100;
|
||||
spread: 10;
|
||||
blur: 0;
|
||||
}
|
Before Width: | Height: | Size: 527 B After Width: | Height: | Size: 527 B |
8
testsuite/gsk/compare/outset_shadow_offset_x.node
Normal file
@@ -0,0 +1,8 @@
|
||||
outset-shadow {
|
||||
outline: 100 100 100 100;
|
||||
color: black;
|
||||
dx: 10;
|
||||
dy: 0;
|
||||
spread: 10;
|
||||
blur: 0;
|
||||
}
|
Before Width: | Height: | Size: 391 B After Width: | Height: | Size: 391 B |
8
testsuite/gsk/compare/outset_shadow_offset_y.node
Normal file
@@ -0,0 +1,8 @@
|
||||
outset-shadow {
|
||||
outline: 100 100 100 100;
|
||||
color: black;
|
||||
dx: 0;
|
||||
dy: 10;
|
||||
spread: 10;
|
||||
blur: 0;
|
||||
}
|
Before Width: | Height: | Size: 391 B After Width: | Height: | Size: 391 B |
8
testsuite/gsk/compare/outset_shadow_simple.node
Normal file
@@ -0,0 +1,8 @@
|
||||
outset-shadow {
|
||||
outline: 100 100 100 100;
|
||||
color: black;
|
||||
dx: 0;
|
||||
dy: 0;
|
||||
spread: 10;
|
||||
blur: 0;
|
||||
}
|
Before Width: | Height: | Size: 396 B After Width: | Height: | Size: 396 B |
Before Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 39 KiB |
Before Width: | Height: | Size: 39 KiB |
Before Width: | Height: | Size: 243 KiB |
Before Width: | Height: | Size: 157 B |
Before Width: | Height: | Size: 613 B |
BIN
testsuite/gsk/gl/cross-fade-in-opacity.gl.png
Normal file
After Width: | Height: | Size: 618 B |
23
testsuite/gsk/gl/cross-fade-in-opacity.node
Normal file
@@ -0,0 +1,23 @@
|
||||
/* Background */
|
||||
color {
|
||||
bounds: 0 0 200 200;
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* It is important that the following node structure leaves the
|
||||
* start child of the cross-fade node invisible. */
|
||||
opacity {
|
||||
opacity: 0.4;
|
||||
|
||||
child: cross-fade {
|
||||
start: color {
|
||||
color: blue;
|
||||
bounds: 0 0 100 100;
|
||||
}
|
||||
end: color {
|
||||
color: red;
|
||||
bounds: 100 100 100 100;
|
||||
}
|
||||
progress: 1.0;
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 394 B |
Before Width: | Height: | Size: 744 B |
Before Width: | Height: | Size: 543 B |
Before Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 7.5 KiB |
Before Width: | Height: | Size: 344 B |
Before Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 761 B |
Before Width: | Height: | Size: 790 B |
BIN
testsuite/gsk/gl/shadow-in-opacity.gl.png
Normal file
After Width: | Height: | Size: 408 B |
24
testsuite/gsk/gl/shadow-in-opacity.node
Normal file
@@ -0,0 +1,24 @@
|
||||
|
||||
/*
|
||||
The opacity node should affect both the shadow node child
|
||||
(the node that's NOT colored) and the actual shadow.
|
||||
I.e. the output should be entirely empty.
|
||||
*/
|
||||
opacity {
|
||||
opacity: 0;
|
||||
|
||||
child: shadow {
|
||||
child: cross-fade {
|
||||
start: color {
|
||||
color: blue;
|
||||
bounds: 0 0 100 100;
|
||||
}
|
||||
end: color {
|
||||
color: red;
|
||||
bounds: 100 100 100 100;
|
||||
}
|
||||
progress: 0;
|
||||
}
|
||||
shadows: green 200 0 0;
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 1011 B |