Compare commits
189 Commits
editor-exp
...
parse-dash
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a488be07a7 | ||
|
|
7d84cd8020 | ||
|
|
5fcc8b62ec | ||
|
|
1b61cc91bb | ||
|
|
c65b978ac6 | ||
|
|
798234a776 | ||
|
|
e75a27354c | ||
|
|
3b79ad1e7e | ||
|
|
1b57e5ac80 | ||
|
|
5aaa4cc452 | ||
|
|
56022d3a58 | ||
|
|
3f356d0b59 | ||
|
|
dced4a4796 | ||
|
|
256d537242 | ||
|
|
2c2ee6a27a | ||
|
|
eed54eac7a | ||
|
|
098c402a97 | ||
|
|
b87095330a | ||
|
|
1037fc8387 | ||
|
|
3fc0df814c | ||
|
|
ae552b3064 | ||
|
|
12d9d71449 | ||
|
|
7772e4f925 | ||
|
|
88055f62c5 | ||
|
|
4c7f009cc1 | ||
|
|
7c084b78f6 | ||
|
|
e207e1ee9d | ||
|
|
598e14868c | ||
|
|
ae09d17590 | ||
|
|
d2e8c639ca | ||
|
|
06f0ab7e8f | ||
|
|
0d1fd1ce72 | ||
|
|
d18ecca089 | ||
|
|
47560f8369 | ||
|
|
260be0300e | ||
|
|
dca9fb5801 | ||
|
|
f2012c6185 | ||
|
|
cc43dee619 | ||
|
|
78f3d86a25 | ||
|
|
3869e4a818 | ||
|
|
7a6b489dc6 | ||
|
|
282c065b77 | ||
|
|
1f0622b27e | ||
|
|
0a85189e04 | ||
|
|
3301a1af0b | ||
|
|
245479f71d | ||
|
|
d92b659d02 | ||
|
|
7bee50c4f6 | ||
|
|
bad2324318 | ||
|
|
dd15accb79 | ||
|
|
1d1f35576a | ||
|
|
15458b5af3 | ||
|
|
5ffe9a68ed | ||
|
|
f341bd563b | ||
|
|
70edacc68d | ||
|
|
091176ae48 | ||
|
|
d9cfb94a80 | ||
|
|
3c7ca28f1f | ||
|
|
25e518326c | ||
|
|
a853307fb7 | ||
|
|
0ae541671d | ||
|
|
ef0d6c7290 | ||
|
|
2217509701 | ||
|
|
16e46a73f3 | ||
|
|
526771751f | ||
|
|
dfa6591675 | ||
|
|
6252517aac | ||
|
|
51e440fa03 | ||
|
|
d82fb6f20a | ||
|
|
44de6a6cbe | ||
|
|
64e27cd87d | ||
|
|
60e75f8a2a | ||
|
|
57eda94dde | ||
|
|
723fb6c8be | ||
|
|
6c85ed1ba1 | ||
|
|
2e58274f23 | ||
|
|
4ade0afe03 | ||
|
|
93fb45c689 | ||
|
|
684a015c98 | ||
|
|
f7fcd2e425 | ||
|
|
c6c637fe21 | ||
|
|
33fc4d6495 | ||
|
|
33ff927522 | ||
|
|
2d39cdbff8 | ||
|
|
8d675810e8 | ||
|
|
8b25481c26 | ||
|
|
5fdf96f496 | ||
|
|
b61991a023 | ||
|
|
91d302a201 | ||
|
|
35a1a62d50 | ||
|
|
ac7a4cb94c | ||
|
|
bacd7ef92f | ||
|
|
980dc44f4a | ||
|
|
2d648f84a9 | ||
|
|
aef0943f61 | ||
|
|
3e146171cb | ||
|
|
24bbaceaa4 | ||
|
|
318cf132e9 | ||
|
|
8bbc143ed1 | ||
|
|
220d130c0f | ||
|
|
e28a32a7c7 | ||
|
|
88f8b77d77 | ||
|
|
1066374909 | ||
|
|
34a2595dfb | ||
|
|
76fcd5cf25 | ||
|
|
32e6ed4eca | ||
|
|
2983c0be70 | ||
|
|
e0bf6585de | ||
|
|
c045b0be4c | ||
|
|
d8c094944a | ||
|
|
510bf86268 | ||
|
|
f3bea027a0 | ||
|
|
bce3b6f34a | ||
|
|
01274dfbb9 | ||
|
|
533a2cf9ec | ||
|
|
5dbc5bbf22 | ||
|
|
79c999cf76 | ||
|
|
e375bc8838 | ||
|
|
79505f940b | ||
|
|
0f087deb2a | ||
|
|
e245883f91 | ||
|
|
4e9bd13892 | ||
|
|
65e9b8fe66 | ||
|
|
e9f870fee6 | ||
|
|
e5e5966934 | ||
|
|
5c407365e3 | ||
|
|
7e251d81c2 | ||
|
|
92d1d52c59 | ||
|
|
ef436e4dce | ||
|
|
1a07e336bf | ||
|
|
169355f771 | ||
|
|
299c6a3d6f | ||
|
|
17698bfd2e | ||
|
|
377592cb62 | ||
|
|
015cebc046 | ||
|
|
0a5e5023a8 | ||
|
|
a9b8ad6181 | ||
|
|
7331683c01 | ||
|
|
cca6a66518 | ||
|
|
891462e5af | ||
|
|
95cd6fe206 | ||
|
|
63534fd9eb | ||
|
|
e836e3380e | ||
|
|
f693beab57 | ||
|
|
2d2df42a94 | ||
|
|
fb0a4fa457 | ||
|
|
be0003c108 | ||
|
|
eb1e24a5bf | ||
|
|
a3e98558d3 | ||
|
|
f2b682dd9c | ||
|
|
b826ef4f4d | ||
|
|
a2fdeb99e0 | ||
|
|
df8d28f5fe | ||
|
|
838c51cc92 | ||
|
|
315ded687c | ||
|
|
6855346269 | ||
|
|
c2d6f900d9 | ||
|
|
85c2d5f14e | ||
|
|
b3eb912cf3 | ||
|
|
81d9205369 | ||
|
|
152a335cee | ||
|
|
b84650c2a3 | ||
|
|
722bea2943 | ||
|
|
6c337b949d | ||
|
|
db97a35dc7 | ||
|
|
c2fda63b0d | ||
|
|
5da64e9e34 | ||
|
|
3f360aa883 | ||
|
|
a444045386 | ||
|
|
584a807ed6 | ||
|
|
72e5697804 | ||
|
|
93aff8a129 | ||
|
|
c3cfaab479 | ||
|
|
663e3d0811 | ||
|
|
2dc35ec0c1 | ||
|
|
a791f235e6 | ||
|
|
7df9cc1b47 | ||
|
|
4449344fad | ||
|
|
e1c9f50d26 | ||
|
|
1f029229dc | ||
|
|
a80dd28e35 | ||
|
|
16077fbdac | ||
|
|
1906bb5263 | ||
|
|
a8b907a33c | ||
|
|
f2a2e97004 | ||
|
|
a86923de94 | ||
|
|
4412f25c9f | ||
|
|
16bdaa11ce | ||
|
|
46afb4a9a4 |
@@ -23,8 +23,8 @@ flatpak build ${builddir} meson \
|
||||
-Dbuild-testsuite=false \
|
||||
-Dbuild-examples=false \
|
||||
-Dintrospection=disabled \
|
||||
-Ddemos=true \
|
||||
-Dprofile=devel \
|
||||
-Dbuild-demos=true \
|
||||
-Ddemo-profile=devel \
|
||||
_flatpak_build
|
||||
|
||||
flatpak build ${builddir} ninja -C _flatpak_build install
|
||||
|
||||
79
NEWS
79
NEWS
@@ -1,6 +1,85 @@
|
||||
Overview of Changes in 4.11.4, xx-xx-xxxx
|
||||
=========================================
|
||||
|
||||
* GtkFileChooser:
|
||||
- Default to sorting folders first
|
||||
- Fix a crash when visiting recent files
|
||||
|
||||
* GtkTextView:
|
||||
- Fix corner cases in word navigation
|
||||
|
||||
* GtkMenuButton:
|
||||
- Normalize label layout
|
||||
|
||||
* GtkDropDown:
|
||||
- Add support for sections
|
||||
|
||||
* GtkVideo:
|
||||
- Make the overlay icon clickable
|
||||
|
||||
* GtkWindow:
|
||||
- Clear the resize cursors to avoid artifacts
|
||||
|
||||
* GtkFileDialog:
|
||||
- Always set initial-folder
|
||||
|
||||
* GtkDropDown:
|
||||
- Update on expression changes
|
||||
|
||||
* Accessibility:
|
||||
- Improvements all over the place: GtkButton, GtkPasswordEntry,
|
||||
GtkFontChooserDialog, GtkColorChooserDialog, GtkShortcutsWindow,
|
||||
GtkMenuButton, GtkAboutDialog, GtkFileChooserDialog, GtkStackSidebar,
|
||||
GtkStackSwitcher, GtkMediaControls, GtkColorDialogButton, GtkDropDown,
|
||||
GtkInfoBar, GtkNotebook, GtkPrintUnixDialog, GtkModelButton
|
||||
- Make name computation follow the ARIA spec more closely
|
||||
- Adapt name computation for the common 'nested button' scenario
|
||||
- Change many containers to use `generic` instead of `group`
|
||||
- Use `generic` as the default role
|
||||
- Use `application` instead of `window` for windows
|
||||
- Add properties for accessible names of not directly exposed
|
||||
widgets in GtkListView, GtkGridView and GtkColumnView
|
||||
|
||||
* DND:
|
||||
- Fix criticals when drops are rejected
|
||||
|
||||
* X11:
|
||||
- Fix regressions in GLX setup
|
||||
|
||||
* Windows:
|
||||
- Center newly created transient windows
|
||||
|
||||
* Vulkan:
|
||||
- Add antialising for gradients
|
||||
- Do less work on clipped away nodes
|
||||
- Redo image uploading
|
||||
- Support different image depths and formats
|
||||
- Add a pipeline cache
|
||||
|
||||
* Demos:
|
||||
- gtk4-demo: Improve window sizing
|
||||
- gtk4-demo: Improve focus behavior
|
||||
- gtk4-demo: Add many missing a11y properties
|
||||
|
||||
* Tools:
|
||||
- gtk4-builder-tool: Make render an alias screenshot
|
||||
|
||||
* Inspector:
|
||||
- Show more information in the a11y tab
|
||||
- Add an accessibility overlay with warnings and recommendations
|
||||
- Limit the width of the a11y tab
|
||||
|
||||
* Build:
|
||||
- Require GLib 2.76
|
||||
- Make asan builds work again
|
||||
|
||||
* Translation updates:
|
||||
Brazilian Portuguese
|
||||
Catalan
|
||||
Czech
|
||||
Georgian
|
||||
|
||||
|
||||
Overview of Changes in 4.11.3, 05-06-2023
|
||||
=========================================
|
||||
|
||||
|
||||
@@ -335,6 +335,9 @@
|
||||
<file>paintable_symbolic.c</file>
|
||||
<file>panes.c</file>
|
||||
<file>password_entry.c</file>
|
||||
<file>path_fill.c</file>
|
||||
<file>path_maze.c</file>
|
||||
<file>path_text.c</file>
|
||||
<file>peg_solitaire.c</file>
|
||||
<file>pickers.c</file>
|
||||
<file>printing.c</file>
|
||||
@@ -420,6 +423,9 @@
|
||||
<gresource prefix="/fontrendering">
|
||||
<file>fontrendering.ui</file>
|
||||
</gresource>
|
||||
<gresource prefix="/path_text">
|
||||
<file>path_text.ui</file>
|
||||
</gresource>
|
||||
<gresource prefix="/org/gtk/Demo4">
|
||||
<file>icons/16x16/actions/application-exit.png</file>
|
||||
<file>icons/16x16/actions/document-new.png</file>
|
||||
|
||||
@@ -162,27 +162,39 @@ click_done (GtkGesture *gesture)
|
||||
gtk_widget_insert_after (item, canvas, last_child);
|
||||
}
|
||||
|
||||
/* GtkSettings treats `GTK_THEME=foo:dark` as theme name `foo`, variant `dark`,
|
||||
* and our embedded CSS files let `foo-dark` work as an alias for `foo:dark`. */
|
||||
static gboolean
|
||||
has_dark_suffix (const char *theme)
|
||||
{
|
||||
return g_str_has_suffix (theme, ":dark") ||
|
||||
g_str_has_suffix (theme, "-dark");
|
||||
}
|
||||
|
||||
/* So we can make a good guess whether the current theme is dark by checking for
|
||||
* either: it is suffixed `[:-]dark`, or Settings:…prefer-dark-theme is TRUE. */
|
||||
static gboolean
|
||||
theme_is_dark (void)
|
||||
{
|
||||
const char *env_theme;
|
||||
GtkSettings *settings;
|
||||
char *theme;
|
||||
gboolean prefer_dark;
|
||||
gboolean dark;
|
||||
|
||||
/* Like GtkSettings, 1st see if theme is overridden by environment variable */
|
||||
env_theme = g_getenv ("GTK_THEME");
|
||||
if (env_theme != NULL)
|
||||
return has_dark_suffix (env_theme);
|
||||
|
||||
/* If not, test Settings:…theme-name in the same way OR :…prefer-dark-theme */
|
||||
settings = gtk_settings_get_default ();
|
||||
g_object_get (settings,
|
||||
"gtk-theme-name", &theme,
|
||||
"gtk-application-prefer-dark-theme", &prefer_dark,
|
||||
NULL);
|
||||
|
||||
if ((strcmp (theme, "Adwaita") == 0 && prefer_dark) || strcmp (theme, "HighContrastInverse") == 0)
|
||||
dark = TRUE;
|
||||
else
|
||||
dark = FALSE;
|
||||
|
||||
dark = prefer_dark || has_dark_suffix (theme);
|
||||
g_free (theme);
|
||||
|
||||
return dark;
|
||||
}
|
||||
|
||||
|
||||
@@ -52,6 +52,10 @@ setup_listitem_cb (GtkListItemFactory *factory,
|
||||
|
||||
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
|
||||
image = gtk_image_new ();
|
||||
gtk_accessible_update_property (GTK_ACCESSIBLE (image),
|
||||
GTK_ACCESSIBLE_PROPERTY_LABEL,
|
||||
"App icon",
|
||||
-1);
|
||||
gtk_image_set_icon_size (GTK_IMAGE (image), GTK_ICON_SIZE_LARGE);
|
||||
gtk_box_append (GTK_BOX (box), image);
|
||||
label = gtk_label_new ("");
|
||||
@@ -79,6 +83,7 @@ bind_listitem_cb (GtkListItemFactory *factory,
|
||||
|
||||
gtk_image_set_from_gicon (GTK_IMAGE (image), g_app_info_get_icon (app_info));
|
||||
gtk_label_set_label (GTK_LABEL (label), g_app_info_get_display_name (app_info));
|
||||
gtk_list_item_set_accessible_label (list_item, g_app_info_get_display_name (app_info));
|
||||
}
|
||||
|
||||
/* In more complex code, we would also need functions to unbind and teardown
|
||||
|
||||
@@ -431,6 +431,9 @@ setup_listitem_cb (GtkListItemFactory *factory,
|
||||
picture = gtk_picture_new ();
|
||||
gtk_expression_bind (expression, picture, "paintable", picture);
|
||||
gtk_box_append (GTK_BOX (box), picture);
|
||||
gtk_accessible_update_relation (GTK_ACCESSIBLE (picture),
|
||||
GTK_ACCESSIBLE_RELATION_LABELLED_BY, location_label, NULL,
|
||||
-1);
|
||||
|
||||
|
||||
/* And finally, everything comes together.
|
||||
@@ -487,6 +490,9 @@ do_listview_clocks (GtkWidget *do_widget)
|
||||
|
||||
model = GTK_SELECTION_MODEL (gtk_no_selection_new (create_clocks_model ()));
|
||||
gridview = gtk_grid_view_new (model, factory);
|
||||
gtk_accessible_update_property (GTK_ACCESSIBLE (gridview),
|
||||
GTK_ACCESSIBLE_PROPERTY_LABEL, "Clocks",
|
||||
-1);
|
||||
gtk_scrollable_set_hscroll_policy (GTK_SCROLLABLE (gridview), GTK_SCROLL_NATURAL);
|
||||
gtk_scrollable_set_vscroll_policy (GTK_SCROLLABLE (gridview), GTK_SCROLL_NATURAL);
|
||||
|
||||
|
||||
@@ -72,6 +72,9 @@ demos = files([
|
||||
'paintable_symbolic.c',
|
||||
'panes.c',
|
||||
'password_entry.c',
|
||||
'path_fill.c',
|
||||
'path_maze.c',
|
||||
'path_text.c',
|
||||
'peg_solitaire.c',
|
||||
'pickers.c',
|
||||
'printing.c',
|
||||
@@ -158,17 +161,7 @@ demos_h = custom_target('gtk4 demo header',
|
||||
command: [ find_program('geninclude.py'), '@OUTPUT@', '@INPUT@' ],
|
||||
)
|
||||
|
||||
objcopy_supports_add_symbol = false
|
||||
objcopy = find_program('objcopy', required : false)
|
||||
if objcopy.found()
|
||||
objcopy_supports_add_symbol = run_command(objcopy, '--help', check: false).stdout().contains('--add-symbol')
|
||||
endif
|
||||
|
||||
ld = find_program('ld', required : false)
|
||||
|
||||
if not meson.is_cross_build() and build_machine.cpu_family() != 'arm' and build_machine.system() == 'linux' and objcopy.found() and objcopy_supports_add_symbol and ld.found()
|
||||
glib_compile_resources = find_program('glib-compile-resources')
|
||||
|
||||
if can_use_objcopy_for_resources
|
||||
# Create the resource blob
|
||||
gtkdemo_gresource = custom_target('gtkdemo.gresource',
|
||||
input : 'demo.gresource.xml',
|
||||
|
||||
321
demos/gtk-demo/path_fill.c
Normal file
321
demos/gtk-demo/path_fill.c
Normal file
@@ -0,0 +1,321 @@
|
||||
/* Path/Fill
|
||||
*
|
||||
* This demo shows how to use PangoCairo to draw text with more than
|
||||
* just a single color.
|
||||
*/
|
||||
|
||||
#include <glib/gi18n.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "paintable.h"
|
||||
|
||||
#define GTK_TYPE_PATH_PAINTABLE (gtk_path_paintable_get_type ())
|
||||
G_DECLARE_FINAL_TYPE (GtkPathPaintable, gtk_path_paintable, GTK, PATH_PAINTABLE, GObject)
|
||||
|
||||
struct _GtkPathPaintable
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
int width;
|
||||
int height;
|
||||
GskPath *path;
|
||||
GdkPaintable *background;
|
||||
};
|
||||
|
||||
struct _GtkPathPaintableClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
static int
|
||||
gtk_path_paintable_get_intrinsic_width (GdkPaintable *paintable)
|
||||
{
|
||||
GtkPathPaintable *self = GTK_PATH_PAINTABLE (paintable);
|
||||
|
||||
if (self->background)
|
||||
return MAX (gdk_paintable_get_intrinsic_width (self->background), self->width);
|
||||
else
|
||||
return self->width;
|
||||
}
|
||||
|
||||
static int
|
||||
gtk_path_paintable_get_intrinsic_height (GdkPaintable *paintable)
|
||||
{
|
||||
GtkPathPaintable *self = GTK_PATH_PAINTABLE (paintable);
|
||||
|
||||
if (self->background)
|
||||
return MAX (gdk_paintable_get_intrinsic_height (self->background), self->height);
|
||||
else
|
||||
return self->height;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_path_paintable_snapshot (GdkPaintable *paintable,
|
||||
GdkSnapshot *snapshot,
|
||||
double width,
|
||||
double height)
|
||||
{
|
||||
GtkPathPaintable *self = GTK_PATH_PAINTABLE (paintable);
|
||||
|
||||
#if 0
|
||||
gtk_snapshot_push_fill (snapshot, self->path, GSK_FILL_RULE_WINDING);
|
||||
#else
|
||||
GskStroke *stroke = gsk_stroke_new (4.0);
|
||||
gtk_snapshot_push_stroke (snapshot, self->path, stroke);
|
||||
gsk_stroke_free (stroke);
|
||||
#endif
|
||||
|
||||
if (self->background)
|
||||
{
|
||||
gdk_paintable_snapshot (self->background, snapshot, width, height);
|
||||
}
|
||||
else
|
||||
{
|
||||
gtk_snapshot_append_linear_gradient (snapshot,
|
||||
&GRAPHENE_RECT_INIT (0, 0, width, height),
|
||||
&GRAPHENE_POINT_INIT (0, 0),
|
||||
&GRAPHENE_POINT_INIT (width, height),
|
||||
(GskColorStop[8]) {
|
||||
{ 0.0, { 1.0, 0.0, 0.0, 1.0 } },
|
||||
{ 0.2, { 1.0, 0.0, 0.0, 1.0 } },
|
||||
{ 0.3, { 1.0, 1.0, 0.0, 1.0 } },
|
||||
{ 0.4, { 0.0, 1.0, 0.0, 1.0 } },
|
||||
{ 0.6, { 0.0, 1.0, 1.0, 1.0 } },
|
||||
{ 0.7, { 0.0, 0.0, 1.0, 1.0 } },
|
||||
{ 0.8, { 1.0, 0.0, 1.0, 1.0 } },
|
||||
{ 1.0, { 1.0, 0.0, 1.0, 1.0 } }
|
||||
},
|
||||
8);
|
||||
}
|
||||
|
||||
gtk_snapshot_pop (snapshot);
|
||||
|
||||
}
|
||||
|
||||
static GdkPaintableFlags
|
||||
gtk_path_paintable_get_flags (GdkPaintable *paintable)
|
||||
{
|
||||
GtkPathPaintable *self = GTK_PATH_PAINTABLE (paintable);
|
||||
|
||||
if (self->background)
|
||||
return gdk_paintable_get_flags (self->background);
|
||||
else
|
||||
return GDK_PAINTABLE_STATIC_CONTENTS | GDK_PAINTABLE_STATIC_SIZE;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_path_paintable_paintable_init (GdkPaintableInterface *iface)
|
||||
{
|
||||
iface->get_intrinsic_width = gtk_path_paintable_get_intrinsic_width;
|
||||
iface->get_intrinsic_height = gtk_path_paintable_get_intrinsic_height;
|
||||
iface->snapshot = gtk_path_paintable_snapshot;
|
||||
iface->get_flags = gtk_path_paintable_get_flags;
|
||||
}
|
||||
|
||||
/* When defining the GType, we need to implement the GdkPaintable interface */
|
||||
G_DEFINE_TYPE_WITH_CODE (GtkPathPaintable, gtk_path_paintable, G_TYPE_OBJECT,
|
||||
G_IMPLEMENT_INTERFACE (GDK_TYPE_PAINTABLE,
|
||||
gtk_path_paintable_paintable_init))
|
||||
|
||||
/* Here's the boilerplate for the GObject declaration.
|
||||
* We need to disconnect the signals here that we set up elsewhere
|
||||
*/
|
||||
static void
|
||||
gtk_path_paintable_dispose (GObject *object)
|
||||
{
|
||||
GtkPathPaintable *self = GTK_PATH_PAINTABLE (object);
|
||||
|
||||
if (self->background)
|
||||
{
|
||||
g_signal_handlers_disconnect_by_func (self->background, gdk_paintable_invalidate_contents, self);
|
||||
g_signal_handlers_disconnect_by_func (self->background, gdk_paintable_invalidate_size, self);
|
||||
g_clear_object (&self->background);
|
||||
}
|
||||
|
||||
G_OBJECT_CLASS (gtk_path_paintable_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_path_paintable_class_init (GtkPathPaintableClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->dispose = gtk_path_paintable_dispose;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_path_paintable_init (GtkPathPaintable *self)
|
||||
{
|
||||
}
|
||||
|
||||
/* And finally, we add a simple constructor.
|
||||
* It is declared in the header so that the other examples
|
||||
* can use it.
|
||||
*/
|
||||
GdkPaintable *
|
||||
gtk_path_paintable_new (GskPath *path,
|
||||
GdkPaintable *background,
|
||||
int width,
|
||||
int height)
|
||||
{
|
||||
GtkPathPaintable *self;
|
||||
|
||||
self = g_object_new (GTK_TYPE_PATH_PAINTABLE, NULL);
|
||||
self->path = path;
|
||||
self->background = background;
|
||||
if (self->background)
|
||||
{
|
||||
g_object_ref (self->background);
|
||||
g_signal_connect_swapped (self->background, "invalidate-contents", G_CALLBACK (gdk_paintable_invalidate_contents), self);
|
||||
g_signal_connect_swapped (self->background, "invalidate-size", G_CALLBACK (gdk_paintable_invalidate_size), self);
|
||||
}
|
||||
self->width = width;
|
||||
self->height = height;
|
||||
|
||||
return GDK_PAINTABLE (self);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_path_paintable_set_path (GtkPathPaintable *self,
|
||||
GskPath *path)
|
||||
{
|
||||
g_clear_pointer (&self->path, gsk_path_unref);
|
||||
self->path = gsk_path_ref (path);
|
||||
|
||||
gdk_paintable_invalidate_contents (GDK_PAINTABLE (self));
|
||||
}
|
||||
|
||||
static GskPath *
|
||||
create_hexagon (GtkWidget *widget)
|
||||
{
|
||||
GskPathBuilder *builder;
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
|
||||
gsk_path_builder_move_to (builder, 120, 0);
|
||||
gsk_path_builder_line_to (builder, 360, 0);
|
||||
gsk_path_builder_line_to (builder, 480, 208);
|
||||
gsk_path_builder_line_to (builder, 360, 416);
|
||||
gsk_path_builder_line_to (builder, 120, 416);
|
||||
gsk_path_builder_line_to (builder, 0, 208);
|
||||
gsk_path_builder_close (builder);
|
||||
|
||||
return gsk_path_builder_free_to_path (builder);
|
||||
}
|
||||
|
||||
static GskPath *
|
||||
create_path_from_text (GtkWidget *widget)
|
||||
{
|
||||
cairo_surface_t *surface;
|
||||
cairo_t *cr;
|
||||
cairo_path_t *path;
|
||||
PangoLayout *layout;
|
||||
PangoFontDescription *desc;
|
||||
GskPath *result;
|
||||
|
||||
surface = cairo_recording_surface_create (CAIRO_CONTENT_COLOR_ALPHA, NULL);
|
||||
cr = cairo_create (surface);
|
||||
|
||||
layout = gtk_widget_create_pango_layout (widget, "Pango power!\nPango power!\nPango power!");
|
||||
desc = pango_font_description_from_string ("sans bold 36");
|
||||
pango_layout_set_font_description (layout, desc);
|
||||
pango_font_description_free (desc);
|
||||
|
||||
pango_cairo_layout_path (cr, layout);
|
||||
path = cairo_copy_path_flat (cr);
|
||||
result = gsk_path_new_from_cairo (path);
|
||||
|
||||
cairo_path_destroy (path);
|
||||
g_object_unref (layout);
|
||||
cairo_destroy (cr);
|
||||
cairo_surface_destroy (surface);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
update_path (GtkWidget *widget,
|
||||
GdkFrameClock *frame_clock,
|
||||
gpointer measure)
|
||||
{
|
||||
float progress = gdk_frame_clock_get_frame_time (frame_clock) % (60 * G_USEC_PER_SEC) / (float) (30 * G_USEC_PER_SEC);
|
||||
GskPathBuilder *builder;
|
||||
GskPath *path;
|
||||
graphene_point_t pos;
|
||||
graphene_vec2_t tangent;
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
gsk_path_measure_add_segment (measure,
|
||||
builder,
|
||||
progress > 1 ? (progress - 1) * gsk_path_measure_get_length (measure) : 0.0,
|
||||
(progress < 1 ? progress : 1.0) * gsk_path_measure_get_length (measure));
|
||||
|
||||
gsk_path_measure_get_point (measure,
|
||||
(progress > 1 ? (progress - 1) : progress) * gsk_path_measure_get_length (measure),
|
||||
&pos,
|
||||
&tangent);
|
||||
gsk_path_builder_move_to (builder, pos.x + 5 * graphene_vec2_get_x (&tangent), pos.y + 5 * graphene_vec2_get_y (&tangent));
|
||||
gsk_path_builder_line_to (builder, pos.x + 3 * graphene_vec2_get_y (&tangent), pos.y + 3 * graphene_vec2_get_x (&tangent));
|
||||
gsk_path_builder_line_to (builder, pos.x - 3 * graphene_vec2_get_y (&tangent), pos.y - 3 * graphene_vec2_get_x (&tangent));
|
||||
gsk_path_builder_close (builder);
|
||||
path = gsk_path_builder_free_to_path (builder);
|
||||
|
||||
gtk_path_paintable_set_path (GTK_PATH_PAINTABLE (gtk_picture_get_paintable (GTK_PICTURE (widget))),
|
||||
path);
|
||||
gsk_path_unref (path);
|
||||
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
do_path_fill (GtkWidget *do_widget)
|
||||
{
|
||||
static GtkWidget *window = NULL;
|
||||
|
||||
if (!window)
|
||||
{
|
||||
GtkWidget *picture;
|
||||
GdkPaintable *paintable;
|
||||
GtkMediaStream *stream;
|
||||
GskPath *path;
|
||||
graphene_rect_t bounds;
|
||||
GskPathMeasure *measure;
|
||||
|
||||
window = gtk_window_new ();
|
||||
gtk_window_set_resizable (GTK_WINDOW (window), TRUE);
|
||||
gtk_window_set_title (GTK_WINDOW (window), "Path Fill");
|
||||
g_object_add_weak_pointer (G_OBJECT (window), (gpointer *)&window);
|
||||
|
||||
#if 0
|
||||
stream = gtk_media_file_new_for_resource ("/images/gtk-logo.webm");
|
||||
#else
|
||||
stream = gtk_nuclear_media_stream_new ();
|
||||
#endif
|
||||
gtk_media_stream_play (stream);
|
||||
gtk_media_stream_set_loop (stream, TRUE);
|
||||
|
||||
path = create_hexagon (window);
|
||||
path = create_path_from_text (window);
|
||||
gsk_path_get_bounds (path, &bounds);
|
||||
|
||||
paintable = gtk_path_paintable_new (path,
|
||||
GDK_PAINTABLE (stream),
|
||||
bounds.origin.x + bounds.size.width,
|
||||
bounds.origin.y + bounds.size.height);
|
||||
picture = gtk_picture_new_for_paintable (paintable);
|
||||
measure = gsk_path_measure_new (path);
|
||||
gtk_widget_add_tick_callback (picture, update_path, measure, (GDestroyNotify) gsk_path_measure_unref);
|
||||
gtk_picture_set_content_fit (GTK_PICTURE (picture), GTK_CONTENT_FIT_CONTAIN);
|
||||
gtk_picture_set_can_shrink (GTK_PICTURE (picture), FALSE);
|
||||
g_object_unref (paintable);
|
||||
|
||||
gtk_window_set_child (GTK_WINDOW (window), picture);
|
||||
}
|
||||
|
||||
if (!gtk_widget_get_visible (window))
|
||||
gtk_window_present (GTK_WINDOW (window));
|
||||
else
|
||||
gtk_window_destroy (GTK_WINDOW (window));
|
||||
|
||||
return window;
|
||||
}
|
||||
338
demos/gtk-demo/path_maze.c
Normal file
338
demos/gtk-demo/path_maze.c
Normal file
@@ -0,0 +1,338 @@
|
||||
/* Path/Maze
|
||||
*
|
||||
* This demo shows how to use a GskPath to create a maze and use
|
||||
* gsk_path_measure_get_closest_point() to check the mouse stays
|
||||
* on the path.
|
||||
*
|
||||
* It also shows off the performance of GskPath (or not) as this
|
||||
* is a rather complex path.
|
||||
*/
|
||||
|
||||
#include <glib/gi18n.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "paintable.h"
|
||||
|
||||
#define MAZE_GRID_SIZE 20
|
||||
#define MAZE_STROKE_SIZE_ACTIVE (MAZE_GRID_SIZE - 4)
|
||||
#define MAZE_STROKE_SIZE_INACTIVE (MAZE_GRID_SIZE - 12)
|
||||
#define MAZE_WIDTH 31
|
||||
#define MAZE_HEIGHT 21
|
||||
|
||||
#define GTK_TYPE_MAZE (gtk_maze_get_type ())
|
||||
G_DECLARE_FINAL_TYPE (GtkMaze, gtk_maze, GTK, MAZE, GtkWidget)
|
||||
|
||||
struct _GtkMaze
|
||||
{
|
||||
GtkWidget parent_instance;
|
||||
|
||||
int width;
|
||||
int height;
|
||||
GskPath *path;
|
||||
GskPathMeasure *measure;
|
||||
GdkPaintable *background;
|
||||
|
||||
gboolean active;
|
||||
};
|
||||
|
||||
struct _GtkMazeClass
|
||||
{
|
||||
GtkWidgetClass parent_class;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GtkMaze, gtk_maze, GTK_TYPE_WIDGET)
|
||||
|
||||
static void
|
||||
gtk_maze_measure (GtkWidget *widget,
|
||||
GtkOrientation orientation,
|
||||
int for_size,
|
||||
int *minimum,
|
||||
int *natural,
|
||||
int *minimum_baseline,
|
||||
int *natural_baseline)
|
||||
{
|
||||
GtkMaze *self = GTK_MAZE (widget);
|
||||
|
||||
if (orientation == GTK_ORIENTATION_HORIZONTAL)
|
||||
*minimum = *natural = self->width;
|
||||
else
|
||||
*minimum = *natural = self->height;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_maze_snapshot (GtkWidget *widget,
|
||||
GdkSnapshot *snapshot)
|
||||
{
|
||||
GtkMaze *self = GTK_MAZE (widget);
|
||||
double width = gtk_widget_get_width (widget);
|
||||
double height = gtk_widget_get_height (widget);
|
||||
GskStroke *stroke;
|
||||
|
||||
stroke = gsk_stroke_new (MAZE_STROKE_SIZE_INACTIVE);
|
||||
if (self->active)
|
||||
gsk_stroke_set_line_width (stroke, MAZE_STROKE_SIZE_ACTIVE);
|
||||
gsk_stroke_set_line_join (stroke, GSK_LINE_JOIN_ROUND);
|
||||
gsk_stroke_set_line_cap (stroke, GSK_LINE_CAP_ROUND);
|
||||
gtk_snapshot_push_stroke (snapshot, self->path, stroke);
|
||||
gsk_stroke_free (stroke);
|
||||
|
||||
if (self->background)
|
||||
{
|
||||
gdk_paintable_snapshot (self->background, snapshot, width, height);
|
||||
}
|
||||
else
|
||||
{
|
||||
gtk_snapshot_append_linear_gradient (snapshot,
|
||||
&GRAPHENE_RECT_INIT (0, 0, width, height),
|
||||
&GRAPHENE_POINT_INIT (0, 0),
|
||||
&GRAPHENE_POINT_INIT (width, height),
|
||||
(GskColorStop[8]) {
|
||||
{ 0.0, { 1.0, 0.0, 0.0, 1.0 } },
|
||||
{ 0.2, { 1.0, 0.0, 0.0, 1.0 } },
|
||||
{ 0.3, { 1.0, 1.0, 0.0, 1.0 } },
|
||||
{ 0.4, { 0.0, 1.0, 0.0, 1.0 } },
|
||||
{ 0.6, { 0.0, 1.0, 1.0, 1.0 } },
|
||||
{ 0.7, { 0.0, 0.0, 1.0, 1.0 } },
|
||||
{ 0.8, { 1.0, 0.0, 1.0, 1.0 } },
|
||||
{ 1.0, { 1.0, 0.0, 1.0, 1.0 } }
|
||||
},
|
||||
8);
|
||||
}
|
||||
|
||||
gtk_snapshot_pop (snapshot);
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_maze_dispose (GObject *object)
|
||||
{
|
||||
GtkMaze *self = GTK_MAZE (object);
|
||||
|
||||
g_clear_pointer (&self->path, gsk_path_unref);
|
||||
g_clear_pointer (&self->measure, gsk_path_measure_unref);
|
||||
if (self->background)
|
||||
{
|
||||
g_signal_handlers_disconnect_matched (self->background, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, self);
|
||||
g_clear_object (&self->background);
|
||||
}
|
||||
|
||||
G_OBJECT_CLASS (gtk_maze_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_maze_class_init (GtkMazeClass *klass)
|
||||
{
|
||||
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->dispose = gtk_maze_dispose;
|
||||
|
||||
widget_class->measure = gtk_maze_measure;
|
||||
widget_class->snapshot = gtk_maze_snapshot;
|
||||
}
|
||||
|
||||
static void
|
||||
pointer_motion (GtkEventControllerMotion *controller,
|
||||
double x,
|
||||
double y,
|
||||
GtkMaze *self)
|
||||
{
|
||||
if (!self->active)
|
||||
return;
|
||||
|
||||
if (gsk_path_measure_get_closest_point (self->measure, &GRAPHENE_POINT_INIT (x, y), NULL) <= MAZE_STROKE_SIZE_ACTIVE / 2.0f)
|
||||
return;
|
||||
|
||||
self->active = FALSE;
|
||||
gtk_widget_queue_draw (GTK_WIDGET (self));
|
||||
}
|
||||
|
||||
static void
|
||||
pointer_leave (GtkEventControllerMotion *controller,
|
||||
GtkMaze *self)
|
||||
{
|
||||
if (!self->active)
|
||||
{
|
||||
self->active = TRUE;
|
||||
gtk_widget_queue_draw (GTK_WIDGET (self));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_maze_init (GtkMaze *self)
|
||||
{
|
||||
GtkEventController *controller;
|
||||
|
||||
controller = GTK_EVENT_CONTROLLER (gtk_event_controller_motion_new ());
|
||||
g_signal_connect (controller, "motion", G_CALLBACK (pointer_motion), self);
|
||||
g_signal_connect (controller, "leave", G_CALLBACK (pointer_leave), self);
|
||||
gtk_widget_add_controller (GTK_WIDGET (self), controller);
|
||||
|
||||
self->active = TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_maze_set_path (GtkMaze *self,
|
||||
GskPath *path)
|
||||
{
|
||||
g_clear_pointer (&self->path, gsk_path_unref);
|
||||
g_clear_pointer (&self->measure, gsk_path_measure_unref);
|
||||
self->path = gsk_path_ref (path);
|
||||
self->measure = gsk_path_measure_new (path);
|
||||
|
||||
gtk_widget_queue_draw (GTK_WIDGET (self));
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
gtk_maze_new (GskPath *path,
|
||||
GdkPaintable *background,
|
||||
int width,
|
||||
int height)
|
||||
{
|
||||
GtkMaze *self;
|
||||
|
||||
self = g_object_new (GTK_TYPE_MAZE, NULL);
|
||||
|
||||
gtk_maze_set_path (self, path);
|
||||
gsk_path_unref (path);
|
||||
self->background = background;
|
||||
if (self->background)
|
||||
{
|
||||
g_signal_connect_swapped (self->background, "invalidate-contents", G_CALLBACK (gtk_widget_queue_draw), self);
|
||||
g_signal_connect_swapped (self->background, "invalidate-size", G_CALLBACK (gtk_widget_queue_resize), self);
|
||||
}
|
||||
self->width = width;
|
||||
self->height = height;
|
||||
|
||||
return GTK_WIDGET (self);
|
||||
}
|
||||
|
||||
static void
|
||||
add_point_to_maze (GtkBitset *maze,
|
||||
GskPathBuilder *builder,
|
||||
guint x,
|
||||
guint y)
|
||||
{
|
||||
gboolean set[4] = { FALSE, FALSE, FALSE, FALSE };
|
||||
guint dir;
|
||||
|
||||
gtk_bitset_add (maze, y * MAZE_WIDTH + x);
|
||||
|
||||
while (TRUE)
|
||||
{
|
||||
set[0] = set[0] || x == 0 || gtk_bitset_contains (maze, y * MAZE_WIDTH + x - 1);
|
||||
set[1] = set[1] || y == 0 || gtk_bitset_contains (maze, (y - 1) * MAZE_WIDTH + x);
|
||||
set[2] = set[2] || x + 1 == MAZE_WIDTH || gtk_bitset_contains (maze, y * MAZE_WIDTH + x + 1);
|
||||
set[3] = set[3] || y + 1 == MAZE_HEIGHT || gtk_bitset_contains (maze, (y + 1) * MAZE_WIDTH + x);
|
||||
|
||||
if (set[0] && set[1] && set[2] && set[3])
|
||||
return;
|
||||
|
||||
do
|
||||
{
|
||||
dir = g_random_int_range (0, 4);
|
||||
}
|
||||
while (set[dir]);
|
||||
|
||||
switch (dir)
|
||||
{
|
||||
case 0:
|
||||
gsk_path_builder_move_to (builder, (x + 0.5) * MAZE_GRID_SIZE, (y + 0.5) * MAZE_GRID_SIZE);
|
||||
gsk_path_builder_line_to (builder, (x - 0.5) * MAZE_GRID_SIZE, (y + 0.5) * MAZE_GRID_SIZE);
|
||||
add_point_to_maze (maze, builder, x - 1, y);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
gsk_path_builder_move_to (builder, (x + 0.5) * MAZE_GRID_SIZE, (y + 0.5) * MAZE_GRID_SIZE);
|
||||
gsk_path_builder_line_to (builder, (x + 0.5) * MAZE_GRID_SIZE, (y - 0.5) * MAZE_GRID_SIZE);
|
||||
add_point_to_maze (maze, builder, x, y - 1);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
gsk_path_builder_move_to (builder, (x + 0.5) * MAZE_GRID_SIZE, (y + 0.5) * MAZE_GRID_SIZE);
|
||||
gsk_path_builder_line_to (builder, (x + 1.5) * MAZE_GRID_SIZE, (y + 0.5) * MAZE_GRID_SIZE);
|
||||
add_point_to_maze (maze, builder, x + 1, y);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
gsk_path_builder_move_to (builder, (x + 0.5) * MAZE_GRID_SIZE, (y + 0.5) * MAZE_GRID_SIZE);
|
||||
gsk_path_builder_line_to (builder, (x + 0.5) * MAZE_GRID_SIZE, (y + 1.5) * MAZE_GRID_SIZE);
|
||||
add_point_to_maze (maze, builder, x, y + 1);
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static GskPath *
|
||||
create_path_for_maze (GtkWidget *widget)
|
||||
{
|
||||
GskPathBuilder *builder;
|
||||
GtkBitset *maze;
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
maze = gtk_bitset_new_empty ();
|
||||
/* make sure the outer lines are unreachable:
|
||||
* Set the full range, then remove the center again. */
|
||||
gtk_bitset_add_range (maze, 0, MAZE_WIDTH * MAZE_HEIGHT);
|
||||
gtk_bitset_remove_rectangle (maze, MAZE_WIDTH + 1, MAZE_WIDTH - 2, MAZE_HEIGHT - 2, MAZE_WIDTH);
|
||||
|
||||
/* Fill the maze */
|
||||
add_point_to_maze (maze, builder, MAZE_WIDTH / 2, MAZE_HEIGHT / 2);
|
||||
|
||||
/* Add start and stop lines */
|
||||
gsk_path_builder_move_to (builder, 1.5 * MAZE_GRID_SIZE, -0.5 * MAZE_GRID_SIZE);
|
||||
gsk_path_builder_line_to (builder, 1.5 * MAZE_GRID_SIZE, 1.5 * MAZE_GRID_SIZE);
|
||||
gsk_path_builder_move_to (builder, (MAZE_WIDTH - 1.5) * MAZE_GRID_SIZE, (MAZE_HEIGHT - 1.5) * MAZE_GRID_SIZE);
|
||||
gsk_path_builder_line_to (builder, (MAZE_WIDTH - 1.5) * MAZE_GRID_SIZE, (MAZE_HEIGHT + 0.5) * MAZE_GRID_SIZE);
|
||||
|
||||
|
||||
gtk_bitset_unref (maze);
|
||||
|
||||
return gsk_path_builder_free_to_path (builder);
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
do_path_maze (GtkWidget *do_widget)
|
||||
{
|
||||
static GtkWidget *window = NULL;
|
||||
|
||||
if (!window)
|
||||
{
|
||||
GtkWidget *maze;
|
||||
GtkMediaStream *stream;
|
||||
GskPath *path;
|
||||
|
||||
window = gtk_window_new ();
|
||||
gtk_window_set_resizable (GTK_WINDOW (window), TRUE);
|
||||
gtk_window_set_title (GTK_WINDOW (window), "Follow the maze with the mouse");
|
||||
g_object_add_weak_pointer (G_OBJECT (window), (gpointer *)&window);
|
||||
|
||||
#if 0
|
||||
stream = gtk_media_file_new_for_resource ("/images/gtk-logo.webm");
|
||||
#else
|
||||
stream = gtk_nuclear_media_stream_new ();
|
||||
#endif
|
||||
gtk_media_stream_play (stream);
|
||||
gtk_media_stream_set_loop (stream, TRUE);
|
||||
|
||||
path = create_path_for_maze (window);
|
||||
|
||||
maze = gtk_maze_new (path,
|
||||
GDK_PAINTABLE (stream),
|
||||
MAZE_WIDTH * MAZE_GRID_SIZE,
|
||||
MAZE_HEIGHT * MAZE_GRID_SIZE);
|
||||
|
||||
gtk_window_set_child (GTK_WINDOW (window), maze);
|
||||
}
|
||||
|
||||
if (!gtk_widget_get_visible (window))
|
||||
gtk_window_present (GTK_WINDOW (window));
|
||||
else
|
||||
gtk_window_destroy (GTK_WINDOW (window));
|
||||
|
||||
return window;
|
||||
}
|
||||
580
demos/gtk-demo/path_text.c
Normal file
580
demos/gtk-demo/path_text.c
Normal file
@@ -0,0 +1,580 @@
|
||||
/* Path/Text
|
||||
*
|
||||
* This demo shows how to use GskPath to animate a path along another path.
|
||||
*/
|
||||
|
||||
#include <glib/gi18n.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#define GTK_TYPE_PATH_WIDGET (gtk_path_widget_get_type ())
|
||||
G_DECLARE_FINAL_TYPE (GtkPathWidget, gtk_path_widget, GTK, PATH_WIDGET, GtkWidget)
|
||||
|
||||
#define POINT_SIZE 8
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_TEXT,
|
||||
PROP_EDITABLE,
|
||||
N_PROPS
|
||||
};
|
||||
|
||||
struct _GtkPathWidget
|
||||
{
|
||||
GtkWidget parent_instance;
|
||||
|
||||
char *text;
|
||||
gboolean editable;
|
||||
|
||||
graphene_point_t points[4];
|
||||
|
||||
guint active_point;
|
||||
float line_closest;
|
||||
|
||||
GskPath *line_path;
|
||||
GskPathMeasure *line_measure;
|
||||
GskPath *text_path;
|
||||
|
||||
GdkPaintable *background;
|
||||
};
|
||||
|
||||
struct _GtkPathWidgetClass
|
||||
{
|
||||
GtkWidgetClass parent_class;
|
||||
};
|
||||
|
||||
static GParamSpec *properties[N_PROPS] = { NULL, };
|
||||
|
||||
G_DEFINE_TYPE (GtkPathWidget, gtk_path_widget, GTK_TYPE_WIDGET)
|
||||
|
||||
static GskPath *
|
||||
create_path_from_text (GtkWidget *widget,
|
||||
const char *text)
|
||||
{
|
||||
cairo_surface_t *surface;
|
||||
cairo_t *cr;
|
||||
cairo_path_t *path;
|
||||
PangoLayout *layout;
|
||||
PangoFontDescription *desc;
|
||||
GskPath *result;
|
||||
|
||||
surface = cairo_recording_surface_create (CAIRO_CONTENT_COLOR_ALPHA, NULL);
|
||||
cr = cairo_create (surface);
|
||||
|
||||
layout = gtk_widget_create_pango_layout (widget, text);
|
||||
desc = pango_font_description_from_string ("sans bold 36");
|
||||
pango_layout_set_font_description (layout, desc);
|
||||
pango_font_description_free (desc);
|
||||
|
||||
cairo_move_to (cr, 0, - pango_layout_get_baseline (layout) / (double) PANGO_SCALE);
|
||||
pango_cairo_layout_path (cr, layout);
|
||||
path = cairo_copy_path_flat (cr);
|
||||
result = gsk_path_new_from_cairo (path);
|
||||
|
||||
cairo_path_destroy (path);
|
||||
g_object_unref (layout);
|
||||
cairo_destroy (cr);
|
||||
cairo_surface_destroy (surface);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GskPathMeasure *measure;
|
||||
GskPathBuilder *builder;
|
||||
double scale;
|
||||
} GtkPathTransform;
|
||||
|
||||
static void
|
||||
gtk_path_transform_point (GskPathMeasure *measure,
|
||||
const graphene_point_t *pt,
|
||||
float scale,
|
||||
graphene_point_t *res)
|
||||
{
|
||||
graphene_vec2_t tangent;
|
||||
|
||||
gsk_path_measure_get_point (measure, pt->x * scale, res, &tangent);
|
||||
|
||||
res->x -= pt->y * scale * graphene_vec2_get_y (&tangent);
|
||||
res->y += pt->y * scale * graphene_vec2_get_x (&tangent);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_path_transform_op (GskPathOperation op,
|
||||
const graphene_point_t *pts,
|
||||
gsize n_pts,
|
||||
gpointer data)
|
||||
{
|
||||
GtkPathTransform *transform = data;
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case GSK_PATH_MOVE:
|
||||
{
|
||||
graphene_point_t res;
|
||||
gtk_path_transform_point (transform->measure, &pts[0], transform->scale, &res);
|
||||
gsk_path_builder_move_to (transform->builder, res.x, res.y);
|
||||
}
|
||||
break;
|
||||
|
||||
case GSK_PATH_LINE:
|
||||
{
|
||||
graphene_point_t res;
|
||||
gtk_path_transform_point (transform->measure, &pts[1], transform->scale, &res);
|
||||
gsk_path_builder_line_to (transform->builder, res.x, res.y);
|
||||
}
|
||||
break;
|
||||
|
||||
case GSK_PATH_CURVE:
|
||||
{
|
||||
graphene_point_t res[3];
|
||||
gtk_path_transform_point (transform->measure, &pts[1], transform->scale, &res[0]);
|
||||
gtk_path_transform_point (transform->measure, &pts[2], transform->scale, &res[1]);
|
||||
gtk_path_transform_point (transform->measure, &pts[3], transform->scale, &res[2]);
|
||||
gsk_path_builder_curve_to (transform->builder, res[0].x, res[0].y, res[1].x, res[1].y, res[2].x, res[2].y);
|
||||
}
|
||||
break;
|
||||
|
||||
case GSK_PATH_CLOSE:
|
||||
gsk_path_builder_close (transform->builder);
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static GskPath *
|
||||
gtk_path_transform (GskPathMeasure *measure,
|
||||
GskPath *path)
|
||||
{
|
||||
GtkPathTransform transform = { measure, gsk_path_builder_new () };
|
||||
graphene_rect_t bounds;
|
||||
|
||||
gsk_path_get_bounds (path, &bounds);
|
||||
if (bounds.origin.x + bounds.size.width > 0)
|
||||
transform.scale = gsk_path_measure_get_length (measure) / (bounds.origin.x + bounds.size.width);
|
||||
else
|
||||
transform.scale = 1.0f;
|
||||
|
||||
gsk_path_foreach (path, GSK_PATH_FOREACH_ALLOW_CURVES, gtk_path_transform_op, &transform);
|
||||
|
||||
return gsk_path_builder_free_to_path (transform.builder);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_path_widget_clear_text_path (GtkPathWidget *self)
|
||||
{
|
||||
g_clear_pointer (&self->text_path, gsk_path_unref);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_path_widget_clear_paths (GtkPathWidget *self)
|
||||
{
|
||||
gtk_path_widget_clear_text_path (self);
|
||||
|
||||
g_clear_pointer (&self->line_path, gsk_path_unref);
|
||||
g_clear_pointer (&self->line_measure, gsk_path_measure_unref);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_path_widget_create_text_path (GtkPathWidget *self)
|
||||
{
|
||||
GskPath *path;
|
||||
|
||||
gtk_path_widget_clear_text_path (self);
|
||||
|
||||
if (self->line_measure == NULL)
|
||||
return;
|
||||
|
||||
path = create_path_from_text (GTK_WIDGET (self), self->text);
|
||||
self->text_path = gtk_path_transform (self->line_measure, path);
|
||||
|
||||
gsk_path_unref (path);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_path_widget_create_paths (GtkPathWidget *self)
|
||||
{
|
||||
double width = gtk_widget_get_width (GTK_WIDGET (self));
|
||||
double height = gtk_widget_get_height (GTK_WIDGET (self));
|
||||
GskPathBuilder *builder;
|
||||
|
||||
gtk_path_widget_clear_paths (self);
|
||||
|
||||
if (width <= 0 || height <= 0)
|
||||
return;
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
gsk_path_builder_move_to (builder,
|
||||
self->points[0].x * width, self->points[0].y * height);
|
||||
gsk_path_builder_curve_to (builder,
|
||||
self->points[1].x * width, self->points[1].y * height,
|
||||
self->points[2].x * width, self->points[2].y * height,
|
||||
self->points[3].x * width, self->points[3].y * height);
|
||||
self->line_path = gsk_path_builder_free_to_path (builder);
|
||||
|
||||
self->line_measure = gsk_path_measure_new (self->line_path);
|
||||
|
||||
gtk_path_widget_create_text_path (self);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_path_widget_allocate (GtkWidget *widget,
|
||||
int width,
|
||||
int height,
|
||||
int baseline)
|
||||
{
|
||||
GtkPathWidget *self = GTK_PATH_WIDGET (widget);
|
||||
|
||||
GTK_WIDGET_CLASS (gtk_path_widget_parent_class)->size_allocate (widget, width, height, baseline);
|
||||
|
||||
gtk_path_widget_create_paths (self);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_path_widget_snapshot (GtkWidget *widget,
|
||||
GtkSnapshot *snapshot)
|
||||
{
|
||||
GtkPathWidget *self = GTK_PATH_WIDGET (widget);
|
||||
double width = gtk_widget_get_width (widget);
|
||||
double height = gtk_widget_get_height (widget);
|
||||
GskPath *path;
|
||||
GskStroke *stroke;
|
||||
gsize i;
|
||||
|
||||
/* frosted glass the background */
|
||||
gtk_snapshot_push_blur (snapshot, 100);
|
||||
gdk_paintable_snapshot (self->background, snapshot, width, height);
|
||||
gtk_snapshot_append_color (snapshot, &(GdkRGBA) { 1, 1, 1, 0.6 }, &GRAPHENE_RECT_INIT (0, 0, width, height));
|
||||
gtk_snapshot_pop (snapshot);
|
||||
|
||||
/* draw the text */
|
||||
if (self->text_path)
|
||||
{
|
||||
gtk_snapshot_push_fill (snapshot, self->text_path, GSK_FILL_RULE_WINDING);
|
||||
gdk_paintable_snapshot (self->background, snapshot, width, height);
|
||||
|
||||
/* ... with an emboss effect */
|
||||
stroke = gsk_stroke_new (2.0);
|
||||
gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT(1, 1));
|
||||
gtk_snapshot_push_stroke (snapshot, self->text_path, stroke);
|
||||
gtk_snapshot_append_color (snapshot, &(GdkRGBA) { 0, 0, 0, 0.2 }, &GRAPHENE_RECT_INIT (0, 0, width, height));
|
||||
gsk_stroke_free (stroke);
|
||||
gtk_snapshot_pop (snapshot);
|
||||
|
||||
gtk_snapshot_pop (snapshot);
|
||||
}
|
||||
|
||||
if (self->editable && self->line_path)
|
||||
{
|
||||
GskPathBuilder *builder;
|
||||
|
||||
/* draw the control line */
|
||||
stroke = gsk_stroke_new (1.0);
|
||||
gtk_snapshot_push_stroke (snapshot, self->line_path, stroke);
|
||||
gsk_stroke_free (stroke);
|
||||
gtk_snapshot_append_color (snapshot, &(GdkRGBA) { 0, 0, 0, 1 }, &GRAPHENE_RECT_INIT (0, 0, width, height));
|
||||
gtk_snapshot_pop (snapshot);
|
||||
|
||||
/* draw the points */
|
||||
builder = gsk_path_builder_new ();
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
gsk_path_builder_add_circle (builder, &GRAPHENE_POINT_INIT (self->points[i].x * width, self->points[i].y * height), POINT_SIZE);
|
||||
}
|
||||
path = gsk_path_builder_free_to_path (builder);
|
||||
|
||||
gtk_snapshot_push_fill (snapshot, path, GSK_FILL_RULE_WINDING);
|
||||
gtk_snapshot_append_color (snapshot, &(GdkRGBA) { 1, 1, 1, 1 }, &GRAPHENE_RECT_INIT (0, 0, width, height));
|
||||
gtk_snapshot_pop (snapshot);
|
||||
|
||||
stroke = gsk_stroke_new (1.0);
|
||||
gtk_snapshot_push_stroke (snapshot, path, stroke);
|
||||
gsk_stroke_free (stroke);
|
||||
gtk_snapshot_append_color (snapshot, &(GdkRGBA) { 0, 0, 0, 1 }, &GRAPHENE_RECT_INIT (0, 0, width, height));
|
||||
gtk_snapshot_pop (snapshot);
|
||||
|
||||
gsk_path_unref (path);
|
||||
}
|
||||
|
||||
if (self->line_closest >= 0)
|
||||
{
|
||||
GskPathBuilder *builder;
|
||||
graphene_point_t closest;
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
gsk_path_measure_get_point (self->line_measure, self->line_closest, &closest, NULL);
|
||||
gsk_path_builder_add_circle (builder, &closest, POINT_SIZE);
|
||||
path = gsk_path_builder_free_to_path (builder);
|
||||
|
||||
gtk_snapshot_push_fill (snapshot, path, GSK_FILL_RULE_WINDING);
|
||||
gtk_snapshot_append_color (snapshot, &(GdkRGBA) { 0, 0, 1, 1 }, &GRAPHENE_RECT_INIT (0, 0, width, height));
|
||||
gtk_snapshot_pop (snapshot);
|
||||
|
||||
gsk_path_unref (path);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_path_widget_set_text (GtkPathWidget *self,
|
||||
const char *text)
|
||||
{
|
||||
if (g_strcmp0 (self->text, text) == 0)
|
||||
return;
|
||||
|
||||
g_free (self->text);
|
||||
self->text = g_strdup (text);
|
||||
|
||||
gtk_path_widget_create_paths (self);
|
||||
|
||||
gtk_widget_queue_draw (GTK_WIDGET (self));
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_TEXT]);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_path_widget_set_editable (GtkPathWidget *self,
|
||||
gboolean editable)
|
||||
{
|
||||
if (self->editable == editable)
|
||||
return;
|
||||
|
||||
self->editable = editable;
|
||||
|
||||
gtk_widget_queue_draw (GTK_WIDGET (self));
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_EDITABLE]);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_path_widget_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
|
||||
{
|
||||
GtkPathWidget *self = GTK_PATH_WIDGET (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_TEXT:
|
||||
gtk_path_widget_set_text (self, g_value_get_string (value));
|
||||
break;
|
||||
|
||||
case PROP_EDITABLE:
|
||||
gtk_path_widget_set_editable (self, g_value_get_boolean (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_path_widget_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkPathWidget *self = GTK_PATH_WIDGET (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_TEXT:
|
||||
g_value_set_string (value, self->text);
|
||||
break;
|
||||
|
||||
case PROP_EDITABLE:
|
||||
g_value_set_boolean (value, self->editable);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_path_widget_dispose (GObject *object)
|
||||
{
|
||||
GtkPathWidget *self = GTK_PATH_WIDGET (object);
|
||||
|
||||
gtk_path_widget_clear_paths (self);
|
||||
|
||||
G_OBJECT_CLASS (gtk_path_widget_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_path_widget_class_init (GtkPathWidgetClass *klass)
|
||||
{
|
||||
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->dispose = gtk_path_widget_dispose;
|
||||
object_class->set_property = gtk_path_widget_set_property;
|
||||
object_class->get_property = gtk_path_widget_get_property;
|
||||
|
||||
widget_class->size_allocate = gtk_path_widget_allocate;
|
||||
widget_class->snapshot = gtk_path_widget_snapshot;
|
||||
|
||||
properties[PROP_TEXT] =
|
||||
g_param_spec_string ("text",
|
||||
"text",
|
||||
"Text transformed along a path",
|
||||
NULL,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
properties[PROP_EDITABLE] =
|
||||
g_param_spec_boolean ("editable",
|
||||
"editable",
|
||||
"If the path can be edited by the user",
|
||||
FALSE,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
g_object_class_install_properties (object_class, N_PROPS, properties);
|
||||
}
|
||||
|
||||
static void
|
||||
drag_begin (GtkGestureDrag *gesture,
|
||||
double x,
|
||||
double y,
|
||||
GtkPathWidget *self)
|
||||
{
|
||||
graphene_point_t mouse = GRAPHENE_POINT_INIT (x, y);
|
||||
double width = gtk_widget_get_width (GTK_WIDGET (self));
|
||||
double height = gtk_widget_get_height (GTK_WIDGET (self));
|
||||
gsize i;
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
if (graphene_point_distance (&GRAPHENE_POINT_INIT (self->points[i].x * width, self->points[i].y * height), &mouse, NULL, NULL) <= POINT_SIZE)
|
||||
{
|
||||
self->active_point = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == 4)
|
||||
{
|
||||
gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
|
||||
return;
|
||||
}
|
||||
|
||||
gtk_widget_queue_draw (GTK_WIDGET (self));
|
||||
}
|
||||
|
||||
static void
|
||||
drag_update (GtkGestureDrag *drag,
|
||||
double offset_x,
|
||||
double offset_y,
|
||||
GtkPathWidget *self)
|
||||
{
|
||||
double width = gtk_widget_get_width (GTK_WIDGET (self));
|
||||
double height = gtk_widget_get_height (GTK_WIDGET (self));
|
||||
double start_x, start_y;
|
||||
|
||||
gtk_gesture_drag_get_start_point (drag, &start_x, &start_y);
|
||||
|
||||
self->points[self->active_point] = GRAPHENE_POINT_INIT ((start_x + offset_x) / width,
|
||||
(start_y + offset_y) / height);
|
||||
self->points[self->active_point].x = CLAMP (self->points[self->active_point].x, 0, 1);
|
||||
self->points[self->active_point].y = CLAMP (self->points[self->active_point].y, 0, 1);
|
||||
|
||||
gtk_path_widget_create_paths (self);
|
||||
|
||||
gtk_widget_queue_draw (GTK_WIDGET (self));
|
||||
}
|
||||
|
||||
static void
|
||||
pointer_motion (GtkEventControllerMotion *controller,
|
||||
double x,
|
||||
double y,
|
||||
GtkPathWidget *self)
|
||||
{
|
||||
gsk_path_measure_get_closest_point_full (self->line_measure,
|
||||
&GRAPHENE_POINT_INIT (x, y),
|
||||
INFINITY,
|
||||
&self->line_closest,
|
||||
NULL, NULL, NULL);
|
||||
|
||||
gtk_widget_queue_draw (GTK_WIDGET (self));
|
||||
}
|
||||
|
||||
static void
|
||||
pointer_leave (GtkEventControllerMotion *controller,
|
||||
GtkPathWidget *self)
|
||||
{
|
||||
self->line_closest = -1;
|
||||
|
||||
gtk_widget_queue_draw (GTK_WIDGET (self));
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_path_widget_init (GtkPathWidget *self)
|
||||
{
|
||||
GtkEventController *controller;
|
||||
|
||||
controller = GTK_EVENT_CONTROLLER (gtk_gesture_drag_new ());
|
||||
g_signal_connect (controller, "drag-begin", G_CALLBACK (drag_begin), self);
|
||||
g_signal_connect (controller, "drag-update", G_CALLBACK (drag_update), self);
|
||||
g_signal_connect (controller, "drag-end", G_CALLBACK (drag_update), self);
|
||||
gtk_widget_add_controller (GTK_WIDGET (self), controller);
|
||||
|
||||
controller = GTK_EVENT_CONTROLLER (gtk_event_controller_motion_new ());
|
||||
g_signal_connect (controller, "enter", G_CALLBACK (pointer_motion), self);
|
||||
g_signal_connect (controller, "motion", G_CALLBACK (pointer_motion), self);
|
||||
g_signal_connect (controller, "leave", G_CALLBACK (pointer_leave), self);
|
||||
gtk_widget_add_controller (GTK_WIDGET (self), controller);
|
||||
|
||||
self->line_closest = -1;
|
||||
|
||||
self->points[0] = GRAPHENE_POINT_INIT (0.1, 0.9);
|
||||
self->points[1] = GRAPHENE_POINT_INIT (0.3, 0.1);
|
||||
self->points[2] = GRAPHENE_POINT_INIT (0.7, 0.1);
|
||||
self->points[3] = GRAPHENE_POINT_INIT (0.9, 0.9);
|
||||
|
||||
self->background = GDK_PAINTABLE (gdk_texture_new_from_resource ("/sliding_puzzle/portland-rose.jpg"));
|
||||
|
||||
gtk_path_widget_set_text (self, "It's almost working");
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
gtk_path_widget_new (void)
|
||||
{
|
||||
GtkPathWidget *self;
|
||||
|
||||
self = g_object_new (GTK_TYPE_PATH_WIDGET, NULL);
|
||||
|
||||
return GTK_WIDGET (self);
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
do_path_text (GtkWidget *do_widget)
|
||||
{
|
||||
static GtkWidget *window = NULL;
|
||||
|
||||
if (!window)
|
||||
{
|
||||
GtkBuilder *builder;
|
||||
|
||||
g_type_ensure (GTK_TYPE_PATH_WIDGET);
|
||||
|
||||
builder = gtk_builder_new_from_resource ("/path_text/path_text.ui");
|
||||
window = GTK_WIDGET (gtk_builder_get_object (builder, "window"));
|
||||
gtk_window_set_display (GTK_WINDOW (window),
|
||||
gtk_widget_get_display (do_widget));
|
||||
g_object_add_weak_pointer (G_OBJECT (window), (gpointer *) &window);
|
||||
g_object_unref (builder);
|
||||
}
|
||||
|
||||
if (!gtk_widget_get_visible (window))
|
||||
gtk_window_present (GTK_WINDOW (window));
|
||||
else
|
||||
gtk_window_destroy (GTK_WINDOW (window));
|
||||
|
||||
return window;
|
||||
}
|
||||
38
demos/gtk-demo/path_text.ui
Normal file
38
demos/gtk-demo/path_text.ui
Normal file
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<object class="GtkWindow" id="window">
|
||||
<property name="title" translatable="yes">Text along a Path</property>
|
||||
<child type="titlebar">
|
||||
<object class="GtkHeaderBar">
|
||||
<child type="end">
|
||||
<object class="GtkToggleButton" id="edit-toggle">
|
||||
<property name="icon-name">document-edit-symbolic</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkRevealer">
|
||||
<property name="reveal-child" bind-source="edit-toggle" bind-property="active" bind-flags="sync-create"></property>
|
||||
<child>
|
||||
<object class="GtkEntry" id="text">
|
||||
<property name="text">Through the looking glass</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkPathWidget" id="view">
|
||||
<property name="editable" bind-source="edit-toggle" bind-property="active" bind-flags="sync-create"></property>
|
||||
<property name="text" bind-source="text" bind-property="text" bind-flags="sync-create"></property>
|
||||
<property name="hexpand">true</property>
|
||||
<property name="vexpand">true</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
||||
@@ -1,16 +1,6 @@
|
||||
# demos/widget-factory
|
||||
|
||||
objcopy_supports_add_symbol = false
|
||||
objcopy = find_program('objcopy', required : false)
|
||||
if objcopy.found()
|
||||
objcopy_supports_add_symbol = run_command(objcopy, '--help', check: false).stdout().contains('--add-symbol')
|
||||
endif
|
||||
|
||||
ld = find_program('ld', required : false)
|
||||
|
||||
if not meson.is_cross_build() and build_machine.cpu_family() != 'arm' and build_machine.system() == 'linux' and objcopy.found() and objcopy_supports_add_symbol and ld.found()
|
||||
glib_compile_resources = find_program('glib-compile-resources')
|
||||
|
||||
if can_use_objcopy_for_resources
|
||||
# Create the resource blob
|
||||
widgetfactory_gresource = custom_target('widgetfactory.gresource',
|
||||
input : 'widget-factory.gresource.xml',
|
||||
|
||||
@@ -1572,6 +1572,9 @@ Suspendisse feugiat quam quis dolor accumsan cursus.</property>
|
||||
<property name="valign">3</property>
|
||||
<child>
|
||||
<object class="GtkVolumeButton">
|
||||
<accessibility>
|
||||
<property name="label" translatable="1">Volume</property>
|
||||
</accessibility>
|
||||
<property name="orientation">1</property>
|
||||
<property name="valign">3</property>
|
||||
<property name="value">.5</property>
|
||||
@@ -1584,6 +1587,9 @@ Suspendisse feugiat quam quis dolor accumsan cursus.</property>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScaleButton" id="mic-button">
|
||||
<accessibility>
|
||||
<property name="label" translatable="1">Microphone gain</property>
|
||||
</accessibility>
|
||||
<property name="has-tooltip">1</property>
|
||||
<property name="icons">microphone-sensitivity-muted-symbolic
|
||||
microphone-sensitivity-high-symbolic
|
||||
|
||||
@@ -16,6 +16,7 @@ SYNOPSIS
|
||||
| **gtk4-builder-tool** enumerate [OPTIONS...] <FILE>
|
||||
| **gtk4-builder-tool** simplify [OPTIONS...] <FILE>
|
||||
| **gtk4-builder-tool** preview [OPTIONS...] <FILE>
|
||||
| **gtk4-builder-tool** render [OPTIONS...] <FILE>
|
||||
| **gtk4-builder-tool** screenshot [OPTIONS...] <FILE>
|
||||
|
||||
DESCRIPTION
|
||||
@@ -69,12 +70,11 @@ file to use.
|
||||
|
||||
Load style information from the given CSS file.
|
||||
|
||||
Screenshot
|
||||
^^^^^^^^^^
|
||||
Render
|
||||
^^^^^^
|
||||
|
||||
The ``screenshot`` command saves a rendering of the UI definition file
|
||||
as a png image or node file. The name of the file to write can be specified as
|
||||
a second FILE argument.
|
||||
The ``render`` command saves a rendering of the UI definition file as a png image
|
||||
or node file. The name of the file to write can be specified as a second FILE argument.
|
||||
|
||||
This command accepts options to specify the ID of the toplevel object and a CSS
|
||||
file to use.
|
||||
@@ -96,6 +96,11 @@ file to use.
|
||||
|
||||
Overwrite an existing file.
|
||||
|
||||
Screenshot
|
||||
^^^^^^^^^^
|
||||
|
||||
The ``screenshot`` command is an alias for ``render``.
|
||||
|
||||
Simplification
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
|
||||
@@ -211,6 +211,9 @@ A number of options affect behavior instead of logging:
|
||||
`portals`
|
||||
: Force the use of [portals](https://docs.flatpak.org/en/latest/portals.html)
|
||||
|
||||
`no-portals`
|
||||
: Disable use of [portals](https://docs.flatpak.org/en/latest/portals.html)
|
||||
|
||||
`gl-disable`
|
||||
: Disable OpenGL support
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ described by a set of *attributes*.
|
||||
Roles define the taxonomy and semantics of a UI control to any assistive
|
||||
technology application; for instance, a button will have a role of
|
||||
`GTK_ACCESSIBLE_ROLE_BUTTON`; an entry will have a role of
|
||||
`GTK_ACCESSIBLE_ROLE_TEXTBOX`; a toggle button will have a role of
|
||||
`GTK_ACCESSIBLE_ROLE_TEXTBOX`; a check button will have a role of
|
||||
`GTK_ACCESSIBLE_ROLE_CHECKBOX`; etc.
|
||||
|
||||
Each role is part of the widget's instance, and **cannot** be changed over
|
||||
@@ -46,6 +46,7 @@ Each role name is part of the #GtkAccessibleRole enumeration.
|
||||
|
||||
| Role name | Description | Related GTK widget |
|
||||
|-----------|-------------|--------------------|
|
||||
| `APPLICATION` | An application window | [class@Gtk.Window] |
|
||||
| `BUTTON` | A control that performs an action when pressed | [class@Gtk.Button], [class@Gtk.LinkButton], [class@Gtk.Expander] |
|
||||
| `CHECKBOX` | A control that has three possible value: `true`, `false`, or `undefined` | [class@Gtk.CheckButton] |
|
||||
| `COMBOBOX` | A control that can be expanded to show a list of possible values to select | [class@Gtk.ComboBox] |
|
||||
@@ -78,7 +79,6 @@ Each role name is part of the #GtkAccessibleRole enumeration.
|
||||
| `TAB_PANEL` | A page in a notebook or stack | [class@Gtk.Stack] |
|
||||
| `TEXT_BOX` | A type of input that allows free-form text as its value. | [class@Gtk.Entry], [class@Gtk.PasswordEntry], [class@Gtk.TextView] |
|
||||
| `TREE_GRID` | A treeview-like columned list | [class@Gtk.ColumnView] |
|
||||
| `WINDOW` | An application window | [class@Gtk.Window] |
|
||||
| `...` | … |
|
||||
|
||||
See the [WAI-ARIA](https://www.w3.org/WAI/PF/aria/appendices#quickref) list
|
||||
@@ -216,6 +216,16 @@ are accessible as part of the development process. The GTK Inspector shows
|
||||
the accessible attributes of each widget, and also provides an overlay that
|
||||
can highlight accessibility issues.
|
||||
|
||||
It is possible to set accessible attributes in UI files as well:
|
||||
```xml
|
||||
<object class="GtkButton" id="button1">
|
||||
<accessibility>
|
||||
<property name="label">Download</property>
|
||||
<relation name="labelled-by">label1</relation>
|
||||
/accessibility>
|
||||
</object>
|
||||
```
|
||||
|
||||
## Implementations
|
||||
|
||||
Each UI control implements the `GtkAccessible` interface to allow widget and
|
||||
@@ -257,6 +267,13 @@ turn automatically any widget into a `GtkButton`; but if your widget behaves
|
||||
like a button, using the %GTK_ACCESSIBLE_ROLE_BUTTON will allow any
|
||||
assistive technology to handle it like they would a `GtkButton`.
|
||||
|
||||
For widgets that act as containers of other widgets, you should use
|
||||
%GTK_ACCESSIBLE_ROLE_GROUP if the grouping of the children is semantic
|
||||
in nature; for instance, the children of a [class@Gtk.HeaderBar] are
|
||||
grouped together on the header of a window. For generic containers that
|
||||
only impose a layout on their children, you should use
|
||||
%GTK_ACCESSIBLE_ROLE_GENERIC instead.
|
||||
|
||||
### Attributes can both hide and enhance
|
||||
|
||||
Accessible attributes can be used to override the content of a UI element,
|
||||
@@ -365,3 +382,6 @@ To allow changing the value via accessible technologies, you can export
|
||||
actions. Since the accessibility interfaces only support actions
|
||||
without parameters, you should provide actions such as `increase-value`
|
||||
and `decrease-value`.
|
||||
|
||||
Since GTK 4.10, the best way to suppose changing the value is by implementing
|
||||
the [iface@Gtk.AccessibleRange] interface.
|
||||
|
||||
@@ -100,6 +100,10 @@ struct _GdkDisplay
|
||||
VkDevice vk_device;
|
||||
VkQueue vk_queue;
|
||||
uint32_t vk_queue_family_index;
|
||||
VkPipelineCache vk_pipeline_cache;
|
||||
gsize vk_pipeline_cache_size;
|
||||
char *vk_pipeline_cache_etag;
|
||||
guint vk_save_pipeline_cache_source;
|
||||
|
||||
guint vulkan_refcount;
|
||||
#endif /* GDK_RENDERING_VULKAN */
|
||||
|
||||
@@ -52,6 +52,7 @@ struct _GdkVulkanContextPrivate {
|
||||
GdkMemoryFormat gdk_format;
|
||||
} formats[4];
|
||||
GdkMemoryDepth current_format;
|
||||
GdkMemoryFormat offscreen_formats[4];
|
||||
|
||||
VkSwapchainKHR swapchain;
|
||||
VkSemaphore draw_semaphore;
|
||||
@@ -693,6 +694,11 @@ gdk_vulkan_context_real_init (GInitable *initable,
|
||||
if (!priv->vulkan_ref)
|
||||
return FALSE;
|
||||
|
||||
priv->offscreen_formats[GDK_MEMORY_U8] = GDK_MEMORY_B8G8R8A8_PREMULTIPLIED;
|
||||
priv->offscreen_formats[GDK_MEMORY_U16] = GDK_MEMORY_R16G16B16A16_PREMULTIPLIED;
|
||||
priv->offscreen_formats[GDK_MEMORY_FLOAT16] = GDK_MEMORY_R16G16B16A16_FLOAT_PREMULTIPLIED;
|
||||
priv->offscreen_formats[GDK_MEMORY_FLOAT32] = GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED;
|
||||
|
||||
if (surface == NULL)
|
||||
{
|
||||
for (i = 0; i < G_N_ELEMENTS (priv->formats); i++)
|
||||
@@ -748,6 +754,7 @@ gdk_vulkan_context_real_init (GInitable *initable,
|
||||
{
|
||||
priv->formats[GDK_MEMORY_U8].vk_format = formats[i];
|
||||
priv->formats[GDK_MEMORY_U8].gdk_format = GDK_MEMORY_B8G8R8A8_PREMULTIPLIED;
|
||||
priv->offscreen_formats[GDK_MEMORY_U8] = GDK_MEMORY_B8G8R8A8_PREMULTIPLIED;
|
||||
};
|
||||
break;
|
||||
|
||||
@@ -827,6 +834,15 @@ out_surface:
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
GdkMemoryFormat
|
||||
gdk_vulkan_context_get_offscreen_format (GdkVulkanContext *context,
|
||||
GdkMemoryDepth depth)
|
||||
{
|
||||
GdkVulkanContextPrivate *priv = gdk_vulkan_context_get_instance_private (context);
|
||||
|
||||
return priv->offscreen_formats[depth];
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_vulkan_context_initable_init (GInitableIface *iface)
|
||||
{
|
||||
@@ -915,6 +931,216 @@ gdk_vulkan_context_get_queue_family_index (GdkVulkanContext *context)
|
||||
return gdk_draw_context_get_display (GDK_DRAW_CONTEXT (context))->vk_queue_family_index;
|
||||
}
|
||||
|
||||
static char *
|
||||
gdk_vulkan_get_pipeline_cache_dirname (void)
|
||||
{
|
||||
return g_build_filename (g_get_user_cache_dir (), "gtk-4.0", "vulkan-pipeline-cache", NULL);
|
||||
}
|
||||
|
||||
static GFile *
|
||||
gdk_vulkan_get_pipeline_cache_file (GdkDisplay *display)
|
||||
{
|
||||
VkPhysicalDeviceProperties props;
|
||||
char *dirname, *basename, *path;
|
||||
GFile *result;
|
||||
|
||||
vkGetPhysicalDeviceProperties (display->vk_physical_device, &props);
|
||||
|
||||
dirname = gdk_vulkan_get_pipeline_cache_dirname ();
|
||||
basename = g_strdup_printf ("%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x"
|
||||
"-%02x%02x%02x%02x%02x%02x.%u",
|
||||
props.pipelineCacheUUID[0], props.pipelineCacheUUID[1],
|
||||
props.pipelineCacheUUID[2], props.pipelineCacheUUID[3],
|
||||
props.pipelineCacheUUID[4], props.pipelineCacheUUID[5],
|
||||
props.pipelineCacheUUID[6], props.pipelineCacheUUID[7],
|
||||
props.pipelineCacheUUID[8], props.pipelineCacheUUID[9],
|
||||
props.pipelineCacheUUID[10], props.pipelineCacheUUID[11],
|
||||
props.pipelineCacheUUID[12], props.pipelineCacheUUID[13],
|
||||
props.pipelineCacheUUID[14], props.pipelineCacheUUID[15],
|
||||
props.driverVersion);
|
||||
|
||||
path = g_build_filename (dirname, basename, NULL);
|
||||
result = g_file_new_for_path (path);
|
||||
|
||||
g_free (path);
|
||||
g_free (basename);
|
||||
g_free (dirname);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static VkPipelineCache
|
||||
gdk_display_load_pipeline_cache (GdkDisplay *display)
|
||||
{
|
||||
GError *error = NULL;
|
||||
VkPipelineCache result;
|
||||
GFile *cache_file;
|
||||
char *etag, *data;
|
||||
gsize size;
|
||||
|
||||
cache_file = gdk_vulkan_get_pipeline_cache_file (display);
|
||||
if (!g_file_load_contents (cache_file, NULL, &data, &size, &etag, &error))
|
||||
{
|
||||
GDK_DEBUG (VULKAN, "failed to load Vulkan pipeline cache file '%s': %s\n",
|
||||
g_file_peek_path (cache_file), error->message);
|
||||
g_object_unref (cache_file);
|
||||
g_clear_error (&error);
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
if (GDK_VK_CHECK (vkCreatePipelineCache, display->vk_device,
|
||||
&(VkPipelineCacheCreateInfo) {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO,
|
||||
.initialDataSize = size,
|
||||
.pInitialData = data,
|
||||
},
|
||||
NULL,
|
||||
&result) != VK_SUCCESS)
|
||||
result = VK_NULL_HANDLE;
|
||||
|
||||
g_free (data);
|
||||
g_free (display->vk_pipeline_cache_etag);
|
||||
display->vk_pipeline_cache_etag = etag;
|
||||
display->vk_pipeline_cache_size = size;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gdk_vulkan_save_pipeline_cache (GdkDisplay *display)
|
||||
{
|
||||
GError *error = NULL;
|
||||
VkDevice device;
|
||||
VkPipelineCache cache;
|
||||
GFile *file;
|
||||
char *path;
|
||||
size_t size;
|
||||
char *data, *etag;
|
||||
|
||||
device = display->vk_device;
|
||||
cache = display->vk_pipeline_cache;
|
||||
|
||||
GDK_VK_CHECK (vkGetPipelineCacheData, device, cache, &size, NULL);
|
||||
if (size == 0)
|
||||
return TRUE;
|
||||
|
||||
if (size == display->vk_pipeline_cache_size)
|
||||
{
|
||||
GDK_DEBUG (VULKAN, "pipeline cache size (%zu bytes) unchanged, skipping save", size);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
data = g_malloc (size);
|
||||
if (GDK_VK_CHECK (vkGetPipelineCacheData, device, cache, &size, data) != VK_SUCCESS)
|
||||
{
|
||||
g_free (data);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
path = gdk_vulkan_get_pipeline_cache_dirname ();
|
||||
if (g_mkdir_with_parents (path, 0755) != 0)
|
||||
{
|
||||
g_warning_once ("Failed to create pipeline cache directory");
|
||||
g_free (path);
|
||||
return FALSE;
|
||||
}
|
||||
g_free (path);
|
||||
|
||||
file = gdk_vulkan_get_pipeline_cache_file (display);
|
||||
|
||||
GDK_DEBUG (VULKAN, "Saving pipeline cache to %s", g_file_peek_path (file));
|
||||
|
||||
if (!g_file_replace_contents (file,
|
||||
data,
|
||||
size,
|
||||
display->vk_pipeline_cache_etag,
|
||||
FALSE,
|
||||
0,
|
||||
&etag,
|
||||
NULL,
|
||||
&error))
|
||||
{
|
||||
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WRONG_ETAG))
|
||||
{
|
||||
VkPipelineCache new_cache;
|
||||
|
||||
GDK_DEBUG (VULKAN, "Pipeline cache file modified, merging into current");
|
||||
new_cache = gdk_display_load_pipeline_cache (display);
|
||||
if (new_cache)
|
||||
{
|
||||
GDK_VK_CHECK (vkMergePipelineCaches, device, cache, 1, &new_cache);
|
||||
vkDestroyPipelineCache (device, new_cache, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_clear_pointer (&display->vk_pipeline_cache_etag, g_free);
|
||||
}
|
||||
g_clear_error (&error);
|
||||
g_object_unref (file);
|
||||
|
||||
/* try again */
|
||||
return gdk_vulkan_save_pipeline_cache (display);
|
||||
}
|
||||
|
||||
g_warning ("Failed to save pipeline cache: %s", error->message);
|
||||
g_clear_error (&error);
|
||||
g_object_unref (file);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
g_object_unref (file);
|
||||
g_free (display->vk_pipeline_cache_etag);
|
||||
display->vk_pipeline_cache_etag = etag;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gdk_vulkan_save_pipeline_cache_cb (gpointer data)
|
||||
{
|
||||
GdkDisplay *display = data;
|
||||
|
||||
gdk_vulkan_save_pipeline_cache (display);
|
||||
|
||||
display->vk_save_pipeline_cache_source = 0;
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
void
|
||||
gdk_vulkan_context_pipeline_cache_updated (GdkVulkanContext *self)
|
||||
{
|
||||
GdkDisplay *display = gdk_draw_context_get_display (GDK_DRAW_CONTEXT (self));
|
||||
|
||||
g_clear_handle_id (&display->vk_save_pipeline_cache_source, g_source_remove);
|
||||
display->vk_save_pipeline_cache_source = g_timeout_add_seconds_full (G_PRIORITY_DEFAULT_IDLE - 10,
|
||||
10, /* random choice that is not now */
|
||||
gdk_vulkan_save_pipeline_cache_cb,
|
||||
display,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_display_create_pipeline_cache (GdkDisplay *display)
|
||||
{
|
||||
display->vk_pipeline_cache = gdk_display_load_pipeline_cache (display);
|
||||
|
||||
GDK_VK_CHECK (vkCreatePipelineCache, display->vk_device,
|
||||
&(VkPipelineCacheCreateInfo) {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO,
|
||||
},
|
||||
NULL,
|
||||
&display->vk_pipeline_cache);
|
||||
}
|
||||
|
||||
VkPipelineCache
|
||||
gdk_vulkan_context_get_pipeline_cache (GdkVulkanContext *self)
|
||||
{
|
||||
g_return_val_if_fail (GDK_IS_VULKAN_CONTEXT (self), NULL);
|
||||
|
||||
return gdk_draw_context_get_display (GDK_DRAW_CONTEXT (self))->vk_pipeline_cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_vulkan_context_get_image_format:
|
||||
* @context: a `GdkVulkanContext`
|
||||
@@ -1168,6 +1394,7 @@ gdk_display_create_vulkan_device (GdkDisplay *display,
|
||||
.descriptorBindingPartiallyBound = VK_TRUE,
|
||||
.descriptorBindingVariableDescriptorCount = VK_TRUE,
|
||||
.descriptorBindingSampledImageUpdateAfterBind = VK_TRUE,
|
||||
.descriptorBindingStorageBufferUpdateAfterBind = VK_TRUE,
|
||||
}
|
||||
},
|
||||
NULL,
|
||||
@@ -1364,6 +1591,8 @@ gdk_display_create_vulkan_instance (GdkDisplay *display,
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gdk_display_create_pipeline_cache (display);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -1392,6 +1621,16 @@ gdk_display_unref_vulkan (GdkDisplay *display)
|
||||
if (display->vulkan_refcount > 0)
|
||||
return;
|
||||
|
||||
if (display->vk_save_pipeline_cache_source)
|
||||
{
|
||||
gdk_vulkan_save_pipeline_cache_cb (display);
|
||||
g_assert (display->vk_save_pipeline_cache_source == 0);
|
||||
}
|
||||
vkDestroyPipelineCache (display->vk_device, display->vk_pipeline_cache, NULL);
|
||||
display->vk_device = VK_NULL_HANDLE;
|
||||
g_clear_pointer (&display->vk_pipeline_cache_etag, g_free);
|
||||
display->vk_pipeline_cache_size = 0;
|
||||
|
||||
vkDestroyDevice (display->vk_device, NULL);
|
||||
display->vk_device = VK_NULL_HANDLE;
|
||||
if (display->vk_debug_callback != VK_NULL_HANDLE)
|
||||
|
||||
@@ -69,9 +69,15 @@ gdk_vulkan_handle_result (VkResult res,
|
||||
|
||||
#define GDK_VK_CHECK(func, ...) gdk_vulkan_handle_result (func (__VA_ARGS__), G_STRINGIFY (func))
|
||||
|
||||
gboolean gdk_display_ref_vulkan (GdkDisplay *display,
|
||||
GError **error);
|
||||
void gdk_display_unref_vulkan (GdkDisplay *display);
|
||||
gboolean gdk_display_ref_vulkan (GdkDisplay *display,
|
||||
GError **error);
|
||||
void gdk_display_unref_vulkan (GdkDisplay *display);
|
||||
|
||||
VkPipelineCache gdk_vulkan_context_get_pipeline_cache (GdkVulkanContext *self);
|
||||
void gdk_vulkan_context_pipeline_cache_updated (GdkVulkanContext *self);
|
||||
|
||||
GdkMemoryFormat gdk_vulkan_context_get_offscreen_format (GdkVulkanContext *context,
|
||||
GdkMemoryDepth depth);
|
||||
|
||||
#else /* !GDK_RENDERING_VULKAN */
|
||||
|
||||
|
||||
@@ -271,6 +271,8 @@ collect_reused_child_nodes (GskRenderer *renderer,
|
||||
case GSK_CROSS_FADE_NODE:
|
||||
case GSK_BLUR_NODE:
|
||||
case GSK_MASK_NODE:
|
||||
case GSK_FILL_NODE:
|
||||
case GSK_STROKE_NODE:
|
||||
|
||||
default:
|
||||
|
||||
@@ -859,6 +861,8 @@ gsk_broadway_renderer_add_node (GskRenderer *renderer,
|
||||
case GSK_CROSS_FADE_NODE:
|
||||
case GSK_BLUR_NODE:
|
||||
case GSK_GL_SHADER_NODE:
|
||||
case GSK_FILL_NODE:
|
||||
case GSK_STROKE_NODE:
|
||||
default:
|
||||
break; /* Fallback */
|
||||
}
|
||||
|
||||
@@ -165,7 +165,7 @@ void half_to_float4 (const guint16 h[4], float f[4]) __attribute__((ifunc ("reso
|
||||
void float_to_half (const float *f, guint16 *h, int n) __attribute__((ifunc ("resolve_float_to_half")));
|
||||
void half_to_float (const guint16 *h, float *f, int n) __attribute__((ifunc ("resolve_half_to_float")));
|
||||
|
||||
static void *
|
||||
static void * __attribute__ ((no_sanitize_address))
|
||||
resolve_float_to_half4 (void)
|
||||
{
|
||||
__builtin_cpu_init ();
|
||||
@@ -175,7 +175,7 @@ resolve_float_to_half4 (void)
|
||||
return float_to_half4_c;
|
||||
}
|
||||
|
||||
static void *
|
||||
static void * __attribute__ ((no_sanitize_address))
|
||||
resolve_half_to_float4 (void)
|
||||
{
|
||||
__builtin_cpu_init ();
|
||||
@@ -185,7 +185,7 @@ resolve_half_to_float4 (void)
|
||||
return half_to_float4_c;
|
||||
}
|
||||
|
||||
static void *
|
||||
static void * __attribute__ ((no_sanitize_address))
|
||||
resolve_float_to_half (void)
|
||||
{
|
||||
__builtin_cpu_init ();
|
||||
@@ -195,7 +195,7 @@ resolve_float_to_half (void)
|
||||
return float_to_half_c;
|
||||
}
|
||||
|
||||
static void *
|
||||
static void * __attribute__ ((no_sanitize_address))
|
||||
resolve_half_to_float (void)
|
||||
{
|
||||
__builtin_cpu_init ();
|
||||
|
||||
@@ -686,17 +686,21 @@ gsk_gl_driver_cache_texture (GskGLDriver *self,
|
||||
const GskTextureKey *key,
|
||||
guint texture_id)
|
||||
{
|
||||
GskTextureKey *k;
|
||||
|
||||
g_assert (GSK_IS_GL_DRIVER (self));
|
||||
g_assert (key != NULL);
|
||||
g_assert (texture_id > 0);
|
||||
g_assert (g_hash_table_contains (self->textures, GUINT_TO_POINTER (texture_id)));
|
||||
|
||||
k = g_memdup (key, sizeof *key);
|
||||
if (!g_hash_table_contains (self->key_to_texture_id, key))
|
||||
{
|
||||
GskTextureKey *k;
|
||||
|
||||
g_hash_table_insert (self->key_to_texture_id, k, GUINT_TO_POINTER (texture_id));
|
||||
g_hash_table_insert (self->texture_id_to_key, GUINT_TO_POINTER (texture_id), k);
|
||||
k = g_memdup (key, sizeof *key);
|
||||
|
||||
g_assert (!g_hash_table_contains (self->texture_id_to_key, GUINT_TO_POINTER (texture_id)));
|
||||
g_hash_table_insert (self->key_to_texture_id, k, GUINT_TO_POINTER (texture_id));
|
||||
g_hash_table_insert (self->texture_id_to_key, GUINT_TO_POINTER (texture_id), k);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -254,6 +254,8 @@ node_supports_2d_transform (const GskRenderNode *node)
|
||||
case GSK_BLEND_NODE:
|
||||
case GSK_BLUR_NODE:
|
||||
case GSK_MASK_NODE:
|
||||
case GSK_FILL_NODE:
|
||||
case GSK_STROKE_NODE:
|
||||
return TRUE;
|
||||
|
||||
case GSK_SHADOW_NODE:
|
||||
@@ -308,6 +310,8 @@ node_supports_transform (const GskRenderNode *node)
|
||||
case GSK_BLEND_NODE:
|
||||
case GSK_BLUR_NODE:
|
||||
case GSK_MASK_NODE:
|
||||
case GSK_FILL_NODE:
|
||||
case GSK_STROKE_NODE:
|
||||
return TRUE;
|
||||
|
||||
case GSK_SHADOW_NODE:
|
||||
@@ -803,6 +807,8 @@ gsk_gl_render_job_untransform_bounds (GskGLRenderJob *job,
|
||||
|
||||
out_rect->origin.x -= job->offset_x;
|
||||
out_rect->origin.y -= job->offset_y;
|
||||
|
||||
gsk_transform_unref (transform);
|
||||
}
|
||||
|
||||
static inline void
|
||||
@@ -4111,6 +4117,14 @@ gsk_gl_render_job_visit_node (GskGLRenderJob *job,
|
||||
gsk_gl_render_job_visit_as_fallback (job, node);
|
||||
break;
|
||||
|
||||
case GSK_FILL_NODE:
|
||||
gsk_gl_render_job_visit_as_fallback (job, node);
|
||||
break;
|
||||
|
||||
case GSK_STROKE_NODE:
|
||||
gsk_gl_render_job_visit_as_fallback (job, node);
|
||||
break;
|
||||
|
||||
case GSK_NOT_A_RENDER_NODE:
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
|
||||
@@ -20,11 +20,15 @@
|
||||
#define __GSK_H_INSIDE__
|
||||
|
||||
#include <gsk/gskenums.h>
|
||||
#include <gsk/gskglshader.h>
|
||||
#include <gsk/gskpath.h>
|
||||
#include <gsk/gskpathbuilder.h>
|
||||
#include <gsk/gskpathmeasure.h>
|
||||
#include <gsk/gskrenderer.h>
|
||||
#include <gsk/gskrendernode.h>
|
||||
#include <gsk/gskroundedrect.h>
|
||||
#include <gsk/gskstroke.h>
|
||||
#include <gsk/gsktransform.h>
|
||||
#include <gsk/gskglshader.h>
|
||||
|
||||
#include <gsk/gskcairorenderer.h>
|
||||
|
||||
|
||||
@@ -42,6 +42,8 @@
|
||||
* @GSK_REPEAT_NODE: A node that repeats the child's contents
|
||||
* @GSK_CLIP_NODE: A node that clips its child to a rectangular area
|
||||
* @GSK_ROUNDED_CLIP_NODE: A node that clips its child to a rounded rectangle
|
||||
* @GSK_FILL_NODE: A node that fills a path
|
||||
* @GSK_STROKE_NODE: A node that strokes a path
|
||||
* @GSK_SHADOW_NODE: A node that draws a shadow below its child
|
||||
* @GSK_BLEND_NODE: A node that blends two children together
|
||||
* @GSK_CROSS_FADE_NODE: A node that cross-fades between two children
|
||||
@@ -74,6 +76,8 @@ typedef enum {
|
||||
GSK_REPEAT_NODE,
|
||||
GSK_CLIP_NODE,
|
||||
GSK_ROUNDED_CLIP_NODE,
|
||||
GSK_FILL_NODE,
|
||||
GSK_STROKE_NODE,
|
||||
GSK_SHADOW_NODE,
|
||||
GSK_BLEND_NODE,
|
||||
GSK_CROSS_FADE_NODE,
|
||||
@@ -170,6 +174,101 @@ typedef enum {
|
||||
GSK_CORNER_BOTTOM_LEFT
|
||||
} GskCorner;
|
||||
|
||||
/**
|
||||
* GskFillRule:
|
||||
* @GSK_FILL_RULE_WINDING: If the path crosses the ray from
|
||||
* left-to-right, counts +1. If the path crosses the ray
|
||||
* from right to left, counts -1. (Left and right are determined
|
||||
* from the perspective of looking along the ray from the starting
|
||||
* point.) If the total count is non-zero, the point will be filled.
|
||||
* @GSK_FILL_RULE_EVEN_ODD: Counts the total number of
|
||||
* intersections, without regard to the orientation of the contour. If
|
||||
* the total number of intersections is odd, the point will be
|
||||
* filled.
|
||||
*
|
||||
* #GskFillRule is used to select how paths are filled, for example in
|
||||
* gsk_fill_node_new(). Whether or not a point is included in the fill is
|
||||
* determined by taking a ray from that point to infinity and looking
|
||||
* at intersections with the path. The ray can be in any direction,
|
||||
* as long as it doesn't pass through the end point of a segment
|
||||
* or have a tricky intersection such as intersecting tangent to the path.
|
||||
* (Note that filling is not actually implemented in this way. This
|
||||
* is just a description of the rule that is applied.)
|
||||
*
|
||||
* New entries may be added in future versions.
|
||||
**/
|
||||
typedef enum {
|
||||
GSK_FILL_RULE_WINDING,
|
||||
GSK_FILL_RULE_EVEN_ODD
|
||||
} GskFillRule;
|
||||
|
||||
/**
|
||||
* GskLineCap:
|
||||
* @GSK_LINE_CAP_BUTT: Start and stop the line exactly at the start
|
||||
* and end point
|
||||
* @GSK_LINE_CAP_ROUND: Use a round ending, the center of the circle
|
||||
* is the start or end point.
|
||||
* @GSK_LINE_CAP_SQUARE: use squared ending, the center of the square
|
||||
* is the start or end point.
|
||||
*
|
||||
* Specifies how to render the start and end points of contours or
|
||||
* dashes when stroking.
|
||||
*
|
||||
* The default line cap style is %GSK_LINE_CAP_BUTT.
|
||||
*/
|
||||
typedef enum {
|
||||
GSK_LINE_CAP_BUTT,
|
||||
GSK_LINE_CAP_ROUND,
|
||||
GSK_LINE_CAP_SQUARE
|
||||
} GskLineCap;
|
||||
|
||||
/**
|
||||
* GskLineJoin:
|
||||
* @GSK_LINE_JOIN_MITER: Use a sharp, angled corner
|
||||
* @GSK_LINE_JOIN_MITER_CLIP: Use a sharp, angled corner, at a distance
|
||||
* @GSK_LINE_JOIN_ROUND: Use a round join, the center of the circle is
|
||||
* the joint point
|
||||
* @GSK_LINE_JOIN_BEVEL: Use a cut-off join, the join is cut off at half
|
||||
* the line width from the joint point
|
||||
*
|
||||
* Specifies how to render the junction of two lines when stroking.
|
||||
*
|
||||
* See gsk_stroke_set_miter_limit() for details on the difference between
|
||||
* @GSK_LINE_JOIN_MITER and @GSK_LINE_JOIN_MITER_CLIP.
|
||||
*
|
||||
* The default line join style is %GSK_LINE_JOIN_MITER.
|
||||
**/
|
||||
typedef enum {
|
||||
GSK_LINE_JOIN_MITER,
|
||||
GSK_LINE_JOIN_MITER_CLIP,
|
||||
GSK_LINE_JOIN_ROUND,
|
||||
GSK_LINE_JOIN_BEVEL
|
||||
} GskLineJoin;
|
||||
|
||||
/**
|
||||
* GskPathOperation:
|
||||
* @GSK_PATH_MOVE: A move-to operation, with 1 point describing the
|
||||
* target point.
|
||||
* @GSK_PATH_LINE: A line-to operation, with 2 points describing the
|
||||
* start and end point of a straight line.
|
||||
* @GSK_PATH_CLOSE: A close operation ending the current contour with
|
||||
* a line back to the starting point. Two points describe the start
|
||||
* and end of the line.
|
||||
* @GSK_PATH_CURVE: A curve-to operation describing a cubic Bézier curve
|
||||
* with 4 points describing the start point, the two control points
|
||||
* and the end point of the curve.
|
||||
*
|
||||
* Path operations can be used to approximate a #GskPath.
|
||||
*
|
||||
* More values may be added in the future.
|
||||
**/
|
||||
typedef enum {
|
||||
GSK_PATH_MOVE,
|
||||
GSK_PATH_CLOSE,
|
||||
GSK_PATH_LINE,
|
||||
GSK_PATH_CURVE,
|
||||
} GskPathOperation;
|
||||
|
||||
/**
|
||||
* GskSerializationError:
|
||||
* @GSK_SERIALIZATION_UNSUPPORTED_FORMAT: The format can not be identified
|
||||
|
||||
2819
gsk/gskpath.c
Normal file
2819
gsk/gskpath.c
Normal file
File diff suppressed because it is too large
Load Diff
108
gsk/gskpath.h
Normal file
108
gsk/gskpath.h
Normal file
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright © 2020 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 __GSK_PATH_H__
|
||||
#define __GSK_PATH_H__
|
||||
|
||||
#if !defined (__GSK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gsk/gsk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
|
||||
#include <gsk/gsktypes.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* GskPathForeachFlags:
|
||||
* @GSK_PATH_FOREACH_ALLOW_CURVES: Allow emission of %GSK_PATH_CURVE
|
||||
* operations.
|
||||
*
|
||||
* Flags that can be passed to gsk_path_foreach() to enable additional
|
||||
* features.
|
||||
*
|
||||
* By default, gsk_path_foreach() will only emit a path with all operations
|
||||
* flattened to straight lines to allow for maximum compatibility. The only
|
||||
* operations emitted will be %GSK_PATH_MOVE, %GSK_PATH_LINE and %GSK_PATH_CLOSE.
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
GSK_PATH_FOREACH_ALLOW_CURVES = (1 << 0)
|
||||
} GskPathForeachFlags;
|
||||
|
||||
/**
|
||||
* GskPathForeachFunc:
|
||||
* @op: The operation to perform
|
||||
* @pts: The points of the operation
|
||||
* @n_pts: The number of points
|
||||
* @user_data: The user data provided with the function
|
||||
*
|
||||
* Prototype of the callback to iterate throught the operations of
|
||||
* a path.
|
||||
*
|
||||
* Returns: %TRUE to continue evaluating the path, %FALSE to
|
||||
* immediately abort and not call the function again.
|
||||
*/
|
||||
typedef gboolean (* GskPathForeachFunc) (GskPathOperation op,
|
||||
const graphene_point_t *pts,
|
||||
gsize n_pts,
|
||||
gpointer user_data);
|
||||
|
||||
#define GSK_TYPE_PATH (gsk_path_get_type ())
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GType gsk_path_get_type (void) G_GNUC_CONST;
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskPath * gsk_path_new_from_cairo (const cairo_path_t *path);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskPath * gsk_path_ref (GskPath *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_path_unref (GskPath *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_path_print (GskPath *self,
|
||||
GString *string);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
char * gsk_path_to_string (GskPath *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskPath * gsk_path_parse (const char *string);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_path_to_cairo (GskPath *self,
|
||||
cairo_t *cr);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gsk_path_is_empty (GskPath *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gsk_path_get_bounds (GskPath *self,
|
||||
graphene_rect_t *bounds);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gsk_path_foreach (GskPath *self,
|
||||
GskPathForeachFlags flags,
|
||||
GskPathForeachFunc func,
|
||||
gpointer user_data);
|
||||
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GskPath, gsk_path_unref)
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GSK_PATH_H__ */
|
||||
753
gsk/gskpathbuilder.c
Normal file
753
gsk/gskpathbuilder.c
Normal file
@@ -0,0 +1,753 @@
|
||||
/*
|
||||
* Copyright © 2020 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 "gskpathbuilder.h"
|
||||
|
||||
#include "gskpathprivate.h"
|
||||
|
||||
/**
|
||||
* SECTION:gskpathbuilder
|
||||
* @Title: Building paths
|
||||
* @Short_description: Building paths of lines and curves
|
||||
* @See_also: #GskPath, #GskPathMeasure
|
||||
*
|
||||
* This section describes how to construct #GskPath structures.
|
||||
*
|
||||
* A path is constructed like this:
|
||||
*
|
||||
* |[<!-- language="C" -->
|
||||
* GskPath *
|
||||
* construct_path (void)
|
||||
* {
|
||||
* GskPathBuilder *builder;
|
||||
*
|
||||
* builder = gsk_path_builder_new ();
|
||||
*
|
||||
* // add contours to the path here
|
||||
*
|
||||
* return gsk_path_builder_free_to_path (builder);
|
||||
* ]|
|
||||
*
|
||||
* Adding contours to the path can be done in two ways.
|
||||
* The easiest option is to use the `gsk_path_builder_add_*` group
|
||||
* of functions that add predefined contours to the current path,
|
||||
* either common shapes like gsk_path_builder_add_circle()
|
||||
* or by adding from other paths like gsk_path_builder_add_path().
|
||||
*
|
||||
* The other option is to define each line and curve manually with
|
||||
* the `gsk_path_builder_*_to` group of functions. You start with
|
||||
* a call to gsk_path_builder_move_to() to set the starting point
|
||||
* and then use multiple calls to any of the drawing functions to
|
||||
* move the pen along the plane. Once you are done, you can call
|
||||
* gsk_path_builder_close() to close the path by connecting it
|
||||
* back with a line to the starting point.
|
||||
* This is similar for how paths are drawn in Cairo.
|
||||
*/
|
||||
|
||||
/**
|
||||
* GskPathBuilder:
|
||||
*
|
||||
* A #GskPathBuilder struct is an opaque struct. It is meant to
|
||||
* not be kept around and only be used to create new #GskPath
|
||||
* objects.
|
||||
*/
|
||||
|
||||
struct _GskPathBuilder
|
||||
{
|
||||
int ref_count;
|
||||
|
||||
GSList *contours; /* (reverse) list of already recorded contours */
|
||||
|
||||
GskPathFlags flags; /* flags for the current path */
|
||||
graphene_point_t current_point; /* the point all drawing ops start from */
|
||||
GArray *ops; /* operations for current contour - size == 0 means no current contour */
|
||||
GArray *points; /* points for the operations */
|
||||
};
|
||||
|
||||
G_DEFINE_BOXED_TYPE (GskPathBuilder,
|
||||
gsk_path_builder,
|
||||
gsk_path_builder_ref,
|
||||
gsk_path_builder_unref)
|
||||
|
||||
|
||||
/**
|
||||
* gsk_path_builder_new:
|
||||
*
|
||||
* Create a new #GskPathBuilder object. The resulting builder
|
||||
* would create an empty #GskPath. Use addition functions to add
|
||||
* types to it.
|
||||
*
|
||||
* Returns: a new #GskPathBuilder
|
||||
**/
|
||||
GskPathBuilder *
|
||||
gsk_path_builder_new (void)
|
||||
{
|
||||
GskPathBuilder *self;
|
||||
|
||||
self = g_slice_new0 (GskPathBuilder);
|
||||
self->ref_count = 1;
|
||||
|
||||
self->ops = g_array_new (FALSE, FALSE, sizeof (GskStandardOperation));
|
||||
self->points = g_array_new (FALSE, FALSE, sizeof (graphene_point_t));
|
||||
|
||||
/* Be explicit here */
|
||||
self->current_point = GRAPHENE_POINT_INIT (0, 0);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_builder_ref:
|
||||
* @self: a #GskPathBuilder
|
||||
*
|
||||
* Acquires a reference on the given builder.
|
||||
*
|
||||
* This function is intended primarily for bindings. #GskPathBuilder objects
|
||||
* should not be kept around.
|
||||
*
|
||||
* Returns: (transfer none): the given #GskPathBuilder with
|
||||
* its reference count increased
|
||||
*/
|
||||
GskPathBuilder *
|
||||
gsk_path_builder_ref (GskPathBuilder *self)
|
||||
{
|
||||
g_return_val_if_fail (self != NULL, NULL);
|
||||
g_return_val_if_fail (self->ref_count > 0, NULL);
|
||||
|
||||
self->ref_count += 1;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_path_builder_ensure_current (GskPathBuilder *self)
|
||||
{
|
||||
if (self->ops->len != 0)
|
||||
return;
|
||||
|
||||
self->flags = GSK_PATH_FLAT;
|
||||
g_array_append_vals (self->ops, &(GskStandardOperation) { GSK_PATH_MOVE, 0 }, 1);
|
||||
g_array_append_val (self->points, self->current_point);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_path_builder_append_current (GskPathBuilder *self,
|
||||
GskPathOperation op,
|
||||
gsize n_points,
|
||||
const graphene_point_t *points)
|
||||
{
|
||||
gsk_path_builder_ensure_current (self);
|
||||
|
||||
g_array_append_vals (self->ops, &(GskStandardOperation) { op, self->points->len - 1 }, 1);
|
||||
g_array_append_vals (self->points, points, n_points);
|
||||
|
||||
self->current_point = points[n_points - 1];
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_path_builder_end_current (GskPathBuilder *self)
|
||||
{
|
||||
GskContour *contour;
|
||||
|
||||
if (self->ops->len == 0)
|
||||
return;
|
||||
|
||||
contour = gsk_standard_contour_new (self->flags,
|
||||
(GskStandardOperation *) self->ops->data,
|
||||
self->ops->len,
|
||||
(graphene_point_t *) self->points->data,
|
||||
self->points->len);
|
||||
|
||||
g_array_set_size (self->ops, 0);
|
||||
g_array_set_size (self->points, 0);
|
||||
|
||||
/* do this at the end to avoid inflooping when add_contour calls back here */
|
||||
gsk_path_builder_add_contour (self, contour);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_path_builder_clear (GskPathBuilder *self)
|
||||
{
|
||||
gsk_path_builder_end_current (self);
|
||||
|
||||
g_slist_free_full (self->contours, g_free);
|
||||
self->contours = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_builder_unref:
|
||||
* @self: a #GskPathBuilder
|
||||
*
|
||||
* Releases a reference on the given builder.
|
||||
*/
|
||||
void
|
||||
gsk_path_builder_unref (GskPathBuilder *self)
|
||||
{
|
||||
g_return_if_fail (self != NULL);
|
||||
g_return_if_fail (self->ref_count > 0);
|
||||
|
||||
self->ref_count -= 1;
|
||||
|
||||
if (self->ref_count > 0)
|
||||
return;
|
||||
|
||||
gsk_path_builder_clear (self);
|
||||
g_array_unref (self->ops);
|
||||
g_array_unref (self->points);
|
||||
g_slice_free (GskPathBuilder, self);
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_builder_free_to_path: (skip)
|
||||
* @self: a #GskPathBuilder
|
||||
*
|
||||
* Creates a new #GskPath from the current state of the
|
||||
* given builder, and frees the @builder instance.
|
||||
*
|
||||
* Returns: (transfer full): the newly created #GskPath
|
||||
* with all the contours added to the builder
|
||||
*/
|
||||
GskPath *
|
||||
gsk_path_builder_free_to_path (GskPathBuilder *self)
|
||||
{
|
||||
GskPath *res;
|
||||
|
||||
g_return_val_if_fail (self != NULL, NULL);
|
||||
|
||||
res = gsk_path_builder_to_path (self);
|
||||
|
||||
gsk_path_builder_unref (self);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_builder_to_path:
|
||||
* @self: a #GskPathBuilder
|
||||
*
|
||||
* Creates a new #GskPath from the given builder.
|
||||
*
|
||||
* The given #GskPathBuilder is reset once this function returns;
|
||||
* you cannot call this function multiple times on the same builder instance.
|
||||
*
|
||||
* This function is intended primarily for bindings. C code should use
|
||||
* gsk_path_builder_free_to_path().
|
||||
*
|
||||
* Returns: (transfer full): the newly created #GskPath
|
||||
* with all the contours added to the builder
|
||||
*/
|
||||
GskPath *
|
||||
gsk_path_builder_to_path (GskPathBuilder *self)
|
||||
{
|
||||
GskPath *path;
|
||||
|
||||
g_return_val_if_fail (self != NULL, NULL);
|
||||
|
||||
gsk_path_builder_end_current (self);
|
||||
|
||||
self->contours = g_slist_reverse (self->contours);
|
||||
|
||||
path = gsk_path_new_from_contours (self->contours);
|
||||
|
||||
gsk_path_builder_clear (self);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
void
|
||||
gsk_path_builder_add_contour (GskPathBuilder *self,
|
||||
GskContour *contour)
|
||||
{
|
||||
gsk_path_builder_end_current (self);
|
||||
|
||||
self->contours = g_slist_prepend (self->contours, contour);
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_builder_get_current_point:
|
||||
* @self: a #GskPathBuilder
|
||||
*
|
||||
* Gets the current point. The current point is used for relative
|
||||
* drawing commands and updated after every operation.
|
||||
*
|
||||
* When the builder is created, the default current point is set to (0, 0).
|
||||
*
|
||||
* Returns: (transfer none): The current point
|
||||
**/
|
||||
const graphene_point_t *
|
||||
gsk_path_builder_get_current_point (GskPathBuilder *self)
|
||||
{
|
||||
g_return_val_if_fail (self != NULL, NULL);
|
||||
|
||||
return &self->current_point;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_builder_add_path:
|
||||
* @self: a #GskPathBuilder
|
||||
* @path: (transfer none): the path to append
|
||||
*
|
||||
* Appends all of @path to the builder.
|
||||
**/
|
||||
void
|
||||
gsk_path_builder_add_path (GskPathBuilder *self,
|
||||
GskPath *path)
|
||||
{
|
||||
gsize i;
|
||||
|
||||
g_return_if_fail (self != NULL);
|
||||
g_return_if_fail (path != NULL);
|
||||
|
||||
for (i = 0; i < gsk_path_get_n_contours (path); i++)
|
||||
{
|
||||
const GskContour *contour = gsk_path_get_contour (path, i);
|
||||
|
||||
gsk_path_builder_add_contour (self, gsk_contour_dup (contour));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_builder_add_rect:
|
||||
* @self: A #GskPathBuilder
|
||||
* @rect: The rectangle to create a path for
|
||||
*
|
||||
* Adds @rect as a new contour to the path built by the builder.
|
||||
*
|
||||
* If the width or height of the rectangle is negative, the start
|
||||
* point will be on the right or bottom, respectively.
|
||||
*
|
||||
* If the the width or height are 0, the path will be a closed
|
||||
* horizontal or vertical line. If both are 0, it'll be a closed dot.
|
||||
**/
|
||||
void
|
||||
gsk_path_builder_add_rect (GskPathBuilder *self,
|
||||
const graphene_rect_t *rect)
|
||||
{
|
||||
GskContour *contour;
|
||||
|
||||
g_return_if_fail (self != NULL);
|
||||
|
||||
contour = gsk_rect_contour_new (rect);
|
||||
gsk_path_builder_add_contour (self, contour);
|
||||
|
||||
gsk_contour_get_start_end (contour, NULL, &self->current_point);
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_builder_add_circle:
|
||||
* @self: a #GskPathBuilder
|
||||
* @center: the center of the circle
|
||||
* @radius: the radius of the circle
|
||||
*
|
||||
* Adds a circle with the @center and @radius.
|
||||
**/
|
||||
void
|
||||
gsk_path_builder_add_circle (GskPathBuilder *self,
|
||||
const graphene_point_t *center,
|
||||
float radius)
|
||||
{
|
||||
GskContour *contour;
|
||||
|
||||
g_return_if_fail (self != NULL);
|
||||
g_return_if_fail (center != NULL);
|
||||
g_return_if_fail (radius > 0);
|
||||
|
||||
contour = gsk_circle_contour_new (center, radius, 0, 360);
|
||||
gsk_path_builder_add_contour (self, contour);
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_builder_move_to:
|
||||
* @self: a #GskPathBuilder
|
||||
* @x: x coordinate
|
||||
* @y: y coordinate
|
||||
*
|
||||
* Starts a new contour by placing the pen at @x, @y.
|
||||
*
|
||||
* If gsk_path_builder_move_to() is called twice in succession, the first
|
||||
* call will result in a contour made up of a single point. The second call
|
||||
* will start a new contour.
|
||||
**/
|
||||
void
|
||||
gsk_path_builder_move_to (GskPathBuilder *self,
|
||||
float x,
|
||||
float y)
|
||||
{
|
||||
g_return_if_fail (self != NULL);
|
||||
|
||||
gsk_path_builder_end_current (self);
|
||||
|
||||
self->current_point = GRAPHENE_POINT_INIT(x, y);
|
||||
|
||||
gsk_path_builder_ensure_current (self);
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_builder_rel_move_to:
|
||||
* @self: a #GskPathBuilder
|
||||
* @x: x offset
|
||||
* @y: y offset
|
||||
*
|
||||
* Starts a new contour by placing the pen at @x, @y relative to the current
|
||||
* point.
|
||||
*
|
||||
* This is the relative version of gsk_path_builder_move_to().
|
||||
**/
|
||||
void
|
||||
gsk_path_builder_rel_move_to (GskPathBuilder *self,
|
||||
float x,
|
||||
float y)
|
||||
{
|
||||
g_return_if_fail (self != NULL);
|
||||
|
||||
gsk_path_builder_move_to (self,
|
||||
self->current_point.x + x,
|
||||
self->current_point.y + y);
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_builder_line_to:
|
||||
* @self: a #GskPathBuilder
|
||||
* @x: x coordinate
|
||||
* @y: y coordinate
|
||||
*
|
||||
* Draws a line from the current point to @x, @y and makes it the new current
|
||||
* point.
|
||||
**/
|
||||
void
|
||||
gsk_path_builder_line_to (GskPathBuilder *self,
|
||||
float x,
|
||||
float y)
|
||||
{
|
||||
g_return_if_fail (self != NULL);
|
||||
|
||||
/* skip the line if it goes to the same point */
|
||||
if (graphene_point_equal (&self->current_point,
|
||||
&GRAPHENE_POINT_INIT (x, y)))
|
||||
return;
|
||||
|
||||
gsk_path_builder_append_current (self,
|
||||
GSK_PATH_LINE,
|
||||
1, (graphene_point_t[1]) {
|
||||
GRAPHENE_POINT_INIT (x, y)
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_builder_rel_line_to:
|
||||
* @self: a #GskPathBuilder
|
||||
* @x: x offset
|
||||
* @y: y offset
|
||||
*
|
||||
* Draws a line from the current point to a point offset to it by @x, @y
|
||||
* and makes it the new current point.
|
||||
*
|
||||
* This is the relative version of gsk_path_builder_line_to().
|
||||
**/
|
||||
void
|
||||
gsk_path_builder_rel_line_to (GskPathBuilder *self,
|
||||
float x,
|
||||
float y)
|
||||
{
|
||||
g_return_if_fail (self != NULL);
|
||||
|
||||
gsk_path_builder_line_to (self,
|
||||
self->current_point.x + x,
|
||||
self->current_point.y + y);
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_builder_curve_to:
|
||||
* @self: a #GskPathBuilder
|
||||
* @x1: x coordinate of first control point
|
||||
* @y1: y coordinate of first control point
|
||||
* @x2: x coordinate of second control point
|
||||
* @y2: y coordinate of second control point
|
||||
* @x3: x coordinate of the end of the curve
|
||||
* @y3: y coordinate of the end of the curve
|
||||
*
|
||||
* Adds a [cubic Bézier curve](https://en.wikipedia.org/wiki/B%C3%A9zier_curve)
|
||||
* from the current point to @x3, @y3 with @x1, @y1 and @x2, @y2 as the control
|
||||
* points.
|
||||
*
|
||||
* After this, @x3, @y3 will be the new current point.
|
||||
**/
|
||||
void
|
||||
gsk_path_builder_curve_to (GskPathBuilder *self,
|
||||
float x1,
|
||||
float y1,
|
||||
float x2,
|
||||
float y2,
|
||||
float x3,
|
||||
float y3)
|
||||
{
|
||||
g_return_if_fail (self != NULL);
|
||||
|
||||
self->flags &= ~GSK_PATH_FLAT;
|
||||
gsk_path_builder_append_current (self,
|
||||
GSK_PATH_CURVE,
|
||||
3, (graphene_point_t[3]) {
|
||||
GRAPHENE_POINT_INIT (x1, y1),
|
||||
GRAPHENE_POINT_INIT (x2, y2),
|
||||
GRAPHENE_POINT_INIT (x3, y3)
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_builder_rel_curve_to:
|
||||
* @self: a #GskPathBuilder
|
||||
* @x1: x offset of first control point
|
||||
* @y1: y offset of first control point
|
||||
* @x2: x offset of second control point
|
||||
* @y2: y offset of second control point
|
||||
* @x3: x offset of the end of the curve
|
||||
* @y3: y offset of the end of the curve
|
||||
*
|
||||
* Adds a [cubic Bézier curve](https://en.wikipedia.org/wiki/B%C3%A9zier_curve)
|
||||
* from the current point to @x3, @y3 with @x1, @y1 and @x2, @y2 as the control
|
||||
* points. All coordinates are given relative to the current point.
|
||||
*
|
||||
* This is the relative version of gsk_path_builder_curve_to().
|
||||
**/
|
||||
void
|
||||
gsk_path_builder_rel_curve_to (GskPathBuilder *self,
|
||||
float x1,
|
||||
float y1,
|
||||
float x2,
|
||||
float y2,
|
||||
float x3,
|
||||
float y3)
|
||||
{
|
||||
g_return_if_fail (self != NULL);
|
||||
|
||||
gsk_path_builder_curve_to (self,
|
||||
self->current_point.x + x1,
|
||||
self->current_point.y + y1,
|
||||
self->current_point.x + x2,
|
||||
self->current_point.y + y2,
|
||||
self->current_point.x + x3,
|
||||
self->current_point.y + y3);
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_builder_close:
|
||||
* @self: a #GskPathBuilder
|
||||
*
|
||||
* Ends the current contour with a line back to the start point.
|
||||
*
|
||||
* Note that this is different from calling gsk_path_builder_line_to()
|
||||
* with the start point in that the contour will be closed. A closed
|
||||
* contour behaves different from an open one when stroking its start
|
||||
* and end point are considered connected, so they will be joined
|
||||
* via the line join, and not ended with line caps.
|
||||
**/
|
||||
void
|
||||
gsk_path_builder_close (GskPathBuilder *self)
|
||||
{
|
||||
g_return_if_fail (self != NULL);
|
||||
|
||||
if (self->ops->len == 0)
|
||||
return;
|
||||
|
||||
self->flags |= GSK_PATH_CLOSED;
|
||||
gsk_path_builder_append_current (self,
|
||||
GSK_PATH_CLOSE,
|
||||
1, (graphene_point_t[1]) {
|
||||
g_array_index (self->points, graphene_point_t, 0)
|
||||
});
|
||||
|
||||
gsk_path_builder_end_current (self);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
arc_segment (GskPathBuilder *builder,
|
||||
double cx,
|
||||
double cy,
|
||||
double rx,
|
||||
double ry,
|
||||
double sin_phi,
|
||||
double cos_phi,
|
||||
double sin_th0,
|
||||
double cos_th0,
|
||||
double sin_th1,
|
||||
double cos_th1,
|
||||
double t)
|
||||
{
|
||||
double x1, y1, x2, y2, x3, y3;
|
||||
|
||||
x1 = rx * (cos_th0 - t * sin_th0);
|
||||
y1 = ry * (sin_th0 + t * cos_th0);
|
||||
x3 = rx * cos_th1;
|
||||
y3 = ry * sin_th1;
|
||||
x2 = x3 + rx * (t * sin_th1);
|
||||
y2 = y3 + ry * (-t * cos_th1);
|
||||
|
||||
gsk_path_builder_curve_to (builder,
|
||||
cx + cos_phi * x1 - sin_phi * y1,
|
||||
cy + sin_phi * x1 + cos_phi * y1,
|
||||
cx + cos_phi * x2 - sin_phi * y2,
|
||||
cy + sin_phi * x2 + cos_phi * y2,
|
||||
cx + cos_phi * x3 - sin_phi * y3,
|
||||
cy + sin_phi * x3 + cos_phi * y3);
|
||||
}
|
||||
|
||||
#ifndef HAVE_SINCOS
|
||||
static inline void
|
||||
sincos (double angle,
|
||||
double *y,
|
||||
double *x)
|
||||
{
|
||||
*x = cos (angle);
|
||||
*y = sin (angle);
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
gsk_path_builder_svg_arc_to (GskPathBuilder *builder,
|
||||
float rx,
|
||||
float ry,
|
||||
float x_axis_rotation,
|
||||
gboolean large_arc,
|
||||
gboolean positive_sweep,
|
||||
float x,
|
||||
float y)
|
||||
{
|
||||
graphene_point_t *current;
|
||||
double x1, y1, x2, y2;
|
||||
double phi, sin_phi, cos_phi;
|
||||
double mid_x, mid_y;
|
||||
double lambda;
|
||||
double d;
|
||||
double k;
|
||||
double x1_, y1_;
|
||||
double cx_, cy_;
|
||||
double cx, cy;
|
||||
double ux, uy, u_len;
|
||||
double cos_theta1, theta1;
|
||||
double vx, vy, v_len;
|
||||
double dp_uv;
|
||||
double cos_delta_theta, delta_theta;
|
||||
int i, n_segs;
|
||||
double d_theta, theta;
|
||||
double sin_th0, cos_th0;
|
||||
double sin_th1, cos_th1;
|
||||
double th_half;
|
||||
double t;
|
||||
|
||||
if (builder->points->len > 0)
|
||||
{
|
||||
current = &g_array_index (builder->points, graphene_point_t, builder->points->len - 1);
|
||||
x1 = current->x;
|
||||
y1 = current->y;
|
||||
}
|
||||
else
|
||||
{
|
||||
x1 = 0;
|
||||
y1 = 0;
|
||||
}
|
||||
x2 = x;
|
||||
y2 = y;
|
||||
|
||||
phi = x_axis_rotation * M_PI / 180.0;
|
||||
sincos (phi, &sin_phi, &cos_phi);
|
||||
|
||||
rx = fabs (rx);
|
||||
ry = fabs (ry);
|
||||
|
||||
mid_x = (x1 - x2) / 2;
|
||||
mid_y = (y1 - y2) / 2;
|
||||
|
||||
x1_ = cos_phi * mid_x + sin_phi * mid_y;
|
||||
y1_ = - sin_phi * mid_x + cos_phi * mid_y;
|
||||
|
||||
lambda = (x1_ / rx) * (x1_ / rx) + (y1_ / ry) * (y1_ / ry);
|
||||
if (lambda > 1)
|
||||
{
|
||||
lambda = sqrt (lambda);
|
||||
rx *= lambda;
|
||||
ry *= lambda;
|
||||
}
|
||||
|
||||
d = (rx * y1_) * (rx * y1_) + (ry * x1_) * (ry * x1_);
|
||||
if (d == 0)
|
||||
return;
|
||||
|
||||
k = sqrt (fabs ((rx * ry) * (rx * ry) / d - 1.0));
|
||||
if (positive_sweep == large_arc)
|
||||
k = -k;
|
||||
|
||||
cx_ = k * rx * y1_ / ry;
|
||||
cy_ = -k * ry * x1_ / rx;
|
||||
|
||||
cx = cos_phi * cx_ - sin_phi * cy_ + (x1 + x2) / 2;
|
||||
cy = sin_phi * cx_ + cos_phi * cy_ + (y1 + y2) / 2;
|
||||
|
||||
ux = (x1_ - cx_) / rx;
|
||||
uy = (y1_ - cy_) / ry;
|
||||
u_len = sqrt (ux * ux + uy * uy);
|
||||
if (u_len == 0)
|
||||
return;
|
||||
|
||||
cos_theta1 = CLAMP (ux / u_len, -1, 1);
|
||||
theta1 = acos (cos_theta1);
|
||||
if (uy < 0)
|
||||
theta1 = - theta1;
|
||||
|
||||
vx = (- x1_ - cx_) / rx;
|
||||
vy = (- y1_ - cy_) / ry;
|
||||
v_len = sqrt (vx * vx + vy * vy);
|
||||
if (v_len == 0)
|
||||
return;
|
||||
|
||||
dp_uv = ux * vx + uy * vy;
|
||||
cos_delta_theta = CLAMP (dp_uv / (u_len * v_len), -1, 1);
|
||||
delta_theta = acos (cos_delta_theta);
|
||||
if (ux * vy - uy * vx < 0)
|
||||
delta_theta = - delta_theta;
|
||||
if (positive_sweep && delta_theta < 0)
|
||||
delta_theta += 2 * M_PI;
|
||||
else if (!positive_sweep && delta_theta > 0)
|
||||
delta_theta -= 2 * M_PI;
|
||||
|
||||
n_segs = ceil (fabs (delta_theta / (M_PI_2 + 0.001)));
|
||||
d_theta = delta_theta / n_segs;
|
||||
theta = theta1;
|
||||
sincos (theta1, &sin_th1, &cos_th1);
|
||||
|
||||
th_half = d_theta / 2;
|
||||
t = (8.0 / 3.0) * sin (th_half / 2) * sin (th_half / 2) / sin (th_half);
|
||||
|
||||
for (i = 0; i < n_segs; i++)
|
||||
{
|
||||
theta = theta1;
|
||||
theta1 = theta + d_theta;
|
||||
sin_th0 = sin_th1;
|
||||
cos_th0 = cos_th1;
|
||||
sincos (theta1, &sin_th1, &cos_th1);
|
||||
arc_segment (builder,
|
||||
cx, cy, rx, ry,
|
||||
sin_phi, cos_phi,
|
||||
sin_th0, cos_th0,
|
||||
sin_th1, cos_th1,
|
||||
t);
|
||||
}
|
||||
}
|
||||
101
gsk/gskpathbuilder.h
Normal file
101
gsk/gskpathbuilder.h
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright © 2020 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 __GSK_PATH_BUILDER_H__
|
||||
#define __GSK_PATH_BUILDER_H__
|
||||
|
||||
#if !defined (__GSK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gsk/gsk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
|
||||
#include <gsk/gsktypes.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GSK_TYPE_PATH_BUILDER (gsk_path_builder_get_type ())
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GType gsk_path_builder_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskPathBuilder * gsk_path_builder_new (void);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskPathBuilder * gsk_path_builder_ref (GskPathBuilder *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_path_builder_unref (GskPathBuilder *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskPath * gsk_path_builder_free_to_path (GskPathBuilder *self) G_GNUC_WARN_UNUSED_RESULT;
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskPath * gsk_path_builder_to_path (GskPathBuilder *self) G_GNUC_WARN_UNUSED_RESULT;
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
const graphene_point_t *gsk_path_builder_get_current_point (GskPathBuilder *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_path_builder_add_path (GskPathBuilder *self,
|
||||
GskPath *path);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_path_builder_add_rect (GskPathBuilder *self,
|
||||
const graphene_rect_t *rect);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_path_builder_add_circle (GskPathBuilder *self,
|
||||
const graphene_point_t *center,
|
||||
float radius);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_path_builder_move_to (GskPathBuilder *self,
|
||||
float x,
|
||||
float y);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_path_builder_rel_move_to (GskPathBuilder *self,
|
||||
float x,
|
||||
float y);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_path_builder_line_to (GskPathBuilder *self,
|
||||
float x,
|
||||
float y);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_path_builder_rel_line_to (GskPathBuilder *self,
|
||||
float x,
|
||||
float y);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_path_builder_curve_to (GskPathBuilder *self,
|
||||
float x1,
|
||||
float y1,
|
||||
float x2,
|
||||
float y2,
|
||||
float x3,
|
||||
float y3);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_path_builder_rel_curve_to (GskPathBuilder *self,
|
||||
float x1,
|
||||
float y1,
|
||||
float x2,
|
||||
float y2,
|
||||
float x3,
|
||||
float y3);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_path_builder_close (GskPathBuilder *self);
|
||||
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GskPathBuilder, gsk_path_builder_unref)
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GSK_PATH_BUILDER_H__ */
|
||||
477
gsk/gskpathmeasure.c
Normal file
477
gsk/gskpathmeasure.c
Normal file
@@ -0,0 +1,477 @@
|
||||
/*
|
||||
* Copyright © 2020 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 "gskpathmeasure.h"
|
||||
|
||||
#include "gskpathprivate.h"
|
||||
|
||||
/**
|
||||
* SECTION:gskpathmeasure
|
||||
* @Title: PathMeasure
|
||||
* @Short_description: Measuring operations on paths
|
||||
* @See_also: #GskPath
|
||||
*
|
||||
* #GskPathMeasure is an object that allows measuring operations on #GskPaths.
|
||||
* These operations are useful when implementing animations.
|
||||
*/
|
||||
|
||||
typedef struct _GskContourMeasure GskContourMeasure;
|
||||
|
||||
struct _GskContourMeasure
|
||||
{
|
||||
float length;
|
||||
gpointer contour_data;
|
||||
};
|
||||
|
||||
struct _GskPathMeasure
|
||||
{
|
||||
/*< private >*/
|
||||
guint ref_count;
|
||||
|
||||
GskPath *path;
|
||||
float tolerance;
|
||||
|
||||
float length;
|
||||
gsize n_contours;
|
||||
GskContourMeasure measures[];
|
||||
};
|
||||
|
||||
/**
|
||||
* GskPathMeasure:
|
||||
*
|
||||
* A #GskPathMeasure struct is a reference counted struct
|
||||
* and should be treated as opaque.
|
||||
*/
|
||||
|
||||
G_DEFINE_BOXED_TYPE (GskPathMeasure, gsk_path_measure,
|
||||
gsk_path_measure_ref,
|
||||
gsk_path_measure_unref)
|
||||
|
||||
/**
|
||||
* gsk_path_measure_new:
|
||||
* @path: the path to measure
|
||||
*
|
||||
* Creates a measure object for the given @path.
|
||||
*
|
||||
* Returns: a new #GskPathMeasure representing @path
|
||||
**/
|
||||
GskPathMeasure *
|
||||
gsk_path_measure_new (GskPath *path)
|
||||
{
|
||||
return gsk_path_measure_new_with_tolerance (path, GSK_PATH_TOLERANCE_DEFAULT);
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_measure_new_with_tolerance:
|
||||
* @path: the path to measure
|
||||
* @tolerance: the tolerance for measuring operations
|
||||
*
|
||||
* Creates a measure object for the given @path and @tolerance.
|
||||
*
|
||||
* Returns: a new #GskPathMeasure representing @path
|
||||
**/
|
||||
GskPathMeasure *
|
||||
gsk_path_measure_new_with_tolerance (GskPath *path,
|
||||
float tolerance)
|
||||
{
|
||||
GskPathMeasure *self;
|
||||
gsize i, n_contours;
|
||||
|
||||
g_return_val_if_fail (path != NULL, NULL);
|
||||
g_return_val_if_fail (tolerance > 0, NULL);
|
||||
|
||||
n_contours = gsk_path_get_n_contours (path);
|
||||
|
||||
self = g_malloc0 (sizeof (GskPathMeasure) + n_contours * sizeof (GskContourMeasure));
|
||||
|
||||
self->ref_count = 1;
|
||||
self->path = gsk_path_ref (path);
|
||||
self->tolerance = tolerance;
|
||||
self->n_contours = n_contours;
|
||||
|
||||
for (i = 0; i < n_contours; i++)
|
||||
{
|
||||
self->measures[i].contour_data = gsk_contour_init_measure (path, i,
|
||||
self->tolerance,
|
||||
&self->measures[i].length);
|
||||
self->length += self->measures[i].length;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_measure_ref:
|
||||
* @self: a #GskPathMeasure
|
||||
*
|
||||
* Increases the reference count of a #GskPathMeasure by one.
|
||||
*
|
||||
* Returns: the passed in #GskPathMeasure.
|
||||
**/
|
||||
GskPathMeasure *
|
||||
gsk_path_measure_ref (GskPathMeasure *self)
|
||||
{
|
||||
g_return_val_if_fail (self != NULL, NULL);
|
||||
|
||||
self->ref_count++;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_measure_unref:
|
||||
* @self: a #GskPathMeasure
|
||||
*
|
||||
* Decreases the reference count of a #GskPathMeasure by one.
|
||||
* If the resulting reference count is zero, frees the path_measure.
|
||||
**/
|
||||
void
|
||||
gsk_path_measure_unref (GskPathMeasure *self)
|
||||
{
|
||||
gsize i;
|
||||
|
||||
g_return_if_fail (self != NULL);
|
||||
g_return_if_fail (self->ref_count > 0);
|
||||
|
||||
self->ref_count--;
|
||||
if (self->ref_count > 0)
|
||||
return;
|
||||
|
||||
for (i = 0; i < self->n_contours; i++)
|
||||
{
|
||||
gsk_contour_free_measure (self->path, i, self->measures[i].contour_data);
|
||||
}
|
||||
|
||||
gsk_path_unref (self->path);
|
||||
g_free (self);
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_measure_get_length:
|
||||
* @self: a #GskPathMeasure
|
||||
*
|
||||
* Gets the length of the path being measured.
|
||||
*
|
||||
* The length is cached, so this function does not do any work.
|
||||
*
|
||||
* Returns: The length of the path measured by @self
|
||||
**/
|
||||
float
|
||||
gsk_path_measure_get_length (GskPathMeasure *self)
|
||||
{
|
||||
g_return_val_if_fail (self != NULL, 0);
|
||||
|
||||
return self->length;
|
||||
}
|
||||
|
||||
static float
|
||||
gsk_path_measure_clamp_distance (GskPathMeasure *self,
|
||||
float distance)
|
||||
{
|
||||
if (isnan (distance))
|
||||
return 0;
|
||||
|
||||
return CLAMP (distance, 0, self->length);
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_measure_get_point:
|
||||
* @self: a #GskPathMeasure
|
||||
* @distance: distance into the path
|
||||
* @pos: (out caller-allocates) (optional): The coordinates
|
||||
* of the position at @distance
|
||||
* @tangent: (out caller-allocates) (optional): The tangent
|
||||
* to the position at @distance
|
||||
*
|
||||
* Calculates the coordinates and tangent of the point @distance
|
||||
* units into the path. The value will be clamped to the length
|
||||
* of the path.
|
||||
*
|
||||
* If the point is a discontinuous edge in the path, the returned
|
||||
* point and tangent will describe the line starting at that point
|
||||
* going forward.
|
||||
*
|
||||
* If @self describes an empty path, the returned point will be
|
||||
* set to `(0, 0)` and the tangent will be the x axis or `(1, 0)`.
|
||||
**/
|
||||
void
|
||||
gsk_path_measure_get_point (GskPathMeasure *self,
|
||||
float distance,
|
||||
graphene_point_t *pos,
|
||||
graphene_vec2_t *tangent)
|
||||
{
|
||||
gsize i;
|
||||
|
||||
g_return_if_fail (self != NULL);
|
||||
|
||||
if (pos == NULL && tangent == NULL)
|
||||
return;
|
||||
|
||||
distance = gsk_path_measure_clamp_distance (self, distance);
|
||||
|
||||
for (i = 0; i < self->n_contours; i++)
|
||||
{
|
||||
if (distance < self->measures[i].length)
|
||||
break;
|
||||
|
||||
distance -= self->measures[i].length;
|
||||
}
|
||||
|
||||
/* weird corner cases */
|
||||
if (i == self->n_contours)
|
||||
{
|
||||
/* the empty path goes here */
|
||||
if (self->n_contours == 0)
|
||||
{
|
||||
if (pos)
|
||||
graphene_point_init (pos, 0.f, 0.f);
|
||||
if (tangent)
|
||||
graphene_vec2_init (tangent, 1.f, 0.f);
|
||||
return;
|
||||
}
|
||||
/* rounding errors can make this happen */
|
||||
i = self->n_contours - 1;
|
||||
distance = self->measures[i].length;
|
||||
}
|
||||
|
||||
gsk_contour_get_point (self->path,
|
||||
i,
|
||||
self->measures[i].contour_data,
|
||||
distance,
|
||||
pos,
|
||||
tangent);
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_measure_get_closest_point:
|
||||
* @self: a #GskPathMeasure
|
||||
* @point: the point to fond the closest point to
|
||||
* @out_pos: (out caller-allocates) (optional): return location
|
||||
* for the closest point
|
||||
*
|
||||
* Gets the point on the path that is closest to @point.
|
||||
*
|
||||
* If the path being measured is empty, return 0 and set
|
||||
* @out_pos to (0, 0).
|
||||
*
|
||||
* This is a simpler and slower version of
|
||||
* gsk_path_measure_get_closest_point_full(). Use that one if you
|
||||
* need more control.
|
||||
*
|
||||
* Returns: The offset into the path of the closest point
|
||||
**/
|
||||
float
|
||||
gsk_path_measure_get_closest_point (GskPathMeasure *self,
|
||||
const graphene_point_t *point,
|
||||
graphene_point_t *out_pos)
|
||||
{
|
||||
float result;
|
||||
|
||||
g_return_val_if_fail (self != NULL, 0.0f);
|
||||
|
||||
if (gsk_path_measure_get_closest_point_full (self,
|
||||
point,
|
||||
INFINITY,
|
||||
&result,
|
||||
out_pos,
|
||||
NULL,
|
||||
NULL))
|
||||
return result;
|
||||
|
||||
if (out_pos)
|
||||
*out_pos = GRAPHENE_POINT_INIT (0, 0);
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_measure_get_closest_point_full:
|
||||
* @self: a #GskPathMeasure
|
||||
* @point: the point to fond the closest point to
|
||||
* @threshold: The maximum allowed distance between the path and @point.
|
||||
* Use INFINITY to look for any point.
|
||||
* @out_distance: (out caller-allocates) (optional): The
|
||||
* distance between the found closest point on the path and the given
|
||||
* @point.
|
||||
* @out_pos: (out caller-allocates) (optional): return location
|
||||
* for the closest point
|
||||
* @out_offset: (out caller-allocates) (optional): The offset into
|
||||
* the path of the found point
|
||||
* @out_tangent: (out caller-allocates) (optional): return location for
|
||||
* the tangent at the closest point
|
||||
*
|
||||
* Gets the point on the path that is closest to @point. If no point on
|
||||
* path is closer to @point than @threshold, return %FALSE.
|
||||
*
|
||||
* Returns: %TRUE if a point was found, %FALSE otherwise.
|
||||
**/
|
||||
gboolean
|
||||
gsk_path_measure_get_closest_point_full (GskPathMeasure *self,
|
||||
const graphene_point_t *point,
|
||||
float threshold,
|
||||
float *out_distance,
|
||||
graphene_point_t *out_pos,
|
||||
float *out_offset,
|
||||
graphene_vec2_t *out_tangent)
|
||||
{
|
||||
gboolean result;
|
||||
gsize i;
|
||||
float distance, length;
|
||||
|
||||
g_return_val_if_fail (self != NULL, FALSE);
|
||||
g_return_val_if_fail (point != NULL, FALSE);
|
||||
|
||||
result = FALSE;
|
||||
length = 0;
|
||||
|
||||
for (i = 0; i < self->n_contours; i++)
|
||||
{
|
||||
if (gsk_contour_get_closest_point (self->path,
|
||||
i,
|
||||
self->measures[i].contour_data,
|
||||
self->tolerance,
|
||||
point,
|
||||
threshold,
|
||||
&distance,
|
||||
out_pos,
|
||||
out_offset,
|
||||
out_tangent))
|
||||
{
|
||||
result = TRUE;
|
||||
if (out_offset)
|
||||
*out_offset += length;
|
||||
|
||||
if (distance < self->tolerance)
|
||||
break;
|
||||
threshold = distance - self->tolerance;
|
||||
}
|
||||
|
||||
length += self->measures[i].length;
|
||||
}
|
||||
|
||||
if (result && out_distance)
|
||||
*out_distance = distance;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_measure_in_fill:
|
||||
* @self: a #GskPathMeasure
|
||||
* @point: the point to test
|
||||
* @fill_rule: the fill rule to follow
|
||||
*
|
||||
* Returns whether the given point is inside the area that would be
|
||||
* affected if the path of @self was filled according to @fill_rule.
|
||||
*
|
||||
* Returns: %TRUE if @point is inside
|
||||
*/
|
||||
gboolean
|
||||
gsk_path_measure_in_fill (GskPathMeasure *self,
|
||||
const graphene_point_t *point,
|
||||
GskFillRule fill_rule)
|
||||
{
|
||||
int winding = 0;
|
||||
gboolean on_edge = FALSE;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < self->n_contours; i++)
|
||||
{
|
||||
winding += gsk_contour_get_winding (gsk_path_get_contour (self->path, i),
|
||||
self->measures[i].contour_data,
|
||||
point,
|
||||
&on_edge);
|
||||
if (on_edge)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
switch (fill_rule)
|
||||
{
|
||||
case GSK_FILL_RULE_EVEN_ODD:
|
||||
return winding & 1;
|
||||
case GSK_FILL_RULE_WINDING:
|
||||
return winding != 0;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* gsk_path_measure_add_segment:
|
||||
* @self: a #GskPathMeasure
|
||||
* @builder: the builder to add the segment to
|
||||
* @start: start distance into the path
|
||||
* @end: end distance into the path
|
||||
*
|
||||
* Adds to @builder the segment of @path inbetween @start and @end.
|
||||
*
|
||||
* The distances are given relative to the length of @self's path,
|
||||
* from 0 for the beginning of the path to
|
||||
* gsk_path_measure_get_length() for the end of the path. The values
|
||||
* will be clamped to that range.
|
||||
*
|
||||
* If @start >= @end after clamping, no path will be added.
|
||||
**/
|
||||
void
|
||||
gsk_path_measure_add_segment (GskPathMeasure *self,
|
||||
GskPathBuilder *builder,
|
||||
float start,
|
||||
float end)
|
||||
{
|
||||
gsize i;
|
||||
|
||||
g_return_if_fail (self != NULL);
|
||||
g_return_if_fail (builder != NULL);
|
||||
|
||||
start = gsk_path_measure_clamp_distance (self, start);
|
||||
end = gsk_path_measure_clamp_distance (self, end);
|
||||
if (start >= end)
|
||||
return;
|
||||
|
||||
for (i = 0; i < self->n_contours; i++)
|
||||
{
|
||||
if (self->measures[i].length < start)
|
||||
{
|
||||
start -= self->measures[i].length;
|
||||
end -= self->measures[i].length;
|
||||
}
|
||||
else if (start > 0 || end < self->measures[i].length)
|
||||
{
|
||||
float len = MIN (end, self->measures[i].length);
|
||||
gsk_contour_add_segment (gsk_path_get_contour (self->path, i),
|
||||
builder,
|
||||
self->measures[i].contour_data,
|
||||
start,
|
||||
len);
|
||||
end -= len;
|
||||
start = 0;
|
||||
if (end <= 0)
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
end -= self->measures[i].length;
|
||||
gsk_path_builder_add_contour (builder, gsk_contour_dup (gsk_path_get_contour (self->path, i)));
|
||||
}
|
||||
}
|
||||
}
|
||||
81
gsk/gskpathmeasure.h
Normal file
81
gsk/gskpathmeasure.h
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright © 2020 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 __GSK_PATH_MEASURE_H__
|
||||
#define __GSK_PATH_MEASURE_H__
|
||||
|
||||
#if !defined (__GSK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gsk/gsk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
|
||||
#include <gsk/gskpath.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GSK_TYPE_PATH_MEASURE (gsk_path_measure_get_type ())
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GType gsk_path_measure_get_type (void) G_GNUC_CONST;
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskPathMeasure * gsk_path_measure_new (GskPath *path);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskPathMeasure * gsk_path_measure_new_with_tolerance (GskPath *path,
|
||||
float tolerance);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskPathMeasure * gsk_path_measure_ref (GskPathMeasure *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_path_measure_unref (GskPathMeasure *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
float gsk_path_measure_get_length (GskPathMeasure *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_path_measure_get_point (GskPathMeasure *self,
|
||||
float distance,
|
||||
graphene_point_t *pos,
|
||||
graphene_vec2_t *tangent);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
float gsk_path_measure_get_closest_point (GskPathMeasure *self,
|
||||
const graphene_point_t *point,
|
||||
graphene_point_t *out_pos);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gsk_path_measure_get_closest_point_full (GskPathMeasure *self,
|
||||
const graphene_point_t *point,
|
||||
float threshold,
|
||||
float *out_distance,
|
||||
graphene_point_t *out_pos,
|
||||
float *out_offset,
|
||||
graphene_vec2_t *out_tangent);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_path_measure_add_segment (GskPathMeasure *self,
|
||||
GskPathBuilder *builder,
|
||||
float start,
|
||||
float end);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gsk_path_measure_in_fill (GskPathMeasure *self,
|
||||
const graphene_point_t *point,
|
||||
GskFillRule fill_rule);
|
||||
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GskPathMeasure, gsk_path_measure_unref)
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GSK_PATH_MEASURE_H__ */
|
||||
121
gsk/gskpathprivate.h
Normal file
121
gsk/gskpathprivate.h
Normal file
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright © 2020 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 __GSK_PATH_PRIVATE_H__
|
||||
#define __GSK_PATH_PRIVATE_H__
|
||||
|
||||
#include "gskpath.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/* Same as Skia, so looks like a good value. ¯\_(ツ)_/¯ */
|
||||
#define GSK_PATH_TOLERANCE_DEFAULT (0.5)
|
||||
|
||||
typedef enum
|
||||
{
|
||||
GSK_PATH_FLAT,
|
||||
GSK_PATH_CLOSED
|
||||
} GskPathFlags;
|
||||
|
||||
typedef struct _GskContour GskContour;
|
||||
typedef struct _GskContourClass GskContourClass;
|
||||
|
||||
typedef struct _GskStandardOperation GskStandardOperation;
|
||||
|
||||
struct _GskStandardOperation {
|
||||
GskPathOperation op;
|
||||
gsize point; /* index into points array of the start point (last point of previous op) */
|
||||
};
|
||||
|
||||
GskContour * gsk_rect_contour_new (const graphene_rect_t *rect);
|
||||
GskContour * gsk_circle_contour_new (const graphene_point_t *center,
|
||||
float radius,
|
||||
float start_angle,
|
||||
float end_angle);
|
||||
GskContour * gsk_standard_contour_new (GskPathFlags flags,
|
||||
const GskStandardOperation *ops,
|
||||
gsize n_ops,
|
||||
const graphene_point_t *points,
|
||||
gsize n_points);
|
||||
|
||||
GskPath * gsk_path_new_from_contours (const GSList *contours);
|
||||
|
||||
gsize gsk_path_get_n_contours (GskPath *path);
|
||||
const GskContour * gsk_path_get_contour (GskPath *path,
|
||||
gsize i);
|
||||
gboolean gsk_path_foreach_with_tolerance (GskPath *self,
|
||||
GskPathForeachFlags flags,
|
||||
double tolerance,
|
||||
GskPathForeachFunc func,
|
||||
gpointer user_data);
|
||||
|
||||
GskContour * gsk_contour_dup (const GskContour *src);
|
||||
gpointer gsk_contour_init_measure (GskPath *path,
|
||||
gsize i,
|
||||
float tolerance,
|
||||
float *out_length);
|
||||
void gsk_contour_free_measure (GskPath *path,
|
||||
gsize i,
|
||||
gpointer data);
|
||||
void gsk_contour_get_start_end (const GskContour *self,
|
||||
graphene_point_t *start,
|
||||
graphene_point_t *end);
|
||||
void gsk_contour_get_point (GskPath *path,
|
||||
gsize i,
|
||||
gpointer measure_data,
|
||||
float distance,
|
||||
graphene_point_t *pos,
|
||||
graphene_vec2_t *tangent);
|
||||
gboolean gsk_contour_get_closest_point (GskPath *path,
|
||||
gsize i,
|
||||
gpointer measure_data,
|
||||
float tolerance,
|
||||
const graphene_point_t *point,
|
||||
float threshold,
|
||||
float *out_distance,
|
||||
graphene_point_t *out_pos,
|
||||
float *out_offset,
|
||||
graphene_vec2_t *out_tangent);
|
||||
int gsk_contour_get_winding (const GskContour *self,
|
||||
gpointer measure_data,
|
||||
const graphene_point_t *point,
|
||||
gboolean *on_edge);
|
||||
void gsk_contour_add_segment (const GskContour *self,
|
||||
GskPathBuilder *builder,
|
||||
gpointer measure_data,
|
||||
float start,
|
||||
float end);
|
||||
|
||||
void gsk_path_builder_add_contour (GskPathBuilder *builder,
|
||||
GskContour *contour);
|
||||
|
||||
void gsk_path_builder_svg_arc_to (GskPathBuilder *builder,
|
||||
float rx,
|
||||
float ry,
|
||||
float x_axis_rotation,
|
||||
gboolean large_arc,
|
||||
gboolean positive_sweep,
|
||||
float x,
|
||||
float y);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GSK_PATH_PRIVATE_H__ */
|
||||
|
||||
@@ -158,6 +158,8 @@ GskRenderNode * gsk_render_node_deserialize (GBytes
|
||||
#define GSK_TYPE_REPEAT_NODE (gsk_repeat_node_get_type())
|
||||
#define GSK_TYPE_CLIP_NODE (gsk_clip_node_get_type())
|
||||
#define GSK_TYPE_ROUNDED_CLIP_NODE (gsk_rounded_clip_node_get_type())
|
||||
#define GSK_TYPE_FILL_NODE (gsk_fill_node_get_type())
|
||||
#define GSK_TYPE_STROKE_NODE (gsk_stroke_node_get_type())
|
||||
#define GSK_TYPE_SHADOW_NODE (gsk_shadow_node_get_type())
|
||||
#define GSK_TYPE_BLEND_NODE (gsk_blend_node_get_type())
|
||||
#define GSK_TYPE_CROSS_FADE_NODE (gsk_cross_fade_node_get_type())
|
||||
@@ -186,6 +188,8 @@ typedef struct _GskColorMatrixNode GskColorMatrixNode;
|
||||
typedef struct _GskRepeatNode GskRepeatNode;
|
||||
typedef struct _GskClipNode GskClipNode;
|
||||
typedef struct _GskRoundedClipNode GskRoundedClipNode;
|
||||
typedef struct _GskFillNode GskFillNode;
|
||||
typedef struct _GskStrokeNode GskStrokeNode;
|
||||
typedef struct _GskShadowNode GskShadowNode;
|
||||
typedef struct _GskBlendNode GskBlendNode;
|
||||
typedef struct _GskCrossFadeNode GskCrossFadeNode;
|
||||
@@ -459,6 +463,32 @@ GskRenderNode * gsk_rounded_clip_node_get_child (const GskRender
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
const GskRoundedRect * gsk_rounded_clip_node_get_clip (const GskRenderNode *node) G_GNUC_PURE;
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GType gsk_fill_node_get_type (void) G_GNUC_CONST;
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskRenderNode * gsk_fill_node_new (GskRenderNode *child,
|
||||
GskPath *path,
|
||||
GskFillRule fill_rule);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskRenderNode * gsk_fill_node_get_child (GskRenderNode *node);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskPath * gsk_fill_node_get_path (GskRenderNode *node);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskFillRule gsk_fill_node_get_fill_rule (GskRenderNode *node);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GType gsk_stroke_node_get_type (void) G_GNUC_CONST;
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskRenderNode * gsk_stroke_node_new (GskRenderNode *child,
|
||||
GskPath *path,
|
||||
const GskStroke *stroke);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskRenderNode * gsk_stroke_node_get_child (GskRenderNode *node);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskPath * gsk_stroke_node_get_path (GskRenderNode *node);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
const GskStroke * gsk_stroke_node_get_stroke (GskRenderNode *node);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GType gsk_shadow_node_get_type (void) G_GNUC_CONST;
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
|
||||
@@ -25,8 +25,10 @@
|
||||
#include "gskdebugprivate.h"
|
||||
#include "gskdiffprivate.h"
|
||||
#include "gl/gskglrenderer.h"
|
||||
#include "gskpath.h"
|
||||
#include "gskrendererprivate.h"
|
||||
#include "gskroundedrectprivate.h"
|
||||
#include "gskstrokeprivate.h"
|
||||
#include "gsktransformprivate.h"
|
||||
|
||||
#include "gdk/gdktextureprivate.h"
|
||||
@@ -4370,6 +4372,368 @@ gsk_rounded_clip_node_get_clip (const GskRenderNode *node)
|
||||
return &self->clip;
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
/* {{{ GSK_FILL_NODE */
|
||||
|
||||
struct _GskFillNode
|
||||
{
|
||||
GskRenderNode render_node;
|
||||
|
||||
GskRenderNode *child;
|
||||
GskPath *path;
|
||||
GskFillRule fill_rule;
|
||||
};
|
||||
|
||||
static void
|
||||
gsk_fill_node_finalize (GskRenderNode *node)
|
||||
{
|
||||
GskFillNode *self = (GskFillNode *) node;
|
||||
GskRenderNodeClass *parent_class = g_type_class_peek (g_type_parent (GSK_TYPE_FILL_NODE));
|
||||
|
||||
gsk_render_node_unref (self->child);
|
||||
gsk_path_unref (self->path);
|
||||
|
||||
parent_class->finalize (node);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_fill_node_draw (GskRenderNode *node,
|
||||
cairo_t *cr)
|
||||
{
|
||||
GskFillNode *self = (GskFillNode *) node;
|
||||
|
||||
cairo_save (cr);
|
||||
|
||||
switch (self->fill_rule)
|
||||
{
|
||||
case GSK_FILL_RULE_WINDING:
|
||||
cairo_set_fill_rule (cr, CAIRO_FILL_RULE_WINDING);
|
||||
break;
|
||||
case GSK_FILL_RULE_EVEN_ODD:
|
||||
cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
break;
|
||||
}
|
||||
gsk_path_to_cairo (self->path, cr);
|
||||
cairo_clip (cr);
|
||||
|
||||
gsk_render_node_draw (self->child, cr);
|
||||
|
||||
cairo_restore (cr);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_fill_node_diff (GskRenderNode *node1,
|
||||
GskRenderNode *node2,
|
||||
cairo_region_t *region)
|
||||
{
|
||||
GskFillNode *self1 = (GskFillNode *) node1;
|
||||
GskFillNode *self2 = (GskFillNode *) node2;
|
||||
|
||||
if (self1->path == self2->path)
|
||||
{
|
||||
cairo_region_t *sub;
|
||||
cairo_rectangle_int_t clip_rect;
|
||||
graphene_rect_t rect;
|
||||
|
||||
sub = cairo_region_create();
|
||||
gsk_render_node_diff (self1->child, self2->child, sub);
|
||||
graphene_rect_union (&node1->bounds, &node2->bounds, &rect);
|
||||
rectangle_init_from_graphene (&clip_rect, &rect);
|
||||
cairo_region_intersect_rectangle (sub, &clip_rect);
|
||||
cairo_region_union (region, sub);
|
||||
cairo_region_destroy (sub);
|
||||
}
|
||||
else
|
||||
{
|
||||
gsk_render_node_diff_impossible (node1, node2, region);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_fill_node_class_init (gpointer g_class,
|
||||
gpointer class_data)
|
||||
{
|
||||
GskRenderNodeClass *node_class = g_class;
|
||||
|
||||
node_class->node_type = GSK_FILL_NODE;
|
||||
|
||||
node_class->finalize = gsk_fill_node_finalize;
|
||||
node_class->draw = gsk_fill_node_draw;
|
||||
node_class->diff = gsk_fill_node_diff;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_fill_node_new:
|
||||
* @child: The node to fill the area with
|
||||
* @path: The path describing the area to fill
|
||||
* @fill_rule: The fill rule to use
|
||||
*
|
||||
* Creates a #GskRenderNode that will fill the @child in the area
|
||||
* given by @path and @fill_rule.
|
||||
*
|
||||
* Returns: (transfer none) (type GskFillNode): A new #GskRenderNode
|
||||
*/
|
||||
GskRenderNode *
|
||||
gsk_fill_node_new (GskRenderNode *child,
|
||||
GskPath *path,
|
||||
GskFillRule fill_rule)
|
||||
{
|
||||
GskFillNode *self;
|
||||
GskRenderNode *node;
|
||||
graphene_rect_t path_bounds;
|
||||
|
||||
g_return_val_if_fail (GSK_IS_RENDER_NODE (child), NULL);
|
||||
g_return_val_if_fail (path != NULL, NULL);
|
||||
|
||||
self = gsk_render_node_alloc (GSK_FILL_NODE);
|
||||
node = (GskRenderNode *) self;
|
||||
|
||||
self->child = gsk_render_node_ref (child);
|
||||
self->path = gsk_path_ref (path);
|
||||
self->fill_rule = fill_rule;
|
||||
|
||||
if (gsk_path_get_bounds (path, &path_bounds))
|
||||
graphene_rect_intersection (&path_bounds, &child->bounds, &node->bounds);
|
||||
else
|
||||
graphene_rect_init_from_rect (&node->bounds, graphene_rect_zero ());
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_fill_node_get_child:
|
||||
* @node: (type GskFillNode): a fill #GskRenderNode
|
||||
*
|
||||
* Gets the child node that is getting drawn by the given @node.
|
||||
*
|
||||
* Returns: (transfer none): The child that is getting drawn
|
||||
**/
|
||||
GskRenderNode *
|
||||
gsk_fill_node_get_child (GskRenderNode *node)
|
||||
{
|
||||
GskFillNode *self = (GskFillNode *) node;
|
||||
|
||||
g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_FILL_NODE), NULL);
|
||||
|
||||
return self->child;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_fill_node_get_path:
|
||||
* @node: (type GskFillNode): a fill #GskRenderNode
|
||||
*
|
||||
* Retrievs the path used to describe the area filled with the contents of
|
||||
* the @node.
|
||||
*
|
||||
* Returns: (transfer none): a #GskPath
|
||||
*/
|
||||
GskPath *
|
||||
gsk_fill_node_get_path (GskRenderNode *node)
|
||||
{
|
||||
GskFillNode *self = (GskFillNode *) node;
|
||||
|
||||
g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_FILL_NODE), NULL);
|
||||
|
||||
return self->path;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_fill_node_get_fill_rule:
|
||||
* @node: (type GskFillNode): a fill #GskRenderNode
|
||||
*
|
||||
* Retrievs the fill rule used to determine how the path is filled.
|
||||
*
|
||||
* Returns: a #GskFillRule
|
||||
*/
|
||||
GskFillRule
|
||||
gsk_fill_node_get_fill_rule (GskRenderNode *node)
|
||||
{
|
||||
GskFillNode *self = (GskFillNode *) node;
|
||||
|
||||
g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_FILL_NODE), GSK_FILL_RULE_WINDING);
|
||||
|
||||
return self->fill_rule;
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
/* {{{ GSK_STROKE_NODE */
|
||||
|
||||
struct _GskStrokeNode
|
||||
{
|
||||
GskRenderNode render_node;
|
||||
|
||||
GskRenderNode *child;
|
||||
GskPath *path;
|
||||
GskStroke stroke;
|
||||
};
|
||||
|
||||
static void
|
||||
gsk_stroke_node_finalize (GskRenderNode *node)
|
||||
{
|
||||
GskStrokeNode *self = (GskStrokeNode *) node;
|
||||
GskRenderNodeClass *parent_class = g_type_class_peek (g_type_parent (GSK_TYPE_STROKE_NODE));
|
||||
|
||||
gsk_render_node_unref (self->child);
|
||||
gsk_path_unref (self->path);
|
||||
gsk_stroke_clear (&self->stroke);
|
||||
|
||||
parent_class->finalize (node);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_stroke_node_draw (GskRenderNode *node,
|
||||
cairo_t *cr)
|
||||
{
|
||||
GskStrokeNode *self = (GskStrokeNode *) node;
|
||||
|
||||
cairo_save (cr);
|
||||
|
||||
gsk_cairo_rectangle (cr, &self->child->bounds);
|
||||
cairo_clip (cr);
|
||||
|
||||
cairo_push_group (cr);
|
||||
gsk_render_node_draw (self->child, cr);
|
||||
cairo_pop_group_to_source (cr);
|
||||
|
||||
gsk_stroke_to_cairo (&self->stroke, cr);
|
||||
|
||||
gsk_path_to_cairo (self->path, cr);
|
||||
cairo_stroke (cr);
|
||||
|
||||
cairo_restore (cr);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_stroke_node_diff (GskRenderNode *node1,
|
||||
GskRenderNode *node2,
|
||||
cairo_region_t *region)
|
||||
{
|
||||
GskStrokeNode *self1 = (GskStrokeNode *) node1;
|
||||
GskStrokeNode *self2 = (GskStrokeNode *) node2;
|
||||
|
||||
if (self1->path == self2->path &&
|
||||
gsk_stroke_equal (&self1->stroke, &self2->stroke))
|
||||
{
|
||||
cairo_region_t *sub;
|
||||
|
||||
sub = cairo_region_create();
|
||||
gsk_render_node_diff (self1->child, self2->child, sub);
|
||||
cairo_region_union (region, sub);
|
||||
cairo_region_destroy (sub);
|
||||
}
|
||||
else
|
||||
{
|
||||
gsk_render_node_diff_impossible (node1, node2, region);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_stroke_node_class_init (gpointer g_class,
|
||||
gpointer class_data)
|
||||
{
|
||||
GskRenderNodeClass *node_class = g_class;
|
||||
|
||||
node_class->node_type = GSK_STROKE_NODE;
|
||||
|
||||
node_class->finalize = gsk_stroke_node_finalize;
|
||||
node_class->draw = gsk_stroke_node_draw;
|
||||
node_class->diff = gsk_stroke_node_diff;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_stroke_node_new:
|
||||
* @child: The node to stroke the area with
|
||||
* @path: (transfer none): The path describing the area to stroke
|
||||
* @stroke: (transfer none): The stroke attributes to use
|
||||
*
|
||||
* Creates a #GskRenderNode that will stroke the @child along the given
|
||||
* @path using the attributes defined in @stroke.
|
||||
*
|
||||
* Returns: (transfer none) (type GskStrokeNode): A new #GskRenderNode
|
||||
*/
|
||||
GskRenderNode *
|
||||
gsk_stroke_node_new (GskRenderNode *child,
|
||||
GskPath *path,
|
||||
const GskStroke *stroke)
|
||||
{
|
||||
GskStrokeNode *self;
|
||||
GskRenderNode *node;
|
||||
|
||||
g_return_val_if_fail (GSK_IS_RENDER_NODE (child), NULL);
|
||||
g_return_val_if_fail (path != NULL, NULL);
|
||||
g_return_val_if_fail (stroke != NULL, NULL);
|
||||
|
||||
self = gsk_render_node_alloc (GSK_STROKE_NODE);
|
||||
node = (GskRenderNode *) self;
|
||||
|
||||
self->child = gsk_render_node_ref (child);
|
||||
self->path = gsk_path_ref (path);
|
||||
gsk_stroke_init_copy (&self->stroke, stroke);
|
||||
|
||||
/* XXX: Figure out a way to compute bounds from the path */
|
||||
graphene_rect_init_from_rect (&node->bounds, &child->bounds);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_stroke_node_get_child:
|
||||
* @node: (type GskStrokeNode): a stroke #GskRenderNode
|
||||
*
|
||||
* Gets the child node that is getting drawn by the given @node.
|
||||
*
|
||||
* Returns: (transfer none): The child that is getting drawn
|
||||
**/
|
||||
GskRenderNode *
|
||||
gsk_stroke_node_get_child (GskRenderNode *node)
|
||||
{
|
||||
GskStrokeNode *self = (GskStrokeNode *) node;
|
||||
|
||||
g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_STROKE_NODE), NULL);
|
||||
|
||||
return self->child;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_stroke_node_get_path:
|
||||
* @node: (type GskStrokeNode): a stroke #GskRenderNode
|
||||
*
|
||||
* Retrievs the path that will be stroked with the contents of
|
||||
* the @node.
|
||||
*
|
||||
* Returns: (transfer none): a #GskPath
|
||||
*/
|
||||
GskPath *
|
||||
gsk_stroke_node_get_path (GskRenderNode *node)
|
||||
{
|
||||
GskStrokeNode *self = (GskStrokeNode *) node;
|
||||
|
||||
g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_STROKE_NODE), NULL);
|
||||
|
||||
return self->path;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_stroke_node_get_stroke:
|
||||
* @node: (type GskStrokeNode): a stroke #GskRenderNode
|
||||
*
|
||||
* Retrievs the stroke attributes used in this @node.
|
||||
*
|
||||
* Returns: a #GskStroke
|
||||
*/
|
||||
const GskStroke *
|
||||
gsk_stroke_node_get_stroke (GskRenderNode *node)
|
||||
{
|
||||
GskStrokeNode *self = (GskStrokeNode *) node;
|
||||
|
||||
g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_STROKE_NODE), NULL);
|
||||
|
||||
return &self->stroke;
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
/* {{{ GSK_SHADOW_NODE */
|
||||
|
||||
@@ -5703,6 +6067,8 @@ gsk_mask_node_draw (GskRenderNode *node,
|
||||
cairo_clip (cr);
|
||||
|
||||
cairo_mask (cr, mask_pattern);
|
||||
|
||||
cairo_pattern_destroy (mask_pattern);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -6221,6 +6587,8 @@ GSK_DEFINE_RENDER_NODE_TYPE (gsk_color_matrix_node, GSK_COLOR_MATRIX_NODE)
|
||||
GSK_DEFINE_RENDER_NODE_TYPE (gsk_repeat_node, GSK_REPEAT_NODE)
|
||||
GSK_DEFINE_RENDER_NODE_TYPE (gsk_clip_node, GSK_CLIP_NODE)
|
||||
GSK_DEFINE_RENDER_NODE_TYPE (gsk_rounded_clip_node, GSK_ROUNDED_CLIP_NODE)
|
||||
GSK_DEFINE_RENDER_NODE_TYPE (gsk_fill_node, GSK_FILL_NODE)
|
||||
GSK_DEFINE_RENDER_NODE_TYPE (gsk_stroke_node, GSK_STROKE_NODE)
|
||||
GSK_DEFINE_RENDER_NODE_TYPE (gsk_shadow_node, GSK_SHADOW_NODE)
|
||||
GSK_DEFINE_RENDER_NODE_TYPE (gsk_blend_node, GSK_BLEND_NODE)
|
||||
GSK_DEFINE_RENDER_NODE_TYPE (gsk_cross_fade_node, GSK_CROSS_FADE_NODE)
|
||||
@@ -6369,6 +6737,16 @@ gsk_render_node_init_types_once (void)
|
||||
sizeof (GskDebugNode),
|
||||
gsk_debug_node_class_init);
|
||||
gsk_render_node_types[GSK_DEBUG_NODE] = node_type;
|
||||
|
||||
node_type = gsk_render_node_type_register_static (I_("GskFillNode"),
|
||||
sizeof (GskFillNode),
|
||||
gsk_fill_node_class_init);
|
||||
gsk_render_node_types[GSK_FILL_NODE] = node_type;
|
||||
|
||||
node_type = gsk_render_node_type_register_static (I_("GskStrokeNode"),
|
||||
sizeof (GskStrokeNode),
|
||||
gsk_stroke_node_class_init);
|
||||
gsk_render_node_types[GSK_STROKE_NODE] = node_type;
|
||||
}
|
||||
|
||||
static void
|
||||
|
||||
@@ -23,9 +23,13 @@
|
||||
|
||||
#include "gskrendernodeparserprivate.h"
|
||||
|
||||
#include "gskpath.h"
|
||||
#include "gskpathbuilder.h"
|
||||
#include "gskroundedrectprivate.h"
|
||||
#include "gskrendernodeprivate.h"
|
||||
#include "gskstroke.h"
|
||||
#include "gsktransformprivate.h"
|
||||
#include "gskenumtypes.h"
|
||||
|
||||
#include "gdk/gdkrgbaprivate.h"
|
||||
#include "gdk/gdktextureprivate.h"
|
||||
@@ -1159,6 +1163,26 @@ create_default_render_node (void)
|
||||
return gsk_color_node_new (&GDK_RGBA("FF00CC"), &GRAPHENE_RECT_INIT (0, 0, 50, 50));
|
||||
}
|
||||
|
||||
static GskPath *
|
||||
create_default_path (void)
|
||||
{
|
||||
GskPathBuilder *builder;
|
||||
guint i;
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
|
||||
gsk_path_builder_move_to (builder, 25, 0);
|
||||
for (i = 1; i < 5; i++)
|
||||
{
|
||||
gsk_path_builder_line_to (builder,
|
||||
sin (i * G_PI * 0.8) * 25 + 25,
|
||||
-cos (i * G_PI * 0.8) * 25 + 25);
|
||||
}
|
||||
gsk_path_builder_close (builder);
|
||||
|
||||
return gsk_path_builder_free_to_path (builder);
|
||||
}
|
||||
|
||||
static GskRenderNode *
|
||||
parse_color_node (GtkCssParser *parser,
|
||||
Context *context)
|
||||
@@ -2082,6 +2106,215 @@ parse_rounded_clip_node (GtkCssParser *parser,
|
||||
return result;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
parse_path (GtkCssParser *parser,
|
||||
Context *context,
|
||||
gpointer out_path)
|
||||
{
|
||||
GskPath *path;
|
||||
char *str = NULL;
|
||||
|
||||
if (!parse_string (parser, context, &str))
|
||||
return FALSE;
|
||||
|
||||
path = gsk_path_parse (str);
|
||||
g_free (str);
|
||||
|
||||
if (path == NULL)
|
||||
{
|
||||
gtk_css_parser_error_value (parser, "Invalid path");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
*((GskPath **) out_path) = path;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
clear_path (gpointer inout_path)
|
||||
{
|
||||
g_clear_pointer ((GskPath **) inout_path, gsk_path_unref);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
parse_dash (GtkCssParser *parser,
|
||||
Context *context,
|
||||
gpointer out_dash)
|
||||
{
|
||||
GArray *dash;
|
||||
double d;
|
||||
|
||||
/* because CSS does this, too */
|
||||
if (gtk_css_parser_try_ident (parser, "none"))
|
||||
{
|
||||
*((GArray **) out_dash) = NULL;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
dash = g_array_new (FALSE, FALSE, sizeof (float));
|
||||
do {
|
||||
if (!gtk_css_parser_consume_number (parser, &d))
|
||||
{
|
||||
g_array_free (dash, TRUE);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
g_array_append_vals (dash, (float[1]) { d }, 1);
|
||||
} while (gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_SIGNLESS_NUMBER) ||
|
||||
gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_SIGNLESS_INTEGER));
|
||||
|
||||
*((GArray **) out_dash) = dash;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
clear_dash (gpointer inout_array)
|
||||
{
|
||||
g_clear_pointer ((GArray **) inout_array, g_array_unref);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
parse_enum (GtkCssParser *parser,
|
||||
GType type,
|
||||
gpointer out_value)
|
||||
{
|
||||
GEnumClass *class;
|
||||
GEnumValue *v;
|
||||
char *enum_name;
|
||||
|
||||
enum_name = gtk_css_parser_consume_ident (parser);
|
||||
if (enum_name == NULL)
|
||||
return FALSE;
|
||||
|
||||
class = g_type_class_ref (type);
|
||||
|
||||
v = g_enum_get_value_by_nick (class, enum_name);
|
||||
if (v == NULL)
|
||||
{
|
||||
gtk_css_parser_error_value (parser, "Unknown value \"%s\" for enum \"%s\"",
|
||||
enum_name, g_type_name (type));
|
||||
g_free (enum_name);
|
||||
g_type_class_unref (class);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
*(int*)out_value = v->value;
|
||||
|
||||
g_free (enum_name);
|
||||
g_type_class_unref (class);
|
||||
|
||||
gtk_css_parser_consume_token (parser);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
parse_fill_rule (GtkCssParser *parser,
|
||||
Context *context,
|
||||
gpointer out_rule)
|
||||
{
|
||||
return parse_enum (parser, GSK_TYPE_FILL_RULE, out_rule);
|
||||
}
|
||||
|
||||
static GskRenderNode *
|
||||
parse_fill_node (GtkCssParser *parser,
|
||||
Context *context)
|
||||
{
|
||||
GskRenderNode *child = NULL;
|
||||
GskPath *path = NULL;
|
||||
int rule = GSK_FILL_RULE_WINDING;
|
||||
const Declaration declarations[] = {
|
||||
{ "child", parse_node, clear_node, &child },
|
||||
{ "path", parse_path, clear_path, &path },
|
||||
{ "fill-rule", parse_fill_rule, NULL, &rule },
|
||||
};
|
||||
GskRenderNode *result;
|
||||
|
||||
parse_declarations (parser, context, declarations, G_N_ELEMENTS (declarations));
|
||||
if (child == NULL)
|
||||
child = create_default_render_node ();
|
||||
if (path == NULL)
|
||||
path = create_default_path ();
|
||||
|
||||
result = gsk_fill_node_new (child, path, rule);
|
||||
|
||||
gsk_path_unref (path);
|
||||
|
||||
gsk_render_node_unref (child);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
parse_line_cap (GtkCssParser *parser,
|
||||
Context *context,
|
||||
gpointer out)
|
||||
{
|
||||
return parse_enum (parser, GSK_TYPE_LINE_CAP, out);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
parse_line_join (GtkCssParser *parser,
|
||||
Context *context,
|
||||
gpointer out)
|
||||
{
|
||||
return parse_enum (parser, GSK_TYPE_LINE_JOIN, out);
|
||||
}
|
||||
|
||||
static GskRenderNode *
|
||||
parse_stroke_node (GtkCssParser *parser,
|
||||
Context *context)
|
||||
{
|
||||
GskRenderNode *child = NULL;
|
||||
GskPath *path = NULL;
|
||||
double line_width = 1.0;
|
||||
int line_cap = GSK_LINE_CAP_BUTT;
|
||||
int line_join = GSK_LINE_JOIN_MITER;
|
||||
double miter_limit = 4.0;
|
||||
GArray *dash = NULL;
|
||||
double dash_offset = 0.0;
|
||||
GskStroke *stroke;
|
||||
|
||||
const Declaration declarations[] = {
|
||||
{ "child", parse_node, clear_node, &child },
|
||||
{ "path", parse_path, clear_path, &path },
|
||||
{ "line-width", parse_double, NULL, &line_width },
|
||||
{ "line-cap", parse_line_cap, NULL, &line_cap },
|
||||
{ "line-join", parse_line_join, NULL, &line_join },
|
||||
{ "miter-limit", parse_double, NULL, &miter_limit },
|
||||
{ "dash", parse_dash, clear_dash, &dash },
|
||||
{ "dash-offset", parse_double, NULL, &dash_offset},
|
||||
};
|
||||
GskRenderNode *result;
|
||||
|
||||
parse_declarations (parser, context, declarations, G_N_ELEMENTS (declarations));
|
||||
if (child == NULL)
|
||||
child = create_default_render_node ();
|
||||
if (path == NULL)
|
||||
path = create_default_path ();
|
||||
|
||||
stroke = gsk_stroke_new (line_width);
|
||||
gsk_stroke_set_line_cap (stroke, line_cap);
|
||||
gsk_stroke_set_line_join (stroke, line_join);
|
||||
gsk_stroke_set_miter_limit (stroke, miter_limit);
|
||||
if (dash)
|
||||
{
|
||||
gsk_stroke_set_dash (stroke, (float *) dash->data, dash->len);
|
||||
g_array_free (dash, TRUE);
|
||||
}
|
||||
gsk_stroke_set_dash_offset (stroke, dash_offset);
|
||||
|
||||
result = gsk_stroke_node_new (child, path, stroke);
|
||||
|
||||
gsk_path_unref (path);
|
||||
gsk_stroke_free (stroke);
|
||||
gsk_render_node_unref (child);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static GskRenderNode *
|
||||
parse_shadow_node (GtkCssParser *parser,
|
||||
Context *context)
|
||||
@@ -2164,6 +2397,8 @@ parse_node (GtkCssParser *parser,
|
||||
{ "repeating-linear-gradient", parse_repeating_linear_gradient_node },
|
||||
{ "repeating-radial-gradient", parse_repeating_radial_gradient_node },
|
||||
{ "rounded-clip", parse_rounded_clip_node },
|
||||
{ "fill", parse_fill_node },
|
||||
{ "stroke", parse_stroke_node },
|
||||
{ "shadow", parse_shadow_node },
|
||||
{ "text", parse_text_node },
|
||||
{ "texture", parse_texture_node },
|
||||
@@ -2418,6 +2653,14 @@ printer_init_duplicates_for_node (Printer *printer,
|
||||
printer_init_duplicates_for_node (printer, gsk_debug_node_get_child (node));
|
||||
break;
|
||||
|
||||
case GSK_FILL_NODE:
|
||||
printer_init_duplicates_for_node (printer, gsk_fill_node_get_child (node));
|
||||
break;
|
||||
|
||||
case GSK_STROKE_NODE:
|
||||
printer_init_duplicates_for_node (printer, gsk_stroke_node_get_child (node));
|
||||
break;
|
||||
|
||||
case GSK_BLEND_NODE:
|
||||
printer_init_duplicates_for_node (printer, gsk_blend_node_get_bottom_child (node));
|
||||
printer_init_duplicates_for_node (printer, gsk_blend_node_get_top_child (node));
|
||||
@@ -2477,6 +2720,15 @@ printer_init (Printer *self,
|
||||
printer_init_duplicates_for_node (self, node);
|
||||
}
|
||||
|
||||
static void
|
||||
printer_clear (Printer *self)
|
||||
{
|
||||
if (self->str)
|
||||
g_string_free (self->str, TRUE);
|
||||
g_hash_table_unref (self->named_nodes);
|
||||
g_hash_table_unref (self->named_textures);
|
||||
}
|
||||
|
||||
#define IDENT_LEVEL 2 /* Spaces per level */
|
||||
static void
|
||||
_indent (Printer *self)
|
||||
@@ -2634,7 +2886,7 @@ append_float_param (Printer *p,
|
||||
float value,
|
||||
float default_value)
|
||||
{
|
||||
/* Don't approximate-compare here, better be topo verbose */
|
||||
/* Don't approximate-compare here, better be too verbose */
|
||||
if (value == default_value)
|
||||
return;
|
||||
|
||||
@@ -2809,8 +3061,11 @@ append_escaping_newlines (GString *str,
|
||||
len = strcspn (string, "\n");
|
||||
g_string_append_len (str, string, len);
|
||||
string += len;
|
||||
g_string_append (str, "\\\n");
|
||||
string++;
|
||||
if (*string)
|
||||
{
|
||||
g_string_append (str, "\\\n");
|
||||
string++;
|
||||
}
|
||||
} while (*string);
|
||||
}
|
||||
|
||||
@@ -3009,6 +3264,83 @@ gsk_text_node_serialize_glyphs (GskRenderNode *node,
|
||||
pango_glyph_string_free (ascii);
|
||||
}
|
||||
|
||||
static const char *
|
||||
enum_to_nick (GType type,
|
||||
int value)
|
||||
{
|
||||
GEnumClass *class;
|
||||
GEnumValue *v;
|
||||
|
||||
class = g_type_class_ref (type);
|
||||
v = g_enum_get_value (class, value);
|
||||
g_type_class_unref (class);
|
||||
|
||||
return v->value_nick;
|
||||
}
|
||||
|
||||
static void
|
||||
append_enum_param (Printer *p,
|
||||
const char *param_name,
|
||||
GType type,
|
||||
int value)
|
||||
{
|
||||
_indent (p);
|
||||
g_string_append_printf (p->str, "%s: ", param_name);
|
||||
g_string_append (p->str, enum_to_nick (type, value));
|
||||
g_string_append_c (p->str, ';');
|
||||
g_string_append_c (p->str, '\n');
|
||||
}
|
||||
|
||||
static void
|
||||
append_path_param (Printer *p,
|
||||
const char *param_name,
|
||||
GskPath *path)
|
||||
{
|
||||
char *str, *s;
|
||||
|
||||
_indent (p);
|
||||
g_string_append (p->str, "path: \"\\\n");
|
||||
str = gsk_path_to_string (path);
|
||||
/* Put each command on a new line */
|
||||
for (s = str; *s; s++)
|
||||
{
|
||||
if (*s == ' ' &&
|
||||
(s[1] == 'M' || s[1] == 'C' || s[1] == 'Z' || s[1] == 'L'))
|
||||
*s = '\n';
|
||||
}
|
||||
append_escaping_newlines (p->str, str);
|
||||
g_string_append (p->str, "\";\n");
|
||||
g_free (str);
|
||||
}
|
||||
|
||||
static void
|
||||
append_dash_param (Printer *p,
|
||||
const char *param_name,
|
||||
const float *dash,
|
||||
gsize n_dash)
|
||||
{
|
||||
_indent (p);
|
||||
g_string_append (p->str, "dash: ");
|
||||
|
||||
if (n_dash == 0)
|
||||
{
|
||||
g_string_append (p->str, "none");
|
||||
}
|
||||
else
|
||||
{
|
||||
gsize i;
|
||||
|
||||
string_append_double (p->str, dash[0]);
|
||||
for (i = 1; i < n_dash; i++)
|
||||
{
|
||||
g_string_append_c (p->str, ' ');
|
||||
string_append_double (p->str, dash[i]);
|
||||
}
|
||||
}
|
||||
|
||||
g_string_append (p->str, ";\n");
|
||||
}
|
||||
|
||||
static void
|
||||
render_node_print (Printer *p,
|
||||
GskRenderNode *node)
|
||||
@@ -3178,6 +3510,42 @@ render_node_print (Printer *p,
|
||||
append_rounded_rect_param (p, "clip", gsk_rounded_clip_node_get_clip (node));
|
||||
append_node_param (p, "child", gsk_rounded_clip_node_get_child (node));
|
||||
|
||||
end_node (p);
|
||||
}
|
||||
break;
|
||||
|
||||
case GSK_FILL_NODE:
|
||||
{
|
||||
start_node (p, "fill", node_name);
|
||||
|
||||
append_node_param (p, "child", gsk_fill_node_get_child (node));
|
||||
append_path_param (p, "path", gsk_fill_node_get_path (node));
|
||||
append_enum_param (p, "fill-rule", GSK_TYPE_FILL_RULE, gsk_fill_node_get_fill_rule (node));
|
||||
|
||||
end_node (p);
|
||||
}
|
||||
break;
|
||||
|
||||
case GSK_STROKE_NODE:
|
||||
{
|
||||
const GskStroke *stroke;
|
||||
const float *dash;
|
||||
gsize n_dash;
|
||||
|
||||
start_node (p, "stroke", node_name);
|
||||
|
||||
append_node_param (p, "child", gsk_stroke_node_get_child (node));
|
||||
append_path_param (p, "path", gsk_stroke_node_get_path (node));
|
||||
|
||||
stroke = gsk_stroke_node_get_stroke (node);
|
||||
append_float_param (p, "line-width", gsk_stroke_get_line_width (stroke), 0.0f);
|
||||
append_enum_param (p, "line-cap", GSK_TYPE_LINE_CAP, gsk_stroke_get_line_cap (stroke));
|
||||
append_enum_param (p, "line-join", GSK_TYPE_LINE_JOIN, gsk_stroke_get_line_join (stroke));
|
||||
append_float_param (p, "miter-limit", gsk_stroke_get_miter_limit (stroke), 4.0f);
|
||||
dash = gsk_stroke_get_dash (stroke, &n_dash);
|
||||
if (dash)
|
||||
append_dash_param (p, "dash", dash, n_dash);
|
||||
append_float_param (p, "dash-offset", gsk_stroke_get_dash_offset (stroke), 0.0f);
|
||||
|
||||
end_node (p);
|
||||
}
|
||||
@@ -3686,6 +4054,7 @@ GBytes *
|
||||
gsk_render_node_serialize (GskRenderNode *node)
|
||||
{
|
||||
Printer p;
|
||||
GBytes *res;
|
||||
|
||||
printer_init (&p, node);
|
||||
|
||||
@@ -3705,5 +4074,9 @@ gsk_render_node_serialize (GskRenderNode *node)
|
||||
render_node_print (&p, node);
|
||||
}
|
||||
|
||||
return g_string_free_to_bytes (p.str);
|
||||
res = g_string_free_to_bytes (g_steal_pointer (&p.str));
|
||||
|
||||
printer_clear (&p);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
417
gsk/gskspline.c
Normal file
417
gsk/gskspline.c
Normal file
@@ -0,0 +1,417 @@
|
||||
/*
|
||||
* Copyright © 2002 University of Southern California
|
||||
* 2020 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>
|
||||
* Carl D. Worth <cworth@cworth.org>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gsksplineprivate.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
graphene_point_t last_point;
|
||||
float last_progress;
|
||||
float tolerance;
|
||||
GskSplineAddPointFunc func;
|
||||
gpointer user_data;
|
||||
} GskCubicDecomposition;
|
||||
|
||||
static void
|
||||
gsk_spline_decompose_add_point (GskCubicDecomposition *decomp,
|
||||
const graphene_point_t *pt,
|
||||
float progress)
|
||||
{
|
||||
if (graphene_point_equal (&decomp->last_point, pt))
|
||||
return;
|
||||
|
||||
decomp->func (&decomp->last_point, pt, decomp->last_progress, decomp->last_progress + progress, decomp->user_data);
|
||||
decomp->last_point = *pt;
|
||||
decomp->last_progress += progress;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_spline_cubic_get_coefficients (graphene_point_t coeffs[4],
|
||||
const graphene_point_t pts[4])
|
||||
{
|
||||
coeffs[0] = GRAPHENE_POINT_INIT (pts[3].x - 3.0f * pts[2].x + 3.0f * pts[1].x - pts[0].x,
|
||||
pts[3].y - 3.0f * pts[2].y + 3.0f * pts[1].y - pts[0].y);
|
||||
coeffs[1] = GRAPHENE_POINT_INIT (3.0f * pts[2].x - 6.0f * pts[1].x + 3.0f * pts[0].x,
|
||||
3.0f * pts[2].y - 6.0f * pts[1].y + 3.0f * pts[0].y);
|
||||
coeffs[2] = GRAPHENE_POINT_INIT (3.0f * pts[1].x - 3.0f * pts[0].x,
|
||||
3.0f * pts[1].y - 3.0f * pts[0].y);
|
||||
coeffs[3] = pts[0];
|
||||
}
|
||||
|
||||
void
|
||||
gsk_spline_get_point_cubic (const graphene_point_t pts[4],
|
||||
float progress,
|
||||
graphene_point_t *pos,
|
||||
graphene_vec2_t *tangent)
|
||||
{
|
||||
graphene_point_t c[4];
|
||||
|
||||
gsk_spline_cubic_get_coefficients (c, pts);
|
||||
|
||||
if (pos)
|
||||
*pos = GRAPHENE_POINT_INIT (((c[0].x * progress + c[1].x) * progress +c[2].x) * progress + c[3].x,
|
||||
((c[0].y * progress + c[1].y) * progress +c[2].y) * progress + c[3].y);
|
||||
if (tangent)
|
||||
{
|
||||
graphene_vec2_init (tangent,
|
||||
(3.0f * c[0].x * progress + 2.0f * c[1].x) * progress + c[2].x,
|
||||
(3.0f * c[0].y * progress + 2.0f * c[1].y) * progress + c[2].y);
|
||||
graphene_vec2_normalize (tangent, tangent);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gsk_spline_split_cubic (const graphene_point_t pts[4],
|
||||
graphene_point_t result1[4],
|
||||
graphene_point_t result2[4],
|
||||
float progress)
|
||||
{
|
||||
graphene_point_t ab, bc, cd;
|
||||
graphene_point_t abbc, bccd;
|
||||
graphene_point_t final;
|
||||
|
||||
graphene_point_interpolate (&pts[0], &pts[1], progress, &ab);
|
||||
graphene_point_interpolate (&pts[1], &pts[2], progress, &bc);
|
||||
graphene_point_interpolate (&pts[2], &pts[3], progress, &cd);
|
||||
graphene_point_interpolate (&ab, &bc, progress, &abbc);
|
||||
graphene_point_interpolate (&bc, &cd, progress, &bccd);
|
||||
graphene_point_interpolate (&abbc, &bccd, progress, &final);
|
||||
|
||||
memcpy (result1, (graphene_point_t[4]) { pts[0], ab, abbc, final }, sizeof (graphene_point_t[4]));
|
||||
memcpy (result2, (graphene_point_t[4]) { final, bccd, cd, pts[3] }, sizeof (graphene_point_t[4]));
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* Return an upper bound on the error (squared) that could result from
|
||||
* approximating a spline as a line segment connecting the two endpoints. */
|
||||
static float
|
||||
gsk_spline_error_squared (const graphene_point_t pts[4])
|
||||
{
|
||||
float bdx, bdy, berr;
|
||||
float cdx, cdy, cerr;
|
||||
|
||||
/* We are going to compute the distance (squared) between each of the the b
|
||||
* and c control points and the segment a-b. The maximum of these two
|
||||
* distances will be our approximation error. */
|
||||
|
||||
bdx = pts[1].x - pts[0].x;
|
||||
bdy = pts[1].y - pts[0].y;
|
||||
|
||||
cdx = pts[2].x - pts[0].x;
|
||||
cdy = pts[2].y - pts[0].y;
|
||||
|
||||
if (!graphene_point_equal (&pts[0], &pts[3]))
|
||||
{
|
||||
float dx, dy, u, v;
|
||||
|
||||
/* Intersection point (px):
|
||||
* px = p1 + u(p2 - p1)
|
||||
* (p - px) ∙ (p2 - p1) = 0
|
||||
* Thus:
|
||||
* u = ((p - p1) ∙ (p2 - p1)) / ∥p2 - p1∥²;
|
||||
*/
|
||||
|
||||
dx = pts[3].x - pts[0].x;
|
||||
dy = pts[3].y - pts[0].y;
|
||||
v = dx * dx + dy * dy;
|
||||
|
||||
u = bdx * dx + bdy * dy;
|
||||
if (u <= 0)
|
||||
{
|
||||
/* bdx -= 0;
|
||||
* bdy -= 0;
|
||||
*/
|
||||
}
|
||||
else if (u >= v)
|
||||
{
|
||||
bdx -= dx;
|
||||
bdy -= dy;
|
||||
}
|
||||
else
|
||||
{
|
||||
bdx -= u/v * dx;
|
||||
bdy -= u/v * dy;
|
||||
}
|
||||
|
||||
u = cdx * dx + cdy * dy;
|
||||
if (u <= 0)
|
||||
{
|
||||
/* cdx -= 0;
|
||||
* cdy -= 0;
|
||||
*/
|
||||
}
|
||||
else if (u >= v)
|
||||
{
|
||||
cdx -= dx;
|
||||
cdy -= dy;
|
||||
}
|
||||
else
|
||||
{
|
||||
cdx -= u/v * dx;
|
||||
cdy -= u/v * dy;
|
||||
}
|
||||
}
|
||||
|
||||
berr = bdx * bdx + bdy * bdy;
|
||||
cerr = cdx * cdx + cdy * cdy;
|
||||
if (berr > cerr)
|
||||
return berr;
|
||||
else
|
||||
return cerr;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* taken from Skia, including the very descriptive name */
|
||||
static gboolean
|
||||
gsk_spline_cubic_too_curvy (const graphene_point_t pts[4],
|
||||
float tolerance)
|
||||
{
|
||||
graphene_point_t p;
|
||||
|
||||
graphene_point_interpolate (&pts[0], &pts[3], 1.0f / 3, &p);
|
||||
if (ABS (p.x - pts[1].x) + ABS (p.y - pts[1].y) > tolerance)
|
||||
return TRUE;
|
||||
|
||||
graphene_point_interpolate (&pts[0], &pts[3], 2.0f / 3, &p);
|
||||
if (ABS (p.x - pts[2].x) + ABS (p.y - pts[2].y) > tolerance)
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_spline_decompose_into (GskCubicDecomposition *decomp,
|
||||
const graphene_point_t pts[4],
|
||||
float progress)
|
||||
{
|
||||
graphene_point_t left[4], right[4];
|
||||
|
||||
if (!gsk_spline_cubic_too_curvy (pts, decomp->tolerance) || progress < 1 / 1024.f)
|
||||
{
|
||||
gsk_spline_decompose_add_point (decomp, &pts[3], progress);
|
||||
return;
|
||||
}
|
||||
|
||||
gsk_spline_split_cubic (pts, left, right, 0.5);
|
||||
|
||||
gsk_spline_decompose_into (decomp, left, progress / 2);
|
||||
gsk_spline_decompose_into (decomp, right, progress / 2);
|
||||
}
|
||||
|
||||
void
|
||||
gsk_spline_decompose_cubic (const graphene_point_t pts[4],
|
||||
float tolerance,
|
||||
GskSplineAddPointFunc add_point_func,
|
||||
gpointer user_data)
|
||||
{
|
||||
GskCubicDecomposition decomp = { pts[0], 0.0f, tolerance, add_point_func, user_data };
|
||||
|
||||
gsk_spline_decompose_into (&decomp, pts, 1.0f);
|
||||
|
||||
g_assert (graphene_point_equal (&decomp.last_point, &pts[3]));
|
||||
g_assert (decomp.last_progress == 1.0f || decomp.last_progress == 0.0f);
|
||||
}
|
||||
|
||||
/* Spline deviation from the circle in radius would be given by:
|
||||
|
||||
error = sqrt (x**2 + y**2) - 1
|
||||
|
||||
A simpler error function to work with is:
|
||||
|
||||
e = x**2 + y**2 - 1
|
||||
|
||||
From "Good approximation of circles by curvature-continuous Bezier
|
||||
curves", Tor Dokken and Morten Daehlen, Computer Aided Geometric
|
||||
Design 8 (1990) 22-41, we learn:
|
||||
|
||||
abs (max(e)) = 4/27 * sin**6(angle/4) / cos**2(angle/4)
|
||||
|
||||
and
|
||||
abs (error) =~ 1/2 * e
|
||||
|
||||
Of course, this error value applies only for the particular spline
|
||||
approximation that is used in _cairo_gstate_arc_segment.
|
||||
*/
|
||||
static float
|
||||
arc_error_normalized (float angle)
|
||||
{
|
||||
return 2.0/27.0 * pow (sin (angle / 4), 6) / pow (cos (angle / 4), 2);
|
||||
}
|
||||
|
||||
static float
|
||||
arc_max_angle_for_tolerance_normalized (float tolerance)
|
||||
{
|
||||
float angle, error;
|
||||
guint i;
|
||||
|
||||
/* Use table lookup to reduce search time in most cases. */
|
||||
struct {
|
||||
float angle;
|
||||
float error;
|
||||
} table[] = {
|
||||
{ G_PI / 1.0, 0.0185185185185185036127 },
|
||||
{ G_PI / 2.0, 0.000272567143730179811158 },
|
||||
{ G_PI / 3.0, 2.38647043651461047433e-05 },
|
||||
{ G_PI / 4.0, 4.2455377443222443279e-06 },
|
||||
{ G_PI / 5.0, 1.11281001494389081528e-06 },
|
||||
{ G_PI / 6.0, 3.72662000942734705475e-07 },
|
||||
{ G_PI / 7.0, 1.47783685574284411325e-07 },
|
||||
{ G_PI / 8.0, 6.63240432022601149057e-08 },
|
||||
{ G_PI / 9.0, 3.2715520137536980553e-08 },
|
||||
{ G_PI / 10.0, 1.73863223499021216974e-08 },
|
||||
{ G_PI / 11.0, 9.81410988043554039085e-09 },
|
||||
};
|
||||
|
||||
for (i = 0; i < G_N_ELEMENTS (table); i++)
|
||||
{
|
||||
if (table[i].error < tolerance)
|
||||
return table[i].angle;
|
||||
}
|
||||
|
||||
i++;
|
||||
do {
|
||||
angle = G_PI / i++;
|
||||
error = arc_error_normalized (angle);
|
||||
} while (error > tolerance);
|
||||
|
||||
return angle;
|
||||
}
|
||||
|
||||
static guint
|
||||
arc_segments_needed (float angle,
|
||||
float radius,
|
||||
float tolerance)
|
||||
{
|
||||
float max_angle;
|
||||
|
||||
/* the error is amplified by at most the length of the
|
||||
* major axis of the circle; see cairo-pen.c for a more detailed analysis
|
||||
* of this. */
|
||||
max_angle = arc_max_angle_for_tolerance_normalized (tolerance / radius);
|
||||
|
||||
return ceil (fabs (angle) / max_angle);
|
||||
}
|
||||
|
||||
/* We want to draw a single spline approximating a circular arc radius
|
||||
R from angle A to angle B. Since we want a symmetric spline that
|
||||
matches the endpoints of the arc in position and slope, we know
|
||||
that the spline control points must be:
|
||||
|
||||
(R * cos(A), R * sin(A))
|
||||
(R * cos(A) - h * sin(A), R * sin(A) + h * cos (A))
|
||||
(R * cos(B) + h * sin(B), R * sin(B) - h * cos (B))
|
||||
(R * cos(B), R * sin(B))
|
||||
|
||||
for some value of h.
|
||||
|
||||
"Approximation of circular arcs by cubic polynomials", Michael
|
||||
Goldapp, Computer Aided Geometric Design 8 (1991) 227-238, provides
|
||||
various values of h along with error analysis for each.
|
||||
|
||||
From that paper, a very practical value of h is:
|
||||
|
||||
h = 4/3 * R * tan(angle/4)
|
||||
|
||||
This value does not give the spline with minimal error, but it does
|
||||
provide a very good approximation, (6th-order convergence), and the
|
||||
error expression is quite simple, (see the comment for
|
||||
_arc_error_normalized).
|
||||
*/
|
||||
static gboolean
|
||||
gsk_spline_decompose_arc_segment (const graphene_point_t *center,
|
||||
float radius,
|
||||
float angle_A,
|
||||
float angle_B,
|
||||
GskSplineAddCurveFunc curve_func,
|
||||
gpointer user_data)
|
||||
{
|
||||
float r_sin_A, r_cos_A;
|
||||
float r_sin_B, r_cos_B;
|
||||
float h;
|
||||
|
||||
r_sin_A = radius * sin (angle_A);
|
||||
r_cos_A = radius * cos (angle_A);
|
||||
r_sin_B = radius * sin (angle_B);
|
||||
r_cos_B = radius * cos (angle_B);
|
||||
|
||||
h = 4.0/3.0 * tan ((angle_B - angle_A) / 4.0);
|
||||
|
||||
return curve_func ((graphene_point_t[4]) {
|
||||
GRAPHENE_POINT_INIT (
|
||||
center->x + r_cos_A,
|
||||
center->y + r_sin_A
|
||||
),
|
||||
GRAPHENE_POINT_INIT (
|
||||
center->x + r_cos_A - h * r_sin_A,
|
||||
center->y + r_sin_A + h * r_cos_A
|
||||
),
|
||||
GRAPHENE_POINT_INIT (
|
||||
center->x + r_cos_B + h * r_sin_B,
|
||||
center->y + r_sin_B - h * r_cos_B
|
||||
),
|
||||
GRAPHENE_POINT_INIT (
|
||||
center->x + r_cos_B,
|
||||
center->y + r_sin_B
|
||||
)
|
||||
},
|
||||
user_data);
|
||||
}
|
||||
|
||||
gboolean
|
||||
gsk_spline_decompose_arc (const graphene_point_t *center,
|
||||
float radius,
|
||||
float tolerance,
|
||||
float start_angle,
|
||||
float end_angle,
|
||||
GskSplineAddCurveFunc curve_func,
|
||||
gpointer user_data)
|
||||
{
|
||||
float step = start_angle - end_angle;
|
||||
guint i, n_segments;
|
||||
|
||||
/* Recurse if drawing arc larger than pi */
|
||||
if (ABS (step) > G_PI)
|
||||
{
|
||||
float mid_angle = (start_angle + end_angle) / 2.0;
|
||||
|
||||
return gsk_spline_decompose_arc (center, radius, tolerance, start_angle, mid_angle, curve_func, user_data)
|
||||
&& gsk_spline_decompose_arc (center, radius, tolerance, mid_angle, end_angle, curve_func, user_data);
|
||||
}
|
||||
else if (ABS (step) < tolerance)
|
||||
{
|
||||
return gsk_spline_decompose_arc_segment (center, radius, start_angle, end_angle, curve_func, user_data);
|
||||
}
|
||||
|
||||
n_segments = arc_segments_needed (ABS (step), radius, tolerance);
|
||||
step = (end_angle - start_angle) / n_segments;
|
||||
|
||||
for (i = 0; i < n_segments - 1; i++, start_angle += step)
|
||||
{
|
||||
if (!gsk_spline_decompose_arc_segment (center, radius, start_angle, start_angle + step, curve_func, user_data))
|
||||
return FALSE;
|
||||
}
|
||||
return gsk_spline_decompose_arc_segment (center, radius, start_angle, end_angle, curve_func, user_data);
|
||||
}
|
||||
|
||||
60
gsk/gsksplineprivate.h
Normal file
60
gsk/gsksplineprivate.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright © 2020 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 __GSK_SPLINE_PRIVATE_H__
|
||||
#define __GSK_SPLINE_PRIVATE_H__
|
||||
|
||||
#include "gskpath.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef void (* GskSplineAddPointFunc) (const graphene_point_t *from,
|
||||
const graphene_point_t *to,
|
||||
float from_progress,
|
||||
float to_progress,
|
||||
gpointer user_data);
|
||||
|
||||
void gsk_spline_get_point_cubic (const graphene_point_t pts[4],
|
||||
float progress,
|
||||
graphene_point_t *pos,
|
||||
graphene_vec2_t *tangent);
|
||||
void gsk_spline_split_cubic (const graphene_point_t pts[4],
|
||||
graphene_point_t result1[4],
|
||||
graphene_point_t result2[4],
|
||||
float progress);
|
||||
void gsk_spline_decompose_cubic (const graphene_point_t pts[4],
|
||||
float tolerance,
|
||||
GskSplineAddPointFunc add_point_func,
|
||||
gpointer user_data);
|
||||
|
||||
typedef gboolean (* GskSplineAddCurveFunc) (const graphene_point_t curve[4],
|
||||
gpointer user_data);
|
||||
gboolean gsk_spline_decompose_arc (const graphene_point_t *center,
|
||||
float radius,
|
||||
float tolerance,
|
||||
float start_angle,
|
||||
float end_angle,
|
||||
GskSplineAddCurveFunc curve_func,
|
||||
gpointer user_data);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GSK_SPLINE_PRIVATE_H__ */
|
||||
|
||||
441
gsk/gskstroke.c
Normal file
441
gsk/gskstroke.c
Normal file
@@ -0,0 +1,441 @@
|
||||
/*
|
||||
* Copyright © 2020 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 "gskstrokeprivate.h"
|
||||
|
||||
/**
|
||||
* SECTION:gskstroke
|
||||
* @Title: Stroke
|
||||
* @Short_description: Properties of a stroke operation
|
||||
* @See_also: #GskPath, gsk_stroke_node_new()
|
||||
*
|
||||
* This section describes the #GskStroke structure that is used to
|
||||
* describe lines and curves that are more complex than simple rectangles.
|
||||
*
|
||||
* #GskStroke is an immutable struct. After creation, you cannot change
|
||||
* the types it represents. Instead, new #GskStroke have to be created.
|
||||
* The #GskStrokeBuilder structure is meant to help in this endeavor.
|
||||
*/
|
||||
|
||||
/**
|
||||
* GskStroke:
|
||||
*
|
||||
* A #GskStroke struct is an opaque struct that should be copied
|
||||
* on use.
|
||||
*/
|
||||
|
||||
G_DEFINE_BOXED_TYPE (GskStroke, gsk_stroke,
|
||||
gsk_stroke_copy,
|
||||
gsk_stroke_free)
|
||||
|
||||
|
||||
/**
|
||||
* gsk_stroke_new:
|
||||
* @line_width: line width of the stroke. Must be > 0
|
||||
*
|
||||
* Creates a new #GskStroke with the given @line_width.
|
||||
*
|
||||
* Returns: a new #GskStroke
|
||||
**/
|
||||
GskStroke *
|
||||
gsk_stroke_new (float line_width)
|
||||
{
|
||||
GskStroke *self;
|
||||
|
||||
g_return_val_if_fail (line_width > 0, NULL);
|
||||
|
||||
self = g_new0 (GskStroke, 1);
|
||||
|
||||
self->line_width = line_width;
|
||||
self->line_cap = GSK_LINE_CAP_BUTT;
|
||||
self->line_join = GSK_LINE_JOIN_MITER;
|
||||
self->miter_limit = 4.f; /* following svg */
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_stroke_copy:
|
||||
* @other: #GskStroke to copy
|
||||
*
|
||||
* Creates a copy of the given @other stroke.
|
||||
*
|
||||
* Returns: a new #GskStroke. Use gsk_stroke_free() to free it.
|
||||
**/
|
||||
GskStroke *
|
||||
gsk_stroke_copy (const GskStroke *other)
|
||||
{
|
||||
GskStroke *self;
|
||||
|
||||
g_return_val_if_fail (other != NULL, NULL);
|
||||
|
||||
self = g_new (GskStroke, 1);
|
||||
|
||||
gsk_stroke_init_copy (self, other);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_stroke_free:
|
||||
* @self: a #GskStroke
|
||||
*
|
||||
* Frees a #GskStroke.
|
||||
**/
|
||||
void
|
||||
gsk_stroke_free (GskStroke *self)
|
||||
{
|
||||
if (self == NULL)
|
||||
return;
|
||||
|
||||
gsk_stroke_clear (self);
|
||||
|
||||
g_free (self);
|
||||
}
|
||||
|
||||
void
|
||||
gsk_stroke_to_cairo (const GskStroke *self,
|
||||
cairo_t *cr)
|
||||
{
|
||||
cairo_set_line_width (cr, self->line_width);
|
||||
|
||||
/* gcc can optimize that to a direct case. This catches later additions to the enum */
|
||||
switch (self->line_cap)
|
||||
{
|
||||
case GSK_LINE_CAP_BUTT:
|
||||
cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
|
||||
break;
|
||||
case GSK_LINE_CAP_ROUND:
|
||||
cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
|
||||
break;
|
||||
case GSK_LINE_CAP_SQUARE:
|
||||
cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
break;
|
||||
}
|
||||
|
||||
/* gcc can optimize that to a direct case. This catches later additions to the enum */
|
||||
switch (self->line_join)
|
||||
{
|
||||
case GSK_LINE_JOIN_MITER:
|
||||
case GSK_LINE_JOIN_MITER_CLIP:
|
||||
cairo_set_line_join (cr, CAIRO_LINE_JOIN_MITER);
|
||||
break;
|
||||
case GSK_LINE_JOIN_ROUND:
|
||||
cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
|
||||
break;
|
||||
case GSK_LINE_JOIN_BEVEL:
|
||||
cairo_set_line_join (cr, CAIRO_LINE_JOIN_BEVEL);
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
break;
|
||||
}
|
||||
|
||||
cairo_set_miter_limit (cr, self->miter_limit);
|
||||
|
||||
if (self->dash_length)
|
||||
{
|
||||
gsize i;
|
||||
double *dash = g_newa (double, self->n_dash);
|
||||
|
||||
for (i = 0; i < self->n_dash; i++)
|
||||
{
|
||||
dash[i] = self->dash[i];
|
||||
}
|
||||
cairo_set_dash (cr, dash, self->n_dash, self->dash_offset);
|
||||
}
|
||||
else
|
||||
cairo_set_dash (cr, NULL, 0, 0.0);
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_stroke_equal:
|
||||
* @stroke1: the first #GskStroke
|
||||
* @stroke2: the second #GskStroke
|
||||
*
|
||||
* Checks if 2 strokes are identical.
|
||||
*
|
||||
* Returns: %TRUE if the 2 strokes are equal, %FALSE otherwise
|
||||
**/
|
||||
gboolean
|
||||
gsk_stroke_equal (gconstpointer stroke1,
|
||||
gconstpointer stroke2)
|
||||
{
|
||||
const GskStroke *self1 = stroke1;
|
||||
const GskStroke *self2 = stroke2;
|
||||
|
||||
return self1->line_width == self2->line_width;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_stroke_set_line_width:
|
||||
* @self: a #GskStroke
|
||||
* @line_width: width of the line in pixels
|
||||
*
|
||||
* Sets the line width to be used when stroking. The line width
|
||||
* must be > 0.
|
||||
**/
|
||||
void
|
||||
gsk_stroke_set_line_width (GskStroke *self,
|
||||
float line_width)
|
||||
{
|
||||
g_return_if_fail (self != NULL);
|
||||
g_return_if_fail (line_width > 0);
|
||||
|
||||
self->line_width = line_width;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_stroke_get_line_width:
|
||||
* @self: a #GskStroke
|
||||
*
|
||||
* Gets the line width used.
|
||||
*
|
||||
* Returns: The line width
|
||||
**/
|
||||
float
|
||||
gsk_stroke_get_line_width (const GskStroke *self)
|
||||
{
|
||||
g_return_val_if_fail (self != NULL, 0.0);
|
||||
|
||||
return self->line_width;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_stroke_set_line_cap:
|
||||
* @self: a #GskStroke
|
||||
* @line_cap: the #GskLineCap
|
||||
*
|
||||
* Sets the line cap to be used when stroking.
|
||||
* See #GskLineCap for details.
|
||||
**/
|
||||
void
|
||||
gsk_stroke_set_line_cap (GskStroke *self,
|
||||
GskLineCap line_cap)
|
||||
{
|
||||
g_return_if_fail (self != NULL);
|
||||
|
||||
self->line_cap = line_cap;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_stroke_get_line_cap:
|
||||
* @self: a #GskStroke
|
||||
*
|
||||
* Gets the line cap used. See #GskLineCap for details.
|
||||
*
|
||||
* Returns: The line cap
|
||||
**/
|
||||
GskLineCap
|
||||
gsk_stroke_get_line_cap (const GskStroke *self)
|
||||
{
|
||||
g_return_val_if_fail (self != NULL, 0.0);
|
||||
|
||||
return self->line_cap;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_stroke_set_line_join:
|
||||
* @self: a #GskStroke
|
||||
* @line_join: The line join to use
|
||||
*
|
||||
* Sets the line join to be used when stroking.
|
||||
* See #GskLineJoin for details.
|
||||
**/
|
||||
void
|
||||
gsk_stroke_set_line_join (GskStroke *self,
|
||||
GskLineJoin line_join)
|
||||
{
|
||||
g_return_if_fail (self != NULL);
|
||||
|
||||
self->line_join = line_join;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_stroke_get_line_join:
|
||||
* @self: a #GskStroke
|
||||
*
|
||||
* Gets the line join used. See #GskLineJoin for details.
|
||||
*
|
||||
* Returns: The line join
|
||||
**/
|
||||
GskLineJoin
|
||||
gsk_stroke_get_line_join (const GskStroke *self)
|
||||
{
|
||||
g_return_val_if_fail (self != NULL, 0.0);
|
||||
|
||||
return self->line_join;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_stroke_set_miter_limit:
|
||||
* @self: a #GskStroke
|
||||
* @limit: the miter limit, must be non-negative
|
||||
*
|
||||
* Sets the limit for the distance from the corner where sharp
|
||||
* turns of joins get cut off. The miter limit is in units of
|
||||
* line width.
|
||||
*
|
||||
* For joins of type %GSK_LINE_JOIN_MITER that exceed the miter
|
||||
* limit, the join gets rendered as if it was of type
|
||||
* %GSK_LINE_JOIN_BEVEL. For joins of type %GSK_LINE_JOIN_MITER_CLIP,
|
||||
* the miter is clipped at a distance of half the miter limit.
|
||||
*/
|
||||
void
|
||||
gsk_stroke_set_miter_limit (GskStroke *self,
|
||||
float limit)
|
||||
{
|
||||
g_return_if_fail (self != NULL);
|
||||
g_return_if_fail (limit >= 0);
|
||||
|
||||
self->miter_limit = limit;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_stroke_get_miter_limit:
|
||||
* @self: a #GskStroke
|
||||
*
|
||||
* Returns the miter limit of a #GskStroke.
|
||||
*/
|
||||
float
|
||||
gsk_stroke_get_miter_limit (const GskStroke *self)
|
||||
{
|
||||
g_return_val_if_fail (self != NULL, 4.f);
|
||||
|
||||
return self->miter_limit;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_stroke_set_dash:
|
||||
* @self: a #GskStroke
|
||||
* @dash: (array length=n_dash) (transfer none) (nullable):
|
||||
* the array of dashes
|
||||
* @n_dash: number of elements in @dash
|
||||
*
|
||||
* Sets the dash pattern to use by this stroke. A dash pattern is specified by
|
||||
* an array of alternating non-negative values. Each value provides the length
|
||||
* of alternate "on" and "off" portions of the stroke.
|
||||
*
|
||||
* Each "on" segment will have caps applied as if the segment were a separate
|
||||
* contour. In particular, it is valid to use an "on" length of 0 with
|
||||
* @GSK_LINE_CAP_ROUND or @GSK_LINE_CAP_SQUARE to draw dots or squares along
|
||||
* a path.
|
||||
*
|
||||
* If @n_dash is 0, if all elements in @dash are 0, or if there are negative
|
||||
* values in @dash, then dashing is disabled.
|
||||
*
|
||||
* If @n_dash is 1, an alternating "on" and "off" pattern with the single
|
||||
* dash length provided is assumed.
|
||||
*
|
||||
* If @n_dash is uneven, the dash array will be used with the first element
|
||||
* in @dash defining an "on" or "off" in alternating passes through the array.
|
||||
*
|
||||
* You can specify a starting offset into the dash with
|
||||
* @gsk_stroke_set_dash_offset().
|
||||
**/
|
||||
void
|
||||
gsk_stroke_set_dash (GskStroke *self,
|
||||
const float *dash,
|
||||
gsize n_dash)
|
||||
{
|
||||
float dash_length;
|
||||
gsize i;
|
||||
|
||||
g_return_if_fail (self != NULL);
|
||||
g_return_if_fail (dash != NULL || n_dash == 0);
|
||||
|
||||
dash_length = 0;
|
||||
for (i = 0; i < n_dash; i++)
|
||||
{
|
||||
if (!(dash[i] >= 0)) /* should catch NaN */
|
||||
{
|
||||
g_critical ("invalid value in dash array at position %zu", i);
|
||||
return;
|
||||
}
|
||||
dash_length += dash[i];
|
||||
}
|
||||
|
||||
self->dash_length = dash_length;
|
||||
g_free (self->dash);
|
||||
self->dash = g_memdup (dash, sizeof (gfloat) * n_dash);
|
||||
self->n_dash = n_dash;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_stroke_get_dash:
|
||||
* @self: a #GskStroke
|
||||
* @n_dash: (out caller-allocates): number of elements
|
||||
* in the array returned
|
||||
*
|
||||
* Gets the dash array in use or %NULL if dashing is disabled.
|
||||
*
|
||||
* Returns: (array length=n_dash) (transfer none) (nullable):
|
||||
* The dash array or %NULL if the dash array is empty.
|
||||
**/
|
||||
const float *
|
||||
gsk_stroke_get_dash (const GskStroke *self,
|
||||
gsize *n_dash)
|
||||
{
|
||||
g_return_val_if_fail (self != NULL, NULL);
|
||||
g_return_val_if_fail (n_dash != NULL, NULL);
|
||||
|
||||
*n_dash = self->n_dash;
|
||||
|
||||
return self->dash;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_stroke_set_dash_offset:
|
||||
* @self: a #GskStroke
|
||||
* @offset: offset into the dash pattern
|
||||
*
|
||||
* Sets the offset into the dash pattern set via gsk_stroke_set_dash() where
|
||||
* dashing should begin.
|
||||
*
|
||||
* This is an offset into the length of the path, not an index into the array values of
|
||||
* the dash array.
|
||||
**/
|
||||
void
|
||||
gsk_stroke_set_dash_offset (GskStroke *self,
|
||||
float offset)
|
||||
{
|
||||
g_return_if_fail (self != NULL);
|
||||
|
||||
self->dash_offset = offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_stroke_get_dash_offset:
|
||||
* @self: a #GskStroke
|
||||
*
|
||||
* Returns the dash_offset of a #GskStroke.
|
||||
*/
|
||||
float
|
||||
gsk_stroke_get_dash_offset (const GskStroke *self)
|
||||
{
|
||||
g_return_val_if_fail (self != NULL, 4.f);
|
||||
|
||||
return self->dash_offset;
|
||||
}
|
||||
87
gsk/gskstroke.h
Normal file
87
gsk/gskstroke.h
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright © 2020 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 __GSK_STROKE_H__
|
||||
#define __GSK_STROKE_H__
|
||||
|
||||
#if !defined (__GSK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gsk/gsk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
|
||||
#include <gsk/gsktypes.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GSK_TYPE_STROKE (gsk_stroke_get_type ())
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GType gsk_stroke_get_type (void) G_GNUC_CONST;
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskStroke * gsk_stroke_new (float line_width);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskStroke * gsk_stroke_copy (const GskStroke *other);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_stroke_free (GskStroke *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gsk_stroke_equal (gconstpointer stroke1,
|
||||
gconstpointer stroke2);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_stroke_set_line_width (GskStroke *self,
|
||||
float line_width);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
float gsk_stroke_get_line_width (const GskStroke *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_stroke_set_line_cap (GskStroke *self,
|
||||
GskLineCap line_cap);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskLineCap gsk_stroke_get_line_cap (const GskStroke *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_stroke_set_line_join (GskStroke *self,
|
||||
GskLineJoin line_join);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskLineJoin gsk_stroke_get_line_join (const GskStroke *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_stroke_set_miter_limit (GskStroke *self,
|
||||
float limit);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
float gsk_stroke_get_miter_limit (const GskStroke *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_stroke_set_dash (GskStroke *self,
|
||||
const float *dash,
|
||||
gsize n_dash);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
const float * gsk_stroke_get_dash (const GskStroke *self,
|
||||
gsize *n_dash);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_stroke_set_dash_offset (GskStroke *self,
|
||||
float offset);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
float gsk_stroke_get_dash_offset (const GskStroke *self);
|
||||
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GSK_STROKE_H__ */
|
||||
63
gsk/gskstrokeprivate.h
Normal file
63
gsk/gskstrokeprivate.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright © 2020 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 __GSK_STROKE_PRIVATE_H__
|
||||
#define __GSK_STROKE_PRIVATE_H__
|
||||
|
||||
#include "gskstroke.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
struct _GskStroke
|
||||
{
|
||||
float line_width;
|
||||
GskLineCap line_cap;
|
||||
GskLineJoin line_join;
|
||||
float miter_limit;
|
||||
|
||||
float *dash;
|
||||
gsize n_dash;
|
||||
float dash_length; /* sum of all dashes in the array */
|
||||
float dash_offset;
|
||||
};
|
||||
|
||||
static inline void
|
||||
gsk_stroke_init_copy (GskStroke *stroke,
|
||||
const GskStroke *other)
|
||||
{
|
||||
*stroke = *other;
|
||||
|
||||
stroke->dash = g_memdup (other->dash, stroke->n_dash * sizeof (float));
|
||||
}
|
||||
|
||||
static inline void
|
||||
gsk_stroke_clear (GskStroke *stroke)
|
||||
{
|
||||
g_clear_pointer (&stroke->dash, g_free);
|
||||
stroke->n_dash = 0; /* better safe than sorry */
|
||||
}
|
||||
|
||||
void gsk_stroke_to_cairo (const GskStroke *self,
|
||||
cairo_t *cr);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GSK_STROKE_PRIVATE_H__ */
|
||||
|
||||
@@ -25,6 +25,10 @@
|
||||
#include <gdk/gdk.h>
|
||||
#include <gsk/gskenums.h>
|
||||
|
||||
typedef struct _GskPath GskPath;
|
||||
typedef struct _GskPathBuilder GskPathBuilder;
|
||||
typedef struct _GskPathMeasure GskPathMeasure;
|
||||
typedef struct _GskRenderer GskRenderer;
|
||||
typedef struct _GskStroke GskStroke;
|
||||
typedef struct _GskTransform GskTransform;
|
||||
|
||||
|
||||
@@ -26,11 +26,15 @@ gsk_public_sources = files([
|
||||
'gskdiff.c',
|
||||
'gskcairorenderer.c',
|
||||
'gskglshader.c',
|
||||
'gskpath.c',
|
||||
'gskpathbuilder.c',
|
||||
'gskpathmeasure.c',
|
||||
'gskrenderer.c',
|
||||
'gskrendernode.c',
|
||||
'gskrendernodeimpl.c',
|
||||
'gskrendernodeparser.c',
|
||||
'gskroundedrect.c',
|
||||
'gskstroke.c',
|
||||
'gsktransform.c',
|
||||
'gl/gskglrenderer.c',
|
||||
])
|
||||
@@ -40,6 +44,7 @@ gsk_private_sources = files([
|
||||
'gskdebug.c',
|
||||
'gskprivate.c',
|
||||
'gskprofiler.c',
|
||||
'gskspline.c',
|
||||
'gl/gskglattachmentstate.c',
|
||||
'gl/gskglbuffer.c',
|
||||
'gl/gskglcommandqueue.c',
|
||||
@@ -66,9 +71,13 @@ gsk_public_headers = files([
|
||||
'gskcairorenderer.h',
|
||||
'gskenums.h',
|
||||
'gskglshader.h',
|
||||
'gskpath.h',
|
||||
'gskpathbuilder.h',
|
||||
'gskpathmeasure.h',
|
||||
'gskrenderer.h',
|
||||
'gskrendernode.h',
|
||||
'gskroundedrect.h',
|
||||
'gskstroke.h',
|
||||
'gsktransform.h',
|
||||
'gsktypes.h',
|
||||
])
|
||||
@@ -103,6 +112,7 @@ gsk_private_vulkan_shaders = []
|
||||
# on constantly regenerated files.
|
||||
gsk_private_vulkan_compiled_shaders = []
|
||||
gsk_private_vulkan_compiled_shaders_deps = []
|
||||
gsk_private_vulkan_shader_headers = []
|
||||
|
||||
if have_vulkan
|
||||
gsk_private_sources += files([
|
||||
@@ -197,6 +207,7 @@ libgsk = static_library('gsk',
|
||||
gsk_private_sources,
|
||||
gsk_enums,
|
||||
gskresources,
|
||||
gsk_private_vulkan_shader_headers,
|
||||
],
|
||||
dependencies: gsk_deps,
|
||||
include_directories: [ confinc, ],
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
#include "gskvulkanblendmodepipelineprivate.h"
|
||||
|
||||
#include "vulkan/resources/blend-mode.vert.h"
|
||||
|
||||
struct _GskVulkanBlendModePipeline
|
||||
{
|
||||
GObject parent_instance;
|
||||
@@ -9,89 +11,12 @@ struct _GskVulkanBlendModePipeline
|
||||
|
||||
typedef struct _GskVulkanBlendModeInstance GskVulkanBlendModeInstance;
|
||||
|
||||
struct _GskVulkanBlendModeInstance
|
||||
{
|
||||
float rect[4];
|
||||
float top_rect[4];
|
||||
float bottom_rect[4];
|
||||
float top_tex_rect[4];
|
||||
float bottom_tex_rect[4];
|
||||
guint32 top_tex_id[2];
|
||||
guint32 bottom_tex_id[2];
|
||||
guint32 blend_mode;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GskVulkanBlendModePipeline, gsk_vulkan_blend_mode_pipeline, GSK_TYPE_VULKAN_PIPELINE)
|
||||
|
||||
static const VkPipelineVertexInputStateCreateInfo *
|
||||
gsk_vulkan_blend_mode_pipeline_get_input_state_create_info (GskVulkanPipeline *self)
|
||||
{
|
||||
static const VkVertexInputBindingDescription vertexBindingDescriptions[] = {
|
||||
{
|
||||
.binding = 0,
|
||||
.stride = sizeof (GskVulkanBlendModeInstance),
|
||||
.inputRate = VK_VERTEX_INPUT_RATE_INSTANCE
|
||||
}
|
||||
};
|
||||
static const VkVertexInputAttributeDescription vertexInputAttributeDescription[] = {
|
||||
{
|
||||
.location = 0,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
|
||||
.offset = 0,
|
||||
},
|
||||
{
|
||||
.location = 1,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
|
||||
.offset = G_STRUCT_OFFSET (GskVulkanBlendModeInstance, top_rect),
|
||||
},
|
||||
{
|
||||
.location = 2,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
|
||||
.offset = G_STRUCT_OFFSET (GskVulkanBlendModeInstance, bottom_rect),
|
||||
},
|
||||
{
|
||||
.location = 3,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
|
||||
.offset = G_STRUCT_OFFSET (GskVulkanBlendModeInstance, top_tex_rect),
|
||||
},
|
||||
{
|
||||
.location = 4,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
|
||||
.offset = G_STRUCT_OFFSET (GskVulkanBlendModeInstance, bottom_tex_rect),
|
||||
},
|
||||
{
|
||||
.location = 5,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32_UINT,
|
||||
.offset = G_STRUCT_OFFSET (GskVulkanBlendModeInstance, top_tex_id),
|
||||
},
|
||||
{
|
||||
.location = 6,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32_UINT,
|
||||
.offset = G_STRUCT_OFFSET (GskVulkanBlendModeInstance, bottom_tex_id),
|
||||
},
|
||||
{
|
||||
.location = 7,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32_UINT,
|
||||
.offset = G_STRUCT_OFFSET (GskVulkanBlendModeInstance, blend_mode),
|
||||
}
|
||||
};
|
||||
static const VkPipelineVertexInputStateCreateInfo info = {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
|
||||
.vertexBindingDescriptionCount = G_N_ELEMENTS (vertexBindingDescriptions),
|
||||
.pVertexBindingDescriptions = vertexBindingDescriptions,
|
||||
.vertexAttributeDescriptionCount = G_N_ELEMENTS (vertexInputAttributeDescription),
|
||||
.pVertexAttributeDescriptions = vertexInputAttributeDescription
|
||||
};
|
||||
|
||||
return &info;
|
||||
return &gsk_vulkan_blend_mode_info;
|
||||
}
|
||||
|
||||
static void
|
||||
|
||||
@@ -2,68 +2,19 @@
|
||||
|
||||
#include "gskvulkanblurpipelineprivate.h"
|
||||
|
||||
#include "vulkan/resources/blur.vert.h"
|
||||
|
||||
struct _GskVulkanBlurPipeline
|
||||
{
|
||||
GObject parent_instance;
|
||||
};
|
||||
|
||||
typedef struct _GskVulkanBlurInstance GskVulkanBlurInstance;
|
||||
|
||||
struct _GskVulkanBlurInstance
|
||||
{
|
||||
float rect[4];
|
||||
float tex_rect[4];
|
||||
float blur_radius;
|
||||
guint32 tex_id[2];
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GskVulkanBlurPipeline, gsk_vulkan_blur_pipeline, GSK_TYPE_VULKAN_PIPELINE)
|
||||
|
||||
static const VkPipelineVertexInputStateCreateInfo *
|
||||
gsk_vulkan_blur_pipeline_get_input_state_create_info (GskVulkanPipeline *self)
|
||||
{
|
||||
static const VkVertexInputBindingDescription vertexBindingDescriptions[] = {
|
||||
{
|
||||
.binding = 0,
|
||||
.stride = sizeof (GskVulkanBlurInstance),
|
||||
.inputRate = VK_VERTEX_INPUT_RATE_INSTANCE
|
||||
}
|
||||
};
|
||||
static const VkVertexInputAttributeDescription vertexInputAttributeDescription[] = {
|
||||
{
|
||||
.location = 0,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
|
||||
.offset = 0,
|
||||
},
|
||||
{
|
||||
.location = 1,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
|
||||
.offset = G_STRUCT_OFFSET (GskVulkanBlurInstance, tex_rect),
|
||||
},
|
||||
{
|
||||
.location = 2,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32_SFLOAT,
|
||||
.offset = G_STRUCT_OFFSET (GskVulkanBlurInstance, blur_radius),
|
||||
},
|
||||
{
|
||||
.location = 3,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32_UINT,
|
||||
.offset = G_STRUCT_OFFSET (GskVulkanBlurInstance, tex_id),
|
||||
}
|
||||
};
|
||||
static const VkPipelineVertexInputStateCreateInfo info = {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
|
||||
.vertexBindingDescriptionCount = G_N_ELEMENTS (vertexBindingDescriptions),
|
||||
.pVertexBindingDescriptions = vertexBindingDescriptions,
|
||||
.vertexAttributeDescriptionCount = G_N_ELEMENTS (vertexInputAttributeDescription),
|
||||
.pVertexAttributeDescriptions = vertexInputAttributeDescription
|
||||
};
|
||||
|
||||
return &info;
|
||||
return &gsk_vulkan_blur_info;
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -105,7 +56,7 @@ gsk_vulkan_blur_pipeline_collect_vertex_data (GskVulkanBlurPipeline *pipeline,
|
||||
const graphene_point_t *offset,
|
||||
const graphene_rect_t *rect,
|
||||
const graphene_rect_t *tex_rect,
|
||||
double blur_radius)
|
||||
double radius)
|
||||
{
|
||||
GskVulkanBlurInstance *instance = (GskVulkanBlurInstance *) data;
|
||||
|
||||
@@ -117,7 +68,7 @@ gsk_vulkan_blur_pipeline_collect_vertex_data (GskVulkanBlurPipeline *pipeline,
|
||||
instance->tex_rect[1] = tex_rect->origin.y;
|
||||
instance->tex_rect[2] = tex_rect->size.width;
|
||||
instance->tex_rect[3] = tex_rect->size.height;
|
||||
instance->blur_radius = blur_radius;
|
||||
instance->radius = radius;
|
||||
instance->tex_id[0] = tex_id[0];
|
||||
instance->tex_id[1] = tex_id[1];
|
||||
}
|
||||
|
||||
@@ -4,91 +4,19 @@
|
||||
|
||||
#include "gskroundedrectprivate.h"
|
||||
|
||||
#include "vulkan/resources/border.vert.h"
|
||||
|
||||
struct _GskVulkanBorderPipeline
|
||||
{
|
||||
GObject parent_instance;
|
||||
};
|
||||
|
||||
typedef struct _GskVulkanBorderInstance GskVulkanBorderInstance;
|
||||
|
||||
struct _GskVulkanBorderInstance
|
||||
{
|
||||
float rect[12];
|
||||
float widths[4];
|
||||
float colors[16];
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GskVulkanBorderPipeline, gsk_vulkan_border_pipeline, GSK_TYPE_VULKAN_PIPELINE)
|
||||
|
||||
static const VkPipelineVertexInputStateCreateInfo *
|
||||
gsk_vulkan_border_pipeline_get_input_state_create_info (GskVulkanPipeline *self)
|
||||
{
|
||||
static const VkVertexInputBindingDescription vertexBindingDescriptions[] = {
|
||||
{
|
||||
.binding = 0,
|
||||
.stride = sizeof (GskVulkanBorderInstance),
|
||||
.inputRate = VK_VERTEX_INPUT_RATE_INSTANCE
|
||||
}
|
||||
};
|
||||
static const VkVertexInputAttributeDescription vertexInputAttributeDescription[] = {
|
||||
{
|
||||
.location = 0,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
|
||||
.offset = G_STRUCT_OFFSET (GskVulkanBorderInstance, rect),
|
||||
},
|
||||
{
|
||||
.location = 1,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
|
||||
.offset = G_STRUCT_OFFSET (GskVulkanBorderInstance, rect) + 4 * sizeof (float),
|
||||
},
|
||||
{
|
||||
.location = 2,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
|
||||
.offset = G_STRUCT_OFFSET (GskVulkanBorderInstance, rect) + 8 * sizeof (float),
|
||||
},
|
||||
{
|
||||
.location = 3,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
|
||||
.offset = G_STRUCT_OFFSET (GskVulkanBorderInstance, widths),
|
||||
},
|
||||
{
|
||||
.location = 4,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
|
||||
.offset = G_STRUCT_OFFSET (GskVulkanBorderInstance, colors),
|
||||
},
|
||||
{
|
||||
.location = 5,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
|
||||
.offset = G_STRUCT_OFFSET (GskVulkanBorderInstance, colors) + 4 * sizeof (float),
|
||||
},
|
||||
{
|
||||
.location = 6,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
|
||||
.offset = G_STRUCT_OFFSET (GskVulkanBorderInstance, colors) + 8 * sizeof (float),
|
||||
},
|
||||
{
|
||||
.location = 7,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
|
||||
.offset = G_STRUCT_OFFSET (GskVulkanBorderInstance, colors) + 12 * sizeof (float),
|
||||
}
|
||||
};
|
||||
static const VkPipelineVertexInputStateCreateInfo info = {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
|
||||
.vertexBindingDescriptionCount = G_N_ELEMENTS (vertexBindingDescriptions),
|
||||
.pVertexBindingDescriptions = vertexBindingDescriptions,
|
||||
.vertexAttributeDescriptionCount = G_N_ELEMENTS (vertexInputAttributeDescription),
|
||||
.pVertexAttributeDescriptions = vertexInputAttributeDescription
|
||||
};
|
||||
|
||||
return &info;
|
||||
return &gsk_vulkan_border_info;
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -137,11 +65,11 @@ gsk_vulkan_border_pipeline_collect_vertex_data (GskVulkanBorderPipeline *pipelin
|
||||
gsk_rounded_rect_to_float (rect, offset, instance->rect);
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
instance->widths[i] = widths[i];
|
||||
instance->colors[4 * i + 0] = colors[i].red;
|
||||
instance->colors[4 * i + 1] = colors[i].green;
|
||||
instance->colors[4 * i + 2] = colors[i].blue;
|
||||
instance->colors[4 * i + 3] = colors[i].alpha;
|
||||
instance->border_widths[i] = widths[i];
|
||||
instance->border_colors[4 * i + 0] = colors[i].red;
|
||||
instance->border_colors[4 * i + 1] = colors[i].green;
|
||||
instance->border_colors[4 * i + 2] = colors[i].blue;
|
||||
instance->border_colors[4 * i + 3] = colors[i].alpha;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
#include "gskvulkanboxshadowpipelineprivate.h"
|
||||
|
||||
#include "vulkan/resources/inset-shadow.vert.h"
|
||||
|
||||
#include "gskroundedrectprivate.h"
|
||||
|
||||
struct _GskVulkanBoxShadowPipeline
|
||||
@@ -9,82 +11,12 @@ struct _GskVulkanBoxShadowPipeline
|
||||
GObject parent_instance;
|
||||
};
|
||||
|
||||
typedef struct _GskVulkanBoxShadowInstance GskVulkanBoxShadowInstance;
|
||||
|
||||
struct _GskVulkanBoxShadowInstance
|
||||
{
|
||||
float outline[12];
|
||||
float color[4];
|
||||
float offset[2];
|
||||
float spread;
|
||||
float blur_radius;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GskVulkanBoxShadowPipeline, gsk_vulkan_box_shadow_pipeline, GSK_TYPE_VULKAN_PIPELINE)
|
||||
|
||||
static const VkPipelineVertexInputStateCreateInfo *
|
||||
gsk_vulkan_box_shadow_pipeline_get_input_state_create_info (GskVulkanPipeline *self)
|
||||
{
|
||||
static const VkVertexInputBindingDescription vertexBindingDescriptions[] = {
|
||||
{
|
||||
.binding = 0,
|
||||
.stride = sizeof (GskVulkanBoxShadowInstance),
|
||||
.inputRate = VK_VERTEX_INPUT_RATE_INSTANCE
|
||||
}
|
||||
};
|
||||
static const VkVertexInputAttributeDescription vertexInputAttributeDescription[] = {
|
||||
{
|
||||
.location = 0,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
|
||||
.offset = G_STRUCT_OFFSET (GskVulkanBoxShadowInstance, outline),
|
||||
},
|
||||
{
|
||||
.location = 1,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
|
||||
.offset = G_STRUCT_OFFSET (GskVulkanBoxShadowInstance, outline) + 4 * sizeof (float),
|
||||
},
|
||||
{
|
||||
.location = 2,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
|
||||
.offset = G_STRUCT_OFFSET (GskVulkanBoxShadowInstance, outline) + 8 * sizeof (float),
|
||||
},
|
||||
{
|
||||
.location = 3,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
|
||||
.offset = G_STRUCT_OFFSET (GskVulkanBoxShadowInstance, color),
|
||||
},
|
||||
{
|
||||
.location = 4,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32_SFLOAT,
|
||||
.offset = G_STRUCT_OFFSET (GskVulkanBoxShadowInstance, offset),
|
||||
},
|
||||
{
|
||||
.location = 5,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32_SFLOAT,
|
||||
.offset = G_STRUCT_OFFSET (GskVulkanBoxShadowInstance, spread),
|
||||
},
|
||||
{
|
||||
.location = 6,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32_SFLOAT,
|
||||
.offset = G_STRUCT_OFFSET (GskVulkanBoxShadowInstance, blur_radius),
|
||||
}
|
||||
};
|
||||
static const VkPipelineVertexInputStateCreateInfo info = {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
|
||||
.vertexBindingDescriptionCount = G_N_ELEMENTS (vertexBindingDescriptions),
|
||||
.pVertexBindingDescriptions = vertexBindingDescriptions,
|
||||
.vertexAttributeDescriptionCount = G_N_ELEMENTS (vertexInputAttributeDescription),
|
||||
.pVertexAttributeDescriptions = vertexInputAttributeDescription
|
||||
};
|
||||
|
||||
return &info;
|
||||
return &gsk_vulkan_inset_shadow_info;
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -130,7 +62,7 @@ gsk_vulkan_box_shadow_pipeline_collect_vertex_data (GskVulkanBoxShadowPipeline *
|
||||
float spread,
|
||||
float blur_radius)
|
||||
{
|
||||
GskVulkanBoxShadowInstance *instance = (GskVulkanBoxShadowInstance *) data;
|
||||
GskVulkanInsetShadowInstance *instance = (GskVulkanInsetShadowInstance *) data;
|
||||
|
||||
gsk_rounded_rect_to_float (outline, offset, instance->outline);
|
||||
instance->color[0] = color->red;
|
||||
|
||||
@@ -78,8 +78,8 @@ gsk_vulkan_buffer_new_map (GdkVulkanContext *context,
|
||||
{
|
||||
return gsk_vulkan_buffer_new_internal (context,
|
||||
size,
|
||||
(mode & GSK_VULKAN_READ ? VK_BUFFER_USAGE_TRANSFER_SRC_BIT : 0) |
|
||||
(mode & GSK_VULKAN_WRITE ? VK_BUFFER_USAGE_TRANSFER_DST_BIT : 0));
|
||||
(mode & GSK_VULKAN_READ ? VK_BUFFER_USAGE_TRANSFER_DST_BIT : 0) |
|
||||
(mode & GSK_VULKAN_WRITE ? VK_BUFFER_USAGE_TRANSFER_SRC_BIT : 0));
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -2,54 +2,19 @@
|
||||
|
||||
#include "gskvulkancolorpipelineprivate.h"
|
||||
|
||||
#include "vulkan/resources/color.vert.h"
|
||||
|
||||
struct _GskVulkanColorPipeline
|
||||
{
|
||||
GObject parent_instance;
|
||||
};
|
||||
|
||||
typedef struct _GskVulkanColorInstance GskVulkanColorInstance;
|
||||
|
||||
struct _GskVulkanColorInstance
|
||||
{
|
||||
float rect[4];
|
||||
float color[4];
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GskVulkanColorPipeline, gsk_vulkan_color_pipeline, GSK_TYPE_VULKAN_PIPELINE)
|
||||
|
||||
static const VkPipelineVertexInputStateCreateInfo *
|
||||
gsk_vulkan_color_pipeline_get_input_state_create_info (GskVulkanPipeline *self)
|
||||
{
|
||||
static const VkVertexInputBindingDescription vertexBindingDescriptions[] = {
|
||||
{
|
||||
.binding = 0,
|
||||
.stride = sizeof (GskVulkanColorInstance),
|
||||
.inputRate = VK_VERTEX_INPUT_RATE_INSTANCE
|
||||
}
|
||||
};
|
||||
static const VkVertexInputAttributeDescription vertexInputAttributeDescription[] = {
|
||||
{
|
||||
.location = 0,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
|
||||
.offset = 0,
|
||||
},
|
||||
{
|
||||
.location = 1,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
|
||||
.offset = G_STRUCT_OFFSET (GskVulkanColorInstance, color),
|
||||
}
|
||||
};
|
||||
static const VkPipelineVertexInputStateCreateInfo info = {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
|
||||
.vertexBindingDescriptionCount = G_N_ELEMENTS (vertexBindingDescriptions),
|
||||
.pVertexBindingDescriptions = vertexBindingDescriptions,
|
||||
.vertexAttributeDescriptionCount = G_N_ELEMENTS (vertexInputAttributeDescription),
|
||||
.pVertexAttributeDescriptions = vertexInputAttributeDescription
|
||||
};
|
||||
|
||||
return &info;
|
||||
return &gsk_vulkan_color_info;
|
||||
}
|
||||
|
||||
static void
|
||||
|
||||
@@ -2,61 +2,19 @@
|
||||
|
||||
#include "gskvulkancolortextpipelineprivate.h"
|
||||
|
||||
#include "vulkan/resources/texture.vert.h"
|
||||
|
||||
struct _GskVulkanColorTextPipeline
|
||||
{
|
||||
GObject parent_instance;
|
||||
};
|
||||
|
||||
typedef struct _GskVulkanColorTextInstance GskVulkanColorTextInstance;
|
||||
|
||||
struct _GskVulkanColorTextInstance
|
||||
{
|
||||
float rect[4];
|
||||
float tex_rect[4];
|
||||
guint32 tex_id[2];
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GskVulkanColorTextPipeline, gsk_vulkan_color_text_pipeline, GSK_TYPE_VULKAN_PIPELINE)
|
||||
|
||||
static const VkPipelineVertexInputStateCreateInfo *
|
||||
gsk_vulkan_color_text_pipeline_get_input_state_create_info (GskVulkanPipeline *self)
|
||||
{
|
||||
static const VkVertexInputBindingDescription vertexBindingDescriptions[] = {
|
||||
{
|
||||
.binding = 0,
|
||||
.stride = sizeof (GskVulkanColorTextInstance),
|
||||
.inputRate = VK_VERTEX_INPUT_RATE_INSTANCE
|
||||
}
|
||||
};
|
||||
static const VkVertexInputAttributeDescription vertexInputAttributeDescription[] = {
|
||||
{
|
||||
.location = 0,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
|
||||
.offset = G_STRUCT_OFFSET (GskVulkanColorTextInstance, rect),
|
||||
},
|
||||
{
|
||||
.location = 1,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
|
||||
.offset = G_STRUCT_OFFSET (GskVulkanColorTextInstance, tex_rect),
|
||||
},
|
||||
{
|
||||
.location = 2,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32_UINT,
|
||||
.offset = G_STRUCT_OFFSET (GskVulkanColorTextInstance, tex_id),
|
||||
}
|
||||
};
|
||||
static const VkPipelineVertexInputStateCreateInfo info = {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
|
||||
.vertexBindingDescriptionCount = G_N_ELEMENTS (vertexBindingDescriptions),
|
||||
.pVertexBindingDescriptions = vertexBindingDescriptions,
|
||||
.vertexAttributeDescriptionCount = G_N_ELEMENTS (vertexInputAttributeDescription),
|
||||
.pVertexAttributeDescriptions = vertexInputAttributeDescription
|
||||
};
|
||||
|
||||
return &info;
|
||||
return &gsk_vulkan_texture_info;
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -105,7 +63,7 @@ gsk_vulkan_color_text_pipeline_collect_vertex_data (GskVulkanColorTextPipeline *
|
||||
guint num_glyphs,
|
||||
float scale)
|
||||
{
|
||||
GskVulkanColorTextInstance *instances = (GskVulkanColorTextInstance *) data;
|
||||
GskVulkanTextureInstance *instances = (GskVulkanTextureInstance *) data;
|
||||
int i;
|
||||
int count = 0;
|
||||
int x_position = 0;
|
||||
@@ -121,7 +79,7 @@ gsk_vulkan_color_text_pipeline_collect_vertex_data (GskVulkanColorTextPipeline *
|
||||
{
|
||||
double cx = (x_position + gi->geometry.x_offset) / PANGO_SCALE;
|
||||
double cy = gi->geometry.y_offset / PANGO_SCALE;
|
||||
GskVulkanColorTextInstance *instance = &instances[count];
|
||||
GskVulkanTextureInstance *instance = &instances[count];
|
||||
GskVulkanCachedGlyph *glyph;
|
||||
|
||||
glyph = gsk_vulkan_renderer_get_cached_glyph (renderer,
|
||||
|
||||
@@ -2,96 +2,19 @@
|
||||
|
||||
#include "gskvulkancrossfadepipelineprivate.h"
|
||||
|
||||
#include "vulkan/resources/cross-fade.vert.h"
|
||||
|
||||
struct _GskVulkanCrossFadePipeline
|
||||
{
|
||||
GObject parent_instance;
|
||||
};
|
||||
|
||||
typedef struct _GskVulkanCrossFadeInstance GskVulkanCrossFadeInstance;
|
||||
|
||||
struct _GskVulkanCrossFadeInstance
|
||||
{
|
||||
float rect[4];
|
||||
float start_rect[4];
|
||||
float end_rect[4];
|
||||
float start_tex_rect[4];
|
||||
float end_tex_rect[4];
|
||||
guint32 start_tex_id[2];
|
||||
guint32 end_tex_id[2];
|
||||
float progress;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GskVulkanCrossFadePipeline, gsk_vulkan_cross_fade_pipeline, GSK_TYPE_VULKAN_PIPELINE)
|
||||
|
||||
static const VkPipelineVertexInputStateCreateInfo *
|
||||
gsk_vulkan_cross_fade_pipeline_get_input_state_create_info (GskVulkanPipeline *self)
|
||||
{
|
||||
static const VkVertexInputBindingDescription vertexBindingDescriptions[] = {
|
||||
{
|
||||
.binding = 0,
|
||||
.stride = sizeof (GskVulkanCrossFadeInstance),
|
||||
.inputRate = VK_VERTEX_INPUT_RATE_INSTANCE
|
||||
}
|
||||
};
|
||||
static const VkVertexInputAttributeDescription vertexInputAttributeDescription[] = {
|
||||
{
|
||||
.location = 0,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
|
||||
.offset = 0,
|
||||
},
|
||||
{
|
||||
.location = 1,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
|
||||
.offset = G_STRUCT_OFFSET (GskVulkanCrossFadeInstance, start_rect),
|
||||
},
|
||||
{
|
||||
.location = 2,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
|
||||
.offset = G_STRUCT_OFFSET (GskVulkanCrossFadeInstance, end_rect),
|
||||
},
|
||||
{
|
||||
.location = 3,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
|
||||
.offset = G_STRUCT_OFFSET (GskVulkanCrossFadeInstance, start_tex_rect),
|
||||
},
|
||||
{
|
||||
.location = 4,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
|
||||
.offset = G_STRUCT_OFFSET (GskVulkanCrossFadeInstance, end_tex_rect),
|
||||
},
|
||||
{
|
||||
.location = 5,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32_UINT,
|
||||
.offset = G_STRUCT_OFFSET (GskVulkanCrossFadeInstance, start_tex_id),
|
||||
},
|
||||
{
|
||||
.location = 6,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32_UINT,
|
||||
.offset = G_STRUCT_OFFSET (GskVulkanCrossFadeInstance, end_tex_id),
|
||||
},
|
||||
{
|
||||
.location = 7,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32_SFLOAT,
|
||||
.offset = G_STRUCT_OFFSET (GskVulkanCrossFadeInstance, progress),
|
||||
}
|
||||
};
|
||||
static const VkPipelineVertexInputStateCreateInfo info = {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
|
||||
.vertexBindingDescriptionCount = G_N_ELEMENTS (vertexBindingDescriptions),
|
||||
.pVertexBindingDescriptions = vertexBindingDescriptions,
|
||||
.vertexAttributeDescriptionCount = G_N_ELEMENTS (vertexInputAttributeDescription),
|
||||
.pVertexAttributeDescriptions = vertexInputAttributeDescription
|
||||
};
|
||||
|
||||
return &info;
|
||||
return &gsk_vulkan_cross_fade_info;
|
||||
}
|
||||
|
||||
static void
|
||||
|
||||
@@ -2,93 +2,19 @@
|
||||
|
||||
#include "gskvulkaneffectpipelineprivate.h"
|
||||
|
||||
#include "vulkan/resources/color-matrix.vert.h"
|
||||
|
||||
struct _GskVulkanEffectPipeline
|
||||
{
|
||||
GObject parent_instance;
|
||||
};
|
||||
|
||||
typedef struct _GskVulkanEffectInstance GskVulkanEffectInstance;
|
||||
|
||||
struct _GskVulkanEffectInstance
|
||||
{
|
||||
float rect[4];
|
||||
float tex_rect[4];
|
||||
float color_matrix[16];
|
||||
float color_offset[4];
|
||||
guint32 tex_id[2];
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GskVulkanEffectPipeline, gsk_vulkan_effect_pipeline, GSK_TYPE_VULKAN_PIPELINE)
|
||||
|
||||
static const VkPipelineVertexInputStateCreateInfo *
|
||||
gsk_vulkan_effect_pipeline_get_input_state_create_info (GskVulkanPipeline *self)
|
||||
{
|
||||
static const VkVertexInputBindingDescription vertexBindingDescriptions[] = {
|
||||
{
|
||||
.binding = 0,
|
||||
.stride = sizeof (GskVulkanEffectInstance),
|
||||
.inputRate = VK_VERTEX_INPUT_RATE_INSTANCE
|
||||
}
|
||||
};
|
||||
static const VkVertexInputAttributeDescription vertexInputAttributeDescription[] = {
|
||||
{
|
||||
.location = 0,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
|
||||
.offset = 0,
|
||||
},
|
||||
{
|
||||
.location = 1,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
|
||||
.offset = G_STRUCT_OFFSET (GskVulkanEffectInstance, tex_rect),
|
||||
},
|
||||
{
|
||||
.location = 2,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
|
||||
.offset = G_STRUCT_OFFSET (GskVulkanEffectInstance, color_matrix),
|
||||
},
|
||||
{
|
||||
.location = 3,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
|
||||
.offset = G_STRUCT_OFFSET (GskVulkanEffectInstance, color_matrix) + sizeof (float) * 4,
|
||||
},
|
||||
{
|
||||
.location = 4,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
|
||||
.offset = G_STRUCT_OFFSET (GskVulkanEffectInstance, color_matrix) + sizeof (float) * 8,
|
||||
},
|
||||
{
|
||||
.location = 5,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
|
||||
.offset = G_STRUCT_OFFSET (GskVulkanEffectInstance, color_matrix) + sizeof (float) * 12,
|
||||
},
|
||||
{
|
||||
.location = 6,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
|
||||
.offset = G_STRUCT_OFFSET (GskVulkanEffectInstance, color_offset),
|
||||
},
|
||||
{
|
||||
.location = 7,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32_UINT,
|
||||
.offset = G_STRUCT_OFFSET (GskVulkanEffectInstance, tex_id),
|
||||
}
|
||||
};
|
||||
static const VkPipelineVertexInputStateCreateInfo info = {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
|
||||
.vertexBindingDescriptionCount = G_N_ELEMENTS (vertexBindingDescriptions),
|
||||
.pVertexBindingDescriptions = vertexBindingDescriptions,
|
||||
.vertexAttributeDescriptionCount = G_N_ELEMENTS (vertexInputAttributeDescription),
|
||||
.pVertexAttributeDescriptions = vertexInputAttributeDescription
|
||||
};
|
||||
|
||||
return &info;
|
||||
return &gsk_vulkan_color_matrix_info;
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -133,7 +59,7 @@ gsk_vulkan_effect_pipeline_collect_vertex_data (GskVulkanEffectPipeline *pipelin
|
||||
const graphene_matrix_t *color_matrix,
|
||||
const graphene_vec4_t *color_offset)
|
||||
{
|
||||
GskVulkanEffectInstance *instance = (GskVulkanEffectInstance *) data;
|
||||
GskVulkanColorMatrixInstance *instance = (GskVulkanColorMatrixInstance *) data;
|
||||
|
||||
instance->rect[0] = rect->origin.x + offset->x;
|
||||
instance->rect[1] = rect->origin.y + offset->y;
|
||||
|
||||
@@ -488,24 +488,6 @@ gsk_memory_format_get_fallback (GdkMemoryFormat format)
|
||||
}
|
||||
}
|
||||
|
||||
GdkMemoryFormat
|
||||
gsk_render_node_get_preferred_vulkan_format (GskRenderNode *node)
|
||||
{
|
||||
switch (gsk_render_node_get_preferred_depth (node))
|
||||
{
|
||||
case GDK_MEMORY_U8:
|
||||
return GDK_MEMORY_R8G8B8A8_PREMULTIPLIED;
|
||||
case GDK_MEMORY_U16:
|
||||
return GDK_MEMORY_R16G16B16A16_PREMULTIPLIED;
|
||||
case GDK_MEMORY_FLOAT16:
|
||||
return GDK_MEMORY_R16G16B16A16_FLOAT_PREMULTIPLIED;
|
||||
case GDK_MEMORY_FLOAT32:
|
||||
return GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED;
|
||||
default:
|
||||
g_return_val_if_reached (GDK_MEMORY_R8G8B8A8_PREMULTIPLIED);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gsk_vulkan_context_supports_format (GdkVulkanContext *context,
|
||||
VkFormat format)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <gdk/gdk.h>
|
||||
#include <gsk/gskrendernode.h>
|
||||
|
||||
#include "gskvulkanbufferprivate.h"
|
||||
#include "gskvulkancommandpoolprivate.h"
|
||||
@@ -22,8 +21,6 @@ void gsk_vulkan_uploader_free (GskVulk
|
||||
void gsk_vulkan_uploader_reset (GskVulkanUploader *self);
|
||||
void gsk_vulkan_uploader_upload (GskVulkanUploader *self);
|
||||
|
||||
GdkMemoryFormat gsk_render_node_get_preferred_vulkan_format (GskRenderNode *node);
|
||||
|
||||
GskVulkanImage * gsk_vulkan_image_new_for_swapchain (GdkVulkanContext *context,
|
||||
VkImage image,
|
||||
VkFormat format,
|
||||
|
||||
@@ -2,82 +2,19 @@
|
||||
|
||||
#include "gskvulkanlineargradientpipelineprivate.h"
|
||||
|
||||
#include "vulkan/resources/linear.vert.h"
|
||||
|
||||
struct _GskVulkanLinearGradientPipeline
|
||||
{
|
||||
GObject parent_instance;
|
||||
};
|
||||
|
||||
typedef struct _GskVulkanLinearGradientInstance GskVulkanLinearGradientInstance;
|
||||
|
||||
struct _GskVulkanLinearGradientInstance
|
||||
{
|
||||
float rect[4];
|
||||
float start[2];
|
||||
float end[2];
|
||||
gint32 repeating;
|
||||
gint32 offset;
|
||||
gint32 stop_count;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GskVulkanLinearGradientPipeline, gsk_vulkan_linear_gradient_pipeline, GSK_TYPE_VULKAN_PIPELINE)
|
||||
|
||||
static const VkPipelineVertexInputStateCreateInfo *
|
||||
gsk_vulkan_linear_gradient_pipeline_get_input_state_create_info (GskVulkanPipeline *self)
|
||||
{
|
||||
static const VkVertexInputBindingDescription vertexBindingDescriptions[] = {
|
||||
{
|
||||
.binding = 0,
|
||||
.stride = sizeof (GskVulkanLinearGradientInstance),
|
||||
.inputRate = VK_VERTEX_INPUT_RATE_INSTANCE
|
||||
}
|
||||
};
|
||||
static const VkVertexInputAttributeDescription vertexInputAttributeDescription[] = {
|
||||
{
|
||||
.location = 0,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
|
||||
.offset = 0,
|
||||
},
|
||||
{
|
||||
.location = 1,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32_SFLOAT,
|
||||
.offset = G_STRUCT_OFFSET (GskVulkanLinearGradientInstance, start),
|
||||
},
|
||||
{
|
||||
.location = 2,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32_SFLOAT,
|
||||
.offset = G_STRUCT_OFFSET (GskVulkanLinearGradientInstance, end),
|
||||
},
|
||||
{
|
||||
.location = 3,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32_SINT,
|
||||
.offset = G_STRUCT_OFFSET (GskVulkanLinearGradientInstance, repeating),
|
||||
},
|
||||
{
|
||||
.location = 4,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32_SINT,
|
||||
.offset = G_STRUCT_OFFSET (GskVulkanLinearGradientInstance, offset),
|
||||
},
|
||||
{
|
||||
.location = 5,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32_SINT,
|
||||
.offset = G_STRUCT_OFFSET (GskVulkanLinearGradientInstance, stop_count),
|
||||
}
|
||||
};
|
||||
static const VkPipelineVertexInputStateCreateInfo info = {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
|
||||
.vertexBindingDescriptionCount = G_N_ELEMENTS (vertexBindingDescriptions),
|
||||
.pVertexBindingDescriptions = vertexBindingDescriptions,
|
||||
.vertexAttributeDescriptionCount = G_N_ELEMENTS (vertexInputAttributeDescription),
|
||||
.pVertexAttributeDescriptions = vertexInputAttributeDescription
|
||||
};
|
||||
|
||||
return &info;
|
||||
return &gsk_vulkan_linear_info;
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -123,7 +60,7 @@ gsk_vulkan_linear_gradient_pipeline_collect_vertex_data (GskVulkanLinearGradient
|
||||
gsize gradient_offset,
|
||||
gsize n_stops)
|
||||
{
|
||||
GskVulkanLinearGradientInstance *instance = (GskVulkanLinearGradientInstance *) data;
|
||||
GskVulkanLinearInstance *instance = (GskVulkanLinearInstance *) data;
|
||||
|
||||
instance->rect[0] = rect->origin.x + offset->x;
|
||||
instance->rect[1] = rect->origin.y + offset->y;
|
||||
@@ -134,7 +71,7 @@ gsk_vulkan_linear_gradient_pipeline_collect_vertex_data (GskVulkanLinearGradient
|
||||
instance->end[0] = end->x + offset->x;
|
||||
instance->end[1] = end->y + offset->y;
|
||||
instance->repeating = repeating;
|
||||
instance->offset = gradient_offset;
|
||||
instance->stop_offset = gradient_offset;
|
||||
instance->stop_count = n_stops;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#include "gskvulkanpushconstantsprivate.h"
|
||||
#include "gskvulkanshaderprivate.h"
|
||||
|
||||
#include "gdk/gdkvulkancontextprivate.h"
|
||||
|
||||
#include <graphene.h>
|
||||
|
||||
typedef struct _GskVulkanPipelinePrivate GskVulkanPipelinePrivate;
|
||||
@@ -87,7 +89,7 @@ gsk_vulkan_pipeline_new (GType pipeline_type,
|
||||
priv->vertex_stride = vertex_input_state->pVertexBindingDescriptions[0].stride;
|
||||
|
||||
GSK_VK_CHECK (vkCreateGraphicsPipelines, device,
|
||||
VK_NULL_HANDLE,
|
||||
gdk_vulkan_context_get_pipeline_cache (context),
|
||||
1,
|
||||
&(VkGraphicsPipelineCreateInfo) {
|
||||
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
|
||||
@@ -160,6 +162,8 @@ gsk_vulkan_pipeline_new (GType pipeline_type,
|
||||
NULL,
|
||||
&priv->pipeline);
|
||||
|
||||
gdk_vulkan_context_pipeline_cache_updated (context);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
@@ -41,6 +41,8 @@
|
||||
#define GDK_ARRAY_NO_MEMSET 1
|
||||
#include "gdk/gdkarrayimpl.c"
|
||||
|
||||
#define N_DESCRIPTOR_SETS 3
|
||||
|
||||
struct _GskVulkanRender
|
||||
{
|
||||
GskRenderer *renderer;
|
||||
@@ -52,7 +54,7 @@ struct _GskVulkanRender
|
||||
|
||||
GskVulkanCommandPool *command_pool;
|
||||
VkFence fence;
|
||||
VkDescriptorSetLayout descriptor_set_layout;
|
||||
VkDescriptorSetLayout descriptor_set_layouts[N_DESCRIPTOR_SETS];
|
||||
VkPipelineLayout pipeline_layout;
|
||||
GskVulkanUploader *uploader;
|
||||
|
||||
@@ -60,7 +62,7 @@ struct _GskVulkanRender
|
||||
GskDescriptorImageInfos descriptor_samplers;
|
||||
GskDescriptorBufferInfos descriptor_buffers;
|
||||
VkDescriptorPool descriptor_pool;
|
||||
VkDescriptorSet descriptor_set;
|
||||
VkDescriptorSet descriptor_sets[N_DESCRIPTOR_SETS];
|
||||
GskVulkanPipeline *pipelines[GSK_VULKAN_N_PIPELINES];
|
||||
|
||||
GskVulkanImage *target;
|
||||
@@ -148,9 +150,9 @@ gsk_vulkan_render_new (GskRenderer *renderer,
|
||||
&(VkDescriptorPoolCreateInfo) {
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
|
||||
.flags = VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT,
|
||||
.maxSets = 1,
|
||||
.poolSizeCount = 3,
|
||||
.pPoolSizes = (VkDescriptorPoolSize[3]) {
|
||||
.maxSets = N_DESCRIPTOR_SETS,
|
||||
.poolSizeCount = N_DESCRIPTOR_SETS,
|
||||
.pPoolSizes = (VkDescriptorPoolSize[N_DESCRIPTOR_SETS]) {
|
||||
{
|
||||
.type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,
|
||||
.descriptorCount = DESCRIPTOR_POOL_MAXITEMS
|
||||
@@ -171,7 +173,7 @@ gsk_vulkan_render_new (GskRenderer *renderer,
|
||||
GSK_VK_CHECK (vkCreateDescriptorSetLayout, device,
|
||||
&(VkDescriptorSetLayoutCreateInfo) {
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
|
||||
.bindingCount = 3,
|
||||
.bindingCount = 1,
|
||||
.flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT,
|
||||
.pBindings = (VkDescriptorSetLayoutBinding[3]) {
|
||||
{
|
||||
@@ -179,30 +181,12 @@ gsk_vulkan_render_new (GskRenderer *renderer,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,
|
||||
.descriptorCount = DESCRIPTOR_POOL_MAXITEMS,
|
||||
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT
|
||||
},
|
||||
{
|
||||
.binding = 1,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER,
|
||||
.descriptorCount = DESCRIPTOR_POOL_MAXITEMS,
|
||||
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT
|
||||
},
|
||||
{
|
||||
.binding = 2,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
||||
.descriptorCount = DESCRIPTOR_POOL_MAXITEMS,
|
||||
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT
|
||||
},
|
||||
}
|
||||
},
|
||||
.pNext = &(VkDescriptorSetLayoutBindingFlagsCreateInfo) {
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO,
|
||||
.bindingCount = 3,
|
||||
.pBindingFlags = (VkDescriptorBindingFlags[3]) {
|
||||
VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT
|
||||
| VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT
|
||||
| VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT,
|
||||
VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT
|
||||
| VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT
|
||||
| VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT,
|
||||
.bindingCount = 1,
|
||||
.pBindingFlags = (VkDescriptorBindingFlags[1]) {
|
||||
VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT
|
||||
| VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT
|
||||
| VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT,
|
||||
@@ -210,15 +194,65 @@ gsk_vulkan_render_new (GskRenderer *renderer,
|
||||
}
|
||||
},
|
||||
NULL,
|
||||
&self->descriptor_set_layout);
|
||||
&self->descriptor_set_layouts[0]);
|
||||
|
||||
GSK_VK_CHECK (vkCreateDescriptorSetLayout, device,
|
||||
&(VkDescriptorSetLayoutCreateInfo) {
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
|
||||
.bindingCount = 1,
|
||||
.flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT,
|
||||
.pBindings = (VkDescriptorSetLayoutBinding[1]) {
|
||||
{
|
||||
.binding = 0,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER,
|
||||
.descriptorCount = DESCRIPTOR_POOL_MAXITEMS,
|
||||
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT
|
||||
}
|
||||
},
|
||||
.pNext = &(VkDescriptorSetLayoutBindingFlagsCreateInfo) {
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO,
|
||||
.bindingCount = 1,
|
||||
.pBindingFlags = (VkDescriptorBindingFlags[1]) {
|
||||
VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT
|
||||
| VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT
|
||||
| VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT,
|
||||
},
|
||||
}
|
||||
},
|
||||
NULL,
|
||||
&self->descriptor_set_layouts[1]);
|
||||
|
||||
GSK_VK_CHECK (vkCreateDescriptorSetLayout, device,
|
||||
&(VkDescriptorSetLayoutCreateInfo) {
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
|
||||
.bindingCount = 1,
|
||||
.flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT,
|
||||
.pBindings = (VkDescriptorSetLayoutBinding[1]) {
|
||||
{
|
||||
.binding = 0,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
||||
.descriptorCount = DESCRIPTOR_POOL_MAXITEMS,
|
||||
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT
|
||||
},
|
||||
},
|
||||
.pNext = &(VkDescriptorSetLayoutBindingFlagsCreateInfo) {
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO,
|
||||
.bindingCount = 1,
|
||||
.pBindingFlags = (VkDescriptorBindingFlags[1]) {
|
||||
VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT
|
||||
| VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT
|
||||
| VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT,
|
||||
},
|
||||
}
|
||||
},
|
||||
NULL,
|
||||
&self->descriptor_set_layouts[2]);
|
||||
|
||||
GSK_VK_CHECK (vkCreatePipelineLayout, device,
|
||||
&(VkPipelineLayoutCreateInfo) {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
|
||||
.setLayoutCount = 1,
|
||||
.pSetLayouts = (VkDescriptorSetLayout[1]) {
|
||||
self->descriptor_set_layout
|
||||
},
|
||||
.setLayoutCount = G_N_ELEMENTS (self->descriptor_set_layouts),
|
||||
.pSetLayouts = self->descriptor_set_layouts,
|
||||
.pushConstantRangeCount = gsk_vulkan_push_constants_get_range_count (),
|
||||
.pPushConstantRanges = gsk_vulkan_push_constants_get_ranges ()
|
||||
},
|
||||
@@ -384,12 +418,12 @@ gsk_vulkan_render_get_pipeline (GskVulkanRender *self,
|
||||
{ "texture", 1, gsk_vulkan_color_text_pipeline_new },
|
||||
{ "texture-clip", 1, gsk_vulkan_color_text_pipeline_new },
|
||||
{ "texture-clip-rounded", 1, gsk_vulkan_color_text_pipeline_new },
|
||||
{ "crossfade", 2, gsk_vulkan_cross_fade_pipeline_new },
|
||||
{ "crossfade-clip", 2, gsk_vulkan_cross_fade_pipeline_new },
|
||||
{ "crossfade-clip-rounded", 2, gsk_vulkan_cross_fade_pipeline_new },
|
||||
{ "blendmode", 2, gsk_vulkan_blend_mode_pipeline_new },
|
||||
{ "blendmode-clip", 2, gsk_vulkan_blend_mode_pipeline_new },
|
||||
{ "blendmode-clip-rounded", 2, gsk_vulkan_blend_mode_pipeline_new },
|
||||
{ "cross-fade", 2, gsk_vulkan_cross_fade_pipeline_new },
|
||||
{ "cross-fade-clip", 2, gsk_vulkan_cross_fade_pipeline_new },
|
||||
{ "cross-fade-clip-rounded", 2, gsk_vulkan_cross_fade_pipeline_new },
|
||||
{ "blend-mode", 2, gsk_vulkan_blend_mode_pipeline_new },
|
||||
{ "blend-mode-clip", 2, gsk_vulkan_blend_mode_pipeline_new },
|
||||
{ "blend-mode-clip-rounded", 2, gsk_vulkan_blend_mode_pipeline_new },
|
||||
};
|
||||
|
||||
g_return_val_if_fail (type < GSK_VULKAN_N_PIPELINES, NULL);
|
||||
@@ -403,10 +437,18 @@ gsk_vulkan_render_get_pipeline (GskVulkanRender *self,
|
||||
return self->pipelines[type];
|
||||
}
|
||||
|
||||
VkDescriptorSet
|
||||
gsk_vulkan_render_get_descriptor_set (GskVulkanRender *self)
|
||||
void
|
||||
gsk_vulkan_render_bind_descriptor_sets (GskVulkanRender *self,
|
||||
VkCommandBuffer command_buffer)
|
||||
{
|
||||
return self->descriptor_set;
|
||||
vkCmdBindDescriptorSets (command_buffer,
|
||||
VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||
self->pipeline_layout,
|
||||
0,
|
||||
3,
|
||||
self->descriptor_sets,
|
||||
0,
|
||||
NULL);
|
||||
}
|
||||
|
||||
gsize
|
||||
@@ -537,11 +579,6 @@ gsk_vulkan_render_prepare_descriptor_sets (GskVulkanRender *self)
|
||||
gsk_vulkan_render_pass_reserve_descriptor_sets (pass, self);
|
||||
}
|
||||
|
||||
if (gsk_descriptor_image_infos_get_size (&self->descriptor_samplers) == 0 &&
|
||||
gsk_descriptor_image_infos_get_size (&self->descriptor_images) == 0 &&
|
||||
gsk_descriptor_buffer_infos_get_size (&self->descriptor_buffers) == 0)
|
||||
return;
|
||||
|
||||
if (self->storage_buffer_memory)
|
||||
{
|
||||
gsk_vulkan_buffer_unmap (self->storage_buffer);
|
||||
@@ -553,26 +590,26 @@ gsk_vulkan_render_prepare_descriptor_sets (GskVulkanRender *self)
|
||||
&(VkDescriptorSetAllocateInfo) {
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
|
||||
.descriptorPool = self->descriptor_pool,
|
||||
.descriptorSetCount = 1,
|
||||
.pSetLayouts = &self->descriptor_set_layout,
|
||||
.descriptorSetCount = N_DESCRIPTOR_SETS,
|
||||
.pSetLayouts = self->descriptor_set_layouts,
|
||||
.pNext = &(VkDescriptorSetVariableDescriptorCountAllocateInfo) {
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_ALLOCATE_INFO,
|
||||
.descriptorSetCount = 1,
|
||||
.pDescriptorCounts = (uint32_t[1]) {
|
||||
gsk_descriptor_image_infos_get_size (&self->descriptor_images)
|
||||
+ gsk_descriptor_image_infos_get_size (&self->descriptor_samplers)
|
||||
+ gsk_descriptor_buffer_infos_get_size (&self->descriptor_buffers)
|
||||
.descriptorSetCount = N_DESCRIPTOR_SETS,
|
||||
.pDescriptorCounts = (uint32_t[N_DESCRIPTOR_SETS]) {
|
||||
MAX (1, gsk_descriptor_image_infos_get_size (&self->descriptor_images)),
|
||||
MAX (1, gsk_descriptor_image_infos_get_size (&self->descriptor_samplers)),
|
||||
MAX (1, gsk_descriptor_buffer_infos_get_size (&self->descriptor_buffers))
|
||||
}
|
||||
}
|
||||
},
|
||||
&self->descriptor_set);
|
||||
self->descriptor_sets);
|
||||
|
||||
n_descriptor_sets = 0;
|
||||
if (gsk_descriptor_image_infos_get_size (&self->descriptor_images) > 0)
|
||||
{
|
||||
descriptor_sets[n_descriptor_sets++] = (VkWriteDescriptorSet) {
|
||||
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||
.dstSet = self->descriptor_set,
|
||||
.dstSet = self->descriptor_sets[0],
|
||||
.dstBinding = 0,
|
||||
.dstArrayElement = 0,
|
||||
.descriptorCount = gsk_descriptor_image_infos_get_size (&self->descriptor_images),
|
||||
@@ -584,8 +621,8 @@ gsk_vulkan_render_prepare_descriptor_sets (GskVulkanRender *self)
|
||||
{
|
||||
descriptor_sets[n_descriptor_sets++] = (VkWriteDescriptorSet) {
|
||||
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||
.dstSet = self->descriptor_set,
|
||||
.dstBinding = 1,
|
||||
.dstSet = self->descriptor_sets[1],
|
||||
.dstBinding = 0,
|
||||
.dstArrayElement = 0,
|
||||
.descriptorCount = gsk_descriptor_image_infos_get_size (&self->descriptor_samplers),
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER,
|
||||
@@ -596,8 +633,8 @@ gsk_vulkan_render_prepare_descriptor_sets (GskVulkanRender *self)
|
||||
{
|
||||
descriptor_sets[n_descriptor_sets++] = (VkWriteDescriptorSet) {
|
||||
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||
.dstSet = self->descriptor_set,
|
||||
.dstBinding = 2,
|
||||
.dstSet = self->descriptor_sets[2],
|
||||
.dstBinding = 0,
|
||||
.dstArrayElement = 0,
|
||||
.descriptorCount = gsk_descriptor_buffer_infos_get_size (&self->descriptor_buffers),
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
||||
@@ -741,9 +778,10 @@ gsk_vulkan_render_free (GskVulkanRender *self)
|
||||
gsk_descriptor_image_infos_clear (&self->descriptor_samplers);
|
||||
gsk_descriptor_buffer_infos_clear (&self->descriptor_buffers);
|
||||
|
||||
vkDestroyDescriptorSetLayout (device,
|
||||
self->descriptor_set_layout,
|
||||
NULL);
|
||||
for (i = 0; i < N_DESCRIPTOR_SETS; i++)
|
||||
vkDestroyDescriptorSetLayout (device,
|
||||
self->descriptor_set_layouts[i],
|
||||
NULL);
|
||||
|
||||
vkDestroyFence (device,
|
||||
self->fence,
|
||||
|
||||
@@ -14,8 +14,9 @@
|
||||
|
||||
#include "gdk/gdkdisplayprivate.h"
|
||||
#include "gdk/gdkdrawcontextprivate.h"
|
||||
#include "gdk/gdktextureprivate.h"
|
||||
#include "gdk/gdkprofilerprivate.h"
|
||||
#include "gdk/gdktextureprivate.h"
|
||||
#include "gdk/gdkvulkancontextprivate.h"
|
||||
|
||||
#include <graphene.h>
|
||||
|
||||
@@ -286,7 +287,8 @@ gsk_vulkan_renderer_render_texture (GskRenderer *renderer,
|
||||
ceil (viewport->size.width),
|
||||
ceil (viewport->size.height));
|
||||
image = gsk_vulkan_image_new_for_offscreen (self->vulkan,
|
||||
gsk_render_node_get_preferred_vulkan_format (root),
|
||||
gdk_vulkan_context_get_offscreen_format (self->vulkan,
|
||||
gsk_render_node_get_preferred_depth (root)),
|
||||
rounded_viewport.size.width,
|
||||
rounded_viewport.size.height);
|
||||
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
#include "gskvulkanrendererprivate.h"
|
||||
#include "gskprivate.h"
|
||||
|
||||
#include "gdk/gdkvulkancontextprivate.h"
|
||||
|
||||
#define ORTHO_NEAR_PLANE -10000
|
||||
#define ORTHO_FAR_PLANE 10000
|
||||
|
||||
@@ -1179,6 +1181,8 @@ static const GskVulkanRenderPassNodeFunc nodes_vtable[] = {
|
||||
[GSK_GL_SHADER_NODE] = NULL,
|
||||
[GSK_TEXTURE_SCALE_NODE] = gsk_vulkan_render_pass_add_texture_scale_node,
|
||||
[GSK_MASK_NODE] = NULL,
|
||||
[GSK_FILL_NODE] = NULL,
|
||||
[GSK_STROKE_NODE] = NULL,
|
||||
};
|
||||
|
||||
static void
|
||||
@@ -1275,7 +1279,8 @@ gsk_vulkan_render_pass_render_offscreen (GdkVulkanContext *vulkan,
|
||||
ceil (scale_y * viewport->size.height));
|
||||
|
||||
result = gsk_vulkan_image_new_for_offscreen (vulkan,
|
||||
gsk_render_node_get_preferred_vulkan_format (node),
|
||||
gdk_vulkan_context_get_offscreen_format (vulkan,
|
||||
gsk_render_node_get_preferred_depth (node)),
|
||||
view.size.width, view.size.height);
|
||||
|
||||
#ifdef G_ENABLE_DEBUG
|
||||
@@ -2436,7 +2441,6 @@ gsk_vulkan_render_pass_draw (GskVulkanRenderPass *self,
|
||||
VkPipelineLayout pipeline_layout,
|
||||
VkCommandBuffer command_buffer)
|
||||
{
|
||||
VkDescriptorSet descriptor_set;
|
||||
cairo_rectangle_int_t rect;
|
||||
|
||||
vkCmdSetViewport (command_buffer,
|
||||
@@ -2469,16 +2473,7 @@ gsk_vulkan_render_pass_draw (GskVulkanRenderPass *self,
|
||||
},
|
||||
VK_SUBPASS_CONTENTS_INLINE);
|
||||
|
||||
descriptor_set = gsk_vulkan_render_get_descriptor_set (render);
|
||||
if (descriptor_set)
|
||||
vkCmdBindDescriptorSets (command_buffer,
|
||||
VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||
pipeline_layout,
|
||||
0,
|
||||
1,
|
||||
&descriptor_set,
|
||||
0,
|
||||
NULL);
|
||||
gsk_vulkan_render_bind_descriptor_sets (render, command_buffer);
|
||||
|
||||
gsk_vulkan_render_pass_draw_rect (self, render, pipeline_layout, command_buffer);
|
||||
|
||||
|
||||
@@ -93,7 +93,8 @@ guchar * gsk_vulkan_render_get_buffer_memory (GskVulk
|
||||
gsize size,
|
||||
gsize alignment,
|
||||
gsize *out_offset);
|
||||
VkDescriptorSet gsk_vulkan_render_get_descriptor_set (GskVulkanRender *self);
|
||||
void gsk_vulkan_render_bind_descriptor_sets (GskVulkanRender *self,
|
||||
VkCommandBuffer command_buffer);
|
||||
|
||||
void gsk_vulkan_render_draw (GskVulkanRender *self);
|
||||
|
||||
|
||||
@@ -2,68 +2,19 @@
|
||||
|
||||
#include "gskvulkantextpipelineprivate.h"
|
||||
|
||||
#include "vulkan/resources/mask.vert.h"
|
||||
|
||||
struct _GskVulkanTextPipeline
|
||||
{
|
||||
GObject parent_instance;
|
||||
};
|
||||
|
||||
typedef struct _GskVulkanTextInstance GskVulkanTextInstance;
|
||||
|
||||
struct _GskVulkanTextInstance
|
||||
{
|
||||
float rect[4];
|
||||
float tex_rect[4];
|
||||
float color[4];
|
||||
guint32 tex_id[2];
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GskVulkanTextPipeline, gsk_vulkan_text_pipeline, GSK_TYPE_VULKAN_PIPELINE)
|
||||
|
||||
static const VkPipelineVertexInputStateCreateInfo *
|
||||
gsk_vulkan_text_pipeline_get_input_state_create_info (GskVulkanPipeline *self)
|
||||
{
|
||||
static const VkVertexInputBindingDescription vertexBindingDescriptions[] = {
|
||||
{
|
||||
.binding = 0,
|
||||
.stride = sizeof (GskVulkanTextInstance),
|
||||
.inputRate = VK_VERTEX_INPUT_RATE_INSTANCE
|
||||
}
|
||||
};
|
||||
static const VkVertexInputAttributeDescription vertexInputAttributeDescription[] = {
|
||||
{
|
||||
.location = 0,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
|
||||
.offset = G_STRUCT_OFFSET (GskVulkanTextInstance, rect),
|
||||
},
|
||||
{
|
||||
.location = 1,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
|
||||
.offset = G_STRUCT_OFFSET (GskVulkanTextInstance, tex_rect),
|
||||
},
|
||||
{
|
||||
.location = 2,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
|
||||
.offset = G_STRUCT_OFFSET (GskVulkanTextInstance, color),
|
||||
},
|
||||
{
|
||||
.location = 3,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32_UINT,
|
||||
.offset = G_STRUCT_OFFSET (GskVulkanTextInstance, tex_id),
|
||||
}
|
||||
};
|
||||
static const VkPipelineVertexInputStateCreateInfo info = {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
|
||||
.vertexBindingDescriptionCount = G_N_ELEMENTS (vertexBindingDescriptions),
|
||||
.pVertexBindingDescriptions = vertexBindingDescriptions,
|
||||
.vertexAttributeDescriptionCount = G_N_ELEMENTS (vertexInputAttributeDescription),
|
||||
.pVertexAttributeDescriptions = vertexInputAttributeDescription
|
||||
};
|
||||
|
||||
return &info;
|
||||
return &gsk_vulkan_mask_info;
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -113,7 +64,7 @@ gsk_vulkan_text_pipeline_collect_vertex_data (GskVulkanTextPipeline *pipeline,
|
||||
guint num_glyphs,
|
||||
float scale)
|
||||
{
|
||||
GskVulkanTextInstance *instances = (GskVulkanTextInstance *) data;
|
||||
GskVulkanMaskInstance *instances = (GskVulkanMaskInstance *) data;
|
||||
int i;
|
||||
int count = 0;
|
||||
int x_position = 0;
|
||||
@@ -129,7 +80,7 @@ gsk_vulkan_text_pipeline_collect_vertex_data (GskVulkanTextPipeline *pipeline,
|
||||
{
|
||||
double cx = (x_position + gi->geometry.x_offset) / PANGO_SCALE;
|
||||
double cy = gi->geometry.y_offset / PANGO_SCALE;
|
||||
GskVulkanTextInstance *instance = &instances[count];
|
||||
GskVulkanMaskInstance *instance = &instances[count];
|
||||
GskVulkanCachedGlyph *glyph;
|
||||
|
||||
glyph = gsk_vulkan_renderer_get_cached_glyph (renderer,
|
||||
|
||||
@@ -2,61 +2,19 @@
|
||||
|
||||
#include "gskvulkantexturepipelineprivate.h"
|
||||
|
||||
#include "vulkan/resources/texture.vert.h"
|
||||
|
||||
struct _GskVulkanTexturePipeline
|
||||
{
|
||||
GObject parent_instance;
|
||||
};
|
||||
|
||||
typedef struct _GskVulkanTextureInstance GskVulkanTextureInstance;
|
||||
|
||||
struct _GskVulkanTextureInstance
|
||||
{
|
||||
float rect[4];
|
||||
float tex_rect[4];
|
||||
guint32 tex_id[2];
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GskVulkanTexturePipeline, gsk_vulkan_texture_pipeline, GSK_TYPE_VULKAN_PIPELINE)
|
||||
|
||||
static const VkPipelineVertexInputStateCreateInfo *
|
||||
gsk_vulkan_texture_pipeline_get_input_state_create_info (GskVulkanPipeline *self)
|
||||
{
|
||||
static const VkVertexInputBindingDescription vertexBindingDescriptions[] = {
|
||||
{
|
||||
.binding = 0,
|
||||
.stride = sizeof (GskVulkanTextureInstance),
|
||||
.inputRate = VK_VERTEX_INPUT_RATE_INSTANCE
|
||||
}
|
||||
};
|
||||
static const VkVertexInputAttributeDescription vertexInputAttributeDescription[] = {
|
||||
{
|
||||
.location = 0,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
|
||||
.offset = G_STRUCT_OFFSET (GskVulkanTextureInstance, rect),
|
||||
},
|
||||
{
|
||||
.location = 1,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
|
||||
.offset = G_STRUCT_OFFSET (GskVulkanTextureInstance, tex_rect),
|
||||
},
|
||||
{
|
||||
.location = 2,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32_UINT,
|
||||
.offset = G_STRUCT_OFFSET (GskVulkanTextureInstance, tex_id),
|
||||
}
|
||||
};
|
||||
static const VkPipelineVertexInputStateCreateInfo info = {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
|
||||
.vertexBindingDescriptionCount = G_N_ELEMENTS (vertexBindingDescriptions),
|
||||
.pVertexBindingDescriptions = vertexBindingDescriptions,
|
||||
.vertexAttributeDescriptionCount = G_N_ELEMENTS (vertexInputAttributeDescription),
|
||||
.pVertexAttributeDescriptions = vertexInputAttributeDescription
|
||||
};
|
||||
|
||||
return &info;
|
||||
return &gsk_vulkan_texture_info;
|
||||
}
|
||||
|
||||
static void
|
||||
|
||||
BIN
gsk/vulkan/resources/blend-mode-clip-rounded.frag.spv
Normal file
BIN
gsk/vulkan/resources/blend-mode-clip-rounded.frag.spv
Normal file
Binary file not shown.
BIN
gsk/vulkan/resources/blend-mode-clip-rounded.vert.spv
Normal file
BIN
gsk/vulkan/resources/blend-mode-clip-rounded.vert.spv
Normal file
Binary file not shown.
BIN
gsk/vulkan/resources/blend-mode-clip.frag.spv
Normal file
BIN
gsk/vulkan/resources/blend-mode-clip.frag.spv
Normal file
Binary file not shown.
BIN
gsk/vulkan/resources/blend-mode-clip.vert.spv
Normal file
BIN
gsk/vulkan/resources/blend-mode-clip.vert.spv
Normal file
Binary file not shown.
BIN
gsk/vulkan/resources/blend-mode.frag.spv
Normal file
BIN
gsk/vulkan/resources/blend-mode.frag.spv
Normal file
Binary file not shown.
BIN
gsk/vulkan/resources/blend-mode.vert.spv
Normal file
BIN
gsk/vulkan/resources/blend-mode.vert.spv
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -3,9 +3,7 @@
|
||||
#include "clip.vert.glsl"
|
||||
#include "rounded-rect.glsl"
|
||||
|
||||
layout(location = 0) in vec4 inRect;
|
||||
layout(location = 1) in vec4 inCornerWidths;
|
||||
layout(location = 2) in vec4 inCornerHeights;
|
||||
layout(location = 0) in mat3x4 inRect;
|
||||
layout(location = 3) in vec4 inBorderWidths;
|
||||
layout(location = 4) in mat4 inBorderColors;
|
||||
|
||||
@@ -44,47 +42,47 @@ void main() {
|
||||
int slice_index = gl_VertexIndex / 6;
|
||||
int vert_index = gl_VertexIndex % 6;
|
||||
|
||||
vec4 corner_widths = max (inCornerWidths, inBorderWidths.wyyw);
|
||||
vec4 corner_heights = max (inCornerHeights, inBorderWidths.xxzz);
|
||||
vec4 corner_widths = max (inRect[1], inBorderWidths.wyyw);
|
||||
vec4 corner_heights = max (inRect[2], inBorderWidths.xxzz);
|
||||
|
||||
Rect rect;
|
||||
|
||||
switch (slice_index)
|
||||
{
|
||||
case SLICE_TOP_LEFT:
|
||||
rect = rect_from_gsk (vec4(inRect.xy, corner_widths[TOP_LEFT], corner_heights[TOP_LEFT]));
|
||||
rect = rect_from_gsk (vec4(inRect[0].xy, corner_widths[TOP_LEFT], corner_heights[TOP_LEFT]));
|
||||
rect = rect_round_larger (rect);
|
||||
vert_index = (vert_index + 3) % 6;
|
||||
break;
|
||||
case SLICE_TOP:
|
||||
rect = rect_from_gsk (vec4(inRect.x + corner_widths[TOP_LEFT], inRect.y, inRect.z - corner_widths[TOP_LEFT] - corner_widths[TOP_RIGHT], inBorderWidths[TOP]));
|
||||
rect = rect_from_gsk (vec4(inRect[0].x + corner_widths[TOP_LEFT], inRect[0].y, inRect[0].z - corner_widths[TOP_LEFT] - corner_widths[TOP_RIGHT], inBorderWidths[TOP]));
|
||||
rect = rect_round_smaller_larger (rect);
|
||||
outColor = inBorderColors[TOP];
|
||||
break;
|
||||
case SLICE_TOP_RIGHT:
|
||||
rect = rect_from_gsk (vec4(inRect.x + inRect.z - corner_widths[TOP_RIGHT], inRect.y, corner_widths[TOP_RIGHT], corner_heights[TOP_RIGHT]));
|
||||
rect = rect_from_gsk (vec4(inRect[0].x + inRect[0].z - corner_widths[TOP_RIGHT], inRect[0].y, corner_widths[TOP_RIGHT], corner_heights[TOP_RIGHT]));
|
||||
rect = rect_round_larger (rect);
|
||||
break;
|
||||
case SLICE_RIGHT:
|
||||
rect = rect_from_gsk (vec4(inRect.x + inRect.z - inBorderWidths[RIGHT], inRect.y + corner_heights[TOP_RIGHT], inBorderWidths[RIGHT], inRect.w - corner_heights[TOP_RIGHT] - corner_heights[BOTTOM_RIGHT]));
|
||||
rect = rect_from_gsk (vec4(inRect[0].x + inRect[0].z - inBorderWidths[RIGHT], inRect[0].y + corner_heights[TOP_RIGHT], inBorderWidths[RIGHT], inRect[0].w - corner_heights[TOP_RIGHT] - corner_heights[BOTTOM_RIGHT]));
|
||||
rect = rect_round_larger_smaller (rect);
|
||||
outColor = inBorderColors[RIGHT];
|
||||
break;
|
||||
case SLICE_BOTTOM_RIGHT:
|
||||
rect = rect_from_gsk (vec4(inRect.x + inRect.z - corner_widths[BOTTOM_RIGHT], inRect.y + inRect.w - corner_heights[BOTTOM_RIGHT], corner_widths[BOTTOM_RIGHT], corner_heights[BOTTOM_RIGHT]));
|
||||
rect = rect_from_gsk (vec4(inRect[0].x + inRect[0].z - corner_widths[BOTTOM_RIGHT], inRect[0].y + inRect[0].w - corner_heights[BOTTOM_RIGHT], corner_widths[BOTTOM_RIGHT], corner_heights[BOTTOM_RIGHT]));
|
||||
rect = rect_round_larger (rect);
|
||||
break;
|
||||
case SLICE_BOTTOM:
|
||||
rect = rect_from_gsk (vec4(inRect.x + corner_widths[BOTTOM_LEFT], inRect.y + inRect.w - inBorderWidths[BOTTOM], inRect.z - corner_widths[BOTTOM_LEFT] - corner_widths[BOTTOM_RIGHT], inBorderWidths[BOTTOM]));
|
||||
rect = rect_from_gsk (vec4(inRect[0].x + corner_widths[BOTTOM_LEFT], inRect[0].y + inRect[0].w - inBorderWidths[BOTTOM], inRect[0].z - corner_widths[BOTTOM_LEFT] - corner_widths[BOTTOM_RIGHT], inBorderWidths[BOTTOM]));
|
||||
rect = rect_round_smaller_larger (rect);
|
||||
break;
|
||||
case SLICE_BOTTOM_LEFT:
|
||||
rect = rect_from_gsk (vec4(inRect.x, inRect.y + inRect.w - corner_heights[BOTTOM_LEFT], corner_widths[BOTTOM_LEFT], corner_heights[BOTTOM_LEFT]));
|
||||
rect = rect_from_gsk (vec4(inRect[0].x, inRect[0].y + inRect[0].w - corner_heights[BOTTOM_LEFT], corner_widths[BOTTOM_LEFT], corner_heights[BOTTOM_LEFT]));
|
||||
vert_index = (vert_index + 3) % 6;
|
||||
rect = rect_round_larger (rect);
|
||||
break;
|
||||
case SLICE_LEFT:
|
||||
rect = rect_from_gsk (vec4(inRect.x, inRect.y + corner_heights[TOP_LEFT], inBorderWidths[LEFT], inRect.w - corner_heights[TOP_LEFT] - corner_heights[BOTTOM_LEFT]));
|
||||
rect = rect_from_gsk (vec4(inRect[0].x, inRect[0].y + corner_heights[TOP_LEFT], inBorderWidths[LEFT], inRect[0].w - corner_heights[TOP_LEFT] - corner_heights[BOTTOM_LEFT]));
|
||||
rect = rect_round_larger_smaller (rect);
|
||||
break;
|
||||
}
|
||||
@@ -99,7 +97,7 @@ void main() {
|
||||
gl_Position = push.mvp * vec4 (pos, 0.0, 1.0);
|
||||
outColor = inBorderColors[((gl_VertexIndex / 3 + 15) / 4) % 4];
|
||||
outPos = pos;
|
||||
outRect = RoundedRect(inRect.xyxy + vec4(0,0,inRect.zw), inCornerWidths, inCornerHeights);
|
||||
outRect = RoundedRect(inRect[0].xyxy + vec4(0,0,inRect[0].zw), inRect[1], inRect[2]);
|
||||
outRect = rounded_rect_scale (outRect, push.scale);
|
||||
outBorderWidths = inBorderWidths * push.scale.yxyx;
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user