Compare commits

..

35 Commits

Author SHA1 Message Date
Matthias Clasen 05e8057b5b Fix gtk_column_view_sort_by_column
We must clear the current sort columns before
setting the new one, to prevent double arrows
in the headers.

Fixes: #4696
2022-04-18 20:37:04 -04:00
Matthias Clasen e89365e9e3 gtk-builder-tool: Error out if screenshooting fails
If we don't produce a texture for whatever reason,
apologize and fail.
2022-04-18 11:16:05 -04:00
Timm Bäder 6484807719 Merge branch 'badcel/fix-typo' into 'main'
docs: Fix typo in description of GtkExpression

See merge request GNOME/gtk!4647
2022-04-18 09:27:05 +00:00
Marcel Tiede 527ff05107 docs: Fix typo in description of GtkExpression 2022-04-18 06:20:19 +00:00
Matthias Clasen b89cfdb77f Merge branch 'screenshot-command' into 'main'
gtk-builder-tool: Add a screenshot command

See merge request GNOME/gtk!4645
2022-04-17 17:40:46 +00:00
Matthias Clasen 499687a11d gtk-builder-tool: Add a screenshot command
This is an obvious variation of the preview
command. It can save a .ui file as either
.png or .node.
2022-04-17 12:57:46 -04:00
Matthias Clasen c5e26dd591 gtk-builder-tool: Reshuffle --help
Use GOptionContext better.
2022-04-17 12:57:46 -04:00
Matthias Clasen bc17d1d5ea Merge branch 'wip/exalm/scrolled-window' into 'main'
scrolledwindow: Pick up gtk-overlay-scrolling changes on the fly

See merge request GNOME/gtk!4642
2022-04-17 16:07:59 +00:00
Matthias Clasen b88ac0890e gtk-builder-tool: Small reshuffle
Move the display check into the preview command.
2022-04-17 11:23:00 -04:00
Matthias Clasen d30baf34b3 Merge branch 'filechooser-entry-popup' into 'main'
filechooser: Prevent random completion popups

See merge request GNOME/gtk!4643
2022-04-17 03:51:32 +00:00
Matthias Clasen b927ad2c87 Merge branch 'filechooser-save-sensitive' into 'main'
filechooser: Small fix for save mode

Closes #4851

See merge request GNOME/gtk!4644
2022-04-17 03:31:39 +00:00
Matthias Clasen 4540dac11a filechooser: Prevent random completion popups
It is very irritating when the entry completion popup
appears not in response to user input in the entry.
In particular, when that happens right as the dialog
is shown.

To prevent that, temporarily disable completion
when setting the entry text programmatically.
2022-04-16 23:29:04 -04:00
Matthias Clasen aa9cac695d filechooser: Small fix for save mode
When changing folders, we were making the select
button insensitive when there's no files around.
That doesn't make sense in save mode when we don't
want to select a file but create one.

Fixes: #4851
2022-04-16 22:00:22 -04:00
Alexander Mikhaylenko 52412ca944 scrolledwindow: Pick up gtk-overlay-scrolling changes on the fly 2022-04-16 16:56:17 +04:00
Alexander Mikhaylenko cd82b18cfd scrolledwindow: Fix :kinetic-scrolling setter 2022-04-16 16:30:33 +04:00
Matthias Clasen da5bb6ff22 Merge branch 'wip/exalm/tooltip' into 'main'
tooltipwindow: Don't restrict minimum tooltip label length

Closes #3741

See merge request GNOME/gtk!4640
2022-04-16 00:07:21 +00:00
Alexander Mikhaylenko 7871f1e13b tooltipwindow: Don't restrict minimum tooltip label length
We only care about wrapping at that length, we still want short tooltips
for short labels.

Fixes https://gitlab.gnome.org/GNOME/gtk/-/issues/3741
2022-04-16 03:48:21 +04:00
Alexander Mikhaylenko dd18c7dedf tooltipwindow: Remove a duplicate gtk_widget_class_set_css_name() call 2022-04-16 03:28:50 +04:00
Matthias Clasen a34a6e9b84 Merge branch 'ebassi/for-main' into 'main'
docs: Fix the GtkPaned style

See merge request GNOME/gtk!4637
2022-04-15 00:09:42 +00:00
Emmanuele Bassi 0a5f29c9eb Mark nullable arguments in GtkPaned
The set_start_child() and set_end_child() methods take NULL for the
child argument, as a way to remove the child from the paned widget.
2022-04-14 22:44:17 +01:00
Emmanuele Bassi 417be0719b docs: Fix the GtkPaned style
Use proper links to properties and methods, and drop gtk-doc'isms.
2022-04-14 22:43:40 +01:00
Matthias Clasen 3263a26cad Merge branch 'fix_tooltip' into 'main'
label: Maintain value for `has-tooltip` for labels with links

See merge request GNOME/gtk!4635
2022-04-14 18:00:43 +00:00
Matthias Clasen 00916c2b47 Merge branch 'fix_link_tabs' into 'main'
label: Move focus out of widget after last link

Closes #4681

See merge request GNOME/gtk!4636
2022-04-14 11:36:19 +00:00
Julian Sparber e3320633a6 label: Move focus out of widget after last link
This allows the user to navigate via tab the links in a label and exits
the widget after the last link, when moving forward, and first link,
when moving backward.

This also ensures that ellipsised links arn't focused.

Fixes: https://gitlab.gnome.org/GNOME/gtk/-/issues/4681
2022-04-14 12:48:20 +02:00
Julian Sparber 764a8cea59 label: Maintain value for has-tooltip for labels with links
The `has-tooltip` property gets set to `false` for label with links if no
link is selected. This makes sure to only change the property to `true`
but never to `false`.
2022-04-13 19:05:53 +02:00
Benjamin Otte a2599c2bb9 Merge branch 'clarify-scrollable-docs' into 'main'
scrollable: Clarify when to set adjustment props

See merge request GNOME/gtk!4632
2022-04-12 23:48:55 +00:00
Ivan Molodetskikh e2675306e1 scrollable: Clarify when to set adjustment props
Instead of populating the properties right away (when the widget might
not have been allocated yet, and hence cannot know the right values),
the widget should queue an allocation, where it will populate the
values.
2022-04-12 17:22:51 +03:00
Benjamin Otte 7d1429cb91 Merge branch 'gles-win32' into 'main'
Inspector: Fix running on Windows with GLES (was: fix running GL demos with GLES on Windows)

See merge request GNOME/gtk!4595
2022-04-08 22:00:33 +00:00
Matthias Clasen f13a018739 Merge branch 'matthiasc/for-main' into 'main'
gsk/gl: Typo fix

See merge request GNOME/gtk!4628
2022-04-08 16:14:43 +00:00
Matthias Clasen 84b3b1fab5 gsk/gl: Typo fix 2022-04-08 11:20:12 -04:00
Luca Bacci 048bb6db6f Merge branch 'gdk-win32-fix-mouse-move-crossing-events' into 'main'
GdkWin32: Mouse events fixes

Closes #4722 and #4813

See merge request GNOME/gtk!4620
2022-04-08 08:31:14 +00:00
Luca Bacci cb0c1b3f08 GdkWin32: Generate crossing event after button release
Fixes #4813
2022-04-06 20:27:46 +02:00
Luca Bacci 91f200167e GdkWin32: Correct generation of crossing events when holding an implicit grab
Fixes #4722
2022-04-06 20:27:32 +02:00
Luca Bacci 2b6fb005a8 GdkWin32: Report serial for events 2022-04-06 19:45:50 +02:00
Chun-wei Fan 89d87e3482 inspector/general.c: Check whether GL context supports WGL
We may well be using an EGL context that does not support Desktop (W)GL on
Windows, such as in the case of using libANGLE.  So, check whether WGL is
supported for this running instance before trying to query WGL extensions.

This will get rid of warning messages from libepoxy.
2022-03-30 16:09:18 +08:00
100 changed files with 798 additions and 28788 deletions
File diff suppressed because it is too large Load Diff
-36
View File
@@ -1,36 +0,0 @@
#pragma once
#include <gtk/gtk.h>
G_BEGIN_DECLS
#define CURVE_TYPE_EDITOR (curve_editor_get_type ())
G_DECLARE_FINAL_TYPE (CurveEditor, curve_editor, CURVE, EDITOR, GtkWidget)
GtkWidget * curve_editor_new (void);
void curve_editor_set_edit (CurveEditor *self,
gboolean edit);
void curve_editor_set_path (CurveEditor *self,
GskPath *path);
GskPath * curve_editor_get_path (CurveEditor *self);
void curve_editor_set_stroke (CurveEditor *self,
GskStroke *stroke);
const GskStroke * curve_editor_get_stroke (CurveEditor *self);
void curve_editor_set_color (CurveEditor *self,
GdkRGBA *color);
const GdkRGBA * curve_editor_get_color (CurveEditor *self);
gboolean curve_editor_get_show_outline (CurveEditor *self);
void curve_editor_set_show_outline (CurveEditor *self,
gboolean show_outline);
G_END_DECLS
-272
View File
@@ -1,272 +0,0 @@
/* Path/Curve Editor
*
* This demo shows an elaborate curve editor that you would expect to find
* in a vector graphics editor. It is built on top of GTK's path APIs.
*/
#include <gtk/gtk.h>
#include "curve-editor.h"
static GskPath *
make_circle_path (void)
{
float w = 310;
float h = 310;
float cx = w / 2;
float cy = h / 2;
float pad = 20;
float r = (w - 2 * pad) / 2;
float k = 0.55228;
float kr = k * r;
GskPathBuilder *builder;
builder = gsk_path_builder_new ();
gsk_path_builder_move_to (builder, cx, pad);
gsk_path_builder_curve_to (builder, cx + kr, pad,
w - pad, cy - kr,
w - pad, cy);
gsk_path_builder_curve_to (builder, w - pad, cy + kr,
cx + kr, h - pad,
cx, h - pad);
gsk_path_builder_curve_to (builder, cx - kr, h - pad,
pad, cy + kr,
pad, cy);
gsk_path_builder_curve_to (builder, pad, cy - kr,
cx - kr, pad,
cx, pad);
gsk_path_builder_close (builder);
return gsk_path_builder_free_to_path (builder);
}
static void
edit_changed (GtkToggleButton *button,
GParamSpec *pspec,
CurveEditor *editor)
{
curve_editor_set_edit (editor, gtk_toggle_button_get_active (button));
}
static void
reset (GtkButton *button,
CurveEditor *editor)
{
GskPath *path;
path = make_circle_path ();
curve_editor_set_path (editor, path);
gsk_path_unref (path);
}
static void
line_width_changed (GtkSpinButton *spin,
CurveEditor *editor)
{
GskStroke *stroke;
stroke = gsk_stroke_copy (curve_editor_get_stroke (editor));
gsk_stroke_set_line_width (stroke, gtk_spin_button_get_value (spin));
curve_editor_set_stroke (editor, stroke);
gsk_stroke_free (stroke);
}
static void
cap_changed (GtkDropDown *combo,
GParamSpec *pspec,
CurveEditor *editor)
{
GskStroke *stroke;
stroke = gsk_stroke_copy (curve_editor_get_stroke (editor));
gsk_stroke_set_line_cap (stroke, (GskLineCap)gtk_drop_down_get_selected (combo));
curve_editor_set_stroke (editor, stroke);
gsk_stroke_free (stroke);
}
static void
join_changed (GtkDropDown *combo,
GParamSpec *pspec,
CurveEditor *editor)
{
GskStroke *stroke;
stroke = gsk_stroke_copy (curve_editor_get_stroke (editor));
gsk_stroke_set_line_join (stroke, (GskLineJoin)gtk_drop_down_get_selected (combo));
curve_editor_set_stroke (editor, stroke);
gsk_stroke_free (stroke);
}
static void
color_changed (GtkColorChooser *chooser,
GParamSpec *pspec,
CurveEditor *editor)
{
GdkRGBA color;
gtk_color_chooser_get_rgba (chooser, &color);
curve_editor_set_color (editor, &color);
}
static void
stroke_toggled (GtkCheckButton *button,
CurveEditor *editor)
{
curve_editor_set_show_outline (editor, gtk_check_button_get_active (button));
gtk_widget_queue_draw (GTK_WIDGET (editor));
}
static void
limit_changed (GtkSpinButton *spin,
CurveEditor *editor)
{
GskStroke *stroke;
stroke = gsk_stroke_copy (curve_editor_get_stroke (editor));
gsk_stroke_set_miter_limit (stroke, gtk_spin_button_get_value (spin));
curve_editor_set_stroke (editor, stroke);
gsk_stroke_free (stroke);
}
static void
dashes_changed (GtkEntry *entry,
GParamSpec *spec,
CurveEditor *editor)
{
const char *text;
char **split;
GArray *dash;
GskStroke *stroke;
text = gtk_editable_get_text (GTK_EDITABLE (entry));
split = g_strsplit (text, " ", 0);
dash = g_array_new (FALSE, FALSE, sizeof (float));
for (int i = 0; split[i] != NULL; i++)
{
double d;
char *endp = 0;
d = g_strtod (split[i], &endp);
if (*endp == '\0')
g_array_append_vals (dash, (float[1]) { d }, 1);
}
g_strfreev (split);
stroke = gsk_stroke_copy (curve_editor_get_stroke (editor));
gsk_stroke_set_dash (stroke, (const float *)dash->data, dash->len);
curve_editor_set_stroke (editor, stroke);
gsk_stroke_free (stroke);
g_array_free (dash, TRUE);
}
GtkWidget *
do_curve (GtkWidget *do_widget)
{
static GtkWidget *window = NULL;
GtkWidget *demo;
GtkWidget *edit_toggle;
GtkWidget *reset_button;
GtkWidget *titlebar;
GtkWidget *stroke_toggle;
GtkWidget *line_width_spin;
GtkWidget *stroke_button;
GtkWidget *popover;
GtkWidget *grid;
GtkWidget *cap_combo;
GtkWidget *join_combo;
GtkWidget *color_button;
GtkWidget *limit_spin;
GtkWidget *dash_entry;
if (!window)
{
window = gtk_window_new ();
gtk_window_set_title (GTK_WINDOW (window), "Curve Editor");
g_object_add_weak_pointer (G_OBJECT (window), (gpointer *)&window);
gtk_window_set_default_size (GTK_WINDOW (window), 315, 350);
edit_toggle = gtk_toggle_button_new ();
gtk_button_set_icon_name (GTK_BUTTON (edit_toggle), "document-edit-symbolic");
reset_button = gtk_button_new_from_icon_name ("edit-undo-symbolic");
stroke_button = gtk_menu_button_new ();
gtk_menu_button_set_icon_name (GTK_MENU_BUTTON (stroke_button), "open-menu-symbolic");
popover = gtk_popover_new ();
gtk_menu_button_set_popover (GTK_MENU_BUTTON (stroke_button), popover);
grid = gtk_grid_new ();
gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
gtk_grid_set_column_spacing (GTK_GRID (grid), 6);
gtk_popover_set_child (GTK_POPOVER (popover), grid);
gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Color:"), 0, 0, 1, 1);
color_button = gtk_color_button_new_with_rgba (&(GdkRGBA){ 0., 0., 0., 1.});
gtk_grid_attach (GTK_GRID (grid), color_button, 1, 0, 1, 1);
gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Line width:"), 0, 1, 1, 1);
line_width_spin = gtk_spin_button_new_with_range (1, 20, 1);
gtk_spin_button_set_value (GTK_SPIN_BUTTON (line_width_spin), 1);
gtk_grid_attach (GTK_GRID (grid), line_width_spin, 1, 1, 1, 1);
gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Line cap:"), 0, 2, 1, 1);
cap_combo = gtk_drop_down_new_from_strings ((const char *[]){"Butt", "Round", "Square", NULL});
gtk_grid_attach (GTK_GRID (grid), cap_combo, 1, 2, 1, 1);
gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Line join:"), 0, 3, 1, 1);
join_combo = gtk_drop_down_new_from_strings ((const char *[]){"Miter", "Miter-clip", "Round", "Bevel", "Arcs", NULL});
gtk_grid_attach (GTK_GRID (grid), join_combo, 1, 3, 1, 1);
gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Miter limit:"), 0, 4, 1, 1);
limit_spin = gtk_spin_button_new_with_range (0, 10, 1);
gtk_spin_button_set_digits (GTK_SPIN_BUTTON (limit_spin), 1);
gtk_spin_button_set_value (GTK_SPIN_BUTTON (limit_spin), 4);
gtk_grid_attach (GTK_GRID (grid), limit_spin, 1, 4, 1, 1);
gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Dashes:"), 0, 5, 1, 1);
dash_entry = gtk_entry_new ();
gtk_grid_attach (GTK_GRID (grid), dash_entry, 1, 5, 1, 1);
stroke_toggle = gtk_check_button_new_with_label ("Show outline");
gtk_grid_attach (GTK_GRID (grid), stroke_toggle, 1, 6, 1, 1);
titlebar = gtk_header_bar_new ();
gtk_header_bar_pack_start (GTK_HEADER_BAR (titlebar), edit_toggle);
gtk_header_bar_pack_start (GTK_HEADER_BAR (titlebar), reset_button);
gtk_header_bar_pack_start (GTK_HEADER_BAR (titlebar), stroke_button);
gtk_window_set_titlebar (GTK_WINDOW (window), titlebar);
demo = curve_editor_new ();
g_signal_connect (stroke_toggle, "toggled", G_CALLBACK (stroke_toggled), demo);
g_signal_connect (edit_toggle, "notify::active", G_CALLBACK (edit_changed), demo);
g_signal_connect (reset_button, "clicked", G_CALLBACK (reset), demo);
g_signal_connect (cap_combo, "notify::selected", G_CALLBACK (cap_changed), demo);
g_signal_connect (join_combo, "notify::selected", G_CALLBACK (join_changed), demo);
g_signal_connect (color_button, "notify::rgba", G_CALLBACK (color_changed), demo);
g_signal_connect (line_width_spin, "value-changed", G_CALLBACK (line_width_changed), demo);
g_signal_connect (limit_spin, "value-changed", G_CALLBACK (limit_changed), demo);
g_signal_connect (dash_entry, "notify::text", G_CALLBACK (dashes_changed), demo);
reset (NULL, CURVE_EDITOR (demo));
gtk_spin_button_set_value (GTK_SPIN_BUTTON (line_width_spin), 6);
gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (color_button), &(GdkRGBA) { 1, 0, 0, 1 });
gtk_drop_down_set_selected (GTK_DROP_DOWN (cap_combo), GSK_LINE_CAP_ROUND);
gtk_editable_set_text (GTK_EDITABLE (dash_entry), "0 8");
gtk_window_set_child (GTK_WINDOW (window), demo);
}
if (!gtk_widget_get_visible (window))
gtk_widget_show (window);
else
gtk_window_destroy (GTK_WINDOW (window));
return window;
}
-13
View File
@@ -128,7 +128,6 @@
<file>fishbowl.ui</file>
<file>gtkfishbowl.c</file>
<file>gtkfishbowl.h</file>
<file>tiger.node</file>
</gresource>
<gresource prefix="/frames">
<file>frames.ui</file>
@@ -251,10 +250,6 @@
<gresource prefix="/video-player">
<file>bbb.png</file>
</gresource>
<gresource prefix="/curve">
<file>curve-editor.c</file>
<file>curve-editor.h</file>
</gresource>
<gresource prefix="/sources">
<file>application_demo.c</file>
<file>assistant.c</file>
@@ -272,7 +267,6 @@
<file>css_pixbufs.c</file>
<file>css_shadows.c</file>
<file>cursors.c</file>
<file>curve.c</file>
<file>dialog.c</file>
<file>drawingarea.c</file>
<file>dropdown.c</file>
@@ -293,7 +287,6 @@
<file>gears.c</file>
<file>gestures.c</file>
<file>glarea.c</file>
<file>glyphs.c</file>
<file>gltransition.c</file>
<file>headerbar.c</file>
<file>hypertext.c</file>
@@ -332,9 +325,6 @@
<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>
@@ -419,9 +409,6 @@
<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>
-17
View File
@@ -11,9 +11,6 @@
#include "gtkgears.h"
#include "gskshaderpaintable.h"
#include "nodewidget.h"
#include "graphwidget.h"
const char *const css =
".blurred-button {"
" box-shadow: 0px 0px 5px 10px rgba(0, 0, 0, 0.5);"
@@ -204,18 +201,6 @@ create_menu_button (void)
return w;
}
static GtkWidget *
create_tiger (void)
{
return node_widget_new ("/fishbowl/tiger.node");
}
static GtkWidget *
create_graph (void)
{
return graph_widget_new ();
}
static const struct {
const char *name;
GtkWidget * (*create_func) (void);
@@ -233,8 +218,6 @@ static const struct {
{ "Switch", create_switch },
{ "Menubutton", create_menu_button },
{ "Shader", create_cogs },
{ "Tiger", create_tiger },
{ "Graph", create_graph },
};
static int selected_widget_type = -1;
File diff suppressed because it is too large Load Diff
-164
View File
@@ -1,164 +0,0 @@
#include "graphwidget.h"
struct _GraphWidget
{
GtkWidget parent_instance;
GskPath *path;
GskStroke *stroke;
GdkRGBA color;
GskPath *stroke_path;
guint tick_cb;
guint64 start_time;
double period;
double amplitude;
};
struct _GraphWidgetClass
{
GtkWidgetClass parent_class;
};
G_DEFINE_TYPE (GraphWidget, graph_widget, GTK_TYPE_WIDGET)
static void
update_path (GraphWidget *self,
float amplitude)
{
graphene_point_t p[20];
GskPathBuilder *builder;
g_clear_pointer (&self->path, gsk_path_unref);
g_clear_pointer (&self->stroke_path, gsk_path_unref);
for (int i = 0; i < 20; i++)
{
p[i].x = 10 * i;
p[i].y = 50;
if (i % 4 == 1 || i % 4 == 2)
{
if (i % 8 < 4)
p[i].y += amplitude;
else
p[i].y -= amplitude;
}
}
builder = gsk_path_builder_new ();
gsk_path_builder_move_to (builder, p[0].x, p[0].y);
for (int i = 0; i < 20; i += 4)
gsk_path_builder_curve_to (builder,
p[i+1].x, p[i+1].y,
p[i+2].x, p[i+2].y,
p[i+3].x, p[i+3].y);
self->path = gsk_path_builder_free_to_path (builder);
self->stroke_path = gsk_path_stroke (self->path, self->stroke);
}
static gboolean
tick_cb (GtkWidget *widget,
GdkFrameClock *frame_clock,
gpointer user_data)
{
GraphWidget *self = GRAPH_WIDGET (widget);
guint64 now;
double angle;
now = gdk_frame_clock_get_frame_time (frame_clock);
if (self->start_time == 0)
self->start_time = now;
angle = 360 * (now - self->start_time) / (double)(self->period * G_TIME_SPAN_MINUTE);
update_path (self, sin (angle) * self->amplitude);
gtk_widget_queue_draw (widget);
return G_SOURCE_CONTINUE;
}
static void
graph_widget_init (GraphWidget *self)
{
self->color.red = g_random_double_range (0, 1);
self->color.green = g_random_double_range (0, 1);
self->color.blue = g_random_double_range (0, 1);
self->color.alpha = 1;
self->period = g_random_double_range (0.5, 1);
self->amplitude = g_random_double_range (10, 25);
self->stroke = gsk_stroke_new (2);
update_path (self, 0);
self->start_time = 0;
self->tick_cb = gtk_widget_add_tick_callback (GTK_WIDGET (self), tick_cb, NULL, NULL);
}
static void
graph_widget_dispose (GObject *object)
{
GraphWidget *self = GRAPH_WIDGET (object);
gsk_path_unref (self->path);
gsk_stroke_free (self->stroke);
G_OBJECT_CLASS (graph_widget_parent_class)->dispose (object);
}
static void
graph_widget_snapshot (GtkWidget *widget,
GtkSnapshot *snapshot)
{
GraphWidget *self = GRAPH_WIDGET (widget);
int width, height;
width = gtk_widget_get_width (widget);
height = gtk_widget_get_height (widget);
gtk_snapshot_push_fill (snapshot, self->stroke_path, GSK_FILL_RULE_WINDING);
gtk_snapshot_append_color (snapshot,
&self->color,
&GRAPHENE_RECT_INIT (0, 0, width, height));
gtk_snapshot_pop (snapshot);
}
static void
graph_widget_measure (GtkWidget *widget,
GtkOrientation orientation,
int for_size,
int *minimum,
int *natural,
int *minimum_baseline,
int *natural_baseline)
{
if (orientation == GTK_ORIENTATION_HORIZONTAL)
*minimum = *natural = 200;
else
*minimum = *natural = 100;
}
static void
graph_widget_class_init (GraphWidgetClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
object_class->dispose = graph_widget_dispose;
widget_class->snapshot = graph_widget_snapshot;
widget_class->measure = graph_widget_measure;
}
GtkWidget *
graph_widget_new (void)
{
return g_object_new (GRAPH_TYPE_WIDGET, NULL);
}
-8
View File
@@ -1,8 +0,0 @@
#pragma once
#include <gtk/gtk.h>
#define GRAPH_TYPE_WIDGET (graph_widget_get_type ())
G_DECLARE_FINAL_TYPE (GraphWidget, graph_widget, GRAPH, WIDGET, GtkWidget)
GtkWidget * graph_widget_new (void);
-8
View File
@@ -17,7 +17,6 @@ demos = files([
'css_pixbufs.c',
'css_shadows.c',
'cursors.c',
'curve.c',
'dialog.c',
'drawingarea.c',
'dnd.c',
@@ -35,7 +34,6 @@ demos = files([
'gestures.c',
'glarea.c',
'gltransition.c',
'glyphs.c',
'headerbar.c',
'hypertext.c',
'iconscroll.c',
@@ -72,9 +70,6 @@ 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',
@@ -135,9 +130,6 @@ extra_demo_sources = files([
'unicode-names.c',
'suggestionentry.c',
'language-names.c',
'curve-editor.c',
'nodewidget.c',
'graphwidget.c',
])
if os_unix
-76
View File
@@ -1,76 +0,0 @@
#include "nodewidget.h"
struct _NodeWidget
{
GtkWidget parent_instance;
GskRenderNode *node;
};
struct _NodeWidgetClass
{
GtkWidgetClass parent_class;
};
G_DEFINE_TYPE (NodeWidget, node_widget, GTK_TYPE_WIDGET)
static void
node_widget_init (NodeWidget *self)
{
}
static void
node_widget_dispose (GObject *object)
{
NodeWidget *self = NODE_WIDGET (object);
gsk_render_node_unref (self->node);
G_OBJECT_CLASS (node_widget_parent_class)->dispose (object);
}
static void
node_widget_snapshot (GtkWidget *widget,
GtkSnapshot *snapshot)
{
NodeWidget *self = NODE_WIDGET (widget);
gtk_snapshot_append_node (snapshot, self->node);
}
static void
node_widget_class_init (NodeWidgetClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
object_class->dispose = node_widget_dispose;
widget_class->snapshot = node_widget_snapshot;
}
GtkWidget *
node_widget_new (const char *resource)
{
NodeWidget *self;
GBytes *bytes;
GskRenderNode *node;
graphene_rect_t bounds;
float scale;
GskTransform *transform;
self = g_object_new (NODE_TYPE_WIDGET, NULL);
bytes = g_resources_lookup_data (resource, 0, NULL);
node = gsk_render_node_deserialize (bytes, NULL, NULL);
g_bytes_unref (bytes);
gsk_render_node_get_bounds (node, &bounds);
scale = MIN (100.0/bounds.size.width, 100.0/bounds.size.height);
transform = gsk_transform_scale (NULL, scale, scale);
self->node = gsk_transform_node_new (node, transform);
gsk_transform_unref (transform);
gsk_render_node_unref (node);
return GTK_WIDGET (self);
}
-8
View File
@@ -1,8 +0,0 @@
#pragma once
#include <gtk/gtk.h>
#define NODE_TYPE_WIDGET (node_widget_get_type ())
G_DECLARE_FINAL_TYPE (NodeWidget, node_widget, NODE, WIDGET, GtkWidget)
GtkWidget * node_widget_new (const char *file);
-348
View File
@@ -1,348 +0,0 @@
/* Path/Text 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"
#include "gsk/gskpathdashprivate.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 (2.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 don't need to do anything special here, because we keep no
* data of our own.
*/
static void
gtk_path_paintable_class_init (GtkPathPaintableClass *klass)
{
}
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_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)
{
PangoLayout *layout;
PangoFontDescription *desc;
GskPathBuilder *builder;
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);
builder = gsk_path_builder_new ();
gsk_path_builder_add_layout (builder, layout);
return gsk_path_builder_free_to_path (builder);
}
static gboolean
build_path (GskPathOperation op,
const graphene_point_t *pts,
gsize n_pts,
float weight,
gpointer user_data)
{
GskPathBuilder *builder = user_data;
switch (op)
{
case GSK_PATH_MOVE:
gsk_path_builder_move_to (builder, pts[0].x, pts[0].y);
break;
case GSK_PATH_CLOSE:
gsk_path_builder_close (builder);
break;
case GSK_PATH_LINE:
gsk_path_builder_line_to (builder, pts[1].x, pts[1].y);
break;
case GSK_PATH_CURVE:
gsk_path_builder_curve_to (builder, pts[1].x, pts[1].y, pts[2].x, pts[2].y, pts[3].x, pts[3].y);
break;
case GSK_PATH_CONIC:
gsk_path_builder_conic_to (builder, pts[1].x, pts[1].y, pts[2].x, pts[2].y, weight);
break;
default:
g_assert_not_reached ();
break;
}
return TRUE;
}
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;
GskStroke *stroke;
builder = gsk_path_builder_new ();
gsk_path_builder_add_segment (builder,
measure,
#if 1
0.0, gsk_path_measure_get_length (measure));
#else
progress > 1 ? (progress - 1) * gsk_path_measure_get_length (measure) : 0.0,
(progress < 1 ? progress : 1.0) * gsk_path_measure_get_length (measure));
#endif
path = gsk_path_builder_free_to_path (builder);
stroke = gsk_stroke_new (1);
gsk_stroke_set_dash (stroke, (float[2]) { 10, 5 }, 2);
gsk_stroke_set_dash_offset (stroke, - (gdk_frame_clock_get_frame_time (frame_clock) % G_USEC_PER_SEC) * 15. / G_USEC_PER_SEC);
builder = gsk_path_builder_new ();
gsk_path_dash (path, stroke, 0.2, build_path, builder);
gsk_path_unref (path);
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_keep_aspect_ratio (GTK_PICTURE (picture), FALSE);
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_widget_show (window);
else
gtk_window_destroy (GTK_WINDOW (window));
return window;
}
-339
View File
@@ -1,339 +0,0 @@
/* Path/Maze
* #Keywords: game, mouse
*
* 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] = { };
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_widget_show (window);
else
gtk_window_destroy (GTK_WINDOW (window));
return window;
}
-594
View File
@@ -1,594 +0,0 @@
/* Path/Curved 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 (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,
float weight,
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_CONIC:
{
graphene_point_t res[2];
gtk_path_transform_point (transform->measure, &pts[1], transform->scale, &res[0]);
gtk_path_transform_point (transform->measure, &pts[2], transform->scale, &res[1]);
gsk_path_builder_conic_to (transform->builder, res[0].x, res[0].y, res[1].x, res[1].y, weight);
}
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_CURVE, 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.5 }, &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)
{
/* 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);
}
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);
}
if (self->editable && self->line_path)
{
GskPathBuilder *builder;
/* 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);
}
}
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,
NULL, NULL,
&self->line_closest,
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_widget_show (window);
else
gtk_window_destroy (GTK_WINDOW (window));
return window;
}
-38
View File
@@ -1,38 +0,0 @@
<?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>
File diff suppressed because it is too large Load Diff
+29 -1
View File
@@ -16,6 +16,7 @@ SYNOPSIS
| **gtk4-builder-tool** enumerate <FILE>
| **gtk4-builder-tool** simplify [OPTIONS...] <FILE>
| **gtk4-builder-tool** preview [OPTIONS...] <FILE>
| **gtk4-builder-tool** screenshot [OPTIONS...] <FILE>
DESCRIPTION
-----------
@@ -41,7 +42,7 @@ definition file.
Preview
^^^^^^^
The ``preview`` command displays the UI dfinition file.
The ``preview`` command displays the UI definition file.
This command accepts options to specify the ID of the toplevel object and a CSS
file to use.
@@ -55,6 +56,33 @@ file to use.
Load style information from the given CSS file.
Screenshot
^^^^^^^^^^
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.
This command accepts options to specify the ID of the toplevel object and a CSS
file to use.
``--id=ID``
The ID of the object to preview. If not specified, gtk4-builder-tool will
choose a suitable object on its own.
``--css=FILE``
Load style information from the given CSS file.
``--node``
Write a serialized node file instead of a png image.
``--force``
Overwrite an existing file.
Simplification
^^^^^^^^^^^^^^
+55 -68
View File
@@ -873,6 +873,7 @@ _gdk_win32_append_event (GdkEvent *event)
{
GdkDisplay *display;
GList *link;
gulong serial;
display = gdk_display_get_default ();
@@ -880,8 +881,9 @@ _gdk_win32_append_event (GdkEvent *event)
#if 1
link = _gdk_event_queue_append (display, event);
GDK_NOTE (EVENTS, _gdk_win32_print_event (event));
serial = _gdk_display_get_next_serial (display);
/* event morphing, the passed in may not be valid afterwards */
_gdk_windowing_got_event (display, link, event, 0);
_gdk_windowing_got_event (display, link, event, serial);
#else
_gdk_event_queue_append (display, event);
GDK_NOTE (EVENTS, _gdk_win32_print_event (event));
@@ -2242,6 +2244,10 @@ gdk_event_translate (MSG *msg,
button = 5;
buttonup0:
{
gboolean release_implicit_grab = FALSE;
GdkSurface *prev_surface = NULL;
GDK_NOTE (EVENTS,
g_print (" (%d,%d)",
GET_X_LPARAM (msg->lParam), GET_Y_LPARAM (msg->lParam)));
@@ -2251,41 +2257,18 @@ gdk_event_translate (MSG *msg,
g_set_object (&window, find_window_for_mouse_event (window, msg));
if (pointer_grab != NULL && pointer_grab->implicit)
{
int state = build_pointer_event_state (msg);
{
int state = build_pointer_event_state (msg);
/* We keep the implicit grab until no buttons at all are held down */
if ((state & GDK_ANY_BUTTON_MASK & ~(GDK_BUTTON1_MASK << (button - 1))) == 0)
{
ReleaseCapture ();
/* We keep the implicit grab until no buttons at all are held down */
if ((state & GDK_ANY_BUTTON_MASK & ~(GDK_BUTTON1_MASK << (button - 1))) == 0)
{
release_implicit_grab = TRUE;
prev_surface = pointer_grab->surface;
}
}
new_window = NULL;
hwnd = WindowFromPoint (msg->pt);
if (hwnd != NULL)
{
POINT client_pt = msg->pt;
ScreenToClient (hwnd, &client_pt);
GetClientRect (hwnd, &rect);
if (PtInRect (&rect, client_pt))
new_window = gdk_win32_handle_table_lookup (hwnd);
}
synthesize_crossing_events (display,
_gdk_device_manager->system_pointer,
pointer_grab->surface, new_window,
GDK_CROSSING_UNGRAB,
&msg->pt,
0, /* TODO: Set right mask */
_gdk_win32_get_next_tick (msg->time),
FALSE);
g_set_object (&mouse_window, new_window);
mouse_window_ignored_leave = NULL;
}
}
generate_button_event (GDK_BUTTON_RELEASE, button,
window, msg);
generate_button_event (GDK_BUTTON_RELEASE, button, window, msg);
impl = GDK_WIN32_SURFACE (window);
@@ -2294,8 +2277,37 @@ gdk_event_translate (MSG *msg,
impl->drag_move_resize_context.button == button)
gdk_win32_surface_end_move_resize_drag (window);
if (release_implicit_grab)
{
ReleaseCapture ();
new_window = NULL;
hwnd = WindowFromPoint (msg->pt);
if (hwnd != NULL)
{
POINT client_pt = msg->pt;
ScreenToClient (hwnd, &client_pt);
GetClientRect (hwnd, &rect);
if (PtInRect (&rect, client_pt))
new_window = gdk_win32_handle_table_lookup (hwnd);
}
synthesize_crossing_events (display,
_gdk_device_manager->system_pointer,
prev_surface, new_window,
GDK_CROSSING_UNGRAB,
&msg->pt,
0, /* TODO: Set right mask */
_gdk_win32_get_next_tick (msg->time),
FALSE);
g_set_object (&mouse_window, new_window);
mouse_window_ignored_leave = NULL;
}
return_val = TRUE;
break;
}
case WM_MOUSEMOVE:
GDK_NOTE (EVENTS,
@@ -2320,60 +2332,35 @@ gdk_event_translate (MSG *msg,
pen_touch_input = FALSE;
new_window = window;
g_set_object (&window, find_window_for_mouse_event (window, msg));
if (pointer_grab != NULL)
{
POINT pt;
pt = msg->pt;
new_window = NULL;
hwnd = WindowFromPoint (pt);
if (hwnd != NULL)
{
POINT client_pt = pt;
ScreenToClient (hwnd, &client_pt);
GetClientRect (hwnd, &rect);
if (PtInRect (&rect, client_pt))
new_window = gdk_win32_handle_table_lookup (hwnd);
}
if (!pointer_grab->owner_events &&
new_window != NULL &&
new_window != pointer_grab->surface)
new_window = NULL;
}
if (mouse_window != new_window)
if (mouse_window != window)
{
GDK_NOTE (EVENTS, g_print (" mouse_window %p -> %p",
mouse_window ? GDK_SURFACE_HWND (mouse_window) : NULL,
new_window ? GDK_SURFACE_HWND (new_window) : NULL));
window ? GDK_SURFACE_HWND (window) : NULL));
synthesize_crossing_events (display,
_gdk_device_manager->system_pointer,
mouse_window, new_window,
mouse_window, window,
GDK_CROSSING_NORMAL,
&msg->pt,
0, /* TODO: Set right mask */
_gdk_win32_get_next_tick (msg->time),
FALSE);
g_set_object (&mouse_window, new_window);
g_set_object (&mouse_window, window);
mouse_window_ignored_leave = NULL;
if (new_window != NULL)
track_mouse_event (TME_LEAVE, GDK_SURFACE_HWND (new_window));
if (window != NULL)
track_mouse_event (TME_LEAVE, GDK_SURFACE_HWND (window));
}
else if (new_window != NULL &&
new_window == mouse_window_ignored_leave)
else if (window != NULL && window == mouse_window_ignored_leave)
{
/* If we ignored a leave event for this window and we're now getting
input again we need to re-arm the mouse tracking, as that was
cancelled by the mouseleave. */
mouse_window_ignored_leave = NULL;
track_mouse_event (TME_LEAVE, GDK_SURFACE_HWND (new_window));
track_mouse_event (TME_LEAVE, GDK_SURFACE_HWND (window));
}
g_set_object (&window, find_window_for_mouse_event (window, msg));
impl = GDK_WIN32_SURFACE (window);
/* If we haven't moved, don't create any GDK event. Windows
-4
View File
@@ -269,8 +269,6 @@ collect_reused_child_nodes (GskRenderer *renderer,
case GSK_BLEND_NODE:
case GSK_CROSS_FADE_NODE:
case GSK_BLUR_NODE:
case GSK_FILL_NODE:
case GSK_STROKE_NODE:
default:
@@ -857,8 +855,6 @@ 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 */
}
+1 -1
View File
@@ -796,7 +796,7 @@ gsk_gl_driver_load_texture (GskGLDriver *self,
* to upload data, map to a framebuffer, or other uses which may
* modify the texture immediately.
*
* Typical examples for @format are GK_RGBA8, GL_RGBA16F or GL_RGBA32F.
* Typical examples for @format are GL_RGBA8, GL_RGBA16F or GL_RGBA32F.
*
* Use gsk_gl_driver_release_texture() to release this texture back into
* the pool so it may be reused later in the pipeline.
-5
View File
@@ -3770,11 +3770,6 @@ gsk_gl_render_job_visit_node (GskGLRenderJob *job,
gsk_gl_render_job_visit_as_fallback (job, node);
break;
case GSK_FILL_NODE:
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 ();
-2
View File
@@ -22,8 +22,6 @@
#ifndef __GI_SCANNER__
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GskPath, gsk_path_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GskPathMeasure, gsk_path_measure_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GskRenderer, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GskRenderNode, gsk_render_node_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GskTransform, gsk_transform_unref)
+1 -5
View File
@@ -21,15 +21,11 @@
#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>
-80
View File
@@ -1,80 +0,0 @@
#include "config.h"
#include "gskboundingboxprivate.h"
GskBoundingBox *
gsk_bounding_box_init (GskBoundingBox *self,
const graphene_point_t *a,
const graphene_point_t *b)
{
self->min.x = MIN (a->x, b->x);
self->min.y = MIN (a->y, b->y);
self->max.x = MAX (a->x, b->x);
self->max.y = MAX (a->y, b->y);
return self;
}
GskBoundingBox *
gsk_bounding_box_init_copy (GskBoundingBox *self,
const GskBoundingBox *src)
{
self->min = src->min;
self->max = src->max;
return self;
}
GskBoundingBox *
gsk_bounding_box_init_from_rect (GskBoundingBox *self,
const graphene_rect_t *bounds)
{
self->min = bounds->origin;
self->max.x = bounds->origin.x + bounds->size.width;
self->max.y = bounds->origin.y + bounds->size.height;
return self;
}
void
gsk_bounding_box_expand (GskBoundingBox *self,
const graphene_point_t *p)
{
self->min.x = MIN (self->min.x, p->x);
self->min.y = MIN (self->min.y, p->y);
self->max.x = MAX (self->max.x, p->x);
self->max.y = MAX (self->max.y, p->y);
}
graphene_rect_t *
gsk_bounding_box_to_rect (const GskBoundingBox *self,
graphene_rect_t *rect)
{
rect->origin = self->min;
rect->size.width = self->max.x - self->min.x;
rect->size.height = self->max.y - self->min.y;
return rect;
}
gboolean
gsk_bounding_box_contains_point (const GskBoundingBox *self,
const graphene_point_t *p)
{
return self->min.x <= p->x && p->x <= self->max.x &&
self->min.y <= p->y && p->y <= self->max.y;
}
gboolean
gsk_bounding_box_intersection (const GskBoundingBox *a,
const GskBoundingBox *b,
GskBoundingBox *res)
{
graphene_point_t min, max;
min.x = MAX (a->min.x, b->min.x);
min.y = MAX (a->min.y, b->min.y);
max.x = MIN (a->max.x, b->max.x);
max.y = MIN (a->max.y, b->max.y);
if (res)
gsk_bounding_box_init (res, &min, &max);
return min.x <= max.x && min.y <= max.y;
}
-44
View File
@@ -1,44 +0,0 @@
#pragma once
#include <gsk/gsktypes.h>
G_BEGIN_DECLS
typedef struct _GskBoundingBox GskBoundingBox;
struct _GskBoundingBox {
graphene_point_t min;
graphene_point_t max;
};
GDK_AVAILABLE_IN_ALL
GskBoundingBox * gsk_bounding_box_init (GskBoundingBox *self,
const graphene_point_t *a,
const graphene_point_t *b);
GDK_AVAILABLE_IN_ALL
GskBoundingBox * gsk_bounding_box_init_copy (GskBoundingBox *self,
const GskBoundingBox *src);
GDK_AVAILABLE_IN_ALL
GskBoundingBox * gsk_bounding_box_init_from_rect (GskBoundingBox *self,
const graphene_rect_t *bounds);
GDK_AVAILABLE_IN_ALL
void gsk_bounding_box_expand (GskBoundingBox *self,
const graphene_point_t *p);
GDK_AVAILABLE_IN_ALL
graphene_rect_t *gsk_bounding_box_to_rect (const GskBoundingBox *self,
graphene_rect_t *rect);
GDK_AVAILABLE_IN_ALL
gboolean gsk_bounding_box_contains_point (const GskBoundingBox *self,
const graphene_point_t *p);
GDK_AVAILABLE_IN_ALL
gboolean gsk_bounding_box_intersection (const GskBoundingBox *a,
const GskBoundingBox *b,
GskBoundingBox *res);
G_END_DECLS
-2582
View File
File diff suppressed because it is too large Load Diff
-133
View File
@@ -1,133 +0,0 @@
/*
* 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_CONTOUR_PRIVATE_H__
#define __GSK_CONTOUR_PRIVATE_H__
#include <gskpath.h>
#include "gskpathopprivate.h"
G_BEGIN_DECLS
/* A path is flat if it contains no cubic or conic segments.
* A path is closed if all its contours end with a GSK_PATH_CLOSE
* operation.
*/
typedef enum
{
GSK_PATH_FLAT,
GSK_PATH_CLOSED
} GskPathFlags;
typedef struct _GskContour GskContour;
GskContour * gsk_rect_contour_new (const graphene_rect_t *rect);
GskContour * gsk_rounded_rect_contour_new (const GskRoundedRect *rounded_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 graphene_point_t *points,
gsize n_points,
const gskpathop *ops,
gsize n_ops,
gssize offset);
void gsk_contour_copy (GskContour * dest,
const GskContour *src);
GskContour * gsk_contour_dup (const GskContour *src);
GskContour * gsk_contour_reverse (const GskContour *src);
gsize gsk_contour_get_size (const GskContour *self);
GskPathFlags gsk_contour_get_flags (const GskContour *self);
void gsk_contour_print (const GskContour *self,
GString *string);
gboolean gsk_contour_get_bounds (const GskContour *self,
graphene_rect_t *bounds);
gpointer gsk_contour_init_measure (const GskContour *self,
float tolerance,
float *out_length);
void gsk_contour_free_measure (const GskContour *self,
gpointer data);
gboolean gsk_contour_foreach (const GskContour *self,
float tolerance,
GskPathForeachFunc func,
gpointer user_data);
void gsk_contour_get_start_end (const GskContour *self,
graphene_point_t *start,
graphene_point_t *end);
void gsk_contour_get_point (const GskContour *self,
gpointer measure_data,
float distance,
graphene_point_t *pos,
graphene_vec2_t *tangent);
float gsk_contour_get_curvature (const GskContour *self,
gpointer measure_data,
float distance,
graphene_point_t *center);
gboolean gsk_contour_get_closest_point (const GskContour *self,
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,
gboolean emit_move_to,
float start,
float end);
gboolean gsk_contour_get_stroke_bounds (const GskContour *self,
const GskStroke *stroke,
graphene_rect_t *bounds);
void gsk_contour_add_stroke (const GskContour *contour,
GskPathBuilder *builder,
GskStroke *stroke);
void gsk_contour_default_add_stroke (const GskContour *contour,
GskPathBuilder *builder,
GskStroke *stroke);
void gsk_contour_offset (const GskContour *contour,
GskPathBuilder *builder,
float distance,
GskLineJoin line_join,
float miter_limit);
void gsk_contour_default_offset (const GskContour *contour,
GskPathBuilder *builder,
float distance,
GskLineJoin line_join,
float miter_limit);
gboolean gsk_contour_is_convex (const GskContour *contour);
G_END_DECLS
#endif /* __GSK_CONTOUR_PRIVATE_H__ */
-286
View File
@@ -1,286 +0,0 @@
#include "config.h"
#include "gskconvexityprivate.h"
#include "gskcontourprivate.h"
typedef enum
{
GSK_DIR_CHANGE_UNKNOWN,
GSK_DIR_CHANGE_LEFT,
GSK_DIR_CHANGE_RIGHT,
GSK_DIR_CHANGE_STRAIGHT,
GSK_DIR_CHANGE_REVERSE,
GSK_DIR_CHANGE_INVALID,
} GskDirChange;
typedef enum
{
GSK_PATH_DIRECTION_UNKNOWN,
GSK_PATH_DIRECTION_CLOCKWISE,
GSK_PATH_DIRECTION_COUNTERCLOCKWISE,
} GskPathDirection;
typedef struct _GskConvexityChecker GskConvexityChecker;
struct _GskConvexityChecker
{
graphene_point_t first_point;
graphene_vec2_t first_vec;
graphene_point_t last_point;
graphene_vec2_t last_vec;
GskDirChange expected_direction;
GskPathDirection first_direction;
int reversals;
gboolean finite;
GskConvexity convexity;
int xsign;
int ysign;
int dx;
int dy;
};
static float
cross_product (graphene_vec2_t *a,
graphene_vec2_t *b)
{
float fa[2];
float fb[2];
graphene_vec2_to_float (a, fa);
graphene_vec2_to_float (b, fb);
return fa[0] * fb[1] - fa[1] * fb[0];
}
static GskDirChange
direction_change (GskConvexityChecker *c,
graphene_vec2_t *v)
{
float cross = cross_product (&(c->last_vec), v);
if (!finite (cross))
return GSK_DIR_CHANGE_UNKNOWN;
if (cross == 0)
return graphene_vec2_dot (&(c->last_vec), v) < 0
? GSK_DIR_CHANGE_REVERSE
: GSK_DIR_CHANGE_STRAIGHT;
return cross > 0
? GSK_DIR_CHANGE_RIGHT
: GSK_DIR_CHANGE_LEFT;
}
static gboolean
add_vec (GskConvexityChecker *c,
graphene_vec2_t *v)
{
GskDirChange dir;
int sign;
dir = direction_change (c, v);
switch (dir)
{
case GSK_DIR_CHANGE_LEFT:
case GSK_DIR_CHANGE_RIGHT:
if (c->expected_direction == GSK_DIR_CHANGE_INVALID)
{
c->expected_direction = dir;
c->first_direction = (dir == GSK_DIR_CHANGE_RIGHT)
? GSK_PATH_DIRECTION_CLOCKWISE
: GSK_PATH_DIRECTION_COUNTERCLOCKWISE;
}
else if (c->expected_direction != dir)
{
c->first_direction = GSK_PATH_DIRECTION_UNKNOWN;
c->convexity = GSK_CONVEXITY_CONCAVE;
return FALSE;
}
graphene_vec2_init_from_vec2 (&c->last_vec, v);
break;
case GSK_DIR_CHANGE_STRAIGHT:
break;
case GSK_DIR_CHANGE_REVERSE:
graphene_vec2_init_from_vec2 (&c->last_vec, v);
c->reversals++;
if (c->reversals > 2)
c->convexity = GSK_CONVEXITY_CONCAVE;
return c->reversals < 3;
case GSK_DIR_CHANGE_UNKNOWN:
c->finite = FALSE;
return FALSE;
case GSK_DIR_CHANGE_INVALID:
default:
g_assert_not_reached ();
}
if (graphene_vec2_get_x (v) > 0)
sign = 1;
else if (graphene_vec2_get_x (v) < 0)
sign = -1;
else
sign = 0;
if (sign != 0)
{
if (c->xsign != 42)
{
if (c->xsign != sign)
c->dx++;
if (c->dx > 2)
{
c->convexity = GSK_CONVEXITY_CONCAVE;
return FALSE;
}
}
c->xsign = sign;
}
if (graphene_vec2_get_y (v) > 0)
sign = 1;
else if (graphene_vec2_get_y (v) < 0)
sign = -1;
else
sign = 0;
if (sign != 0)
{
if (c->ysign != 42)
{
if (c->ysign != sign)
c->dy++;
if (c->dy > 2)
{
c->convexity = GSK_CONVEXITY_CONCAVE;
return FALSE;
}
}
c->ysign = sign;
}
return TRUE;
}
static void
gsk_convexity_checker_init (GskConvexityChecker *c)
{
c->first_point = GRAPHENE_POINT_INIT(0,0);
c->last_point = GRAPHENE_POINT_INIT(0,0);
graphene_vec2_init (&c->first_vec, 0, 0);
graphene_vec2_init (&c->last_vec, 0, 0);
c->expected_direction = GSK_DIR_CHANGE_INVALID;
c->first_direction = GSK_PATH_DIRECTION_UNKNOWN;
c->reversals = 0;
c->finite = TRUE;
c->convexity = GSK_CONVEXITY_UNKNOWN;
c->xsign = 42;
c->ysign = 42;
c->dx = 0;
c->dy = 0;
}
static void
gsk_convexity_checker_move (GskConvexityChecker *c,
const graphene_point_t *p)
{
c->first_point = c->last_point = *p;
c->expected_direction = GSK_DIR_CHANGE_INVALID;
c->convexity = GSK_CONVEXITY_CONVEX;
}
static gboolean
gsk_convexity_checker_add_point (GskConvexityChecker *c,
const graphene_point_t *p)
{
graphene_vec2_t v;
if (graphene_point_equal (&c->last_point, p))
return TRUE;
graphene_vec2_init (&v,
p->x - c->last_point.x,
p->y - c->last_point.y);
if (graphene_point_equal (&c->first_point, &c->last_point) &&
c->expected_direction == GSK_DIR_CHANGE_INVALID)
{
graphene_vec2_init_from_vec2 (&c->last_vec, &v);
graphene_vec2_init_from_vec2 (&c->first_vec, &v);
}
else if (!add_vec (c, &v))
{
c->convexity = GSK_CONVEXITY_CONCAVE;
return FALSE;
}
c->last_point = *p;
return TRUE;
}
static gboolean
gsk_convexity_checker_close (GskConvexityChecker *c)
{
if (!(gsk_convexity_checker_add_point (c, &c->first_point) &&
add_vec (c, &c->first_vec)))
{
c->convexity = GSK_CONVEXITY_CONCAVE;
return FALSE;
}
return TRUE;
}
static gboolean
convex_cb (GskPathOperation op,
const graphene_point_t *pts,
gsize n_pts,
float weight,
gpointer user_data)
{
GskConvexityChecker *c = user_data;
switch (op)
{
case GSK_PATH_MOVE:
gsk_convexity_checker_move (c, &pts[0]);
break;
case GSK_PATH_CLOSE:
if (!gsk_convexity_checker_close (c))
return FALSE;
break;
case GSK_PATH_LINE:
if (!gsk_convexity_checker_add_point (c, &pts[1]))
return FALSE;
break;
case GSK_PATH_CURVE:
if (!gsk_convexity_checker_add_point (c, &pts[1]) ||
!gsk_convexity_checker_add_point (c, &pts[2]) ||
!gsk_convexity_checker_add_point (c, &pts[3]))
return FALSE;
break;
case GSK_PATH_CONIC:
if (!gsk_convexity_checker_add_point (c, &pts[1]) ||
!gsk_convexity_checker_add_point (c, &pts[3]))
return FALSE;
break;
default:
g_assert_not_reached ();
}
return TRUE;
}
GskConvexity
gsk_contour_compute_convexity (const GskContour *contour)
{
GskConvexityChecker c;
gsk_convexity_checker_init (&c);
gsk_contour_foreach (contour, 0.001, convex_cb, &c);
g_assert (c.convexity != GSK_CONVEXITY_UNKNOWN);
return c.convexity;
}
-14
View File
@@ -1,14 +0,0 @@
#pragma once
#include "gskcontourprivate.h"
typedef enum
{
GSK_CONVEXITY_UNKNOWN,
GSK_CONVEXITY_CONVEX,
GSK_CONVEXITY_CONCAVE,
} GskConvexity;
GskConvexity gsk_contour_compute_convexity (const GskContour *contour);
-2001
View File
File diff suppressed because it is too large Load Diff
-650
View File
@@ -1,650 +0,0 @@
/*
* Copyright © 2020 Red Hat, Inc
*
* 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: Matthias Clasen <mclasen@redhat.com>
*/
#include "config.h"
#include "gskcurveprivate.h"
static inline gboolean
acceptable (float t)
{
return 0 - FLT_EPSILON <= t && t <= 1 + FLT_EPSILON;
}
static int
line_intersect (const GskCurve *curve1,
const GskCurve *curve2,
float *t1,
float *t2,
graphene_point_t *p,
int n)
{
const graphene_point_t *pts1 = curve1->line.points;
const graphene_point_t *pts2 = curve2->line.points;
float a1 = pts1[0].x - pts1[1].x;
float b1 = pts1[0].y - pts1[1].y;
float a2 = pts2[0].x - pts2[1].x;
float b2 = pts2[0].y - pts2[1].y;
float det = a1 * b2 - b1 * a2;
if (fabs(det) > 0.01)
{
float tt = ((pts1[0].x - pts2[0].x) * b2 - (pts1[0].y - pts2[0].y) * a2) / det;
float ss = - ((pts1[0].y - pts2[0].y) * a1 - (pts1[0].x - pts2[0].x) * b1) / det;
if (acceptable (tt) && acceptable (ss))
{
p[0].x = pts1[0].x + tt * (pts1[1].x - pts1[0].x);
p[0].y = pts1[0].y + tt * (pts1[1].y - pts1[0].y);
t1[0] = tt;
t2[0] = ss;
return 1;
}
}
else /* parallel lines */
{
float r = a1 * (pts1[1].y - pts2[0].y) - (pts1[1].x - pts2[0].x) * b1;
float dist = (r * r) / (a1 * a1 + b1 * b1);
float t, s, tt, ss;
if (dist > 0.01)
return 0;
if (pts1[1].x != pts1[0].x)
{
t = (pts2[0].x - pts1[0].x) / (pts1[1].x - pts1[0].x);
s = (pts2[1].x - pts1[0].x) / (pts1[1].x - pts1[0].x);
}
else
{
t = (pts2[0].y - pts1[0].y) / (pts1[1].y - pts1[0].y);
s = (pts2[1].y - pts1[0].y) / (pts1[1].y - pts1[0].y);
}
if ((t < 0 && s < 0) || (t > 1 && s > 1))
return 0;
if (acceptable (t))
{
t1[0] = t;
t2[0] = 0;
p[0] = pts2[0];
}
else if (t < 0)
{
if (pts2[1].x != pts2[0].x)
tt = (pts1[0].x - pts2[0].x) / (pts2[1].x - pts2[0].x);
else
tt = (pts1[0].y - pts2[0].y) / (pts2[1].y - pts2[0].y);
t1[0] = 0;
t2[0] = tt;
p[0] = pts1[0];
}
else
{
if (pts2[1].x != pts2[0].x)
tt = (pts1[1].x - pts2[0].x) / (pts2[1].x - pts2[0].x);
else
tt = (pts1[1].y - pts2[0].y) / (pts2[1].y - pts2[0].y);
t1[0] = 1;
t2[0] = tt;
p[0] = pts1[1];
}
if (acceptable (s))
{
if (t2[0] == 1)
return 1;
t1[1] = s;
t2[1] = 1;
p[1] = pts2[1];
}
else if (s < 0)
{
if (t1[0] == 0)
return 1;
if (pts2[1].x != pts2[0].x)
ss = (pts1[0].x - pts2[0].x) / (pts2[1].x - pts2[0].x);
else
ss = (pts1[0].y - pts2[0].y) / (pts2[1].y - pts2[0].y);
t1[1] = 0;
t2[1] = ss;
p[1] = pts1[0];
}
else
{
if (t1[0] == 1)
return 1;
if (pts2[1].x != pts2[0].x)
ss = (pts1[1].x - pts2[0].x) / (pts2[1].x - pts2[0].x);
else
ss = (pts1[1].y - pts2[0].y) / (pts2[1].y - pts2[0].y);
t1[1] = 1;
t2[1] = ss;
p[1] = pts1[1];
}
return 2;
}
return 0;
}
static void
get_tangent (const graphene_point_t *p0,
const graphene_point_t *p1,
graphene_vec2_t *t)
{
graphene_vec2_init (t, p1->x - p0->x, p1->y - p0->y);
graphene_vec2_normalize (t, t);
}
static void
align_points (const graphene_point_t *p,
const graphene_point_t *a,
const graphene_point_t *b,
graphene_point_t *q,
int n)
{
graphene_vec2_t n1;
float angle;
float s, c;
float dist;
get_tangent (a, b, &n1);
angle = - atan2 (graphene_vec2_get_y (&n1), graphene_vec2_get_x (&n1));
sincosf (angle, &s, &c);
dist = sqrtf ((a->x - b->x)*(a->x - b->x) + (a->y - b->y)*(a->y - b->y));
for (int i = 0; i < n; i++)
{
q[i].x = ((p[i].x - a->x) * c - (p[i].y - a->y) * s) / dist;
q[i].y = ((p[i].x - a->x) * s + (p[i].y - a->y) * c) / dist;
}
}
static void
find_point_on_line (const graphene_point_t *p1,
const graphene_point_t *p2,
const graphene_point_t *q,
float *t)
{
if (p2->x != p1->x)
*t = (q->x - p1->x) / (p2->x - p1->x);
else
*t = (q->y - p1->y) / (p2->y - p1->y);
}
static float
cuberoot (float v)
{
if (v < 0)
return -pow (-v, 1.f / 3);
return pow (v, 1.f / 3);
}
/* Solve P = 0 where P is
* P = (1-t)^3*pa + 3*t*(1-t)^2*pb + 3*t^2*(1-t)*pc + t^3*pd
*/
static int
get_cubic_roots (float pa, float pb, float pc, float pd, float roots[3])
{
float a, b, c, d;
float q, q2;
float p, p3;
float discriminant;
float u1, v1, sd;
int n_roots = 0;
d = -pa + 3*pb - 3*pc + pd;
a = 3*pa - 6*pb + 3*pc;
b = -3*pa + 3*pb;
c = pa;
if (fabs (d) < 0.0001)
{
if (fabs (a) < 0.0001)
{
if (fabs (b) < 0.0001)
return 0;
if (acceptable (-c / b))
{
roots[0] = -c / b;
return 1;
}
return 0;
}
q = sqrt (b*b - 4*a*c);
roots[n_roots] = (-b + q) / (2 * a);
if (acceptable (roots[n_roots]))
n_roots++;
roots[n_roots] = (-b - q) / (2 * a);
if (acceptable (roots[n_roots]))
n_roots++;
return n_roots;
}
a /= d;
b /= d;
c /= d;
p = (3*b - a*a)/3;
p3 = p/3;
q = (2*a*a*a - 9*a*b + 27*c)/27;
q2 = q/2;
discriminant = q2*q2 + p3*p3*p3;
if (discriminant < 0)
{
float mp3 = -p/3;
float mp33 = mp3*mp3*mp3;
float r = sqrt (mp33);
float t = -q / (2*r);
float cosphi = t < -1 ? -1 : (t > 1 ? 1 : t);
float phi = acos (cosphi);
float crtr = cuberoot (r);
float t1 = 2*crtr;
roots[n_roots] = t1 * cos (phi/3) - a/3;
if (acceptable (roots[n_roots]))
n_roots++;
roots[n_roots] = t1 * cos ((phi + 2*M_PI) / 3) - a/3;
if (acceptable (roots[n_roots]))
n_roots++;
roots[n_roots] = t1 * cos ((phi + 4*M_PI) / 3) - a/3;
if (acceptable (roots[n_roots]))
n_roots++;
return n_roots;
}
if (discriminant == 0)
{
u1 = q2 < 0 ? cuberoot (-q2) : -cuberoot (q2);
roots[n_roots] = 2*u1 - a/3;
if (acceptable (roots[n_roots]))
n_roots++;
roots[n_roots] = -u1 - a/3;
if (acceptable (roots[n_roots]))
n_roots++;
return n_roots;
}
sd = sqrt (discriminant);
u1 = cuberoot (sd - q2);
v1 = cuberoot (sd + q2);
roots[n_roots] = u1 - v1 - a/3;
if (acceptable (roots[n_roots]))
n_roots++;
return n_roots;
}
static int
line_curve_intersect (const GskCurve *curve1,
const GskCurve *curve2,
float *t1,
float *t2,
graphene_point_t *p,
int n)
{
const graphene_point_t *a = &curve1->line.points[0];
const graphene_point_t *b = &curve1->line.points[1];
graphene_point_t pts[4];
float t[3];
int m, i, j;
/* Rotate things to place curve1 on the x axis,
* then solve curve2 for y == 0.
*/
align_points (curve2->curve.points, a, b, pts, 4);
m = get_cubic_roots (pts[0].y, pts[1].y, pts[2].y, pts[3].y, t);
j = 0;
for (i = 0; i < m; i++)
{
t2[j] = t[i];
gsk_curve_get_point (curve2, t2[j], &p[j]);
find_point_on_line (a, b, &p[j], &t1[j]);
if (acceptable (t1[j]))
j++;
if (j == n)
break;
}
return j;
}
static void
curve_intersect_recurse (const GskCurve *curve1,
const GskCurve *curve2,
float t1l,
float t1r,
float t2l,
float t2r,
float *t1,
float *t2,
graphene_point_t *p,
int n,
int *pos,
float tolerance,
int level)
{
GskCurve p11, p12, p21, p22;
GskBoundingBox b1, b2;
float d1, d2;
if (*pos == n)
return;
if (level == 20)
return;
gsk_curve_get_bounds (curve1, &b1);
gsk_curve_get_bounds (curve2, &b2);
if (!gsk_bounding_box_intersection (&b1, &b2, NULL))
return;
gsk_curve_get_tight_bounds (curve1, &b1);
gsk_curve_get_tight_bounds (curve2, &b2);
if (!gsk_bounding_box_intersection (&b1, &b2, NULL))
return;
d1 = (t1r - t1l) / 2;
d2 = (t2r - t2l) / 2;
if (b1.max.x - b1.min.x < tolerance && b1.max.y - b1.min.y < tolerance &&
b2.max.x - b2.min.x < tolerance && b2.max.y - b2.min.y < tolerance)
{
graphene_point_t c;
t1[*pos] = t1l + d1;
t2[*pos] = t2l + d2;
gsk_curve_get_point (curve1, 0.5, &c);
for (int i = 0; i < *pos; i++)
{
if (graphene_point_near (&c, &p[i], 0.1))
return;
}
p[*pos] = c;
(*pos)++;
return;
}
gsk_curve_split (curve1, 0.5, &p11, &p12);
gsk_curve_split (curve2, 0.5, &p21, &p22);
curve_intersect_recurse (&p11, &p21, t1l, t1l + d1, t2l, t2l + d2, t1, t2, p, n, pos, tolerance, level + 1);
curve_intersect_recurse (&p11, &p22, t1l, t1l + d1, t2l + d2, t2r, t1, t2, p, n, pos, tolerance, level + 1);
curve_intersect_recurse (&p12, &p21, t1l + d1, t1r, t2l, t2l + d2, t1, t2, p, n, pos, tolerance, level + 1);
curve_intersect_recurse (&p12, &p22, t1l + d1, t1r, t2l + d2, t2r, t1, t2, p, n, pos, tolerance, level + 1);
}
static int
curve_intersect (const GskCurve *curve1,
const GskCurve *curve2,
float *t1,
float *t2,
graphene_point_t *p,
int n,
float tolerance)
{
int pos = 0;
curve_intersect_recurse (curve1, curve2, 0, 1, 0, 1, t1, t2, p, n, &pos, tolerance, 0);
return pos;
}
static void
get_bounds (const GskCurve *curve,
float tl,
float tr,
GskBoundingBox *bounds)
{
GskCurve c;
gsk_curve_segment (curve, tl, tr, &c);
gsk_curve_get_tight_bounds (&c, bounds);
}
static void
general_intersect_recurse (const GskCurve *curve1,
const GskCurve *curve2,
float t1l,
float t1r,
float t2l,
float t2r,
float *t1,
float *t2,
graphene_point_t *p,
int n,
int *pos,
float tolerance,
int level)
{
GskBoundingBox b1, b2;
float d1, d2;
if (*pos == n)
return;
if (level == 20)
return;
get_bounds (curve1, t1l, t1r, &b1);
get_bounds (curve2, t2l, t2r, &b2);
if (!gsk_bounding_box_intersection (&b1, &b2, NULL))
return;
d1 = (t1r - t1l) / 2;
d2 = (t2r - t2l) / 2;
if (b1.max.x - b1.min.x < tolerance && b1.max.y - b1.min.y < tolerance &&
b2.max.x - b2.min.x < tolerance && b2.max.y - b2.min.y < tolerance)
{
graphene_point_t c;
t1[*pos] = t1l + d1;
t2[*pos] = t2l + d2;
gsk_curve_get_point (curve1, t1[*pos], &c);
for (int i = 0; i < *pos; i++)
{
if (graphene_point_near (&c, &p[i], tolerance))
return;
}
p[*pos] = c;
(*pos)++;
return;
}
/* Note that in the conic case, we cannot just split the curves and
* pass the two halves down, since splitting changes the parametrization,
* and we need the t's to be valid parameters wrt to the original curve.
*
* So, instead, we determine the bounding boxes above by always starting
* from the original curve. That is a bit less efficient, but also works
* for conics.
*/
general_intersect_recurse (curve1, curve2, t1l, t1l + d1, t2l, t2l + d2, t1, t2, p, n, pos, tolerance, level + 1);
general_intersect_recurse (curve1, curve2, t1l, t1l + d1, t2l + d2, t2r, t1, t2, p, n, pos, tolerance, level + 1);
general_intersect_recurse (curve1, curve2, t1l + d1, t1r, t2l, t2l + d2, t1, t2, p, n, pos, tolerance, level + 1);
general_intersect_recurse (curve1, curve2, t1l + d1, t1r, t2l + d2, t2r, t1, t2, p, n, pos, tolerance, level + 1);
}
static int
general_intersect (const GskCurve *curve1,
const GskCurve *curve2,
float *t1,
float *t2,
graphene_point_t *p,
int n,
float tolerance)
{
int pos = 0;
general_intersect_recurse (curve1, curve2, 0, 1, 0, 1, t1, t2, p, n, &pos, tolerance, 0);
return pos;
}
static int
curve_self_intersect (const GskCurve *curve,
float *t1,
float *t2,
graphene_point_t *p,
int n)
{
float tt[3], ss[3], s;
graphene_point_t pp[3];
int m;
GskCurve cs, ce;
if (curve->op != GSK_PATH_CURVE)
return 0;
s = 0.5;
m = gsk_curve_get_curvature_points (curve, tt);
for (int i = 0; i < m; i++)
{
if (gsk_curve_get_curvature (curve, tt[i], NULL) == 0)
{
s = tt[i];
break;
}
}
gsk_curve_split (curve, s, &cs, &ce);
m = curve_intersect (&cs, &ce, tt, ss, pp, 3, 0.001);
if (m > 1)
{
/* One of the (at most 2) intersections we found
* must be the common point where we split the curve.
* It will have a t value of 1 and an s value of 0.
*/
if (fabs (tt[0] - 1) > 1e-3)
{
t1[0] = t2[0] = tt[0] * s;
p[0] = pp[0];
}
else if (fabs (tt[1] - 1) > 1e-3)
{
t1[0] = t2[0] = tt[1] * s;
p[0] = pp[1];
}
if (fabs (ss[0]) > 1e-3)
{
t1[1] = t2[1] = s + ss[0] * (1 - s);
p[1] = pp[0];
}
else if (fabs (ss[1]) > 1e-3)
{
t1[1] = t2[1] = s + ss[1] * (1 - s);
p[1] = pp[1];
}
return 2;
}
return 0;
}
static inline gboolean
curve_equal (const GskCurve *c1,
const GskCurve *c2)
{
gsize curve_size[] = {
sizeof (GskLineCurve),
sizeof (GskLineCurve),
sizeof (GskLineCurve),
sizeof (GskCurveCurve),
sizeof (GskConicCurve)
};
return c1->op == c2->op && memcmp (c1, c2, curve_size[c1->op]) == 0;
}
/* Place intersections between the curves in p, and their Bezier positions
* in t1 and t2, up to n. Return the number of intersections found.
*
* Note that two cubic Beziers can have up to 9 intersections.
*/
int
gsk_curve_intersect (const GskCurve *curve1,
const GskCurve *curve2,
float *t1,
float *t2,
graphene_point_t *p,
int n)
{
GskPathOperation op1 = curve1->op;
GskPathOperation op2 = curve2->op;
if (op1 == GSK_PATH_CLOSE)
op1 = GSK_PATH_LINE;
if (op2 == GSK_PATH_CLOSE)
op2 = GSK_PATH_LINE;
if (curve_equal (curve1, curve2))
return curve_self_intersect (curve1, t1, t2, p, n);
/* We special-case line-line and line-curve intersections,
* since we can solve them directly.
* Everything else is done via bisection.
*/
if (op1 == GSK_PATH_LINE && op2 == GSK_PATH_LINE)
return line_intersect (curve1, curve2, t1, t2, p, n);
else if (op1 == GSK_PATH_LINE && op2 == GSK_PATH_CURVE)
return line_curve_intersect (curve1, curve2, t1, t2, p, n);
else if (op1 == GSK_PATH_CURVE && op2 == GSK_PATH_LINE)
return line_curve_intersect (curve2, curve1, t2, t1, p, n);
else if (op1 == GSK_PATH_CURVE && op2 == GSK_PATH_CURVE)
return curve_intersect (curve1, curve2, t1, t2, p, n, 0.001);
else
return general_intersect (curve1, curve2, t1, t2, p, n, 0.001);
}
-164
View File
@@ -1,164 +0,0 @@
/*
* 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_CURVE_PRIVATE_H__
#define __GSK_CURVE_PRIVATE_H__
#include "gskpathopprivate.h"
#include "gskboundingboxprivate.h"
G_BEGIN_DECLS
typedef gpointer gskpathop;
typedef union _GskCurve GskCurve;
typedef struct _GskLineCurve GskLineCurve;
typedef struct _GskCurveCurve GskCurveCurve;
typedef struct _GskConicCurve GskConicCurve;
struct _GskLineCurve
{
GskPathOperation op;
gboolean padding;
graphene_point_t points[2];
};
struct _GskCurveCurve
{
GskPathOperation op;
gboolean has_coefficients;
graphene_point_t points[4];
graphene_point_t coeffs[4];
};
struct _GskConicCurve
{
GskPathOperation op;
gboolean has_coefficients;
/* points[0], points[1], points[3] are the control points,
* points[2].x is the weight
*/
graphene_point_t points[4];
graphene_point_t num[3];
graphene_point_t denom[3];
};
union _GskCurve
{
GskPathOperation op;
GskLineCurve line;
GskCurveCurve curve;
GskConicCurve conic;
};
typedef gboolean (* GskCurveAddLineFunc) (const graphene_point_t *from,
const graphene_point_t *to,
float from_progress,
float to_progress,
gpointer user_data);
typedef gboolean (* GskCurveAddCurveFunc) (const graphene_point_t points[4],
gpointer user_data);
void gsk_curve_init (GskCurve *curve,
gskpathop op);
void gsk_curve_init_foreach (GskCurve *curve,
GskPathOperation op,
const graphene_point_t *pts,
gsize n_pts,
float weight);
void gsk_curve_get_point (const GskCurve *curve,
float progress,
graphene_point_t *pos);
void gsk_curve_get_tangent (const GskCurve *curve,
float progress,
graphene_vec2_t *tangent);
void gsk_curve_get_normal (const GskCurve *curve,
float progress,
graphene_vec2_t *normal);
void gsk_curve_split (const GskCurve *curve,
float progress,
GskCurve *start,
GskCurve *end);
void gsk_curve_segment (const GskCurve *curve,
float start,
float end,
GskCurve *segment);
gboolean gsk_curve_decompose (const GskCurve *curve,
float tolerance,
GskCurveAddLineFunc add_line_func,
gpointer user_data);
gboolean gsk_curve_decompose_curve (const GskCurve *curve,
float tolerance,
GskCurveAddCurveFunc add_curve_func,
gpointer user_data);
gskpathop gsk_curve_pathop (const GskCurve *curve);
#define gsk_curve_builder_to(curve, builder) gsk_path_builder_pathop_to ((builder), gsk_curve_pathop (curve))
const graphene_point_t *gsk_curve_get_start_point (const GskCurve *curve);
const graphene_point_t *gsk_curve_get_end_point (const GskCurve *curve);
void gsk_curve_get_start_tangent (const GskCurve *curve,
graphene_vec2_t *tangent);
void gsk_curve_get_end_tangent (const GskCurve *curve,
graphene_vec2_t *tangent);
void gsk_curve_get_bounds (const GskCurve *curve,
GskBoundingBox *bounds);
void gsk_curve_get_tight_bounds (const GskCurve *curve,
GskBoundingBox *bounds);
int gsk_curve_intersect (const GskCurve *curve1,
const GskCurve *curve2,
float *t1,
float *t2,
graphene_point_t *p,
int n);
void gsk_curve_offset (const GskCurve *curve,
float distance,
GskCurve *offset_curve);
void gsk_curve_reverse (const GskCurve *curve,
GskCurve *reverse);
float gsk_curve_get_curvature (const GskCurve *curve,
float t,
graphene_point_t *center);
void gsk_curve_print (const GskCurve *curve);
int gsk_curve_get_curvature_points (const GskCurve *curve,
float t[3]);
int gsk_curve_get_cusps (const GskCurve *curve,
float t[2]);
G_END_DECLS
#endif /* __GSK_CURVE_PRIVATE_H__ */
-1
View File
@@ -10,7 +10,6 @@ static const GdkDebugKey gsk_debug_keys[] = {
{ "surface", GSK_DEBUG_SURFACE, "Information about surfaces" },
{ "fallback", GSK_DEBUG_FALLBACK, "Information about fallbacks" },
{ "glyphcache", GSK_DEBUG_GLYPH_CACHE, "Information about glyph caching" },
{ "paths", GSK_DEBUG_PATHS, "Information about path processing" },
{ "geometry", GSK_DEBUG_GEOMETRY, "Show borders (when using cairo)" },
{ "full-redraw", GSK_DEBUG_FULL_REDRAW, "Force full redraws" },
{ "sync", GSK_DEBUG_SYNC, "Sync after each frame" },
-1
View File
@@ -14,7 +14,6 @@ typedef enum {
GSK_DEBUG_VULKAN = 1 << 5,
GSK_DEBUG_FALLBACK = 1 << 6,
GSK_DEBUG_GLYPH_CACHE = 1 << 7,
GSK_DEBUG_PATHS = 1 << 8,
/* flags below may affect behavior */
GSK_DEBUG_GEOMETRY = 1 << 9,
GSK_DEBUG_FULL_REDRAW = 1 << 10,
+3 -105
View File
@@ -1,5 +1,5 @@
/* GSK - The GTK Scene Kit
* Copyright 2016 Endless
* Copyright 2016 Endless
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -50,7 +50,7 @@
* @GSK_BLUR_NODE: A node that applies a blur
* @GSK_DEBUG_NODE: Debug information that does not affect the rendering
* @GSK_GL_SHADER_NODE: A node that uses OpenGL fragment shaders to render
* The type of a node determines what the node is rendering.
*/
typedef enum {
@@ -73,8 +73,6 @@ 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,
@@ -169,107 +167,6 @@ 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
* @GSK_LINE_JOIN_ARCS: Use a sharp angled corner made from circles
*
* Specifies how to render the junction of two lines when stroking.
*
* See [method@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,
GSK_LINE_JOIN_ARCS
} 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.
* @GSK_PATH_CONIC: A weighted quadratic Bézier curve with 3 points
* describing the start point, control point and end point of the
* curve. A weight for the curve will be passed, too.
*
* 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,
GSK_PATH_CONIC,
} GskPathOperation;
/**
* GskSerializationError:
* @GSK_SERIALIZATION_UNSUPPORTED_FORMAT: The format can not be identified
@@ -354,4 +251,5 @@ typedef enum
GSK_GL_UNIFORM_TYPE_VEC4,
} GskGLUniformType;
#endif /* __GSK_TYPES_H__ */
-1526
View File
File diff suppressed because it is too large Load Diff
-159
View File
@@ -1,159 +0,0 @@
/*
* 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_CURVE: Allow emission of `GSK_PATH_CURVE` operations.
* @GSK_PATH_FOREACH_ALLOW_CONIC: Allow emission of `GSK_PATH_CONIC` operations.
*
* Flags that can be passed to [method@Gsk.Path.foreach] to enable additional
* features.
*
* By default, [method@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_CURVE = (1 << 0),
GSK_PATH_FOREACH_ALLOW_CONIC = (1 << 1)
} GskPathForeachFlags;
/**
* GskPathForeachFunc:
* @op: The operation to perform
* @pts: The points of the operation
* @n_pts: The number of points
* @weight: The weight for conic curves, or unused if not a conic curve.
* @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,
float weight,
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_get_stroke_bounds (GskPath *path,
const GskStroke *stroke,
graphene_rect_t *bounds);
GDK_AVAILABLE_IN_ALL
gboolean gsk_path_is_convex (GskPath *self);
GDK_AVAILABLE_IN_ALL
gboolean gsk_path_foreach (GskPath *self,
GskPathForeachFlags flags,
GskPathForeachFunc func,
gpointer user_data);
GDK_AVAILABLE_IN_ALL
GskPath * gsk_path_reverse (GskPath *self);
GDK_AVAILABLE_IN_ALL
GskPath * gsk_path_transform (GskPath *self,
GskTransform *transform);
GDK_AVAILABLE_IN_ALL
GskPath * gsk_path_stroke (GskPath *self,
GskStroke *stroke);
GDK_AVAILABLE_IN_ALL
GskPath * gsk_path_offset (GskPath *self,
float distance,
GskLineJoin line_join,
float miter_limit);
GDK_AVAILABLE_IN_ALL
GskPath * gsk_path_union (GskPath *first,
GskPath *second,
GskFillRule fill_rule);
GDK_AVAILABLE_IN_ALL
GskPath * gsk_path_intersection (GskPath *first,
GskPath *second,
GskFillRule fill_rule);
GDK_AVAILABLE_IN_ALL
GskPath * gsk_path_difference (GskPath *first,
GskPath *second,
GskFillRule fill_rule);
GDK_AVAILABLE_IN_ALL
GskPath * gsk_path_symmetric_difference (GskPath *first,
GskPath *second,
GskFillRule fill_rule);
GDK_AVAILABLE_IN_ALL
GskPath * gsk_path_simplify (GskPath *self,
GskFillRule fill_rule);
G_END_DECLS
#endif /* __GSK_PATH_H__ */
-917
View File
@@ -1,917 +0,0 @@
/*
* 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"
/**
* GskPathBuilder:
*
* A `GskPathBuilder` is an auxiliary object that is used to
* create new `GskPath` objects.
*
* A path is constructed like this:
*
* ```
* 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 [method@Gsk.PathBuilder.add_circle]
* or by adding from other paths like [method@Gsk.PathBuilder.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 [method@Gsk.PathBuilder.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
* [method@Gsk.PathBuilder.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.
*/
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 *builder;
builder = g_slice_new0 (GskPathBuilder);
builder->ref_count = 1;
builder->ops = g_array_new (FALSE, FALSE, sizeof (gskpathop));
builder->points = g_array_new (FALSE, FALSE, sizeof (graphene_point_t));
/* Be explicit here */
builder->current_point = GRAPHENE_POINT_INIT (0, 0);
return builder;
}
/**
* gsk_path_builder_ref:
* @builder: 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 *builder)
{
g_return_val_if_fail (builder != NULL, NULL);
g_return_val_if_fail (builder->ref_count > 0, NULL);
builder->ref_count += 1;
return builder;
}
/* We're cheating here. Out pathops are relative to the NULL pointer,
* so that we can not care about the points GArray reallocating itself
* until we create the contour.
* This does however mean that we need to not use gsk_pathop_get_points()
* without offsetting the returned pointer.
*/
static inline gskpathop
gsk_pathop_encode_index (GskPathOperation op,
gsize index)
{
return gsk_pathop_encode (op, ((graphene_point_t *) NULL) + index);
}
static void
gsk_path_builder_ensure_current (GskPathBuilder *builder)
{
if (builder->ops->len != 0)
return;
builder->flags = GSK_PATH_FLAT;
g_array_append_vals (builder->ops, (gskpathop[1]) { gsk_pathop_encode_index (GSK_PATH_MOVE, 0) }, 1);
g_array_append_val (builder->points, builder->current_point);
}
static void
gsk_path_builder_append_current (GskPathBuilder *builder,
GskPathOperation op,
gsize n_points,
const graphene_point_t *points)
{
gsk_path_builder_ensure_current (builder);
g_array_append_vals (builder->ops, (gskpathop[1]) { gsk_pathop_encode_index (op, builder->points->len - 1) }, 1);
g_array_append_vals (builder->points, points, n_points);
builder->current_point = points[n_points - 1];
}
static void
gsk_path_builder_end_current (GskPathBuilder *builder)
{
GskContour *contour;
if (builder->ops->len == 0)
return;
contour = gsk_standard_contour_new (builder->flags,
(graphene_point_t *) builder->points->data,
builder->points->len,
(gskpathop *) builder->ops->data,
builder->ops->len,
(graphene_point_t *) builder->points->data - (graphene_point_t *) NULL);
g_array_set_size (builder->ops, 0);
g_array_set_size (builder->points, 0);
/* do this at the end to avoid inflooping when add_contour calls back here */
gsk_path_builder_add_contour (builder, contour);
}
static void
gsk_path_builder_clear (GskPathBuilder *builder)
{
gsk_path_builder_end_current (builder);
g_slist_free_full (builder->contours, g_free);
builder->contours = NULL;
}
/**
* gsk_path_builder_unref:
* @builder: a `GskPathBuilder`
*
* Releases a reference on the given @builder.
*/
void
gsk_path_builder_unref (GskPathBuilder *builder)
{
g_return_if_fail (builder != NULL);
g_return_if_fail (builder->ref_count > 0);
builder->ref_count -= 1;
if (builder->ref_count > 0)
return;
gsk_path_builder_clear (builder);
g_array_unref (builder->ops);
g_array_unref (builder->points);
g_slice_free (GskPathBuilder, builder);
}
/**
* gsk_path_builder_free_to_path: (skip)
* @builder: 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 @builder
*/
GskPath *
gsk_path_builder_free_to_path (GskPathBuilder *builder)
{
GskPath *res;
g_return_val_if_fail (builder != NULL, NULL);
res = gsk_path_builder_to_path (builder);
gsk_path_builder_unref (builder);
return res;
}
/**
* gsk_path_builder_to_path:
* @builder: 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 @builder
*/
GskPath *
gsk_path_builder_to_path (GskPathBuilder *builder)
{
GskPath *path;
g_return_val_if_fail (builder != NULL, NULL);
gsk_path_builder_end_current (builder);
builder->contours = g_slist_reverse (builder->contours);
path = gsk_path_new_from_contours (builder->contours);
gsk_path_builder_clear (builder);
return path;
}
void
gsk_path_builder_add_contour (GskPathBuilder *builder,
GskContour *contour)
{
gsk_path_builder_end_current (builder);
builder->contours = g_slist_prepend (builder->contours, contour);
}
/**
* gsk_path_builder_get_current_point:
* @builder: a #GskPathBuilder
*
* Gets the current point. The current point is used for relative
* drawing commands and updated after every operation.
*
* When @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 *builder)
{
g_return_val_if_fail (builder != NULL, NULL);
return &builder->current_point;
}
/**
* gsk_path_builder_add_path:
* @builder: a `GskPathBuilder`
* @path: (transfer none): the path to append
*
* Appends all of @path to @builder.
**/
void
gsk_path_builder_add_path (GskPathBuilder *builder,
GskPath *path)
{
gsize i;
g_return_if_fail (builder != 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 (builder, gsk_contour_dup (contour));
}
}
/**
* gsk_path_builder_add_rect:
* @builder: A `GskPathBuilder`
* @rect: The rectangle to add to @builder
*
* Adds a path representing the given rectangle.
*
* 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 *builder,
const graphene_rect_t *rect)
{
GskContour *contour;
g_return_if_fail (builder != NULL);
contour = gsk_rect_contour_new (rect);
gsk_path_builder_add_contour (builder, contour);
gsk_contour_get_start_end (contour, NULL, &builder->current_point);
}
/**
* gsk_path_builder_add_rounded_rect:
* @builder: a `GskPathBuilder`
* @rect: the rounded rect
*
* Adds @rect as a new contour to the path built in @builder.
**/
void
gsk_path_builder_add_rounded_rect (GskPathBuilder *builder,
const GskRoundedRect *rect)
{
GskContour *contour;
g_return_if_fail (builder != NULL);
g_return_if_fail (rect != NULL);
contour = gsk_rounded_rect_contour_new (rect);
gsk_path_builder_add_contour (builder, contour);
}
/**
* gsk_path_builder_add_circle:
* @builder: 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 *builder,
const graphene_point_t *center,
float radius)
{
GskContour *contour;
g_return_if_fail (builder != 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 (builder, contour);
}
/**
* gsk_path_builder_add_ellipse:
* @builder: a `GskPathBuilder`
* @center: the center point of the ellipse
* @radius: the radius of the ellipse in x/y direction
*
* Adds an ellipse with the given @center and the @radius in
* x/y direction.
**/
void
gsk_path_builder_add_ellipse (GskPathBuilder *builder,
const graphene_point_t *center,
const graphene_size_t *radius)
{
const float weight = sqrt(0.5f);
graphene_point_t pts[8];
g_return_if_fail (builder != NULL);
g_return_if_fail (center != NULL);
g_return_if_fail (radius != NULL);
pts[0] = GRAPHENE_POINT_INIT (center->x + radius->width / 2,
center->y);
pts[1] = GRAPHENE_POINT_INIT (center->x + radius->width / 2,
center->y + radius->height / 2);
pts[2] = GRAPHENE_POINT_INIT (center->x,
center->y + radius->height / 2);
pts[3] = GRAPHENE_POINT_INIT (center->x - radius->width / 2,
center->y + radius->height / 2);
pts[4] = GRAPHENE_POINT_INIT (center->x - radius->width / 2,
center->y);
pts[5] = GRAPHENE_POINT_INIT (center->x - radius->width / 2,
center->y - radius->height / 2);
pts[6] = GRAPHENE_POINT_INIT (center->x,
center->y - radius->height / 2);
pts[7] = GRAPHENE_POINT_INIT (center->x + radius->width / 2,
center->y - radius->height / 2);
gsk_path_builder_move_to (builder, pts[0].x, pts[0].y);
gsk_path_builder_conic_to (builder, pts[1].x, pts[1].y, pts[2].x, pts[2].y, weight);
gsk_path_builder_conic_to (builder, pts[3].x, pts[3].y, pts[4].x, pts[4].y, weight);
gsk_path_builder_conic_to (builder, pts[5].x, pts[5].y, pts[6].x, pts[6].y, weight);
gsk_path_builder_conic_to (builder, pts[7].x, pts[7].y, pts[0].x, pts[0].y, weight);
gsk_path_builder_close (builder);
}
/**
* gsk_path_builder_move_to:
* @builder: 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 *builder,
float x,
float y)
{
g_return_if_fail (builder != NULL);
gsk_path_builder_end_current (builder);
builder->current_point = GRAPHENE_POINT_INIT(x, y);
gsk_path_builder_ensure_current (builder);
}
/**
* gsk_path_builder_rel_move_to:
* @builder: 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 [method@Gsk.PathBuilder.move_to].
**/
void
gsk_path_builder_rel_move_to (GskPathBuilder *builder,
float x,
float y)
{
g_return_if_fail (builder != NULL);
gsk_path_builder_move_to (builder,
builder->current_point.x + x,
builder->current_point.y + y);
}
/**
* gsk_path_builder_line_to:
* @builder: 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 *builder,
float x,
float y)
{
g_return_if_fail (builder != NULL);
/* skip the line if it goes to the same point */
if (graphene_point_equal (&builder->current_point,
&GRAPHENE_POINT_INIT (x, y)))
return;
gsk_path_builder_append_current (builder,
GSK_PATH_LINE,
1, (graphene_point_t[1]) {
GRAPHENE_POINT_INIT (x, y)
});
}
/**
* gsk_path_builder_rel_line_to:
* @builder: 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 [method@Gsk.PathBuilder.line_to].
**/
void
gsk_path_builder_rel_line_to (GskPathBuilder *builder,
float x,
float y)
{
g_return_if_fail (builder != NULL);
gsk_path_builder_line_to (builder,
builder->current_point.x + x,
builder->current_point.y + y);
}
/**
* gsk_path_builder_curve_to:
* @builder: 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 *builder,
float x1,
float y1,
float x2,
float y2,
float x3,
float y3)
{
g_return_if_fail (builder != NULL);
builder->flags &= ~GSK_PATH_FLAT;
gsk_path_builder_append_current (builder,
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:
* @builder: 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 [method@Gsk.PathBuilder.curve_to].
**/
void
gsk_path_builder_rel_curve_to (GskPathBuilder *builder,
float x1,
float y1,
float x2,
float y2,
float x3,
float y3)
{
g_return_if_fail (builder != NULL);
gsk_path_builder_curve_to (builder,
builder->current_point.x + x1,
builder->current_point.y + y1,
builder->current_point.x + x2,
builder->current_point.y + y2,
builder->current_point.x + x3,
builder->current_point.y + y3);
}
/**
* gsk_path_builder_conic_to:
* @builder: a `GskPathBuilder`
* @x1: x coordinate of control point
* @y1: y coordinate of control point
* @x2: x coordinate of the end of the curve
* @y2: y coordinate of the end of the curve
* @weight: weight of the curve
*
* Adds a [conic curve](https://en.wikipedia.org/wiki/Non-uniform_rational_B-spline)
* from the current point to @x2, @y2 with the given
* @weight and @x1, @y1 as the single control point.
*
* Conic curves can be used to draw ellipses and circles.
*
* After this, @x2, @y2 will be the new current point.
**/
void
gsk_path_builder_conic_to (GskPathBuilder *builder,
float x1,
float y1,
float x2,
float y2,
float weight)
{
g_return_if_fail (builder != NULL);
g_return_if_fail (weight >= 0);
builder->flags &= ~GSK_PATH_FLAT;
gsk_path_builder_append_current (builder,
GSK_PATH_CONIC,
3, (graphene_point_t[3]) {
GRAPHENE_POINT_INIT (x1, y1),
GRAPHENE_POINT_INIT (weight, 0),
GRAPHENE_POINT_INIT (x2, y2)
});
}
/**
* gsk_path_builder_rel_conic_to:
* @builder: a `GskPathBuilder`
* @x1: x offset of control point
* @y1: y offset of control point
* @x2: x offset of the end of the curve
* @y2: y offset of the end of the curve
* @weight: weight of the curve
*
* Adds a [conic curve](https://en.wikipedia.org/wiki/Non-uniform_rational_B-spline)
* from the current point to @x2, @y2 with the given
* @weight and @x1, @y1 as the single control point.
*
* This is the relative version of [method@Gsk.PathBuilder.conic_to].
**/
void
gsk_path_builder_rel_conic_to (GskPathBuilder *builder,
float x1,
float y1,
float x2,
float y2,
float weight)
{
g_return_if_fail (builder != NULL);
g_return_if_fail (weight >= 0);
gsk_path_builder_conic_to (builder,
builder->current_point.x + x1,
builder->current_point.y + y1,
builder->current_point.x + x2,
builder->current_point.y + y2,
weight);
}
/**
* gsk_path_builder_close:
* @builder: a `GskPathBuilder`
*
* Ends the current contour with a line back to the start point.
*
* Note that this is different from calling [method@Gsk.PathBuilder.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 *builder)
{
g_return_if_fail (builder != NULL);
if (builder->ops->len == 0)
return;
builder->flags |= GSK_PATH_CLOSED;
gsk_path_builder_append_current (builder,
GSK_PATH_CLOSE,
1, (graphene_point_t[1]) {
g_array_index (builder->points, graphene_point_t, 0)
});
gsk_path_builder_end_current (builder);
}
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);
}
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);
}
}
/**
* gsk_path_builder_add_layout:
* @builder: a `GskPathBuilder`
* @layout: the pango layout to add
*
* Adds the outlines for the glyphs in @layout to @builder.
*/
void
gsk_path_builder_add_layout (GskPathBuilder *builder,
PangoLayout *layout)
{
cairo_surface_t *surface;
cairo_t *cr;
cairo_path_t *cairo_path;
GskPath *path;
surface = cairo_recording_surface_create (CAIRO_CONTENT_COLOR_ALPHA, NULL);
cr = cairo_create (surface);
pango_cairo_layout_path (cr, layout);
cairo_path = cairo_copy_path_flat (cr);
path = gsk_path_new_from_cairo (cairo_path);
gsk_path_builder_add_path (builder, path);
gsk_path_unref (path);
cairo_path_destroy (cairo_path);
cairo_destroy (cr);
cairo_surface_destroy (surface);
}
-132
View File
@@ -1,132 +0,0 @@
/*
* 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/gskroundedrect.h>
#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 *builder);
GDK_AVAILABLE_IN_ALL
void gsk_path_builder_unref (GskPathBuilder *builder);
GDK_AVAILABLE_IN_ALL
GskPath * gsk_path_builder_free_to_path (GskPathBuilder *builder) G_GNUC_WARN_UNUSED_RESULT;
GDK_AVAILABLE_IN_ALL
GskPath * gsk_path_builder_to_path (GskPathBuilder *builder) G_GNUC_WARN_UNUSED_RESULT;
GDK_AVAILABLE_IN_ALL
const graphene_point_t *gsk_path_builder_get_current_point (GskPathBuilder *builder);
GDK_AVAILABLE_IN_ALL
void gsk_path_builder_add_path (GskPathBuilder *builder,
GskPath *path);
GDK_AVAILABLE_IN_ALL
void gsk_path_builder_add_rect (GskPathBuilder *builder,
const graphene_rect_t *rect);
GDK_AVAILABLE_IN_ALL
void gsk_path_builder_add_rounded_rect (GskPathBuilder *builder,
const GskRoundedRect *rect);
GDK_AVAILABLE_IN_ALL
void gsk_path_builder_add_circle (GskPathBuilder *builder,
const graphene_point_t *center,
float radius);
GDK_AVAILABLE_IN_ALL
void gsk_path_builder_add_ellipse (GskPathBuilder *builder,
const graphene_point_t *center,
const graphene_size_t *radius);
/* next function implemented in gskpathmeasure.c */
GDK_AVAILABLE_IN_ALL
void gsk_path_builder_add_segment (GskPathBuilder *builder,
GskPathMeasure *measure,
float start,
float end);
GDK_AVAILABLE_IN_ALL
void gsk_path_builder_move_to (GskPathBuilder *builder,
float x,
float y);
GDK_AVAILABLE_IN_ALL
void gsk_path_builder_rel_move_to (GskPathBuilder *builder,
float x,
float y);
GDK_AVAILABLE_IN_ALL
void gsk_path_builder_line_to (GskPathBuilder *builder,
float x,
float y);
GDK_AVAILABLE_IN_ALL
void gsk_path_builder_rel_line_to (GskPathBuilder *builder,
float x,
float y);
GDK_AVAILABLE_IN_ALL
void gsk_path_builder_curve_to (GskPathBuilder *builder,
float x1,
float y1,
float x2,
float y2,
float x3,
float y3);
GDK_AVAILABLE_IN_ALL
void gsk_path_builder_rel_curve_to (GskPathBuilder *builder,
float x1,
float y1,
float x2,
float y2,
float x3,
float y3);
GDK_AVAILABLE_IN_ALL
void gsk_path_builder_conic_to (GskPathBuilder *builder,
float x1,
float y1,
float x2,
float y2,
float weight);
GDK_AVAILABLE_IN_ALL
void gsk_path_builder_rel_conic_to (GskPathBuilder *builder,
float x1,
float y1,
float x2,
float y2,
float weight);
GDK_AVAILABLE_IN_ALL
void gsk_path_builder_close (GskPathBuilder *builder);
GDK_AVAILABLE_IN_ALL
void gsk_path_builder_add_layout (GskPathBuilder *builder,
PangoLayout *layout);
G_END_DECLS
#endif /* __GSK_PATH_BUILDER_H__ */
-291
View File
@@ -1,291 +0,0 @@
/*
* 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 "gskpathdashprivate.h"
#include "gskcontourprivate.h"
#include "gskcurveprivate.h"
#include "gskpathprivate.h"
#include "gskstrokeprivate.h"
typedef struct
{
float offset; /* how much of the current dash we've spent */
gsize dash_index; /* goes from 0 to n_dash * 2, so we don't have to care about on/off
for uneven dashes */
gboolean on; /* If we're currently dashing or not */
gboolean may_close; /* TRUE if we haven't turned the dash off in this contour */
gboolean needs_move_to; /* If we have emitted the initial move_to() yet */
enum {
NORMAL, /* no special behavior required */
SKIP, /* skip the next dash */
ONLY, /* only do the first dash */
DONE /* done with the first dash */
} first_dash_behavior; /* How to handle the first dash in the loop. We loop closed contours
twice to make sure the first dash and the last dash can get joined */
GskCurve curve; /* Curve we are currently processing */
float collect_start; /* We're collecting multiple line segments when decomposing. */
float collect_length; /* No need to emit a curve for every line segment when the dash is long enough. */
/* from the stroke */
float *dash;
gsize n_dash;
float dash_length;
float dash_offset;
float tolerance;
GskPathForeachFunc func;
gpointer user_data;
} GskPathDash;
static void
gsk_path_dash_setup (GskPathDash *self)
{
self->offset = fmodf (self->dash_offset, 2 * self->dash_length);
self->dash_index = 0;
self->on = TRUE;
self->may_close = TRUE;
while (self->offset > self->dash[self->dash_index % self->n_dash])
{
self->offset -= self->dash[self->dash_index % self->n_dash];
self->dash_index++;
self->on = !self->on;
}
if (self->first_dash_behavior != ONLY)
self->needs_move_to = TRUE;
}
static gboolean
gsk_path_dash_ensure_move_to (GskPathDash *self,
const graphene_point_t *pt)
{
if (!self->needs_move_to)
return TRUE;
if (!self->func (GSK_PATH_MOVE, pt, 1, 0, self->user_data))
return FALSE;
self->needs_move_to = FALSE;
return TRUE;
}
static gboolean
gsk_path_dash_add_line_segment (const graphene_point_t *start,
const graphene_point_t *end,
float t_start,
float t_end,
gpointer user_data)
{
GskPathDash *self = user_data;
float remaining, length, t_step;
length = graphene_point_distance (start, end, NULL, NULL);
if (self->collect_length)
{
t_start = self->collect_start;
length += self->collect_length;
self->collect_length = 0;
}
t_step = t_end - t_start;
remaining = length;
while (remaining)
{
float piece;
if (self->offset + remaining <= self->dash[self->dash_index % self->n_dash])
{
/* try collecting multiple line segments */
if (t_end < 1.0)
{
self->collect_start = t_start + t_step * (length - remaining) / length;
self->collect_length = remaining;
return TRUE;
}
piece = remaining;
}
else
piece = self->dash[self->dash_index % self->n_dash] - self->offset;
if (self->on)
{
if (self->first_dash_behavior != SKIP)
{
GskCurve segment;
if (piece)
{
gsk_curve_segment (&self->curve,
t_start + t_step * (length - remaining) / length,
t_start + t_step * (length - (remaining - piece)) / length,
&segment);
if (!gsk_path_dash_ensure_move_to (self, gsk_curve_get_start_point (&segment)))
return FALSE;
if (!gsk_pathop_foreach (gsk_curve_pathop (&segment), self->func, self->user_data))
return FALSE;
}
else
{
graphene_point_t p;
gsk_curve_get_point (&self->curve, t_start + t_step * (length - remaining) / length, &p);
if (!gsk_path_dash_ensure_move_to (self, &p))
return FALSE;
}
}
}
else
{
self->may_close = FALSE;
if (self->first_dash_behavior == ONLY)
{
self->first_dash_behavior = DONE;
return FALSE;
}
self->first_dash_behavior = NORMAL;
}
if (self->offset + remaining <= self->dash[self->dash_index % self->n_dash])
{
self->offset += remaining;
remaining = 0;
}
else
{
remaining -= piece;
self->offset = 0;
self->dash_index++;
self->dash_index %= 2 * self->n_dash;
self->on = !self->on;
self->needs_move_to = TRUE;
}
}
return TRUE;
}
static gboolean
gsk_path_dash_foreach (GskPathOperation op,
const graphene_point_t *pts,
gsize n_pts,
float weight,
gpointer user_data)
{
GskPathDash *self = user_data;
switch (op)
{
case GSK_PATH_MOVE:
gsk_path_dash_setup (self);
break;
case GSK_PATH_CLOSE:
if (self->may_close)
{
if (graphene_point_equal (&pts[0], &pts[1]))
return self->func (GSK_PATH_CLOSE, pts, 2, 0, self->user_data);
}
else
op = GSK_PATH_LINE;
G_GNUC_FALLTHROUGH;
case GSK_PATH_LINE:
case GSK_PATH_CURVE:
case GSK_PATH_CONIC:
gsk_curve_init_foreach (&self->curve, op, pts, n_pts, weight);
if (!gsk_curve_decompose (&self->curve, self->tolerance, gsk_path_dash_add_line_segment, self))
return FALSE;
break;
default:
g_assert_not_reached ();
break;
}
return TRUE;
}
gboolean
gsk_contour_dash (const GskContour *contour,
GskStroke *stroke,
float tolerance,
GskPathForeachFunc func,
gpointer user_data)
{
GskPathDash self = {
.offset = 0,
.dash = stroke->dash,
.n_dash = stroke->n_dash,
.dash_length = stroke->dash_length,
.dash_offset = stroke->dash_offset,
.tolerance = tolerance,
.func = func,
.user_data = user_data
};
gboolean is_closed = gsk_contour_get_flags (contour) & GSK_PATH_CLOSED ? TRUE : FALSE;
self.first_dash_behavior = is_closed ? SKIP : NORMAL;
if (!gsk_contour_foreach (contour, tolerance, gsk_path_dash_foreach, &self))
return FALSE;
if (is_closed)
{
if (self.first_dash_behavior == NORMAL)
self.first_dash_behavior = ONLY;
else
self.first_dash_behavior = NORMAL;
self.needs_move_to = !self.on;
if (!gsk_contour_foreach (contour, tolerance, gsk_path_dash_foreach, &self) &&
self.first_dash_behavior != DONE)
return FALSE;
}
return TRUE;
}
gboolean
gsk_path_dash (GskPath *path,
GskStroke *stroke,
float tolerance,
GskPathForeachFunc func,
gpointer user_data)
{
gsize i;
/* Dashing disabled, no need to do any work */
if (stroke->dash_length <= 0)
return gsk_path_foreach (path, -1, func, user_data);
for (i = 0; i < gsk_path_get_n_contours (path); i++)
{
if (!gsk_contour_dash (gsk_path_get_contour (path, i), stroke, tolerance, func, user_data))
return FALSE;
}
return TRUE;
}
-50
View File
@@ -1,50 +0,0 @@
/*
* 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_DASH_PRIVATE_H__
#define __GSK_PATH_DASH_PRIVATE_H__
#include <gsk/gskpath.h>
G_BEGIN_DECLS
GDK_AVAILABLE_IN_ALL
gboolean gsk_path_dash (GskPath *path,
GskStroke *stroke,
float tolerance,
GskPathForeachFunc func,
gpointer user_data);
#ifdef GTK_COMPILATION
#include "gskcontourprivate.h"
gboolean gsk_contour_dash (const GskContour *contour,
GskStroke *stroke,
float tolerance,
GskPathForeachFunc func,
gpointer user_data);
#endif /* GTK_COMPILATION */
G_END_DECLS
#endif /* __GSK_PATH_DASH_PRIVATE_H__ */
-677
View File
@@ -1,677 +0,0 @@
/*
* 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 "gskpathbuilder.h"
#include "gskpathbuilder.h"
#include "gskpathprivate.h"
/**
* GskPathMeasure:
*
* `GskPathMeasure` is an object that allows measuring operations
* on `GskPath` objects, to determine quantities like the arc
* length of the path, its curvature, or closest points.
*
* 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;
gsize first;
gsize last;
float length;
gsize n_contours;
GskContourMeasure measures[];
};
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 with a
* default tolerance.
*
* 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;
self->first = 0;
self->last = n_contours;
for (i = 0; i < n_contours; i++)
{
self->measures[i].contour_data = gsk_contour_init_measure (gsk_path_get_contour (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 object.
**/
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 (gsk_path_get_contour (self->path, i),
self->measures[i].contour_data);
}
gsk_path_unref (self->path);
g_free (self);
}
/**
* gsk_path_measure_get_path:
* @self: a `GskPathMeasure`
*
* Returns the path that the measure was created for.
*
* Returns: (transfer none): the path of @self
*/
GskPath *
gsk_path_measure_get_path (GskPathMeasure *self)
{
g_return_val_if_fail (self != NULL, NULL);
return self->path;
}
/**
* gsk_path_measure_get_tolerance:
* @self: a `GskPathMeasure`
*
* Returns the tolerance that the measure was created with.
*
* Returns: the tolerance of @self
*/
float
gsk_path_measure_get_tolerance (GskPathMeasure *self)
{
g_return_val_if_fail (self != NULL, 0.f);
return self->tolerance;
}
/**
* gsk_path_measure_get_n_contours:
* @self: a `GskPathMeasure`
*
* Returns the number of contours in the path being measured.
*
* The returned value is independent of whether @self if restricted
* or not.
*
* Returns: The number of contours
**/
gsize
gsk_path_measure_get_n_contours (GskPathMeasure *self)
{
g_return_val_if_fail (self != NULL, 0);
return self->n_contours;
}
/**
* gsk_path_measure_restrict_to_contour:
* @self: a `GskPathMeasure`
* @contour: contour to restrict to or (gsize) -1 for using the
* whole path
*
* Restricts all functions on the path to just the given @contour.
*
* If @contour >= gsk_path_measure_get_n_contours() - so in
* particular when it is set to -1 - the whole path will be used.
**/
void
gsk_path_measure_restrict_to_contour (GskPathMeasure *self,
gsize contour)
{
if (contour >= self->n_contours)
{
/* use the whole path */
self->first = 0;
self->last = self->n_contours;
}
else
{
/* use just one contour */
self->first = contour;
self->last = contour + 1;
}
self->length = 0;
for (gsize i = self->first; i < self->last; i++)
{
self->length += self->measures[i].length;
}
}
/**
* 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;
}
/**
* gsk_path_measure_is_closed:
* @self: a `GskPathMeasure`
*
* Returns if the path being measured represents a single closed
* contour.
*
* Returns: %TRUE if the current path is closed
**/
gboolean
gsk_path_measure_is_closed (GskPathMeasure *self)
{
const GskContour *contour;
g_return_val_if_fail (self != NULL, FALSE);
/* XXX: is the empty path closed? Currently it's not */
if (self->last - self->first != 1)
return FALSE;
contour = gsk_path_get_contour (self->path, self->first);
return gsk_contour_get_flags (contour) & GSK_PATH_CLOSED ? TRUE : FALSE;
}
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: (optional) (out caller-allocates): The coordinates
* of the position at @distance
* @tangent: (optional) (out caller-allocates): 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 = self->first; i < self->last; i++)
{
if (distance < self->measures[i].length)
break;
distance -= self->measures[i].length;
}
/* weird corner cases */
if (i == self->last)
{
/* the empty path goes here */
if (self->first == self->last)
{
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->last - 1;
distance = self->measures[i].length;
}
gsk_contour_get_point (gsk_path_get_contour (self->path, i),
self->measures[i].contour_data,
distance,
pos,
tangent);
}
/**
* gsk_path_measure_get_curvature:
* @self: a `GskPathMeasure`
* @distance: distance into the path
* @center: (optional) (out caller-allocates): The center
* of the osculating circle at the point
*
* Calculates the curvature at the point @distance units into
* the path.
*
* Optionally, returns the center of the osculating circle as well.
*
* If the curvature is infinite (at line segments), or does
* not exist (at sharp turns), zero is returned, and @center
* is not modified.
*
* Returns: The curvature of the path at the given point
*/
float
gsk_path_measure_get_curvature (GskPathMeasure *self,
float distance,
graphene_point_t *center)
{
gsize i;
g_return_val_if_fail (self != NULL, 0);
distance = gsk_path_measure_clamp_distance (self, distance);
for (i = self->first; i < self->last; i++)
{
if (distance < self->measures[i].length)
break;
distance -= self->measures[i].length;
}
/* weird corner cases */
if (i == self->last)
{
/* the empty path goes here */
if (self->first == self->last)
return 0;
/* rounding errors can make this happen */
i = self->last - 1;
distance = self->measures[i].length;
}
return gsk_contour_get_curvature (gsk_path_get_contour (self->path, i),
self->measures[i].contour_data,
distance,
center);
}
/**
* gsk_path_measure_get_closest_point:
* @self: a `GskPathMeasure`
* @point: the point to find the closest point to
* @out_pos: (optional) (out caller-allocates): 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
* [method@Gsk.PathMeasure.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,
NULL,
out_pos,
&result,
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 find the closest point to
* @threshold: The maximum allowed distance between the path and @point.
* Use INFINITY to look for any point.
* @out_distance: (optional) (out caller-allocates): The
* distance between the found closest point on the path and the given
* @point.
* @out_pos: (optional) (out caller-allocates): return location
* for the closest point
* @out_offset: (optional) (out caller-allocates): The offset into
* the path of the found point
* @out_tangent: (optional) (out caller-allocates): 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 = self->first; i < self->last; i++)
{
if (gsk_contour_get_closest_point (gsk_path_get_contour (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,
graphene_point_t *point,
GskFillRule fill_rule)
{
int winding = 0;
gboolean on_edge = FALSE;
int i;
for (i = self->first; i < self->last; 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 ();
}
}
static void
gsk_path_builder_add_segment_chunk (GskPathBuilder *self,
GskPathMeasure *measure,
gboolean emit_move_to,
float start,
float end)
{
g_assert (start < end);
for (gsize i = measure->first; i < measure->last; i++)
{
if (measure->measures[i].length < start)
{
start -= measure->measures[i].length;
end -= measure->measures[i].length;
}
else if (start > 0 || end < measure->measures[i].length)
{
float len = MIN (end, measure->measures[i].length);
gsk_contour_add_segment (gsk_path_get_contour (measure->path, i),
self,
measure->measures[i].contour_data,
emit_move_to,
start,
len);
end -= len;
start = 0;
if (end <= 0)
break;
}
else
{
end -= measure->measures[i].length;
gsk_path_builder_add_contour (self, gsk_contour_dup (gsk_path_get_contour (measure->path, i)));
}
emit_move_to = TRUE;
}
}
/**
* gsk_path_builder_add_segment:
* @builder: a `GskPathBuilder`
* @measure: the `GskPathMeasure` to take the segment to
* @start: start distance into the path
* @end: end distance into the path
*
* Adds to @builder the segment of @measure from @start to @end.
*
* The distances are given relative to the length of @measure's path,
* from 0 for the beginning of the path to [method@Gsk.PathMeasure.get_length]
* for the end of the path. The values will be clamped to that range.
*
* If @start >= @end after clamping, the path will first add the segment
* from @start to the end of the path, and then add the segment from
* the beginning to @end. If the path is closed, these segments will
* be connected.
**/
void
gsk_path_builder_add_segment (GskPathBuilder *builder,
GskPathMeasure *measure,
float start,
float end)
{
g_return_if_fail (builder != NULL);
g_return_if_fail (measure != NULL);
start = gsk_path_measure_clamp_distance (measure, start);
end = gsk_path_measure_clamp_distance (measure, end);
if (start < end)
{
gsk_path_builder_add_segment_chunk (builder, measure, TRUE, start, end);
}
else
{
/* If the path is closed, we can connect the 2 subpaths. */
gboolean closed = gsk_path_measure_is_closed (measure);
gboolean need_move_to = !closed;
if (start < measure->length)
gsk_path_builder_add_segment_chunk (builder, measure,
TRUE,
start, measure->length);
else
need_move_to = TRUE;
if (end > 0)
gsk_path_builder_add_segment_chunk (builder, measure,
need_move_to,
0, end);
if (start == end && closed)
gsk_path_builder_close (builder);
}
}
-92
View File
@@ -1,92 +0,0 @@
/*
* 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
GskPath * gsk_path_measure_get_path (GskPathMeasure *self) G_GNUC_PURE;
GDK_AVAILABLE_IN_ALL
float gsk_path_measure_get_tolerance (GskPathMeasure *self) G_GNUC_PURE;
GDK_AVAILABLE_IN_ALL
gsize gsk_path_measure_get_n_contours (GskPathMeasure *self) G_GNUC_PURE;
GDK_AVAILABLE_IN_ALL
void gsk_path_measure_restrict_to_contour (GskPathMeasure *self,
gsize contour);
GDK_AVAILABLE_IN_ALL
float gsk_path_measure_get_length (GskPathMeasure *self);
GDK_AVAILABLE_IN_ALL
gboolean gsk_path_measure_is_closed (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_curvature (GskPathMeasure *self,
float distance,
graphene_point_t *center);
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
gboolean gsk_path_measure_in_fill (GskPathMeasure *self,
graphene_point_t *point,
GskFillRule fill_rule);
G_END_DECLS
#endif /* __GSK_PATH_MEASURE_H__ */
-179
View File
@@ -1,179 +0,0 @@
/*
* 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_PATHOP_PRIVATE_H__
#define __GSK_PATHOP_PRIVATE_H__
#include <gsk/gskpath.h>
#include <gsk/gskpathbuilder.h>
G_BEGIN_DECLS
typedef gpointer gskpathop;
static inline
gskpathop gsk_pathop_encode (GskPathOperation op,
const graphene_point_t *pts);
static inline
const graphene_point_t *gsk_pathop_points (gskpathop pop);
static inline
GskPathOperation gsk_pathop_op (gskpathop pop);
static inline
gboolean gsk_pathop_foreach (gskpathop pop,
GskPathForeachFunc func,
gpointer user_data);
/* included inline so tests can use them */
static inline
void gsk_path_builder_pathop_to (GskPathBuilder *builder,
gskpathop op);
static inline
void gsk_path_builder_pathop_reverse_to (GskPathBuilder *builder,
gskpathop op);
/* IMPLEMENTATION */
#define GSK_PATHOP_OPERATION_MASK (0x7)
static inline gskpathop
gsk_pathop_encode (GskPathOperation op,
const graphene_point_t *pts)
{
/* g_assert (op & GSK_PATHOP_OPERATION_MASK == op); */
g_assert ((GPOINTER_TO_SIZE (pts) & GSK_PATHOP_OPERATION_MASK) == 0);
return GSIZE_TO_POINTER (GPOINTER_TO_SIZE (pts) | op);
}
static inline const graphene_point_t *
gsk_pathop_points (gskpathop pop)
{
return GSIZE_TO_POINTER (GPOINTER_TO_SIZE (pop) & ~GSK_PATHOP_OPERATION_MASK);
}
static inline
GskPathOperation gsk_pathop_op (gskpathop pop)
{
return GPOINTER_TO_SIZE (pop) & GSK_PATHOP_OPERATION_MASK;
}
static inline gboolean
gsk_pathop_foreach (gskpathop pop,
GskPathForeachFunc func,
gpointer user_data)
{
switch (gsk_pathop_op (pop))
{
case GSK_PATH_MOVE:
return func (gsk_pathop_op (pop), gsk_pathop_points (pop), 1, 0, user_data);
case GSK_PATH_CLOSE:
case GSK_PATH_LINE:
return func (gsk_pathop_op (pop), gsk_pathop_points (pop), 2, 0, user_data);
case GSK_PATH_CURVE:
return func (gsk_pathop_op (pop), gsk_pathop_points (pop), 4, 0, user_data);
case GSK_PATH_CONIC:
{
const graphene_point_t *pts = gsk_pathop_points (pop);
return func (gsk_pathop_op (pop), (graphene_point_t[3]) { pts[0], pts[1], pts[3] }, 3, pts[2].x, user_data);
}
default:
g_assert_not_reached ();
return TRUE;
}
}
static inline void
gsk_path_builder_pathop_to (GskPathBuilder *builder,
gskpathop op)
{
const graphene_point_t *pts = gsk_pathop_points (op);
switch (gsk_pathop_op (op))
{
case GSK_PATH_MOVE:
gsk_path_builder_move_to (builder, pts[0].x, pts[0].y);
break;
case GSK_PATH_CLOSE:
gsk_path_builder_close (builder);
break;
case GSK_PATH_LINE:
gsk_path_builder_line_to (builder, pts[1].x, pts[1].y);
break;
case GSK_PATH_CURVE:
gsk_path_builder_curve_to (builder, pts[1].x, pts[1].y, pts[2].x, pts[2].y, pts[3].x, pts[3].y);
break;
case GSK_PATH_CONIC:
gsk_path_builder_conic_to (builder, pts[1].x, pts[1].y, pts[3].x, pts[3].y, pts[2].x);
break;
default:
g_assert_not_reached ();
break;
}
}
static inline void
gsk_path_builder_pathop_reverse_to (GskPathBuilder *builder,
gskpathop op)
{
const graphene_point_t *pts = gsk_pathop_points (op);
switch (gsk_pathop_op (op))
{
case GSK_PATH_MOVE:
gsk_path_builder_move_to (builder, pts[0].x, pts[0].y);
break;
case GSK_PATH_CLOSE:
gsk_path_builder_line_to (builder, pts[0].x, pts[0].y);
break;
case GSK_PATH_LINE:
gsk_path_builder_line_to (builder, pts[1].x, pts[1].y);
break;
case GSK_PATH_CURVE:
gsk_path_builder_curve_to (builder, pts[2].x, pts[2].y, pts[1].x, pts[1].y, pts[0].x, pts[0].y);
break;
case GSK_PATH_CONIC:
gsk_path_builder_conic_to (builder, pts[1].x, pts[1].y, pts[0].x, pts[0].y, pts[2].x);
break;
default:
g_assert_not_reached ();
break;
}
}
G_END_DECLS
#endif /* __GSK_PATHOP_PRIVATE_H__ */
-1442
View File
File diff suppressed because it is too large Load Diff
-79
View File
@@ -1,79 +0,0 @@
/*
* 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"
#include "gskcontourprivate.h"
#include "gskpathopprivate.h"
G_BEGIN_DECLS
/* Same as Skia, so looks like a good value. ¯\_(ツ)_/¯ */
#define GSK_PATH_TOLERANCE_DEFAULT (0.5)
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);
GskPathFlags gsk_path_get_flags (GskPath *self);
gboolean gsk_path_foreach_with_tolerance (GskPath *self,
GskPathForeachFlags flags,
double tolerance,
GskPathForeachFunc func,
gpointer user_data);
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);
typedef enum
{
GSK_PATH_OP_SIMPLIFY,
GSK_PATH_OP_UNION,
GSK_PATH_OP_INTERSECTION,
GSK_PATH_OP_DIFFERENCE,
GSK_PATH_OP_XOR
} GskPathOp;
GskPath * gsk_path_op (GskPathOp operation,
GskFillRule fill_rule,
GskPath *first,
GskPath *second);
G_END_DECLS
#endif /* __GSK_PATH_PRIVATE_H__ */
-2073
View File
File diff suppressed because it is too large Load Diff
-32
View File
@@ -158,8 +158,6 @@ 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,8 +184,6 @@ 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;
@@ -449,34 +445,6 @@ 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 (const GskRenderNode *node);
GDK_AVAILABLE_IN_ALL
GskPath * gsk_fill_node_get_path (const GskRenderNode *node);
GDK_AVAILABLE_IN_ALL
GskFillRule gsk_fill_node_get_fill_rule (const 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 (const GskRenderNode *node);
GDK_AVAILABLE_IN_ALL
GskPath * gsk_stroke_node_get_path (const GskRenderNode *node);
GDK_AVAILABLE_IN_ALL
GskPath * gsk_stroke_node_get_stroke_path (const GskRenderNode *node);
GDK_AVAILABLE_IN_ALL
const GskStroke * gsk_stroke_node_get_stroke (const GskRenderNode *node);
GDK_AVAILABLE_IN_ALL
GType gsk_shadow_node_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_ALL
-394
View File
@@ -23,10 +23,8 @@
#include "gskcairoblurprivate.h"
#include "gskdebugprivate.h"
#include "gskdiffprivate.h"
#include "gskpath.h"
#include "gskrendererprivate.h"
#include "gskroundedrectprivate.h"
#include "gskstrokeprivate.h"
#include "gsktransformprivate.h"
#include "gdk/gdktextureprivate.h"
@@ -3812,364 +3810,6 @@ 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);
}
}
/**
* 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 (const GskRenderNode *node)
{
const GskFillNode *self = (const 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 (const GskRenderNode *node)
{
const GskFillNode *self = (const 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 (const GskRenderNode *node)
{
const GskFillNode *self = (const 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;
GskPath *stroke_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_path_unref (self->stroke_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);
}
}
/**
* 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;
graphene_rect_t path_bounds;
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);
self->stroke_path = gsk_path_stroke (path, &self->stroke);
if (gsk_path_get_stroke_bounds (path, stroke, &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_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 (const GskRenderNode *node)
{
const GskStrokeNode *self = (const 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`
*
* Retrieves the path that will be stroked with the contents of
* the @node.
*
* Returns: (transfer none): a `GskPath`
*/
GskPath *
gsk_stroke_node_get_path (const GskRenderNode *node)
{
const GskStrokeNode *self = (const 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_path:
* @node: (type GskStrokeNode): a stroke `GskRenderNode`
*
* Retrieves the stroke path that will be filled.
*
* Returns: (transfer none): a `GskPath`
*/
GskPath *
gsk_stroke_node_get_stroke_path (const GskRenderNode *node)
{
const GskStrokeNode *self = (const GskStrokeNode *) node;
g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_STROKE_NODE), NULL);
return self->stroke_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 (const GskRenderNode *node)
{
const GskStrokeNode *self = (const GskStrokeNode *) node;
g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_STROKE_NODE), NULL);
return &self->stroke;
}
/*** GSK_SHADOW_NODE ***/
/**
@@ -5699,8 +5339,6 @@ 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)
@@ -6000,38 +5638,6 @@ gsk_render_node_init_types_once (void)
gsk_render_node_types[GSK_ROUNDED_CLIP_NODE] = node_type;
}
{
const GskRenderNodeTypeInfo node_info =
{
GSK_FILL_NODE,
sizeof (GskFillNode),
NULL,
gsk_fill_node_finalize,
gsk_fill_node_draw,
NULL,
gsk_fill_node_diff,
};
GType node_type = gsk_render_node_type_register_static (I_("GskFillNode"), &node_info);
gsk_render_node_types[GSK_FILL_NODE] = node_type;
}
{
const GskRenderNodeTypeInfo node_info =
{
GSK_STROKE_NODE,
sizeof (GskStrokeNode),
NULL,
gsk_stroke_node_finalize,
gsk_stroke_node_draw,
NULL,
gsk_stroke_node_diff,
};
GType node_type = gsk_render_node_type_register_static (I_("GskStrokeNode"), &node_info);
gsk_render_node_types[GSK_STROKE_NODE] = node_type;
}
{
const GskRenderNodeTypeInfo node_info =
{
+6 -354
View File
@@ -23,13 +23,9 @@
#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"
@@ -403,10 +399,7 @@ parse_string (GtkCssParser *parser,
token = gtk_css_parser_get_token (parser);
if (!gtk_css_token_is (token, GTK_CSS_TOKEN_STRING))
{
gtk_css_parser_error_syntax (parser, "Expected a string");
return FALSE;
}
return FALSE;
s = g_strdup (token->string.string);
gtk_css_parser_consume_token (parser);
@@ -962,26 +955,6 @@ 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)
{
@@ -1129,7 +1102,7 @@ parse_conic_gradient_node (GtkCssParser *parser)
g_array_append_val (stops, to);
}
result = gsk_conic_gradient_node_new (&bounds, &center, rotation,
result = gsk_conic_gradient_node_new (&bounds, &center, rotation,
(GskColorStop *) stops->data, stops->len);
g_array_free (stops, TRUE);
@@ -1415,7 +1388,7 @@ parse_cairo_node (GtkCssParser *parser)
parse_declarations (parser, declarations, G_N_ELEMENTS(declarations));
node = gsk_cairo_node_new (&bounds);
if (surface != NULL)
{
cairo_t *cr = gsk_cairo_node_get_draw_context (node);
@@ -1792,208 +1765,6 @@ parse_rounded_clip_node (GtkCssParser *parser)
return result;
}
static gboolean
parse_path (GtkCssParser *parser,
gpointer out_path)
{
GskPath *path;
char *str = NULL;
if (!parse_string (parser, &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,
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;
const GtkCssToken *token;
token = gtk_css_parser_get_token (parser);
if (!gtk_css_token_is (token, GTK_CSS_TOKEN_IDENT))
{
gtk_css_parser_error_syntax (parser, "Expected a valid identifier");
return FALSE;
}
class = g_type_class_ref (type);
v = g_enum_get_value_by_nick (class, token->string.string);
if (v == NULL)
{
gtk_css_parser_error_value (parser, "\"%s\" is not a valid identifier here", token->string.string);
g_type_class_unref (class);
return FALSE;
}
*(int*)out_value = v->value;
g_type_class_unref (class);
gtk_css_parser_consume_token (parser);
return TRUE;
}
static gboolean
parse_fill_rule (GtkCssParser *parser,
gpointer out_rule)
{
return parse_enum (parser, GSK_TYPE_FILL_RULE, out_rule);
}
static GskRenderNode *
parse_fill_node (GtkCssParser *parser)
{
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, 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,
gpointer out)
{
return parse_enum (parser, GSK_TYPE_LINE_CAP, out);
}
static gboolean
parse_line_join (GtkCssParser *parser,
gpointer out)
{
return parse_enum (parser, GSK_TYPE_LINE_JOIN, out);
}
static GskRenderNode *
parse_stroke_node (GtkCssParser *parser)
{
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, 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)
{
@@ -2063,7 +1834,6 @@ parse_node (GtkCssParser *parser,
{ "container", parse_container_node },
{ "cross-fade", parse_cross_fade_node },
{ "debug", parse_debug_node },
{ "fill", parse_fill_node },
{ "inset-shadow", parse_inset_shadow_node },
{ "linear-gradient", parse_linear_gradient_node },
{ "radial-gradient", parse_radial_gradient_node },
@@ -2075,7 +1845,6 @@ parse_node (GtkCssParser *parser,
{ "repeating-radial-gradient", parse_repeating_radial_gradient_node },
{ "rounded-clip", parse_rounded_clip_node },
{ "shadow", parse_shadow_node },
{ "stroke", parse_stroke_node },
{ "text", parse_text_node },
{ "texture", parse_texture_node },
{ "transform", parse_transform_node },
@@ -2331,7 +2100,7 @@ append_float_param (Printer *p,
float value,
float default_value)
{
/* Don't approximate-compare here, better be too verbose */
/* Don't approximate-compare here, better be topo verbose */
if (value == default_value)
return;
@@ -2506,11 +2275,8 @@ append_escaping_newlines (GString *str,
len = strcspn (string, "\n");
g_string_append_len (str, string, len);
string += len;
if (*string)
{
g_string_append (str, "\\\n");
string++;
}
g_string_append (str, "\\\n");
string++;
} while (*string);
}
@@ -2625,83 +2391,6 @@ 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[1] == 'O'))
*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)
@@ -2850,42 +2539,6 @@ 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");
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");
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);
}
@@ -3392,4 +3045,3 @@ gsk_render_node_serialize (GskRenderNode *node)
return g_string_free_to_bytes (p.str);
}
-208
View File
@@ -1,208 +0,0 @@
/*
* 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>
/* 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);
}
-41
View File
@@ -1,41 +0,0 @@
/*
* 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 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__ */
-458
View File
@@ -1,458 +0,0 @@
/*
* 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"
/**
* GskStroke:
*
* A `GskStroke` describes the attributes that are used when stroking a path.
*/
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 [method@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);
}
/**
* gsk_stroke_to_cairo:
* @self: a #GskStroke
* @cr: the cairo context to configure
*
* A helper function that sets the stroke parameters
* of @cr from the values found in @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:
case GSK_LINE_JOIN_ARCS:
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 two strokes are identical.
*
* Returns: %TRUE if the two strokes are equal, %FALSE otherwise
**/
gboolean
gsk_stroke_equal (gconstpointer stroke1,
gconstpointer stroke2)
{
const GskStroke *self1 = stroke1;
const GskStroke *self2 = stroke2;
if (self1->line_width != self2->line_width ||
self1->line_cap != self2->line_cap ||
self1->line_join != self2->line_join ||
self1->miter_limit != self2->miter_limit)
return FALSE;
if (self1->n_dash != self2->n_dash)
return FALSE;
for (int i = 0; i < self1->n_dash; i++)
{
if (self1->dash[i] != self2->dash[i])
return FALSE;
}
if (self1->dash_offset != self2->dash_offset)
return FALSE;
return TRUE;
}
/**
* 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) (allow-none): 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 [method@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) (allow-none):
* 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 [method@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;
}
-91
View File
@@ -1,91 +0,0 @@
/*
* 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);
GDK_AVAILABLE_IN_ALL
void gsk_stroke_to_cairo (const GskStroke *self,
cairo_t *cr);
G_END_DECLS
#endif /* __GSK_STROKE_H__ */
-60
View File
@@ -1,60 +0,0 @@
/*
* 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 */
}
G_END_DECLS
#endif /* __GSK_STROKE_PRIVATE_H__ */
-4
View File
@@ -26,11 +26,7 @@
#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;
#endif /* __GSK_TYPES_H__ */
+1 -19
View File
@@ -22,41 +22,27 @@ gsk_private_gl_shaders = [
]
gsk_public_sources = files([
'gskcairorenderer.c',
'gskdiff.c',
'gskcairorenderer.c',
'gskglshader.c',
'gskpath.c',
'gskpathbuilder.c',
'gskpathdash.c',
'gskpathmeasure.c',
'gskpathops.c',
'gskpathstroke.c',
'gskrenderer.c',
'gskrendernode.c',
'gskrendernodeimpl.c',
'gskrendernodeparser.c',
'gskroundedrect.c',
'gskstroke.c',
'gsktransform.c',
'gl/gskglrenderer.c',
])
gsk_private_sources = files([
'gskboundingbox.c',
'gskcairoblur.c',
'gskcontour.c',
'gskconvexity.c',
'gskcurve.c',
'gskcurveintersect.c',
'gskdebug.c',
'gskprivate.c',
'gskprofiler.c',
'gskspline.c',
'gl/gskglattachmentstate.c',
'gl/gskglbuffer.c',
'gl/gskglcommandqueue.c',
'gl/gskglcompiler.c',
'gl/gskglprofiler.c',
'gl/gskgldriver.c',
'gl/gskglglyphlibrary.c',
'gl/gskgliconlibrary.c',
@@ -79,13 +65,9 @@ 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',
'gsk-autocleanup.h',
-2
View File
@@ -260,8 +260,6 @@ gsk_vulkan_render_pass_add_node (GskVulkanRenderPass *self,
case GSK_RADIAL_GRADIENT_NODE:
case GSK_REPEATING_RADIAL_GRADIENT_NODE:
case GSK_CONIC_GRADIENT_NODE:
case GSK_FILL_NODE:
case GSK_STROKE_NODE:
default:
FALLBACK ("Unsupported node '%s'", g_type_name_from_instance ((GTypeInstance *) node));
+2 -3
View File
@@ -1703,9 +1703,8 @@ gtk_column_view_sort_by_column (GtkColumnView *self,
g_return_if_fail (column == NULL || GTK_IS_COLUMN_VIEW_COLUMN (column));
g_return_if_fail (column == NULL || gtk_column_view_column_get_column_view (column) == self);
if (column == NULL)
gtk_column_view_sorter_clear (GTK_COLUMN_VIEW_SORTER (self->sorter));
else
gtk_column_view_sorter_clear (GTK_COLUMN_VIEW_SORTER (self->sorter));
if (column != NULL)
gtk_column_view_sorter_set_column (GTK_COLUMN_VIEW_SORTER (self->sorter),
column,
direction == GTK_SORT_DESCENDING);
+1 -1
View File
@@ -125,7 +125,7 @@
* a `GtkExpression` object is needed like in a `<property>` tag for an expression
* property, or in a `<binding name="property">` tag to bind a property to an expression.
*
* To create an property expression, use the `<lookup>` element. It can have a `type`
* To create a property expression, use the `<lookup>` element. It can have a `type`
* attribute to specify the object type, and a `name` attribute to specify the property
* to look up. The content of `<lookup>` can either be an element specfiying the expression
* to use the object, or a string that specifies the name of the object to use.
+7 -1
View File
@@ -2193,7 +2193,7 @@ update_default (GtkFileChooserWidget *impl)
return;
files = gtk_file_chooser_get_files (GTK_FILE_CHOOSER (impl));
sensitive = (g_list_model_get_n_items (files) > 0);
sensitive = (g_list_model_get_n_items (files) > 0 || impl->action == GTK_FILE_CHOOSER_ACTION_SAVE);
gtk_widget_set_sensitive (button, sensitive);
g_object_unref (files);
@@ -4748,9 +4748,15 @@ update_chooser_entry (GtkFileChooserWidget *impl)
if (change_entry && !impl->auto_selecting_first_row)
{
GtkEntryCompletion *completion = gtk_entry_get_completion (GTK_ENTRY (impl->location_entry));
if (completion)
gtk_entry_completion_set_popup_completion (completion, FALSE);
g_signal_handlers_block_by_func (impl->location_entry, G_CALLBACK (location_entry_changed_cb), impl);
gtk_editable_set_text (GTK_EDITABLE (impl->location_entry), impl->browse_files_last_selected_name);
g_signal_handlers_unblock_by_func (impl->location_entry, G_CALLBACK (location_entry_changed_cb), impl);
if (completion)
gtk_entry_completion_set_popup_completion (completion, TRUE);
if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
_gtk_file_chooser_entry_select_filename (GTK_FILE_CHOOSER_ENTRY (impl->location_entry));
+23 -9
View File
@@ -1793,6 +1793,7 @@ gtk_label_focus (GtkWidget *widget,
{
info->selection_anchor = focus_link->start;
info->selection_end = focus_link->start;
break;
}
}
}
@@ -1856,7 +1857,6 @@ gtk_label_focus (GtkWidget *widget,
{
int focus_link_index;
int new_index = -1;
int i;
if (info->n_links == 0)
goto out;
@@ -1870,29 +1870,39 @@ gtk_label_focus (GtkWidget *widget,
{
case GTK_DIR_TAB_FORWARD:
if (focus_link)
new_index = (focus_link_index + 1) % info->n_links;
new_index = focus_link_index + 1;
else
new_index = 0;
for (i = new_index; i < info->n_links; i++)
if (new_index >= info->n_links)
goto out;
while (new_index < info->n_links)
{
const GtkLabelLink *link = &info->links[i];
const GtkLabelLink *link = &info->links[new_index];
if (!range_is_in_ellipsis (self, link->start, link->end))
break;
new_index++;
}
break;
case GTK_DIR_TAB_BACKWARD:
if (focus_link)
new_index = focus_link_index == 0 ? info->n_links - 1 : focus_link_index - 1;
new_index = focus_link_index - 1;
else
new_index = info->n_links - 1;
for (i = new_index; i >= 0; i--)
if (new_index < 0)
goto out;
while (new_index >= 0)
{
const GtkLabelLink *link = &info->links[i];
const GtkLabelLink *link = &info->links[new_index];
if (!range_is_in_ellipsis (self, link->start, link->end))
break;
new_index--;
}
break;
@@ -1904,7 +1914,7 @@ gtk_label_focus (GtkWidget *widget,
goto out;
}
if (new_index != -1)
if (new_index != -1 && new_index < info->n_links)
{
focus_link = &info->links[new_index];
info->selection_anchor = focus_link->start;
@@ -3615,7 +3625,11 @@ static void
gtk_label_ensure_has_tooltip (GtkLabel *self)
{
guint i;
gboolean has_tooltip = FALSE;
gboolean has_tooltip = gtk_widget_get_has_tooltip(GTK_WIDGET(self));
if (has_tooltip) {
return;
}
for (i = 0; i < self->select_info->n_links; i++)
{
+43 -40
View File
@@ -44,7 +44,7 @@
/**
* GtkPaned:
*
* `GtkPaned` has two panes, arranged either horizontally or vertically.
* A widget with two panes, arranged either horizontally or vertically.
*
* ![An example GtkPaned](panes.png)
*
@@ -63,13 +63,13 @@
* each child inside a [class@Gtk.Frame] so that the gutter appears as a
* ridge. No separator is drawn if one of the children is missing.
*
* Each child has two options that can be set, @resize and @shrink. If
* @resize is true, then when the `GtkPaned` is resized, that child will
* expand or shrink along with the paned widget. If @shrink is true, then
* Each child has two options that can be set, "resize" and "shrink". If
* "resize" is true then, when the `GtkPaned` is resized, that child will
* expand or shrink along with the paned widget. If "shrink" is true, then
* that child can be made smaller than its requisition by the user.
* Setting @shrink to %FALSE allows the application to set a minimum size.
* If @resize is false for both children, then this is treated as if
* @resize is true for both children.
* Setting "shrink" to false allows the application to set a minimum size.
* If "resize" is false for both children, then this is treated as if
* "resize" is true for both children.
*
* The application can set the position of the slider as if it were set
* by the user, by calling [method@Gtk.Paned.set_position].
@@ -431,7 +431,7 @@ gtk_paned_class_init (GtkPanedClass *class)
/**
* GtkPaned:position-set:
*
* %TRUE if the `position` property has been set.
* Whether the [property@Gtk.Paned:position] property has been set.
*/
paned_props[PROP_POSITION_SET] =
g_param_spec_boolean ("position-set",
@@ -443,7 +443,8 @@ gtk_paned_class_init (GtkPanedClass *class)
/**
* GtkPaned:min-position:
*
* The smallest possible value for the position property.
* The smallest possible value for the [property@Gtk.Paned:position]
* property.
*
* This property is derived from the size and shrinkability
* of the widget's children.
@@ -458,7 +459,8 @@ gtk_paned_class_init (GtkPanedClass *class)
/**
* GtkPaned:max-position:
*
* The largest possible value for the position property.
* The largest possible value for the [property@Gtk.Paned:position]
* property.
*
* This property is derived from the size and shrinkability
* of the widget's children.
@@ -573,7 +575,7 @@ gtk_paned_class_init (GtkPanedClass *class)
*
* This is a [keybinding signal](class.SignalAction.html).
*
* The default binding is F6.
* The default binding is <kbd>F6</kbd>.
*/
signals [CYCLE_CHILD_FOCUS] =
g_signal_new (I_("cycle-child-focus"),
@@ -594,7 +596,7 @@ gtk_paned_class_init (GtkPanedClass *class)
*
* This is a [keybinding signal](class.SignalAction.html).
*
* The default binding is Tab.
* The default binding is <kbd>Tab</kbd>.
*/
signals [TOGGLE_HANDLE_FOCUS] =
g_signal_new (I_("toggle-handle-focus"),
@@ -634,7 +636,7 @@ gtk_paned_class_init (GtkPanedClass *class)
*
* This is a [keybinding signal](class.SignalAction.html).
*
* The default binding for this signal is F8.
* The default binding for this signal is <kbd>F8</kbd>.
*/
signals [CYCLE_HANDLE_FOCUS] =
g_signal_new (I_("cycle-handle-focus"),
@@ -655,7 +657,8 @@ gtk_paned_class_init (GtkPanedClass *class)
*
* This is a [keybinding signal](class.SignalAction.html).
*
* The default binding for this signal is Return or Space.
* The default binding for this signal is <kbd>Return</kbd> or
* <kbd>Space</kbd>.
*/
signals [ACCEPT_POSITION] =
g_signal_new (I_("accept-position"),
@@ -678,7 +681,7 @@ gtk_paned_class_init (GtkPanedClass *class)
*
* This is a [keybinding signal](class.SignalAction.html).
*
* The default binding for this signal is Escape.
* The default binding for this signal is <kbd>Escape</kbd>.
*/
signals [CANCEL_POSITION] =
g_signal_new (I_("cancel-position"),
@@ -1555,7 +1558,7 @@ gtk_paned_css_changed (GtkWidget *widget,
*
* Creates a new `GtkPaned` widget.
*
* Returns: a new `GtkPaned`.
* Returns: the newly created paned widget
*/
GtkWidget *
gtk_paned_new (GtkOrientation orientation)
@@ -1568,9 +1571,11 @@ gtk_paned_new (GtkOrientation orientation)
/**
* gtk_paned_set_start_child: (attributes org.gtk.Method.set_property=start-child)
* @paned: a `GtkPaned`
* @child: the widget to add
* @child: (nullable): the widget to add
*
* Sets the start child of @paned to @child.
*
* If @child is `NULL`, the existing child will be removed.
*/
void
gtk_paned_set_start_child (GtkPaned *paned,
@@ -1596,8 +1601,6 @@ gtk_paned_set_start_child (GtkPaned *paned,
*
* Retrieves the start child of the given `GtkPaned`.
*
* See also: `GtkPaned`:start-child
*
* Returns: (transfer none) (nullable): the start child widget
*/
GtkWidget *
@@ -1611,9 +1614,9 @@ gtk_paned_get_start_child (GtkPaned *paned)
/**
* gtk_paned_set_resize_start_child: (attributes org.gtk.Method.set_property=resize-start-child)
* @paned: a `GtkPaned`
* @resize: %TRUE to let the start child be resized
* @resize: true to let the start child be resized
*
* Sets the `GtkPaned`:resize-start-child property
* Sets whether the [property@Gtk.Paned:start-child] can be resized.
*/
void
gtk_paned_set_resize_start_child (GtkPaned *paned,
@@ -1633,9 +1636,9 @@ gtk_paned_set_resize_start_child (GtkPaned *paned,
* gtk_paned_get_resize_start_child: (attributes org.gtk.Method.get_property=resize-start-child)
* @paned: a `GtkPaned`
*
* Returns whether the start child can be resized.
* Returns whether the [property@Gtk.Paned:start-child] can be resized.
*
* Returns: %TRUE if the start child is resizable
* Returns: true if the start child is resizable
*/
gboolean
gtk_paned_get_resize_start_child (GtkPaned *paned)
@@ -1648,9 +1651,9 @@ gtk_paned_get_resize_start_child (GtkPaned *paned)
/**
* gtk_paned_set_shrink_start_child: (attributes org.gtk.Method.set_property=shrink-start-child)
* @paned: a `GtkPaned`
* @resize: %TRUE to let the start child be shrunk
* @resize: true to let the start child be shrunk
*
* Sets the `GtkPaned`:shrink-start-child property
* Sets whether the [property@Gtk.Paned:start-child] can shrink.
*/
void
gtk_paned_set_shrink_start_child (GtkPaned *paned,
@@ -1670,9 +1673,9 @@ gtk_paned_set_shrink_start_child (GtkPaned *paned,
* gtk_paned_get_shrink_start_child: (attributes org.gtk.Method.get_property=shrink-start-child)
* @paned: a `GtkPaned`
*
* Returns whether the start child can be shrunk.
* Returns whether the [property@Gtk.Paned:start-child] can shrink.
*
* Returns: %TRUE if the start child is shrinkable
* Returns: true if the start child is shrinkable
*/
gboolean
gtk_paned_get_shrink_start_child (GtkPaned *paned)
@@ -1685,9 +1688,11 @@ gtk_paned_get_shrink_start_child (GtkPaned *paned)
/**
* gtk_paned_set_end_child: (attributes org.gtk.Method.set_property=end-child)
* @paned: a `GtkPaned`
* @child: the widget to add
* @child: (nullable): the widget to add
*
* Sets the end child of @paned to @child.
*
* If @child is `NULL`, the existing child will be removed.
*/
void
gtk_paned_set_end_child (GtkPaned *paned,
@@ -1713,8 +1718,6 @@ gtk_paned_set_end_child (GtkPaned *paned,
*
* Retrieves the end child of the given `GtkPaned`.
*
* See also: `GtkPaned`:end-child
*
* Returns: (transfer none) (nullable): the end child widget
*/
GtkWidget *
@@ -1728,9 +1731,9 @@ gtk_paned_get_end_child (GtkPaned *paned)
/**
* gtk_paned_set_resize_end_child: (attributes org.gtk.Method.set_property=resize-end-child)
* @paned: a `GtkPaned`
* @resize: %TRUE to let the end child be resized
* @resize: true to let the end child be resized
*
* Sets the `GtkPaned`:resize-end-child property
* Sets whether the [property@Gtk.Paned:end-child] can be resized.
*/
void
gtk_paned_set_resize_end_child (GtkPaned *paned,
@@ -1750,9 +1753,9 @@ gtk_paned_set_resize_end_child (GtkPaned *paned,
* gtk_paned_get_resize_end_child: (attributes org.gtk.Method.get_property=resize-end-child)
* @paned: a `GtkPaned`
*
* Returns whether the end child can be resized.
* Returns whether the [property@Gtk.Paned:end-child] can be resized.
*
* Returns: %TRUE if the end child is resizable
* Returns: true if the end child is resizable
*/
gboolean
gtk_paned_get_resize_end_child (GtkPaned *paned)
@@ -1765,9 +1768,9 @@ gtk_paned_get_resize_end_child (GtkPaned *paned)
/**
* gtk_paned_set_shrink_end_child: (attributes org.gtk.Method.set_property=shrink-end-child)
* @paned: a `GtkPaned`
* @resize: %TRUE to let the end child be shrunk
* @resize: true to let the end child be shrunk
*
* Sets the `GtkPaned`:shrink-end-child property
* Sets whether the [property@Gtk.Paned:end-child] can shrink.
*/
void
gtk_paned_set_shrink_end_child (GtkPaned *paned,
@@ -1787,9 +1790,9 @@ gtk_paned_set_shrink_end_child (GtkPaned *paned,
* gtk_paned_get_shrink_end_child: (attributes org.gtk.Method.get_property=shrink-end-child)
* @paned: a `GtkPaned`
*
* Returns whether the end child can be shrunk.
* Returns whether the [property@Gtk.Paned:end-child] can shrink.
*
* Returns: %TRUE if the end child is shrinkable
* Returns: true if the end child is shrinkable
*/
gboolean
gtk_paned_get_shrink_end_child (GtkPaned *paned)
@@ -1805,7 +1808,7 @@ gtk_paned_get_shrink_end_child (GtkPaned *paned)
*
* Obtains the position of the divider between the two panes.
*
* Returns: position of the divider
* Returns: the position of the divider, in pixels
**/
int
gtk_paned_get_position (GtkPaned *paned)
@@ -1822,7 +1825,7 @@ gtk_paned_get_position (GtkPaned *paned)
* is unset
*
* Sets the position of the divider between the two panes.
**/
*/
void
gtk_paned_set_position (GtkPaned *paned,
int position)
+21 -40
View File
@@ -1291,28 +1291,6 @@ get_border (GtkCssNode *node,
border->left = _gtk_css_number_value_get (style->border->border_left_width, 100);
}
static GskPath *
gtk_popover_get_tail_path (GtkPopover *popover)
{
GskPathBuilder *builder;
int initial_x, initial_y;
int tip_x, tip_y;
int final_x, final_y;
builder = gsk_path_builder_new ();
gtk_popover_get_gap_coords (popover,
&initial_x, &initial_y,
&tip_x, &tip_y,
&final_x, &final_y);
gsk_path_builder_move_to (builder, initial_x, initial_y);
gsk_path_builder_line_to (builder, tip_x, tip_y);
gsk_path_builder_line_to (builder, final_x, final_y);
return gsk_path_builder_free_to_path (builder);
}
static void
gtk_popover_apply_tail_path (GtkPopover *popover,
cairo_t *cr)
@@ -1570,14 +1548,22 @@ create_arrow_render_node (GtkPopover *popover)
GtkWidget *widget = GTK_WIDGET (popover);
GtkStyleContext *context;
GtkBorder border;
cairo_t *cr;
GtkSnapshot *snapshot;
GskPath *path;
snapshot = gtk_snapshot_new ();
cr = gtk_snapshot_append_cairo (snapshot,
&GRAPHENE_RECT_INIT (
0, 0,
gtk_widget_get_width (widget),
gtk_widget_get_height (widget)
));
/* Clip to the arrow shape */
path = gtk_popover_get_tail_path (popover);
gtk_snapshot_push_fill (snapshot, path, GSK_FILL_RULE_WINDING);
cairo_save (cr);
gtk_popover_apply_tail_path (popover, cr);
cairo_clip (cr);
get_border (priv->arrow_node, &border);
@@ -1585,34 +1571,29 @@ create_arrow_render_node (GtkPopover *popover)
gtk_style_context_save_to_node (context, priv->arrow_node);
/* Render the arrow background */
gtk_snapshot_render_background (snapshot, context,
0, 0,
gtk_widget_get_width (widget),
gtk_widget_get_height (widget));
gtk_render_background (context, cr,
0, 0,
gtk_widget_get_width (widget),
gtk_widget_get_height (widget));
/* Render the border of the arrow tip */
if (border.bottom > 0)
{
GtkCssStyle *style;
const GdkRGBA *border_color;
GskStroke *stroke;
graphene_rect_t bounds;
style = gtk_css_node_get_style (priv->arrow_node);
border_color = gtk_css_color_value_get_rgba (style->border->border_left_color ? style->border->border_left_color : style->core->color);
stroke = gsk_stroke_new (border.bottom + 1);
gtk_snapshot_push_stroke (snapshot, path, stroke);
gsk_stroke_free (stroke);
gtk_popover_apply_tail_path (popover, cr);
gdk_cairo_set_source_rgba (cr, border_color);
gsk_path_get_bounds (path, &bounds);
gtk_snapshot_append_color (snapshot, border_color, &bounds);
gtk_snapshot_pop (snapshot);
cairo_set_line_width (cr, border.bottom + 1);
cairo_stroke (cr);
}
gtk_snapshot_pop (snapshot);
gsk_path_unref (path);
cairo_restore (cr);
cairo_destroy (cr);
gtk_style_context_restore (context);
+84 -151
View File
@@ -269,11 +269,11 @@ gtk_border_image_render (GtkBorderImage *image,
source_width, source_height);
gtk_border_image_compute_slice_size (horizontal_slice,
source_width,
source_width,
_gtk_css_number_value_get (_gtk_css_border_value_get_left (image->slice), source_width),
_gtk_css_number_value_get (_gtk_css_border_value_get_right (image->slice), source_width));
gtk_border_image_compute_slice_size (vertical_slice,
source_height,
source_height,
_gtk_css_number_value_get (_gtk_css_border_value_get_top (image->slice), source_height),
_gtk_css_number_value_get (_gtk_css_border_value_get_bottom (image->slice), source_height));
gtk_border_image_compute_border_size (horizontal_border,
@@ -290,7 +290,7 @@ gtk_border_image_render (GtkBorderImage *image,
border_width[GTK_CSS_BOTTOM],
_gtk_css_border_value_get_top (image->width),
_gtk_css_border_value_get_bottom(image->width));
for (v = 0; v < 3; v++)
{
if (vertical_slice[v].size == 0 ||
@@ -357,16 +357,16 @@ snapshot_frame_fill (GtkSnapshot *snapshot,
gtk_snapshot_append_border (snapshot, outline, border_width, colors);
}
static GskStroke *
create_stroke_style (double line_width,
GtkBorderStyle style,
double length)
static void
set_stroke_style (cairo_t *cr,
double line_width,
GtkBorderStyle style,
double length)
{
GskStroke *stroke;
float segments[2];
double segments[2];
double n;
stroke = gsk_stroke_new (line_width);
cairo_set_line_width (cr, line_width);
if (style == GTK_BORDER_STYLE_DOTTED)
{
@@ -374,12 +374,12 @@ create_stroke_style (double line_width,
segments[0] = 0;
segments[1] = n ? length / n : 2;
gsk_stroke_set_dash (stroke, segments, 2);
cairo_set_dash (cr, segments, G_N_ELEMENTS (segments), 0);
gsk_stroke_set_line_cap (stroke, GSK_LINE_CAP_ROUND);
gsk_stroke_set_line_join (stroke, GSK_LINE_JOIN_ROUND);
cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
}
else if (style == GTK_BORDER_STYLE_DASHED)
else
{
n = length / line_width;
/* Optimize the common case of an integer-sized rectangle
@@ -397,33 +397,32 @@ create_stroke_style (double line_width,
segments[0] = n ? (1. / 3) * length / n : 1;
segments[1] = 2 * segments[0];
}
gsk_stroke_set_dash (stroke, segments, 2);
cairo_set_dash (cr, segments, G_N_ELEMENTS (segments), 0);
gsk_stroke_set_line_cap (stroke, GSK_LINE_CAP_SQUARE);
gsk_stroke_set_line_join (stroke, GSK_LINE_JOIN_MITER);
cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
cairo_set_line_join (cr, CAIRO_LINE_JOIN_MITER);
}
else
{
g_assert_not_reached ();
}
return stroke;
}
static void
snapshot_frame_stroke (GtkSnapshot *snapshot,
const GskRoundedRect *border_box,
const float border_width[4],
GdkRGBA colors[4],
guint hidden_side,
GtkBorderStyle stroke_style)
render_frame_stroke (cairo_t *cr,
const GskRoundedRect *border_box,
const double border_width[4],
GdkRGBA colors[4],
guint hidden_side,
GtkBorderStyle stroke_style)
{
gboolean different_colors, different_borders;
GskRoundedRect stroke_box;
GskPathBuilder *builder;
GskPath *path;
GskStroke *stroke;
guint i;
different_colors = !gdk_rgba_equal (&colors[0], &colors[1]) ||
!gdk_rgba_equal (&colors[0], &colors[2]) ||
!gdk_rgba_equal (&colors[0], &colors[3]);
different_borders = border_width[0] != border_width[1] ||
border_width[0] != border_width[2] ||
border_width[0] != border_width[3] ;
stroke_box = *border_box;
gsk_rounded_rect_shrink (&stroke_box,
border_width[GTK_CSS_TOP] / 2.0,
@@ -431,36 +430,32 @@ snapshot_frame_stroke (GtkSnapshot *snapshot,
border_width[GTK_CSS_BOTTOM] / 2.0,
border_width[GTK_CSS_LEFT] / 2.0);
if (border_width[0] == border_width[1] &&
border_width[0] == border_width[2] &&
border_width[0] == border_width[3] &&
hidden_side == 0)
if (!different_colors && !different_borders && hidden_side == 0)
{
double length = 0;
/* FAST PATH:
* Mostly expected to trigger for focus rectangles */
for (i = 0; i < 4; i++)
for (i = 0; i < 4; i++)
{
length += _gtk_rounded_box_guess_length (&stroke_box, i);
}
builder = gsk_path_builder_new ();
gsk_path_builder_add_rounded_rect (builder, &stroke_box);
path = gsk_path_builder_free_to_path (builder);
stroke = create_stroke_style (border_width[0],
stroke_style, length);
gtk_snapshot_push_stroke (snapshot, path, stroke);
gsk_stroke_free (stroke);
gsk_path_unref (path);
gtk_snapshot_append_border (snapshot, border_box, border_width, colors);
gtk_snapshot_pop (snapshot);
gsk_rounded_rect_path (&stroke_box, cr);
gdk_cairo_set_source_rgba (cr, &colors[0]);
set_stroke_style (cr, border_width[0], stroke_style, length);
cairo_stroke (cr);
}
else
{
const float weight = M_SQRT1_2;
GskRoundedRect padding_box;
padding_box = *border_box;
gsk_rounded_rect_shrink (&padding_box,
border_width[GTK_CSS_TOP],
border_width[GTK_CSS_RIGHT],
border_width[GTK_CSS_BOTTOM],
border_width[GTK_CSS_LEFT]);
for (i = 0; i < 4; i++)
{
@@ -470,111 +465,49 @@ snapshot_frame_stroke (GtkSnapshot *snapshot,
if (border_width[i] == 0)
continue;
builder = gsk_path_builder_new ();
cairo_save (cr);
if (i == 0)
{
/* top */
gsk_path_builder_move_to (builder,
stroke_box.bounds.origin.x + stroke_box.corner[GSK_CORNER_TOP_LEFT].width / 2,
stroke_box.bounds.origin.y + stroke_box.corner[GSK_CORNER_TOP_LEFT].height / 2);
gsk_path_builder_conic_to (builder,
stroke_box.bounds.origin.x,
stroke_box.bounds.origin.y,
stroke_box.bounds.origin.x + stroke_box.corner[GSK_CORNER_TOP_LEFT].width,
stroke_box.bounds.origin.y,
weight);
gsk_path_builder_line_to (builder,
stroke_box.bounds.origin.x + stroke_box.bounds.size.width - stroke_box.corner[GSK_CORNER_TOP_RIGHT].width,
stroke_box.bounds.origin.y);
gsk_path_builder_conic_to (builder,
stroke_box.bounds.origin.x + stroke_box.bounds.size.width,
stroke_box.bounds.origin.y,
stroke_box.bounds.origin.x + stroke_box.bounds.size.width - stroke_box.corner[GSK_CORNER_TOP_RIGHT].width / 2,
stroke_box.bounds.origin.y + stroke_box.corner[GSK_CORNER_TOP_RIGHT].height / 2,
weight);
}
_gtk_rounded_box_path_top (border_box, &padding_box, cr);
else if (i == 1)
{
/* right */
gsk_path_builder_move_to (builder,
stroke_box.bounds.origin.x + stroke_box.bounds.size.width - stroke_box.corner[GSK_CORNER_TOP_RIGHT].width / 2,
stroke_box.bounds.origin.y + stroke_box.corner[GSK_CORNER_TOP_RIGHT].height / 2);
gsk_path_builder_conic_to (builder,
stroke_box.bounds.origin.x + stroke_box.bounds.size.width,
stroke_box.bounds.origin.y,
stroke_box.bounds.origin.x + stroke_box.bounds.size.width,
stroke_box.bounds.origin.y + stroke_box.corner[GSK_CORNER_TOP_RIGHT].height,
weight);
gsk_path_builder_line_to (builder,
stroke_box.bounds.origin.x + stroke_box.bounds.size.width,
stroke_box.bounds.origin.y + stroke_box.bounds.size.height - stroke_box.corner[GSK_CORNER_BOTTOM_RIGHT].height);
gsk_path_builder_conic_to (builder,
stroke_box.bounds.origin.x + stroke_box.bounds.size.width,
stroke_box.bounds.origin.y + stroke_box.bounds.size.height,
stroke_box.bounds.origin.x + stroke_box.bounds.size.width - stroke_box.corner[GSK_CORNER_BOTTOM_RIGHT].width / 2,
stroke_box.bounds.origin.y + stroke_box.bounds.size.height - stroke_box.corner[GSK_CORNER_BOTTOM_RIGHT].height / 2,
weight);
}
_gtk_rounded_box_path_right (border_box, &padding_box, cr);
else if (i == 2)
{
/* bottom */
gsk_path_builder_move_to (builder,
stroke_box.bounds.origin.x + stroke_box.bounds.size.width - stroke_box.corner[GSK_CORNER_BOTTOM_RIGHT].width / 2,
stroke_box.bounds.origin.y + stroke_box.bounds.size.height - stroke_box.corner[GSK_CORNER_BOTTOM_RIGHT].height / 2);
gsk_path_builder_conic_to (builder,
stroke_box.bounds.origin.x + stroke_box.bounds.size.width,
stroke_box.bounds.origin.y + stroke_box.bounds.size.height,
stroke_box.bounds.origin.x + stroke_box.bounds.size.width - stroke_box.corner[GSK_CORNER_BOTTOM_RIGHT].width,
stroke_box.bounds.origin.y + stroke_box.bounds.size.height,
weight);
gsk_path_builder_line_to (builder,
stroke_box.bounds.origin.x + stroke_box.corner[GSK_CORNER_BOTTOM_LEFT].width,
stroke_box.bounds.origin.y + stroke_box.bounds.size.height);
gsk_path_builder_conic_to (builder,
stroke_box.bounds.origin.x,
stroke_box.bounds.origin.y + stroke_box.bounds.size.height,
stroke_box.bounds.origin.x + stroke_box.corner[GSK_CORNER_BOTTOM_LEFT].width / 2,
stroke_box.bounds.origin.y + stroke_box.bounds.size.height - stroke_box.corner[GSK_CORNER_BOTTOM_LEFT].height / 2,
weight);
}
_gtk_rounded_box_path_bottom (border_box, &padding_box, cr);
else if (i == 3)
{
/* left */
gsk_path_builder_move_to (builder,
stroke_box.bounds.origin.x + stroke_box.corner[GSK_CORNER_BOTTOM_LEFT].width / 2,
stroke_box.bounds.origin.y + stroke_box.bounds.size.height - stroke_box.corner[GSK_CORNER_BOTTOM_LEFT].height / 2);
gsk_path_builder_conic_to (builder,
stroke_box.bounds.origin.x,
stroke_box.bounds.origin.y + stroke_box.bounds.size.height,
stroke_box.bounds.origin.x,
stroke_box.bounds.origin.y + stroke_box.bounds.size.height - stroke_box.corner[GSK_CORNER_BOTTOM_LEFT].height,
weight);
gsk_path_builder_line_to (builder,
stroke_box.bounds.origin.x,
stroke_box.bounds.origin.y + stroke_box.corner[GSK_CORNER_TOP_LEFT].height);
gsk_path_builder_conic_to (builder,
stroke_box.bounds.origin.x,
stroke_box.bounds.origin.y,
stroke_box.bounds.origin.x + stroke_box.corner[GSK_CORNER_TOP_LEFT].width,
stroke_box.bounds.origin.y,
weight);
}
_gtk_rounded_box_path_left (border_box, &padding_box, cr);
cairo_clip (cr);
path = gsk_path_builder_free_to_path (builder);
stroke = create_stroke_style (border_width[i],
stroke_style,
_gtk_rounded_box_guess_length (&stroke_box, i));
gtk_snapshot_push_stroke (snapshot, path, stroke);
gsk_stroke_free (stroke);
gsk_path_unref (path);
_gtk_rounded_box_path_side (&stroke_box, cr, i);
gtk_snapshot_append_border (snapshot, border_box, border_width, colors);
gdk_cairo_set_source_rgba (cr, &colors[i]);
set_stroke_style (cr,
border_width[i],
stroke_style,
_gtk_rounded_box_guess_length (&stroke_box, i));
cairo_stroke (cr);
gtk_snapshot_pop (snapshot);
cairo_restore (cr);
}
}
}
static void
snapshot_frame_stroke (GtkSnapshot *snapshot,
const GskRoundedRect *outline,
const float border_width[4],
GdkRGBA colors[4],
guint hidden_side,
GtkBorderStyle stroke_style)
{
double double_width[4] = { border_width[0], border_width[1], border_width[2], border_width[3] };
cairo_t *cr;
cr = gtk_snapshot_append_cairo (snapshot,
&outline->bounds);
render_frame_stroke (cr, outline, double_width, colors, hidden_side, stroke_style);
cairo_destroy (cr);
}
static void
color_shade (const GdkRGBA *color,
double factor,
@@ -635,7 +568,7 @@ snapshot_border (GtkSnapshot *snapshot,
else
dont_draw |= (1 << j);
}
snapshot_frame_stroke (snapshot, border_box, border_width, colors, dont_draw, border_style[i]);
}
break;
@@ -651,12 +584,12 @@ snapshot_border (GtkSnapshot *snapshot,
hidden_side |= (1 << j);
else
dont_draw |= (1 << j);
other_border[j] = border_width[j] / 3;
}
snapshot_frame_fill (snapshot, border_box, other_border, colors, dont_draw);
other_box = *border_box;
gsk_rounded_rect_shrink (&other_box,
2 * other_border[GTK_CSS_TOP],
@@ -688,9 +621,9 @@ snapshot_border (GtkSnapshot *snapshot,
dont_draw |= (1 << j);
other_border[j] = border_width[j] / 2;
}
snapshot_frame_fill (snapshot, border_box, other_border, colors, dont_draw);
other_box = *border_box;
gsk_rounded_rect_shrink (&other_box,
other_border[GTK_CSS_TOP],
@@ -705,7 +638,7 @@ snapshot_border (GtkSnapshot *snapshot,
break;
}
}
snapshot_frame_fill (snapshot, border_box, border_width, colors, hidden_side);
}
+7 -8
View File
@@ -29,13 +29,11 @@
* All scrollable widgets should do the following.
*
* - When a parent widget sets the scrollable child widgets adjustments,
* the widget should populate the adjustments
* [property@Gtk.Adjustment:lower],
* [property@Gtk.Adjustment:upper],
* [property@Gtk.Adjustment:step-increment],
* [property@Gtk.Adjustment:page-increment] and
* [property@Gtk.Adjustment:page-size] properties and connect to the
* [signal@Gtk.Adjustment::value-changed] signal.
* the widget should connect to the [signal@Gtk.Adjustment::value-changed]
* signal. The child widget should then populate the adjustments properties
* as soon as possible, which usually means queueing an allocation right away
* and populating the properties in the [vfunc@Gtk.Widget.size_allocate]
* implementation.
*
* - Because its preferred size is the size for a fully expanded widget,
* the scrollable widget must be able to cope with underallocations.
@@ -43,7 +41,8 @@
* [vfunc@Gtk.Widget.size_allocate] implementation.
*
* - When the parent allocates space to the scrollable child widget,
* the widget should update the adjustments properties with new values.
* the widget must ensure the adjustments property values are correct and up
* to date, for example using [method@Gtk.Adjustment.configure].
*
* - When any of the adjustments emits the [signal@Gtk.Adjustment::value-changed]
* signal, the scrollable widget should scroll its contents.
+20 -1
View File
@@ -379,6 +379,7 @@ static void gtk_scrolled_window_measure (GtkWidget *widget,
static void gtk_scrolled_window_map (GtkWidget *widget);
static void gtk_scrolled_window_unmap (GtkWidget *widget);
static void gtk_scrolled_window_realize (GtkWidget *widget);
static void gtk_scrolled_window_unrealize (GtkWidget *widget);
static void _gtk_scrolled_window_set_adjustment_value (GtkScrolledWindow *scrolled_window,
GtkAdjustment *adjustment,
double value);
@@ -587,6 +588,7 @@ gtk_scrolled_window_class_init (GtkScrolledWindowClass *class)
widget_class->map = gtk_scrolled_window_map;
widget_class->unmap = gtk_scrolled_window_unmap;
widget_class->realize = gtk_scrolled_window_realize;
widget_class->unrealize = gtk_scrolled_window_unrealize;
widget_class->direction_changed = gtk_scrolled_window_direction_changed;
widget_class->compute_expand = gtk_scrolled_window_compute_expand;
widget_class->get_request_mode = gtk_scrolled_window_get_request_mode;
@@ -700,7 +702,7 @@ gtk_scrolled_window_class_init (GtkScrolledWindowClass *class)
GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkScrolledWindow:kinetic-scrolling: (attributes org.gtk.Property.get=gtk_scrolled_window_get_kinetic_scrolling org.gtk.Property.set=gtk_scrolled_window_set_overlay_scrolling)
* GtkScrolledWindow:kinetic-scrolling: (attributes org.gtk.Property.get=gtk_scrolled_window_get_kinetic_scrolling org.gtk.Property.set=gtk_scrolled_window_set_kinetic_scrolling)
*
* Whether kinetic scrolling is enabled or not.
*
@@ -3864,15 +3866,32 @@ gtk_scrolled_window_realize (GtkWidget *widget)
{
GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
GtkSettings *settings;
priv->hindicator.scrollbar = priv->hscrollbar;
priv->vindicator.scrollbar = priv->vscrollbar;
gtk_scrolled_window_sync_use_indicators (scrolled_window);
settings = gtk_widget_get_settings (widget);
g_signal_connect_swapped (settings, "notify::gtk-overlay-scrolling",
G_CALLBACK (gtk_scrolled_window_update_use_indicators), widget);
GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->realize (widget);
}
static void
gtk_scrolled_window_unrealize (GtkWidget *widget)
{
GtkSettings *settings;
settings = gtk_widget_get_settings (widget);
g_signal_handlers_disconnect_by_func (settings, gtk_scrolled_window_sync_use_indicators, widget);
GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->unrealize (widget);
}
/**
* gtk_scrolled_window_get_min_content_width: (attributes org.gtk.Method.get_property=min-content-width)
* @scrolled_window: a `GtkScrolledWindow`
+3 -141
View File
@@ -34,7 +34,6 @@
#include "gsk/gskrendernodeprivate.h"
#include "gsk/gskroundedrectprivate.h"
#include "gsk/gskstrokeprivate.h"
#include "gtk/gskpango.h"
@@ -110,14 +109,6 @@ struct _GtkSnapshotState {
struct {
GskRoundedRect bounds;
} rounded_clip;
struct {
GskPath *path;
GskFillRule fill_rule;
} fill;
struct {
GskPath *path;
GskStroke stroke;
} stroke;
struct {
gsize n_shadows;
GskShadow *shadows;
@@ -770,7 +761,7 @@ gtk_snapshot_ensure_translate (GtkSnapshot *snapshot,
gtk_snapshot_autopush_transform (snapshot);
state = gtk_snapshot_get_current_state (snapshot);
}
gsk_transform_to_translate (state->transform, dx, dy);
}
@@ -859,7 +850,7 @@ gtk_snapshot_push_clip (GtkSnapshot *snapshot,
{
GtkSnapshotState *state;
float scale_x, scale_y, dx, dy;
gtk_snapshot_ensure_affine (snapshot, &scale_x, &scale_y, &dx, &dy);
state = gtk_snapshot_push_state (snapshot,
@@ -1105,135 +1096,6 @@ gtk_snapshot_push_rounded_clip (GtkSnapshot *snapshot,
gsk_rounded_rect_scale_affine (&state->data.rounded_clip.bounds, bounds, scale_x, scale_y, dx, dy);
}
static GskRenderNode *
gtk_snapshot_collect_fill (GtkSnapshot *snapshot,
GtkSnapshotState *state,
GskRenderNode **nodes,
guint n_nodes)
{
GskRenderNode *node, *fill_node;
node = gtk_snapshot_collect_default (snapshot, state, nodes, n_nodes);
if (node == NULL)
return NULL;
fill_node = gsk_fill_node_new (node,
state->data.fill.path,
state->data.fill.fill_rule);
if (fill_node->bounds.size.width == 0 ||
fill_node->bounds.size.height == 0)
{
gsk_render_node_unref (node);
gsk_render_node_unref (fill_node);
return NULL;
}
gsk_render_node_unref (node);
return fill_node;
}
static void
gtk_snapshot_clear_fill (GtkSnapshotState *state)
{
gsk_path_unref (state->data.fill.path);
}
/**
* gtk_snapshot_push_fill:
* @snapshot: a `GtkSnapshot`
* @path: The path describing the area to fill
* @fill_rule: The fill rule to use
*
* Fills the area given by @path and @fill_rule with an image and discards everything
* outside of it.
*
* The image is recorded until the next call to [method@Gtk.Snapshot.pop].
*/
void
gtk_snapshot_push_fill (GtkSnapshot *snapshot,
GskPath *path,
GskFillRule fill_rule)
{
GtkSnapshotState *state;
/* FIXME: Is it worth calling ensure_affine() and transforming the path here? */
gtk_snapshot_ensure_identity (snapshot);
state = gtk_snapshot_push_state (snapshot,
gtk_snapshot_get_current_state (snapshot)->transform,
gtk_snapshot_collect_fill,
gtk_snapshot_clear_fill);
state->data.fill.path = gsk_path_ref (path);
state->data.fill.fill_rule = fill_rule;
}
static GskRenderNode *
gtk_snapshot_collect_stroke (GtkSnapshot *snapshot,
GtkSnapshotState *state,
GskRenderNode **nodes,
guint n_nodes)
{
GskRenderNode *node, *stroke_node;
node = gtk_snapshot_collect_default (snapshot, state, nodes, n_nodes);
if (node == NULL)
return NULL;
stroke_node = gsk_stroke_node_new (node,
state->data.stroke.path,
&state->data.stroke.stroke);
if (stroke_node->bounds.size.width == 0 ||
stroke_node->bounds.size.height == 0)
{
gsk_render_node_unref (node);
gsk_render_node_unref (stroke_node);
return NULL;
}
gsk_render_node_unref (node);
return stroke_node;
}
static void
gtk_snapshot_clear_stroke (GtkSnapshotState *state)
{
gsk_path_unref (state->data.stroke.path);
gsk_stroke_clear (&state->data.stroke.stroke);
}
/**
* gtk_snapshot_push_stroke:
* @snapshot: a `GtkSnapshot`
* @path: The path to stroke
* @stroke: The stroke attributes
*
* Strokes the given @path with the attributes given by @stroke and the
* image being recorded until the next call to [method@Gtk.Snapshot.pop].
*/
void
gtk_snapshot_push_stroke (GtkSnapshot *snapshot,
GskPath *path,
const GskStroke *stroke)
{
GtkSnapshotState *state;
/* FIXME: Is it worth calling ensure_affine() and transforming the path here? */
gtk_snapshot_ensure_identity (snapshot);
state = gtk_snapshot_push_state (snapshot,
gtk_snapshot_get_current_state (snapshot)->transform,
gtk_snapshot_collect_stroke,
gtk_snapshot_clear_stroke);
state->data.stroke.path = gsk_path_ref (path);
gsk_stroke_init_copy (&state->data.stroke.stroke, stroke);
}
static GskRenderNode *
gtk_snapshot_collect_shadow (GtkSnapshot *snapshot,
GtkSnapshotState *state,
@@ -2655,7 +2517,7 @@ gtk_snapshot_append_border (GtkSnapshot *snapshot,
gsk_rounded_rect_scale_affine (&real_outline, outline, scale_x, scale_y, dx, dy);
node = gsk_border_node_new (&real_outline,
(float[4]) {
(float[4]) {
border_width[0] * scale_y,
border_width[1] * scale_x,
border_width[2] * scale_y,
-8
View File
@@ -89,14 +89,6 @@ GDK_AVAILABLE_IN_ALL
void gtk_snapshot_push_rounded_clip (GtkSnapshot *snapshot,
const GskRoundedRect *bounds);
GDK_AVAILABLE_IN_ALL
void gtk_snapshot_push_fill (GtkSnapshot *snapshot,
GskPath *path,
GskFillRule fill_rule);
GDK_AVAILABLE_IN_ALL
void gtk_snapshot_push_stroke (GtkSnapshot *snapshot,
GskPath *path,
const GskStroke *stroke);
GDK_AVAILABLE_IN_ALL
void gtk_snapshot_push_shadow (GtkSnapshot *snapshot,
const GskShadow *shadow,
gsize n_shadows);
-3
View File
@@ -388,8 +388,6 @@ gtk_tooltip_window_class_init (GtkTooltipWindowClass *klass)
gtk_widget_class_bind_template_child (widget_class, GtkTooltipWindow, box);
gtk_widget_class_bind_template_child (widget_class, GtkTooltipWindow, image);
gtk_widget_class_bind_template_child (widget_class, GtkTooltipWindow, label);
gtk_widget_class_set_css_name (widget_class, "tooltip");
}
static void
@@ -420,7 +418,6 @@ update_label_width (GtkLabel *label)
len = g_utf8_strlen (text, -1);
gtk_label_set_width_chars (label, MIN (len, 50));
gtk_label_set_max_width_chars (label, MIN (len, 50));
gtk_label_set_wrap (label, TRUE);
}
+4 -3
View File
@@ -45,12 +45,12 @@
#ifdef GDK_WINDOWING_WIN32
#include "win32/gdkwin32.h"
#include "gdkglcontextprivate.h"
#include <epoxy/wgl.h>
#endif
#ifdef GDK_WIN32_ENABLE_EGL
#include <epoxy/egl.h>
#endif
#endif
#ifdef GDK_WINDOWING_MACOS
#include "macos/gdkmacos.h"
@@ -387,7 +387,8 @@ init_gl (GtkInspectorGeneral *gen)
else
#endif
#ifdef GDK_WINDOWING_WIN32
if (GDK_IS_WIN32_DISPLAY (gen->display))
if (GDK_IS_WIN32_DISPLAY (gen->display) &&
gdk_gl_backend_can_be_used (GDK_GL_WGL, NULL))
{
int gl_version;
char *version;
+3 -60
View File
@@ -177,12 +177,6 @@ create_list_model_for_render_node (GskRenderNode *node)
case GSK_ROUNDED_CLIP_NODE:
return create_render_node_list_model ((GskRenderNode *[1]) { gsk_rounded_clip_node_get_child (node) }, 1);
case GSK_FILL_NODE:
return create_render_node_list_model ((GskRenderNode *[1]) { gsk_fill_node_get_child (node) }, 1);
case GSK_STROKE_NODE:
return create_render_node_list_model ((GskRenderNode *[1]) { gsk_stroke_node_get_child (node) }, 1);
case GSK_SHADOW_NODE:
return create_render_node_list_model ((GskRenderNode *[1]) { gsk_shadow_node_get_child (node) }, 1);
@@ -304,10 +298,6 @@ node_type_name (GskRenderNodeType type)
return "Clip";
case GSK_ROUNDED_CLIP_NODE:
return "Rounded Clip";
case GSK_FILL_NODE:
return "Fill";
case GSK_STROKE_NODE:
return "Stroke";
case GSK_SHADOW_NODE:
return "Shadow";
case GSK_BLEND_NODE:
@@ -347,8 +337,6 @@ node_name (GskRenderNode *node)
case GSK_REPEAT_NODE:
case GSK_CLIP_NODE:
case GSK_ROUNDED_CLIP_NODE:
case GSK_FILL_NODE:
case GSK_STROKE_NODE:
case GSK_SHADOW_NODE:
case GSK_BLEND_NODE:
case GSK_CROSS_FADE_NODE:
@@ -721,20 +709,6 @@ add_float_row (GtkListStore *store,
g_free (text);
}
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
populate_render_node_properties (GtkListStore *store,
GskRenderNode *node)
@@ -997,7 +971,9 @@ populate_render_node_properties (GtkListStore *store,
case GSK_BLEND_NODE:
{
GskBlendMode mode = gsk_blend_node_get_blend_mode (node);
add_text_row (store, "Blendmode", enum_to_nick (GSK_TYPE_BLEND_MODE, mode));
tmp = g_enum_to_string (GSK_TYPE_BLEND_MODE, mode);
add_text_row (store, "Blendmode", tmp);
g_free (tmp);
}
break;
@@ -1230,39 +1206,6 @@ populate_render_node_properties (GtkListStore *store,
}
break;
case GSK_FILL_NODE:
{
GskPath *path = gsk_fill_node_get_path (node);
GskFillRule fill_rule = gsk_fill_node_get_fill_rule (node);
tmp = gsk_path_to_string (path);
add_text_row (store, "Path", tmp);
g_free (tmp);
add_text_row (store, "Fill rule", enum_to_nick (GSK_TYPE_FILL_RULE, fill_rule));
}
break;
case GSK_STROKE_NODE:
{
GskPath *path = gsk_stroke_node_get_path (node);
const GskStroke *stroke = gsk_stroke_node_get_stroke (node);
GskLineCap line_cap = gsk_stroke_get_line_cap (stroke);
GskLineJoin line_join = gsk_stroke_get_line_join (stroke);
tmp = gsk_path_to_string (path);
add_text_row (store, "Path", tmp);
g_free (tmp);
tmp = g_strdup_printf ("%.2f", gsk_stroke_get_line_width (stroke));
add_text_row (store, "Line width", tmp);
g_free (tmp);
add_text_row (store, "Line cap", enum_to_nick (GSK_TYPE_LINE_CAP, line_cap));
add_text_row (store, "Line join", enum_to_nick (GSK_TYPE_LINE_JOIN, line_join));
}
break;
case GSK_CONTAINER_NODE:
tmp = g_strdup_printf ("%d", gsk_container_node_get_n_children (node));
add_text_row (store, "Children", tmp);
+4
View File
@@ -403,6 +403,10 @@ modules/printbackends/gtkprintbackendlpr.c
modules/printbackends/gtkprintercups.c
tools/encodesymbolic.c
tools/gtk-builder-tool.c
tools/gtk-builder-tool-enumerate.c
tools/gtk-builder-tool-preview.c
tools/gtk-builder-tool-screenshot.c
tools/gtk-builder-tool-simplify.c
tools/gtk-builder-tool-validate.c
tools/gtk-launch.c
tools/updateiconcache.c
-883
View File
@@ -1,883 +0,0 @@
#include <gtk/gtk.h>
#define DEMO_TYPE_WIDGET (demo_widget_get_type ())
G_DECLARE_FINAL_TYPE (DemoWidget, demo_widget, DEMO, WIDGET, GtkWidget)
struct _DemoWidget
{
GtkWidget parent_instance;
GskPath *orig_path;
GskPath *path;
GskPathMeasure *measure;
graphene_point_t point;
graphene_point_t point2;
graphene_vec2_t tangent;
double start, end;
float curvature;
graphene_point_t center;
gboolean track;
gboolean show_bounding_box;
gboolean show_points;
GtkWidget *label;
gboolean do_stroke;
gboolean do_offset;
GskPath *stroke_path;
GskPathMeasure *stroke_measure;
GskStroke *stroke;
GskPath *outline_path;
GskStroke *outline_stroke;
gboolean inside;
GskFillRule fill_rule;
gboolean do_fill;
graphene_rect_t bounds;
gboolean show_cairo;
double zoom;
};
struct _DemoWidgetClass
{
GtkWidgetClass parent_class;
};
G_DEFINE_TYPE (DemoWidget, demo_widget, GTK_TYPE_WIDGET)
static void
motion (GtkEventControllerMotion *controller,
double x,
double y,
DemoWidget *self)
{
if (self->track)
{
float distance;
char *text;
float t;
gsk_path_measure_get_closest_point_full (self->measure,
&GRAPHENE_POINT_INIT (x, y),
INFINITY,
&distance,
&self->point,
&t,
&self->tangent);
gsk_path_measure_get_point (self->measure, t, &self->point2, NULL);
self->curvature = gsk_path_measure_get_curvature (self->measure, t, &self->center);
text = g_strdup_printf ("%.1f", distance);
gtk_label_set_label (GTK_LABEL (self->label), text);
gtk_widget_queue_draw (GTK_WIDGET (self));
}
if (self->do_stroke && self->do_fill)
{
gboolean inside = TRUE;
inside = gsk_path_measure_in_fill (self->stroke_measure, &GRAPHENE_POINT_INIT (x, y), self->fill_rule);
if (self->inside != inside)
{
self->inside = inside;
gtk_widget_queue_draw (GTK_WIDGET (self));
}
}
}
static void
demo_widget_init (DemoWidget *self)
{
GtkEventController *controller;
self->start = 0;
self->end = 1;
self->label = gtk_label_new ("");
gtk_widget_set_parent (self->label, GTK_WIDGET (self));
gtk_widget_set_halign (self->label, GTK_ALIGN_END);
gtk_widget_set_valign (self->label, GTK_ALIGN_START);
controller = gtk_event_controller_motion_new ();
g_signal_connect (controller, "motion", G_CALLBACK (motion), self);
gtk_widget_add_controller (GTK_WIDGET (self), controller);
}
static void
draw_point (GtkSnapshot *snapshot,
const graphene_point_t *pt)
{
graphene_rect_t bounds;
GdkRGBA color = { 1, 0, 0, 1 };
bounds.origin.x = pt->x - 2;
bounds.origin.y = pt->y - 2;
bounds.size.width = 4;
bounds.size.height = 4;
gtk_snapshot_append_color (snapshot, &color, &bounds);
}
static gboolean
point_cb (GskPathOperation op,
const graphene_point_t *pts,
gsize n_ts,
float weight,
gpointer user_data)
{
GtkSnapshot *snapshot = user_data;
switch (op)
{
case GSK_PATH_MOVE:
draw_point (snapshot, &pts[0]);
break;
case GSK_PATH_CLOSE:
draw_point (snapshot, &pts[0]);
break;
case GSK_PATH_LINE:
draw_point (snapshot, &pts[1]);
break;
case GSK_PATH_CURVE:
draw_point (snapshot, &pts[3]);
break;
case GSK_PATH_CONIC:
default:
g_assert_not_reached ();
}
return TRUE;
}
static void
demo_widget_snapshot (GtkWidget *widget,
GtkSnapshot *snapshot)
{
DemoWidget *self = DEMO_WIDGET (widget);
int width, height;
GskStroke *stroke;
GskPathBuilder *builder;
GskPath *path;
graphene_rect_t bounds;
if (!self->path)
return;
gtk_snapshot_save (snapshot);
gtk_snapshot_scale (snapshot, self->zoom, self->zoom);
width = gtk_widget_get_width (widget);
height = gtk_widget_get_width (widget);
if (self->do_stroke)
{
if (self->show_cairo)
{
cairo_t *cr;
graphene_rect_init (&bounds, 0, 0, width, height);
cr = gtk_snapshot_append_cairo (snapshot, &bounds);
gsk_path_to_cairo (self->path, cr);
gsk_stroke_to_cairo (self->stroke, cr);
cairo_set_source_rgba (cr, 0, 0, 1, 0.2);
cairo_stroke (cr);
cairo_destroy (cr);
}
if (self->do_fill && self->inside)
{
gtk_snapshot_push_fill (snapshot, self->stroke_path, self->fill_rule);
gtk_snapshot_append_color (snapshot,
&(GdkRGBA){ 1, 0, 1, 0.3},
&GRAPHENE_RECT_INIT (0, 0, width, height ));
gtk_snapshot_pop (snapshot);
}
gtk_snapshot_push_stroke (snapshot, self->outline_path, self->outline_stroke);
gtk_snapshot_append_color (snapshot,
&(GdkRGBA){ 0, 0, 0, 0.2},
&GRAPHENE_RECT_INIT (0, 0, width, height ));
gtk_snapshot_pop (snapshot);
stroke = gsk_stroke_new (1);
gtk_snapshot_push_stroke (snapshot, self->path, stroke);
gsk_stroke_free (stroke);
gtk_snapshot_append_color (snapshot,
&(GdkRGBA){ 0, 0, 0, 0.3},
&GRAPHENE_RECT_INIT (0, 0, width, height ));
gtk_snapshot_pop (snapshot);
}
else if (self->do_offset)
{
for (int i = -3; i < 4; i++)
{
path = gsk_path_offset (self->path,
5 * i,
gsk_stroke_get_line_join (self->stroke),
gsk_stroke_get_miter_limit (self->stroke));
gtk_snapshot_push_stroke (snapshot, path, self->outline_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);
}
}
else
{
gtk_snapshot_push_stroke (snapshot, self->path, self->outline_stroke);
gtk_snapshot_append_color (snapshot,
&(GdkRGBA){ 0, 0, 0, 1},
&GRAPHENE_RECT_INIT (0, 0, width, height ));
gtk_snapshot_pop (snapshot);
}
if (self->show_points)
{
gsk_path_foreach (self->path, GSK_PATH_FOREACH_ALLOW_CURVE, point_cb, snapshot);
}
if (self->show_bounding_box)
{
if (gsk_path_get_bounds (self->do_stroke ? self->outline_path : self->path, &bounds))
{
builder = gsk_path_builder_new ();
gsk_path_builder_add_rect (builder, &bounds);
path = gsk_path_builder_free_to_path (builder);
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, 0.5},
&GRAPHENE_RECT_INIT (0, 0, width, height ));
gtk_snapshot_pop (snapshot);
gsk_path_unref (path);
}
}
if (self->track)
{
graphene_point_t p;
p.x = self->point.x + graphene_vec2_get_x (&self->tangent) * 40;
p.y = self->point.y + graphene_vec2_get_y (&self->tangent) * 40;
builder = gsk_path_builder_new ();
gsk_path_builder_move_to (builder, self->point.x, self->point.y);
gsk_path_builder_line_to (builder, p.x, p.y);
path = gsk_path_builder_free_to_path (builder);
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);
builder = gsk_path_builder_new ();
gsk_path_builder_add_circle (builder, &self->point, 5);
gsk_path_builder_add_circle (builder, &p, 2.5);
gsk_path_builder_add_circle (builder, &self->point2, 5);
path = gsk_path_builder_free_to_path (builder);
gtk_snapshot_push_fill (snapshot, path, 0);
gtk_snapshot_append_color (snapshot,
&(GdkRGBA){ 1, 0, 0, 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->curvature != 0)
{
builder = gsk_path_builder_new ();
gsk_path_builder_add_circle (builder, &self->center, fabs (1/self->curvature));
gsk_path_builder_add_circle (builder, &self->center, 3);
path = gsk_path_builder_free_to_path (builder);
stroke = gsk_stroke_new (1.0);
gtk_snapshot_push_stroke (snapshot, path, stroke);
gsk_stroke_free (stroke);
gtk_snapshot_append_color (snapshot,
&(GdkRGBA){ 1, 0, 1, 1},
&GRAPHENE_RECT_INIT (0, 0, width, height ));
gtk_snapshot_pop (snapshot);
}
gtk_widget_snapshot_child (widget, self->label, snapshot);
}
gtk_snapshot_restore (snapshot);
}
static void
demo_widget_measure (GtkWidget *widget,
GtkOrientation orientation,
int for_size,
int *minimum,
int *natural,
int *minimum_baseline,
int *natural_baseline)
{
DemoWidget *self = DEMO_WIDGET (widget);
float size;
if (orientation == GTK_ORIENTATION_HORIZONTAL)
size = self->zoom * (self->bounds.origin.x + self->bounds.size.width);
else
size = self->zoom * (self->bounds.origin.y + self->bounds.size.height);
*minimum = *natural = (int)size;
}
static void
demo_widget_size_allocate (GtkWidget *widget,
int width,
int height,
int baseline)
{
DemoWidget *self = DEMO_WIDGET (widget);
GtkRequisition min, nat;
gtk_widget_get_preferred_size (self->label, &min, &nat);
gtk_widget_size_allocate (self->label,
&(GtkAllocation) { width - nat.width, 0, nat.width, nat.height},
-1);
}
static void
demo_widget_dispose (GObject *object)
{
DemoWidget *self = DEMO_WIDGET (object);
g_clear_pointer (&self->path, gsk_path_unref);
g_clear_pointer (&self->measure, gsk_path_measure_unref);
g_clear_pointer (&self->stroke_path, gsk_path_unref);
g_clear_pointer (&self->stroke_measure, gsk_path_measure_unref);
g_clear_pointer (&self->stroke, gsk_stroke_free);
g_clear_pointer (&self->outline_path, gsk_path_unref);
g_clear_pointer (&self->outline_stroke, gsk_stroke_free);
g_clear_pointer (&self->label, gtk_widget_unparent);
G_OBJECT_CLASS (demo_widget_parent_class)->dispose (object);
}
static void
demo_widget_class_init (DemoWidgetClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
object_class->dispose = demo_widget_dispose;
widget_class->snapshot = demo_widget_snapshot;
widget_class->measure = demo_widget_measure;
widget_class->size_allocate = demo_widget_size_allocate;
}
static GtkWidget *
demo_widget_new (void)
{
return g_object_new (DEMO_TYPE_WIDGET, NULL);
}
static void
update_outline_path (DemoWidget *self)
{
if (self->stroke_path)
{
g_clear_pointer (&self->outline_path, gsk_path_unref);
self->outline_path = gsk_path_ref (self->stroke_path);
gtk_widget_queue_draw (GTK_WIDGET (self));
}
}
static void
update_stroke_path (DemoWidget *self)
{
g_clear_pointer (&self->stroke_path, gsk_path_unref);
g_clear_pointer (&self->stroke_measure, gsk_path_measure_unref);
if (self->do_stroke)
{
self->stroke_path = gsk_path_stroke (self->path, self->stroke);
self->stroke_measure = gsk_path_measure_new (self->stroke_path);
update_outline_path (self);
}
gsk_path_get_bounds (self->orig_path, &self->bounds);
if (self->stroke_path)
{
graphene_rect_t b;
gsk_path_get_bounds (self->stroke_path, &b);
graphene_rect_union (&b, &self->bounds, &self->bounds);
}
gtk_widget_queue_resize (GTK_WIDGET (self));
}
static void
update_path (DemoWidget *self)
{
g_clear_pointer (&self->path, gsk_path_unref);
g_clear_pointer (&self->measure, gsk_path_measure_unref);
if (self->start > 0 || self->end < 1)
{
GskPathMeasure *measure;
GskPathBuilder *builder;
float length;
measure = gsk_path_measure_new (self->orig_path);
length = gsk_path_measure_get_length (measure);
builder = gsk_path_builder_new ();
gsk_path_builder_add_segment (builder, measure, self->start * length, self->end * length);
self->path = gsk_path_builder_free_to_path (builder);
gsk_path_measure_unref (measure);
}
else
self->path = gsk_path_ref (self->orig_path);
self->measure = gsk_path_measure_new (self->path);
update_stroke_path (self);
}
static void
demo_widget_set_path (DemoWidget *self,
GskPath *path)
{
g_clear_pointer (&self->orig_path, gsk_path_unref);
self->orig_path = gsk_path_ref (path);
update_path (self);
}
static void
activate (GtkEntry *entry,
DemoWidget *demo)
{
GskPath *path;
path = gsk_path_parse (gtk_editable_get_text (GTK_EDITABLE (entry)));
if (path)
{
demo_widget_set_path (demo, path);
gsk_path_unref (path);
}
}
static void
init_demo (DemoWidget *demo,
GtkEditable *editable)
{
GskPathBuilder *builder;
GskPath *path;
char *string;
builder = gsk_path_builder_new ();
gsk_path_builder_add_circle (builder, &GRAPHENE_POINT_INIT (150, 150), 100);
gsk_path_builder_add_rect (builder, &GRAPHENE_RECT_INIT (100, 100, 100, 100));
gsk_path_builder_move_to (builder, 300, 150);
gsk_path_builder_curve_to (builder, 300, 50, 400, 50, 400, 150);
gsk_path_builder_curve_to (builder, 400, 250, 500, 250, 500, 150);
gsk_path_builder_line_to (builder, 600, 150);
gsk_path_builder_line_to (builder, 530, 190);
path = gsk_path_builder_free_to_path (builder);
demo_widget_set_path (demo, path);
string = gsk_path_to_string (path);
gtk_editable_set_text (editable, string);
g_free (string);
gsk_path_unref (path);
demo->stroke = gsk_stroke_new (20);
demo->outline_stroke = gsk_stroke_new (1);
demo->zoom = 1;
}
static void
zoom_changed (GtkRange *range,
DemoWidget *self)
{
self->zoom = gtk_range_get_value (range);
gtk_widget_queue_resize (GTK_WIDGET (self));
}
static void
points_toggled (GtkCheckButton *button,
DemoWidget *self)
{
self->show_points = gtk_check_button_get_active (button);
gtk_widget_queue_draw (GTK_WIDGET (self));
}
static void
track_toggled (GtkCheckButton *button,
DemoWidget *self)
{
self->track = gtk_check_button_get_active (button);
gtk_widget_queue_draw (GTK_WIDGET (self));
}
static void
bb_toggled (GtkCheckButton *button,
DemoWidget *self)
{
self->show_bounding_box = gtk_check_button_get_active (button);
gtk_widget_queue_draw (GTK_WIDGET (self));
}
static void
cairo_toggled (GtkCheckButton *button,
DemoWidget *self)
{
self->show_cairo = gtk_check_button_get_active (button);
gtk_widget_queue_draw (GTK_WIDGET (self));
}
static void
stroke_toggled (GtkCheckButton *button,
DemoWidget *self)
{
self->do_stroke = gtk_check_button_get_active (button);
update_stroke_path (self);
gtk_widget_queue_draw (GTK_WIDGET (self));
}
static void
offset_toggled (GtkCheckButton *button,
DemoWidget *self)
{
self->do_offset = gtk_check_button_get_active (button);
gtk_widget_queue_draw (GTK_WIDGET (self));
}
static void
fill_toggled (GtkCheckButton *button,
DemoWidget *self)
{
self->do_fill = gtk_check_button_get_active (button);
gtk_widget_queue_draw (GTK_WIDGET (self));
}
static GtkWidget *start_scale;
static GtkWidget *end_scale;
static void
range_changed (GtkRange *range,
DemoWidget *self)
{
double start, end;
if (range == GTK_RANGE (start_scale))
{
start = gtk_range_get_value (range);
end = MAX (start, gtk_range_get_value (GTK_RANGE (end_scale)));
gtk_range_set_value (GTK_RANGE (end_scale), end);
}
else
{
end = gtk_range_get_value (range);
start = MIN (end, gtk_range_get_value (GTK_RANGE (start_scale)));
gtk_range_set_value (GTK_RANGE (start_scale), start);
}
self->start = start;
self->end = end;
update_path (self);
}
static void
fill_rule_changed (GtkDropDown *combo,
GParamSpec *pspec,
DemoWidget *self)
{
self->fill_rule = (GskFillRule)gtk_drop_down_get_selected (combo);
gtk_widget_queue_draw (GTK_WIDGET (self));
}
static void
cap_changed (GtkDropDown *combo,
GParamSpec *pspec,
DemoWidget *self)
{
gsk_stroke_set_line_cap (self->stroke, (GskLineCap)gtk_drop_down_get_selected (combo));
update_stroke_path (self);
}
static void
join_changed (GtkDropDown *combo,
GParamSpec *pspec,
DemoWidget *self)
{
gsk_stroke_set_line_join (self->stroke, (GskLineJoin)gtk_drop_down_get_selected (combo));
update_stroke_path (self);
}
static void
limit_changed (GtkSpinButton *spin,
DemoWidget *self)
{
gsk_stroke_set_miter_limit (self->stroke, gtk_spin_button_get_value (spin));
update_stroke_path (self);
}
static void
stroke_width_changed (GtkSpinButton *spin,
DemoWidget *self)
{
gsk_stroke_set_line_width (self->stroke, gtk_spin_button_get_value (spin));
update_stroke_path (self);
}
static void
line_width_changed (GtkSpinButton *spin,
DemoWidget *self)
{
gsk_stroke_set_line_width (self->outline_stroke, gtk_spin_button_get_value (spin));
update_outline_path (self);
}
static void
dash_changed (GtkEntry *entry,
DemoWidget *self)
{
const char *text;
char **s;
float dash[20];
int i;
text = gtk_editable_get_text (GTK_EDITABLE (entry));
s = g_strsplit (text, " ", -1);
for (i = 0; s[i]; i++)
{
char *end;
float f;
f = g_strtod (s[i], &end);
if (*end != '\0')
return;
dash[i] = f;
}
g_strfreev (s);
gsk_stroke_set_dash (self->stroke, dash, i);
update_stroke_path (self);
}
static void
dash_offset_changed (GtkSpinButton *spin,
DemoWidget *self)
{
gsk_stroke_set_dash_offset (self->stroke, gtk_spin_button_get_value (spin));
update_stroke_path (self);
}
int
main (int argc, char *argv[])
{
GtkWidget *window, *box, *demo, *entry;
GtkWidget *popover, *button, *grid;
GtkWidget *header, *toggle, *toggle2, *toggle3;
GtkWidget *combo, *spin, *sw, *hbox;
GtkWidget *zoom_scale;
int row;
gtk_init ();
window = gtk_window_new ();
gtk_window_set_default_size (GTK_WINDOW (window), 700, 500);
box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
gtk_window_set_child (GTK_WINDOW (window), box);
demo = demo_widget_new ();
gtk_widget_set_hexpand (demo, TRUE);
gtk_widget_set_vexpand (demo, TRUE);
sw = gtk_scrolled_window_new ();
gtk_box_append (GTK_BOX (box), sw);
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), demo);
header = gtk_header_bar_new ();
button = gtk_menu_button_new ();
gtk_menu_button_set_icon_name (GTK_MENU_BUTTON (button), "emblem-system-symbolic");
gtk_header_bar_pack_start (GTK_HEADER_BAR (header), button);
zoom_scale = gtk_scale_new_with_range (GTK_ORIENTATION_HORIZONTAL, 1, 20, 0.25);
gtk_range_set_value (GTK_RANGE (zoom_scale), 1);
g_signal_connect (zoom_scale, "value-changed", G_CALLBACK (zoom_changed), demo);
gtk_widget_set_size_request (zoom_scale, 150, -1);
gtk_header_bar_pack_end (GTK_HEADER_BAR (header), zoom_scale);
gtk_window_set_titlebar (GTK_WINDOW (window), header);
popover = gtk_popover_new ();
gtk_menu_button_set_popover (GTK_MENU_BUTTON (button), popover);
grid = gtk_grid_new ();
gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
gtk_grid_set_column_spacing (GTK_GRID (grid), 6);
gtk_popover_set_child (GTK_POPOVER (popover), grid);
row = 0;
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
toggle = gtk_check_button_new_with_label ("Plain");
gtk_check_button_set_active (GTK_CHECK_BUTTON (toggle), TRUE);
gtk_box_append (GTK_BOX (hbox), toggle);
gtk_widget_set_hexpand (toggle, TRUE);
toggle2 = gtk_check_button_new_with_label ("Stroke");
gtk_widget_set_hexpand (toggle2, TRUE);
gtk_box_append (GTK_BOX (hbox), toggle2);
gtk_check_button_set_group (GTK_CHECK_BUTTON (toggle2), GTK_CHECK_BUTTON (toggle));
g_signal_connect (toggle2, "toggled", G_CALLBACK (stroke_toggled), demo);
toggle3 = gtk_check_button_new_with_label ("Offset");
gtk_widget_set_hexpand (toggle3, TRUE);
gtk_box_append (GTK_BOX (hbox), toggle3);
gtk_check_button_set_group (GTK_CHECK_BUTTON (toggle3), GTK_CHECK_BUTTON (toggle));
g_signal_connect (toggle3, "toggled", G_CALLBACK (offset_toggled), demo);
gtk_grid_attach (GTK_GRID (grid), hbox, 0, row++, 2, 1);
gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Fill rule"), 0, row, 1, 1);
combo = gtk_drop_down_new_from_strings ((const char *[]){"Winding", "Even-Odd", NULL });
g_signal_connect (combo, "notify::selected", G_CALLBACK (fill_rule_changed), demo);
gtk_grid_attach (GTK_GRID (grid), combo, 1, row++, 1, 1);
gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Line cap:"), 0, row, 1, 1);
combo = gtk_drop_down_new_from_strings ((const char *[]){"Butt", "Round", "Square", NULL});
g_signal_connect (combo, "notify::selected", G_CALLBACK (cap_changed), demo);
gtk_grid_attach (GTK_GRID (grid), combo, 1, row++, 1, 1);
gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Line join:"), 0, row, 1, 1);
combo = gtk_drop_down_new_from_strings ((const char *[]){"Miter", "Miter-clip", "Round", "Bevel", "Arcs", NULL});
g_signal_connect (combo, "notify::selected", G_CALLBACK (join_changed), demo);
gtk_grid_attach (GTK_GRID (grid), combo, 1, row++, 1, 1);
gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Miter limit:"), 0, row, 1, 1);
spin = gtk_spin_button_new_with_range (0, 10, 1);
gtk_spin_button_set_digits (GTK_SPIN_BUTTON (spin), 1);
gtk_spin_button_set_value (GTK_SPIN_BUTTON (spin), 4);
g_signal_connect (spin, "value-changed", G_CALLBACK (limit_changed), demo);
gtk_grid_attach (GTK_GRID (grid), spin, 1, row++, 1, 1);
gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Stroke width:"), 0, row, 1, 1);
spin = gtk_spin_button_new_with_range (1, 40, 1);
gtk_spin_button_set_value (GTK_SPIN_BUTTON (spin), 20);
g_signal_connect (spin, "value-changed", G_CALLBACK (stroke_width_changed), demo);
gtk_grid_attach (GTK_GRID (grid), spin, 1, row++, 1, 1);
gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Line width:"), 0, row, 1, 1);
spin = gtk_spin_button_new_with_range (1, 20, 1);
gtk_spin_button_set_value (GTK_SPIN_BUTTON (spin), 1);
g_signal_connect (spin, "value-changed", G_CALLBACK (line_width_changed), demo);
gtk_grid_attach (GTK_GRID (grid), spin, 1, row++, 1, 1);
gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Dash pattern:"), 0, row, 1, 1);
entry = gtk_entry_new ();
g_signal_connect (entry, "changed", G_CALLBACK (dash_changed), demo);
gtk_grid_attach (GTK_GRID (grid), entry, 1, row++, 1, 1);
gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Dash offset:"), 0, row, 1, 1);
spin = gtk_spin_button_new_with_range (-1000, 1000, 10);
gtk_spin_button_set_digits (GTK_SPIN_BUTTON (spin), 1);
gtk_spin_button_set_value (GTK_SPIN_BUTTON (spin), 0);
g_signal_connect (spin, "value-changed", G_CALLBACK (dash_offset_changed), demo);
gtk_grid_attach (GTK_GRID (grid), spin, 1, row++, 1, 1);
toggle = gtk_check_button_new_with_label ("Show points");
g_signal_connect (toggle, "toggled", G_CALLBACK (points_toggled), demo);
gtk_grid_attach (GTK_GRID (grid), toggle, 1, row++, 1, 1);
toggle = gtk_check_button_new_with_label ("Show closest point");
g_signal_connect (toggle, "toggled", G_CALLBACK (track_toggled), demo);
gtk_grid_attach (GTK_GRID (grid), toggle, 1, row++, 1, 1);
toggle = gtk_check_button_new_with_label ("Show bounding box");
g_signal_connect (toggle, "toggled", G_CALLBACK (bb_toggled), demo);
gtk_grid_attach (GTK_GRID (grid), toggle, 1, row++, 1, 1);
toggle = gtk_check_button_new_with_label ("Highlight on hover");
g_signal_connect (toggle, "toggled", G_CALLBACK (fill_toggled), demo);
gtk_grid_attach (GTK_GRID (grid), toggle, 1, row++, 1, 1);
toggle = gtk_check_button_new_with_label ("Compare cairo stroke");
g_signal_connect (toggle, "toggled", G_CALLBACK (cairo_toggled), demo);
gtk_grid_attach (GTK_GRID (grid), toggle, 1, row++, 1, 1);
entry = gtk_entry_new ();
g_signal_connect (entry, "activate", G_CALLBACK (activate), demo);
gtk_box_append (GTK_BOX (box), entry);
start_scale = gtk_scale_new_with_range (GTK_ORIENTATION_HORIZONTAL, 0, 1, 0.01);
g_signal_connect (start_scale, "value-changed", G_CALLBACK (range_changed), demo);
gtk_box_append (GTK_BOX (box), start_scale);
end_scale = gtk_scale_new_with_range (GTK_ORIENTATION_HORIZONTAL, 0, 1, 0.01);
gtk_range_set_value (GTK_RANGE (end_scale), 1);
g_signal_connect (end_scale, "value-changed", G_CALLBACK (range_changed), demo);
gtk_box_append (GTK_BOX (box), end_scale);
init_demo (DEMO_WIDGET (demo), GTK_EDITABLE (entry));
gtk_window_present (GTK_WINDOW (window));
while (g_list_model_get_n_items (gtk_window_get_toplevels ()) > 0)
g_main_context_iteration (NULL, TRUE);
return 0;
}
-1
View File
@@ -1,6 +1,5 @@
gtk_tests = [
# testname, optional extra sources
['curve2'],
['input'],
['testpopup'],
['testupload'],
-191
View File
@@ -1,191 +0,0 @@
#include <gtk/gtk.h>
#include "gsk/gskcurveprivate.h"
static void
init_random_point (graphene_point_t *p)
{
p->x = g_test_rand_double_range (0, 1000);
p->y = g_test_rand_double_range (0, 1000);
}
static void
init_random_curve_with_op (GskCurve *curve,
GskPathOperation min_op,
GskPathOperation max_op)
{
switch (g_test_rand_int_range (min_op, max_op + 1))
{
case GSK_PATH_LINE:
{
graphene_point_t p[2];
init_random_point (&p[0]);
init_random_point (&p[1]);
gsk_curve_init (curve, gsk_pathop_encode (GSK_PATH_LINE, p));
}
break;
case GSK_PATH_CURVE:
{
graphene_point_t p[4];
init_random_point (&p[0]);
init_random_point (&p[1]);
init_random_point (&p[2]);
init_random_point (&p[3]);
gsk_curve_init (curve, gsk_pathop_encode (GSK_PATH_CURVE, p));
}
break;
case GSK_PATH_CONIC:
{
graphene_point_t p[4];
init_random_point (&p[0]);
init_random_point (&p[1]);
p[2] = GRAPHENE_POINT_INIT (g_test_rand_double_range (0, 20), 0);
init_random_point (&p[3]);
gsk_curve_init (curve, gsk_pathop_encode (GSK_PATH_CONIC, p));
}
break;
default:
g_assert_not_reached ();
}
}
static void
test_intersection (GskPathOperation op1,
GskPathOperation op2)
{
GskCurve curves[10000];
GskCurve *c1, *c2;
gint64 start, time;
gint64 count;
double time_elapsed;
double result;
float t1[9], t2[9];
graphene_point_t p[9];
for (int i = 0; i < 5000; i++)
{
init_random_curve_with_op (&curves[i], op1, op1);
init_random_curve_with_op (&curves[5000 + i], op2, op2);
}
count = 0;
start = time = g_get_monotonic_time ();
while (time - start < G_TIME_SPAN_SECOND * 20)
{
c1 = &curves[g_test_rand_int_range (0, 5000)];
c2 = &curves[g_test_rand_int_range (5000, 10000)];
gsk_curve_intersect (c1, c2, t1, t2, p, 9);
count++;
if (count % 10000 == 0)
time = g_get_monotonic_time ();
}
time_elapsed = (time - start) / (double) G_TIME_SPAN_SECOND;
result = ((double) count / time_elapsed);
g_test_maximized_result (result, "%8.1f ops/s", result);
}
static void
line_intersection (void)
{
test_intersection (GSK_PATH_LINE, GSK_PATH_LINE);
}
static void
line_curve_intersection (void)
{
test_intersection (GSK_PATH_LINE, GSK_PATH_CURVE);
}
static void
curve_intersection (void)
{
test_intersection (GSK_PATH_CURVE, GSK_PATH_CURVE);
}
static void
conic_intersection (void)
{
test_intersection (GSK_PATH_CONIC, GSK_PATH_CONIC);
}
static void
test_get_point (GskPathOperation op)
{
GskCurve curves[10000];
GskCurve *c;
gint64 start, time;
gint64 count;
double time_elapsed;
double result;
float t;
graphene_point_t point;
for (int i = 0; i < 10000; i++)
init_random_curve_with_op (&curves[i], op, op);
count = 0;
start = time = g_get_monotonic_time ();
while (time - start < G_TIME_SPAN_SECOND * 20)
{
c = &curves[g_test_rand_int_range (0, 10000)];
t = g_test_rand_int_range (0, 1);
gsk_curve_get_point (c, t, &point);
count++;
if (count % 10000 == 0)
time = g_get_monotonic_time ();
}
time_elapsed = (time - start) / (double) G_TIME_SPAN_SECOND;
result = ((double) count / time_elapsed);
g_test_maximized_result (result, "%8.1f ops/s", result);
}
static void
line_get_point (void)
{
test_get_point (GSK_PATH_LINE);
}
static void
curve_get_point (void)
{
test_get_point (GSK_PATH_CURVE);
}
static void
conic_get_point (void)
{
test_get_point (GSK_PATH_CONIC);
}
int
main (int argc, char *argv[])
{
g_test_init (&argc, &argv, NULL);
if (g_test_perf ())
{
g_test_add_func ("/curve/perf/intersection/line", line_intersection);
g_test_add_func ("/curve/perf/intersection/line-curve", line_curve_intersection);
g_test_add_func ("/curve/perf/intersection/curve", curve_intersection);
g_test_add_func ("/curve/perf/intersection/conic", conic_intersection);
g_test_add_func ("/curve/perf/get-point/line", line_get_point);
g_test_add_func ("/curve/perf/get-point/curve", curve_get_point);
g_test_add_func ("/curve/perf/get-point/conic", conic_get_point);
}
return g_test_run ();
}
-306
View File
@@ -1,306 +0,0 @@
/*
* 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 <gtk/gtk.h>
#include "gsk/gskcurveprivate.h"
static gboolean
measure_segment (const graphene_point_t *from,
const graphene_point_t *to,
float from_t,
float to_t,
gpointer data)
{
float *length = data;
*length += graphene_point_distance (from, to, NULL, NULL);
return TRUE;
}
static float
measure_length (const GskCurve *curve)
{
float result = 0;
gsk_curve_decompose (curve, 0.5, measure_segment, &result);
return result;
}
/* This is a pretty nasty conic that makes it obvious that split()
* does not respect the progress values, so split() twice with
* scaled factor won't work.
*/
static void
test_conic_segment (void)
{
GskCurve c, s, e, m;
graphene_point_t pts[4] = {
GRAPHENE_POINT_INIT (-1856.131591796875, 46.217609405517578125),
GRAPHENE_POINT_INIT (-1555.9866943359375, 966.0810546875),
GRAPHENE_POINT_INIT (98.94945526123046875, 0),
GRAPHENE_POINT_INIT (-1471.33154296875, 526.701171875)
};
float start = 0.02222645096480846405029296875;
float end = 0.982032716274261474609375;
gsk_curve_init (&c, gsk_pathop_encode (GSK_PATH_CONIC, pts));
gsk_curve_split (&c, start, &s, NULL);
gsk_curve_segment (&c, start, end, &m);
gsk_curve_split (&c, end, NULL, &e);
g_assert_cmpfloat_with_epsilon (measure_length (&c), measure_length (&s) + measure_length (&m) + measure_length (&e), 0.03125);
}
static void
test_curve_tangents (void)
{
GskCurve c;
graphene_point_t p[4];
graphene_vec2_t t;
graphene_point_init (&p[0], 0, 0);
graphene_point_init (&p[1], 100, 0);
gsk_curve_init (&c, gsk_pathop_encode (GSK_PATH_LINE, p));
gsk_curve_get_start_tangent (&c, &t);
g_assert_true (graphene_vec2_near (&t, graphene_vec2_x_axis (), 0.0001));
gsk_curve_get_end_tangent (&c, &t);
g_assert_true (graphene_vec2_near (&t, graphene_vec2_x_axis (), 0.0001));
graphene_point_init (&p[0], 0, 0);
graphene_point_init (&p[1], 0, 100);
gsk_curve_init (&c, gsk_pathop_encode (GSK_PATH_LINE, p));
gsk_curve_get_start_tangent (&c, &t);
g_assert_true (graphene_vec2_near (&t, graphene_vec2_y_axis (), 0.0001));
gsk_curve_get_end_tangent (&c, &t);
g_assert_true (graphene_vec2_near (&t, graphene_vec2_y_axis (), 0.0001));
graphene_point_init (&p[0], 0, 0);
graphene_point_init (&p[1], 100, 0);
p[2] = GRAPHENE_POINT_INIT (g_test_rand_double_range (0, 20), 0);
graphene_point_init (&p[3], 100, 100);
gsk_curve_init (&c, gsk_pathop_encode (GSK_PATH_CONIC, p));
gsk_curve_get_start_tangent (&c, &t);
g_assert_true (graphene_vec2_near (&t, graphene_vec2_x_axis (), 0.0001));
gsk_curve_get_end_tangent (&c, &t);
g_assert_true (graphene_vec2_near (&t, graphene_vec2_y_axis (), 0.0001));
graphene_point_init (&p[0], 0, 0);
graphene_point_init (&p[1], 50, 0);
graphene_point_init (&p[2], 100, 50);
graphene_point_init (&p[3], 100, 100);
gsk_curve_init (&c, gsk_pathop_encode (GSK_PATH_CURVE, p));
gsk_curve_get_start_tangent (&c, &t);
g_assert_true (graphene_vec2_near (&t, graphene_vec2_x_axis (), 0.0001));
gsk_curve_get_end_tangent (&c, &t);
g_assert_true (graphene_vec2_near (&t, graphene_vec2_y_axis (), 0.0001));
}
static void
test_curve_degenerate_tangents (void)
{
GskCurve c;
graphene_point_t p[4];
graphene_vec2_t t;
graphene_point_init (&p[0], 0, 0);
graphene_point_init (&p[1], 0, 0);
graphene_point_init (&p[2], 100, 0);
graphene_point_init (&p[3], 100, 0);
gsk_curve_init (&c, gsk_pathop_encode (GSK_PATH_CURVE, p));
gsk_curve_get_start_tangent (&c, &t);
g_assert_true (graphene_vec2_near (&t, graphene_vec2_x_axis (), 0.0001));
gsk_curve_get_end_tangent (&c, &t);
g_assert_true (graphene_vec2_near (&t, graphene_vec2_x_axis (), 0.0001));
graphene_point_init (&p[0], 0, 0);
graphene_point_init (&p[1], 50, 0);
graphene_point_init (&p[2], 50, 0);
graphene_point_init (&p[3], 100, 0);
gsk_curve_init (&c, gsk_pathop_encode (GSK_PATH_CURVE, p));
gsk_curve_get_start_tangent (&c, &t);
g_assert_true (graphene_vec2_near (&t, graphene_vec2_x_axis (), 0.0001));
gsk_curve_get_end_tangent (&c, &t);
g_assert_true (graphene_vec2_near (&t, graphene_vec2_x_axis (), 0.0001));
}
static void
test_errant_intersection (void)
{
GskCurve c;
GskCurve l;
graphene_point_t p[4];
graphene_point_t q[2];
float t1[3], t2[3];
graphene_point_t s[3];
int n;
graphene_point_init (&p[0], 888, 482);
graphene_point_init (&p[1], 999.333313, 508.666687);
graphene_point_init (&p[2], 1080.83325, 544.333313);
graphene_point_init (&p[3], 1132.5, 589);
gsk_curve_init (&c, gsk_pathop_encode (GSK_PATH_CURVE, p));
graphene_point_init (&q[0], 886, 680);
graphene_point_init (&q[1], 642, 618);
gsk_curve_init (&l, gsk_pathop_encode (GSK_PATH_LINE, q));
n = gsk_curve_intersect (&l, &c, t1, t2, s, G_N_ELEMENTS (s));
g_assert_true (n == 0);
}
static void
test_errant_intersection2 (void)
{
GskCurve c;
GskCurve l;
graphene_point_t p[4];
graphene_point_t q[2];
float t1[3], t2[3];
graphene_point_t s[3];
int n;
graphene_point_init (&p[0], 1119.5, 772);
graphene_point_init (&p[1], 1039.16675, 850.666687);
graphene_point_init (&p[2], 925.333313, 890);
graphene_point_init (&p[3], 778, 890);
gsk_curve_init (&c, gsk_pathop_encode (GSK_PATH_CURVE, p));
graphene_point_init (&q[0], 1052, 1430);
graphene_point_init (&q[1], 734, 762);
gsk_curve_init (&l, gsk_pathop_encode (GSK_PATH_LINE, q));
n = gsk_curve_intersect (&l, &c, t1, t2, s, G_N_ELEMENTS (s));
g_assert_true (n == 1);
graphene_point_init (&q[0], 954, 762);
graphene_point_init (&q[1], 1292, 1430);
gsk_curve_init (&l, gsk_pathop_encode (GSK_PATH_LINE, q));
n = gsk_curve_intersect (&l, &c, t1, t2, s, G_N_ELEMENTS (s));
g_assert_true (n == 1);
graphene_point_init (&p[0], 248, 142);
graphene_point_init (&p[1], 283, 103);
graphene_point_init (&p[2], 333, 80);
graphene_point_init (&p[3], 384, 80);
gsk_curve_init (&c, gsk_pathop_encode (GSK_PATH_CURVE, p));
graphene_point_init (&q[0], 256, 719);
graphene_point_init (&q[1], 256, 76);
gsk_curve_init (&l, gsk_pathop_encode (GSK_PATH_LINE, q));
n = gsk_curve_intersect (&l, &c, t1, t2, s, G_N_ELEMENTS (s));
g_assert_true (n == 1);
}
static void
test_line_intersection_parallel (void)
{
GskCurve l1;
GskCurve l2;
graphene_point_t p1[1];
graphene_point_t p2[2];
float t1[3], t2[3];
graphene_point_t s[3];
int n;
graphene_point_init (&p1[0], 0, 100);
graphene_point_init (&p1[1], 100, 100);
gsk_curve_init (&l1, gsk_pathop_encode (GSK_PATH_LINE, p1));
graphene_point_init (&p2[0], 0, 110);
graphene_point_init (&p2[1], 100, 110);
gsk_curve_init (&l2, gsk_pathop_encode (GSK_PATH_LINE, p2));
n = gsk_curve_intersect (&l1, &l2, t1, t2, s, G_N_ELEMENTS (s));
g_assert_true (n == 0);
graphene_point_init (&p2[0], 110, 100);
graphene_point_init (&p2[1], 210, 100);
gsk_curve_init (&l2, gsk_pathop_encode (GSK_PATH_LINE, p2));
n = gsk_curve_intersect (&l1, &l2, t1, t2, s, G_N_ELEMENTS (s));
g_assert_true (n == 0);
graphene_point_init (&p2[0], 0, 100);
graphene_point_init (&p2[1], -100, 100);
gsk_curve_init (&l2, gsk_pathop_encode (GSK_PATH_LINE, p2));
n = gsk_curve_intersect (&l1, &l2, t1, t2, s, G_N_ELEMENTS (s));
g_assert_true (n == 1);
g_assert_cmpfloat_with_epsilon (t1[0], 0, 0.001);
g_assert_cmpfloat_with_epsilon (t2[0], 0, 0.001);
graphene_point_init (&p2[0], 20, 100);
graphene_point_init (&p2[1], 80, 100);
gsk_curve_init (&l2, gsk_pathop_encode (GSK_PATH_LINE, p2));
n = gsk_curve_intersect (&l1, &l2, t1, t2, s, G_N_ELEMENTS (s));
g_assert_true (n == 2);
g_assert_cmpfloat_with_epsilon (t1[0], 0.2, 0.001);
g_assert_cmpfloat_with_epsilon (t1[1], 0.8, 0.001);
g_assert_cmpfloat_with_epsilon (t2[0], 0, 0.001);
g_assert_cmpfloat_with_epsilon (t2[1], 1, 0.001);
graphene_point_init (&p2[0], 150, 100);
graphene_point_init (&p2[1], 50, 100);
gsk_curve_init (&l2, gsk_pathop_encode (GSK_PATH_LINE, p2));
n = gsk_curve_intersect (&l1, &l2, t1, t2, s, G_N_ELEMENTS (s));
g_assert_true (n == 2);
g_assert_cmpfloat_with_epsilon (t1[0], 1, 0.001);
g_assert_cmpfloat_with_epsilon (t1[1], 0.5, 0.001);
g_assert_cmpfloat_with_epsilon (t2[0], 0.5, 0.001);
g_assert_cmpfloat_with_epsilon (t2[1], 1, 0.001);
}
int
main (int argc,
char *argv[])
{
gtk_test_init (&argc, &argv, NULL);
g_test_add_func ("/curve/special/conic-segment", test_conic_segment);
g_test_add_func ("/curve/special/tangents", test_curve_tangents);
g_test_add_func ("/curve/special/degenerate-tangents", test_curve_degenerate_tangents);
g_test_add_func ("/curve/errant-intersection", test_errant_intersection);
g_test_add_func ("/curve/errant-intersection2", test_errant_intersection2);
g_test_add_func ("/curve/line-intersection-parallel", test_line_intersection_parallel);
return g_test_run ();
}
File diff suppressed because it is too large Load Diff
-181
View File
@@ -1,181 +0,0 @@
/*
* 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 <gtk/gtk.h>
#include "gsk/gskpathdashprivate.h"
static gboolean
build_path (GskPathOperation op,
const graphene_point_t *pts,
gsize n_pts,
float weight,
gpointer user_data)
{
GskPathBuilder *builder = user_data;
switch (op)
{
case GSK_PATH_MOVE:
gsk_path_builder_move_to (builder, pts[0].x, pts[0].y);
break;
case GSK_PATH_CLOSE:
gsk_path_builder_close (builder);
break;
case GSK_PATH_LINE:
gsk_path_builder_line_to (builder, pts[1].x, pts[1].y);
break;
case GSK_PATH_CURVE:
gsk_path_builder_curve_to (builder, pts[1].x, pts[1].y, pts[2].x, pts[2].y, pts[3].x, pts[3].y);
break;
case GSK_PATH_CONIC:
gsk_path_builder_conic_to (builder, pts[1].x, pts[1].y, pts[2].x, pts[2].y, weight);
break;
default:
g_assert_not_reached ();
break;
}
return TRUE;
}
static void
test_simple (void)
{
const struct {
const char *test;
float dash[4];
gsize n_dash;
float dash_offset;
const char *result;
} tests[] = {
/* a line with a dash of a quarter its size, very simple test */
{
"M 0 0 L 20 0",
{ 5, }, 1, 0.f,
"M 0 0 L 5 0 M 10 0 L 15 0",
},
/* a square with a dash of half its size, another simple test */
{
"M 0 0 h 10 v 10 h -10 z",
{ 5, }, 1, 0.f,
"M 10 0 L 10 5 M 10 10 L 5 10 M 0 10 L 0 5 M 0 0 L 5 0"
},
/* a square smaller than the dash, make sure it closes */
{
"M 0 0 h 10 v 10 h -10 z",
{ 50, }, 1, 0.f,
"M 0 0 L 10 0 L 10 10 L 0 10 Z"
},
/* a square exactly the dash's size, make sure it still closes */
{
"M 0 0 h 10 v 10 h -10 z",
{ 40, }, 1, 0.f,
"M 0 0 L 10 0 L 10 10 L 0 10 Z"
},
/* a dash with offset */
{
"M 0 0 h 10 v 10 h -10 z",
{ 5, }, 1, 2.5f,
"M 7.5 0 L 10 0 L 10 2.5 M 10 7.5 L 10 10 L 7.5 10 M 2.5 10 L 0 10 L 0 7.5 M 0 2.5 L 0 0 L 2.5 0"
},
/* a dash with offset, but this time the rect isn't closed */
{
"M 0 0 L 10 0 L 10 10 L 0 10 L 0 0",
{ 5, }, 1, 2.5f,
"M 0 0 L 2.5 0 M 7.5 0 L 10 0 L 10 2.5 M 10 7.5 L 10 10 L 7.5 10 M 2.5 10 L 0 10 L 0 7.5 M 0 2.5 L 0 0"
},
/* a dash with offset into an empty dash */
{
"M 0 0 h 10 v 10 h -10 z",
{ 5, }, 1, 7.5f,
"M 2.5 0 L 7.5 0 M 10 2.5 L 10 7.5 M 7.5 10 L 2.5 10 M 0 7.5 L 0 2.5"
},
/* a dash with offset where the whole rectangle fits into one element - make sure it closes */
{
"M 0 0 h 10 v 10 h -10 z",
{ 1, 1, 100 }, 3, 3.f,
"M 0 0 L 10 0 L 10 10 L 0 10 Z"
},
/* a dash with 0-length elements, aka dotting */
{
"M 0 0 h 10 v 10 h -10 z",
{ 0, 5 }, 2, 0.f,
"M 5 0 M 10 0 M 10 5 M 10 10 M 5 10 M 0 10 M 0 5 M 0 0"
},
/* a dash of a circle */
{
"M 10 5 O 10 10, 5 10, 0.70710676908493042 O 0 10, 0 5, 0.70710676908493042 O 0 0, 5 0, 0.70710676908493042 O 10 0, 10 5, 0.70710676908493042 Z",
{ 32, }, 1, 0.f,
"M 10 5 O 10 10, 5 10, 0.70710676908493042 O 0 10, 0 5, 0.70710676908493042 O 0 0, 5 0, 0.70710676908493042 O 10 0, 10 5, 0.70710676908493042 Z",
},
/* a dash with offset and 2 contours */
{
"M 10 10 h 10 v 10 h -10 z M 20 20 h 10 v 10 h -10 z",
{ 5, }, 1, 2.5f,
"M 17.5 10 L 20 10 L 20 12.5 M 20 17.5 L 20 20 L 17.5 20 M 12.5 20 L 10 20 L 10 17.5 M 10 12.5 L 10 10 L 12.5 10 "
"M 27.5 20 L 30 20 L 30 22.5 M 30 27.5 L 30 30 L 27.5 30 M 22.5 30 L 20 30 L 20 27.5 M 20 22.5 L 20 20 L 22.5 20"
},
};
GskPath *path, *result;
GskPathBuilder *builder;
GskStroke *stroke;
char *s;
for (gsize i = 0; i < G_N_ELEMENTS(tests); i++)
{
stroke = gsk_stroke_new (1);
gsk_stroke_set_dash (stroke, tests[i].dash, tests[i].n_dash);
gsk_stroke_set_dash_offset (stroke, tests[i].dash_offset);
path = gsk_path_parse (tests[i].test);
g_assert_nonnull (path);
s = gsk_path_to_string (path);
g_assert_cmpstr (s, ==, tests[i].test);
g_free (s);
builder = gsk_path_builder_new ();
gsk_path_dash (path, stroke, 0.5, build_path, builder);
result = gsk_path_builder_free_to_path (builder);
s = gsk_path_to_string (result);
g_assert_cmpstr (s, ==, tests[i].result);
g_free (s);
gsk_path_unref (result);
gsk_stroke_free (stroke);
gsk_path_unref (path);
}
}
int
main (int argc,
char *argv[])
{
gtk_test_init (&argc, &argv, NULL);
g_test_add_func ("/dash/simple", test_simple);
return g_test_run ();
}
-12
View File
@@ -160,8 +160,6 @@ node_parser_tests = [
'empty-cross-fade.ref.node',
'empty-debug.node',
'empty-debug.ref.node',
'empty-fill.node',
'empty-fill.ref.node',
'empty-inset-shadow.node',
'empty-inset-shadow.ref.node',
'empty-linear-gradient.node',
@@ -176,8 +174,6 @@ node_parser_tests = [
'empty-rounded-clip.ref.node',
'empty-shadow.node',
'empty-shadow.ref.node',
'empty-stroke.node',
'empty-stroke.ref.node',
'empty-text.node',
'empty-text.ref.node',
'empty-texture.node',
@@ -208,14 +204,6 @@ foreach test : node_parser_tests
endforeach
tests = [
['curve', ['../../gsk/gskcurve.c', '../../gsk/gskcurveintersect.c'], ['-DGTK_COMPILATION']],
['curve-special-cases', ['../../gsk/gskcurve.c', '../../gsk/gskcurveintersect.c'], ['-DGTK_COMPILATION']],
['dash'],
['curve-performance', ['../../gsk/gskcurve.c', '../../gsk/gskcurveintersect.c'], ['-DGTK_COMPILATION']],
['path'],
['path-special-cases'],
['path-stroke'],
['path-ops'],
['rounded-rect'],
['transform'],
['shader'],
-1
View File
@@ -1 +0,0 @@
fill { }
@@ -1,14 +0,0 @@
fill {
child: color {
bounds: 0 0 50 50;
color: rgb(255,0,204);
}
path: "\
M 25 0\
L 39.694629669189453 45.225425720214844\
L 1.2235870361328125 17.274574279785156\
L 48.776412963867188 17.274574279785156\
L 10.305368423461914 45.225425720214844\
Z";
fill-rule: winding;
}
@@ -1 +0,0 @@
stroke { }
@@ -1,16 +0,0 @@
stroke {
child: color {
bounds: 0 0 50 50;
color: rgb(255,0,204);
}
path: "\
M 25 0\
L 39.694629669189453 45.225425720214844\
L 1.2235870361328125 17.274574279785156\
L 48.776412963867188 17.274574279785156\
L 10.305368423461914 45.225425720214844\
Z";
line-width: 1;
line-cap: butt;
line-join: miter;
}
-9
View File
@@ -1,9 +0,0 @@
fill {
child: color {
bounds: 0 0 50 50;
color: rgb(255,0,204);
}
path: "M 150 100 A 50 50 0 0 0 50 100 A 50 50 0 0 0 150 100 z";
fill-rule: even-odd;
}
-9
View File
@@ -1,9 +0,0 @@
fill {
child: color {
bounds: 0 0 50 50;
color: rgb(255,0,204);
}
path: "\
M 150 100 A 50 50 0 0 0 50 100 A 50 50 0 0 0 150 100 z";
fill-rule: even-odd;
}
-13
View File
@@ -1,13 +0,0 @@
stroke {
child: color {
bounds: 0 0 50 50;
color: rgb(255,0,204);
}
path: "M 111 222 h 333 v 444 h -333 z";
line-width: 2.5;
line-cap: round;
line-join: miter-clip;
miter-limit: 5.0;
dash: 1 2 3 4 5;
dash-offset: 1.5;
}
-14
View File
@@ -1,14 +0,0 @@
stroke {
child: color {
bounds: 0 0 50 50;
color: rgb(255,0,204);
}
path: "\
M 111 222 h 333 v 444 h -333 z";
line-width: 2.5;
line-cap: round;
line-join: miter-clip;
miter-limit: 5;
dash: 1 2 3 4 5;
dash-offset: 1.5;
}
-353
View File
@@ -1,353 +0,0 @@
/*
* Copyright © 2022 Red Hat, Inc.
*
* 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: Matthias Clasen <mclasen@redhat.com>
*/
#include <gtk/gtk.h>
typedef enum
{
OP_UNION,
OP_INTERSECTION,
OP_DIFFERENCE,
OP_SYMMETRIC_DIFFERENCE,
} Op;
static void
test_ops_simple (void)
{
struct {
const char *in1;
const char *in2;
Op op;
const char *out;
} tests[] = {
/* partially overlapping edge */
{ "M 100 100 L 100 200 L 200 200 Z",
"M 150 150 L 150 250 L 250 250 Z",
OP_UNION,
"M 100 100 L 100 200 L 150 200 L 150 250 L 250 250 L 200 200 L 150 150 L 100 100 Z" },
{ "M 100 100 L 100 200 L 200 200 Z",
"M 150 150 L 150 250 L 250 250 Z",
OP_INTERSECTION,
"M 150 200 L 200 200 L 150 150 L 150 200 Z" },
{ "M 100 100 L 100 200 L 200 200 Z",
"M 150 150 L 150 250 L 250 250 Z",
OP_DIFFERENCE,
"M 100 100 L 100 200 L 150 200 L 150 150 L 100 100 Z" },
{ "M 100 100 L 100 200 L 200 200 Z",
"M 150 150 L 150 250 L 250 250 Z",
OP_SYMMETRIC_DIFFERENCE,
"M 100 100 L 100 200 L 150 200 L 150 150 L 100 100 Z M 200 200 L 150 200 L 150 250 "
"L 250 250 L 200 200 Z" },
/* two triangles in general position */
{ "M 100 100 L 100 200 L 200 200 Z",
"M 170 120 L 100 240 L 170 240 Z",
OP_UNION,
"M 100 100 L 100 200 L 123.33333587646484 200 L 100 240 L 170 240 L 170 200 L 200 200 "
"L 170 170 L 170 120 L 151.57894897460938 151.57894897460938 L 100 100 Z" },
{ "M 100 100 L 100 200 L 200 200 Z",
"M 170 120 L 100 240 L 170 240 Z",
OP_INTERSECTION,
"M 123.33333587646484 200 L 170 200 L 170 170 L 151.57894897460938 151.57894897460938 "
"L 123.33332824707031 200 Z" },
{ "M 100 100 L 100 200 L 200 200 Z",
"M 170 120 L 100 240 L 170 240 Z",
OP_DIFFERENCE,
"M 100 100 L 100 200 L 123.33333587646484 200 L 151.57894897460938 151.57894897460938 "
"L 100 100 Z M 170 200 L 200 200 L 170 170 L 170 200 Z" },
{ "M 100 100 L 100 200 L 200 200 Z",
"M 170 120 L 100 240 L 170 240 Z",
OP_SYMMETRIC_DIFFERENCE,
"M 100 100 L 100 200 L 123.33333587646484 200 L 151.57894897460938 151.57894897460938 "
"L 100 100 Z M 170 200 L 123.33333587646484 200 L 100 240 L 170 240 L 170 200 Z "
"M 170 200 L 200 200 L 170 170 L 170 200 Z M 151.57894897460938 151.57894897460938 "
"L 170 170 L 170 120 L 151.57894897460938 151.57894897460938 Z" },
/* nested contours, oriented in opposite direction */
{ "M 100 100 L 100 200 L 200 200 Z",
"M 120 140 L 170 190 L 120 190 Z",
OP_UNION,
"M 100 100 L 100 200 L 200 200 L 100 100 Z" },
{ "M 100 100 L 100 200 L 200 200 Z",
"M 120 140 L 170 190 L 120 190 Z",
OP_INTERSECTION,
"M 170 190 L 120 140 L 120 190 L 170 190 Z" },
{ "M 100 100 L 100 200 L 200 200 Z",
"M 120 140 L 170 190 L 120 190 Z",
OP_DIFFERENCE,
"M 100 100 L 100 200 L 200 200 L 100 100 Z M 120 140 L 170 190 L 120 190 L 120 140 Z" },
{ "M 100 100 L 100 200 L 200 200 Z",
"M 120 140 L 170 190 L 120 190 Z",
OP_SYMMETRIC_DIFFERENCE,
"M 100 100 L 100 200 L 200 200 L 100 100 Z M 120 140 L 170 190 L 120 190 L 120 140 Z" },
/* nested contours, oriented in opposite direction, other way around */
{ "M 100 100 L 200 200 L 100 200 Z",
"M 120 140 L 120 190 L 170 190 Z",
OP_UNION,
"M 200 200 L 100 100 L 100 200 L 200 200 Z" },
{ "M 100 100 L 200 200 L 100 200 Z",
"M 120 140 L 120 190 L 170 190 Z",
OP_INTERSECTION,
"M 120 140 L 120 190 L 170 190 L 120 140 Z" },
{ "M 100 100 L 200 200 L 100 200 Z",
"M 120 140 L 120 190 L 170 190 Z",
OP_DIFFERENCE,
"M 200 200 L 100 100 L 100 200 L 200 200 Z M 120 190 L 120 140 L 170 190 L 120 190 Z" },
{ "M 100 100 L 200 200 L 100 200 Z",
"M 120 140 L 120 190 L 170 190 Z",
OP_SYMMETRIC_DIFFERENCE,
"M 200 200 L 100 100 L 100 200 L 200 200 Z M 120 190 L 120 140 L 170 190 L 120 190 Z" },
/* nested contours, oriented in the same direction */
{ "M 100 100 L 100 200 L 200 200 Z",
"M 120 140 L 120 190 L 170 190 Z",
OP_UNION,
"M 100 100 L 100 200 L 200 200 L 100 100 Z" },
{ "M 100 100 L 100 200 L 200 200 Z",
"M 120 140 L 120 190 L 170 190 Z",
OP_INTERSECTION,
"M 120 140 L 120 190 L 170 190 L 120 140 Z" },
{ "M 100 100 L 100 200 L 200 200 Z",
"M 120 140 L 120 190 L 170 190 Z",
OP_DIFFERENCE,
"M 100 100 L 100 200 L 200 200 L 100 100 Z M 120 190 L 120 140 L 170 190 L 120 190 Z" },
{ "M 100 100 L 100 200 L 200 200 Z",
"M 120 140 L 120 190 L 170 190 Z",
OP_SYMMETRIC_DIFFERENCE,
"M 100 100 L 100 200 L 200 200 L 100 100 Z M 120 190 L 120 140 L 170 190 L 120 190 Z" },
/* a 3-way intersection */
{ "M 100 200 L 150 104 L 145 104 L 200 200 Z",
"M 100 108.571 L 200 108.571 L 200 50 L 100 50 Z",
OP_UNION,
"M 147.61904907226562 108.57142639160156 L 100 200 L 200 200 "
"L 147.61904907226562 108.57142639160156 Z M 100 108.57099914550781 "
"L 147.61927795410156 108.57099914550781 L 200 108.57099914550781 L 200 50 "
"L 100 50 L 100 108.57099914550781 Z" },
{ "M 100 200 L 150 104 L 145 104 L 200 200 Z",
"M 100 108.571 L 200 108.571 L 200 50 L 100 50 Z",
OP_INTERSECTION,
"M 147.61904907226562 108.57142639160156 L 150 104 L 145 104 "
"L 147.61904907226562 108.57142639160156 Z" },
{ "M 100 200 L 150 104 L 145 104 L 200 200 Z",
"M 100 108.571 L 200 108.571 L 200 50 L 100 50 Z",
OP_DIFFERENCE,
"M 147.61904907226562 108.57142639160156 L 100 200 L 200 200 "
"L 147.61904907226562 108.57142639160156 Z" },
{ "M 100 200 L 150 104 L 145 104 L 200 200 Z",
"M 100 108.571 L 200 108.571 L 200 50 L 100 50 Z",
OP_SYMMETRIC_DIFFERENCE,
"M 147.61904907226562 108.57142639160156 L 100 200 L 200 200 "
"L 147.61904907226562 108.57142639160156 Z M 150 104 "
"L 147.61904907226562 108.57142639160156 L 200 108.57099914550781 "
"L 200 50 L 100 50 L 100 108.57099914550781 L 147.61927795410156 108.57099914550781 "
"L 145 104 L 150 104 Z" },
/* touching quadratics */
{ "M 100 100 Q 150 200 200 100 Z",
"M 100 200 Q 150 100 200 200 Z",
OP_UNION,
"M 100 100 "
"C 116.65585327148438 133.31172180175781, 133.31172180175781 149.97837829589844, 149.96757507324219 149.99998474121094 "
"C 166.64505004882812 150.0216064453125, 183.32252502441406 133.35494995117188, 200 100 "
"L 100 100 "
"Z "
"M 149.96755981445312 149.99998474121094 "
"C 133.31172180175781 150.02162170410156, 116.65585327148438 166.68827819824219, 100 200 "
"L 200 200 "
"C 183.32252502441406 166.64505004882812, 166.64505004882812 149.9783935546875, 149.96757507324219 150.00001525878906 "
"Z" },
/* overlapping quadratics, two intersections, different orientations */
{ "M 100 100 Q 150 200 200 100 Z",
"M 100 180 Q 150 80 200 180 Z",
OP_UNION,
"M 100 100 "
"C 109.21287536621094 118.42575073242188, 118.42575073242188 131.75888061523438, 127.63862609863281 139.9993896484375 "
"C 118.42625427246094 148.24038696289062, 109.21312713623047 161.57374572753906, 100 180 "
"L 200 180 "
"C 190.78688049316406 161.57374572753906, 181.57374572753906 148.24038696289062, 172.36061096191406 139.99993896484375 "
"C 181.57373046875 131.75961303710938, 190.786865234375 118.42626190185547, 200 100 "
"L 100 100 "
"Z" },
{ "M 100 100 Q 150 200 200 100 Z",
"M 100 180 Q 150 80 200 180 Z",
OP_INTERSECTION,
"M 127.63862609863281 139.9993896484375 "
"C 142.54594421386719 153.33332824707031, 157.45327758789062 153.33355712890625, 172.360595703125 140.00006103515625 "
"C 157.45353698730469 126.66668701171875, 142.54646301269531 126.66668701171875, 127.63938903808594 139.99993896484375 "
"Z" },
{ "M 100 100 Q 150 200 200 100 Z",
"M 100 180 Q 150 80 200 180 Z",
OP_DIFFERENCE,
"M 100 100 "
"C 109.21287536621094 118.42575073242188, 118.42575073242188 131.75888061523438, 127.63862609863281 139.9993896484375 "
"C 142.54646301269531 126.66668701171875, 157.45353698730469 126.66668701171875, 172.36061096191406 139.99993896484375 "
"C 181.57373046875 131.75961303710938, 190.786865234375 118.42626190185547, 200 100 "
"L 100 100 Z" },
{ "M 100 100 Q 150 200 200 100 Z",
"M 100 180 Q 150 80 200 180 Z",
OP_SYMMETRIC_DIFFERENCE,
"M 100 100 "
"C 109.21287536621094 118.42575073242188, 118.42575073242188 131.75888061523438, 127.63862609863281 139.9993896484375 "
"C 142.54646301269531 126.66668701171875, 157.45353698730469 126.66668701171875, 172.36061096191406 139.99993896484375 "
"C 181.57373046875 131.75961303710938, 190.786865234375 118.42626190185547, 200 100 "
"L 100 100 Z "
"M 172.36062622070312 140.00006103515625 "
"C 157.45327758789062 153.33355712890625, 142.54594421386719 153.33332824707031, 127.63862609863281 139.9993896484375 "
"C 118.42625427246094 148.24038696289062, 109.21312713623047 161.57374572753906, 100 180 "
"L 200 180 "
"C 190.78688049316406 161.57374572753906, 181.57374572753906 148.24038696289062, 172.36061096191406 139.99993896484375 "
"Z" },
/* overlapping quadratics, two intersections, same orientation */
{ "M 100 100 Q 150 200 200 100 Z",
"M 100 180 L 200 180 Q 150 80 100 180 Z",
OP_UNION,
"M 100 100 "
"C 109.21287536621094 118.42575073242188, 118.42575073242188 131.75888061523438, 127.63862609863281 139.9993896484375 "
"C 118.42626190185547 148.24037170410156, 109.21312713623047 161.57373046875, 100 180 "
"L 200 180 "
"C 190.78712463378906 161.57424926757812, 181.57424926757812 148.24111938476562, 172.36137390136719 140.00062561035156 "
"C 181.57373046875 131.75961303710938, 190.786865234375 118.42626190185547, 200 100 "
"L 100 100 Z" },
{ "M 100 100 Q 150 200 200 100 Z",
"M 100 180 L 200 180 Q 150 80 100 180 Z",
OP_INTERSECTION,
"M 127.63862609863281 139.9993896484375 "
"C 142.54594421386719 153.33332824707031, 157.45327758789062 153.33355712890625, 172.360595703125 140.00006103515625 "
"C 157.45405578613281 126.66668701171875, 142.54672241210938 126.66645812988281, 127.63939666748047 139.99992370605469 Z" },
{ "M 100 100 Q 150 200 200 100 Z",
"M 100 180 L 200 180 Q 150 80 100 180 Z",
OP_DIFFERENCE,
"M 100 100 "
"C 109.21287536621094 118.42575073242188, 118.42575073242188 131.75888061523438, 127.63862609863281 139.9993896484375 "
"C 142.54672241210938 126.66645812988281, 157.45405578613281 126.66668701171875, 172.36137390136719 140.00062561035156 "
"C 181.57373046875 131.75961303710938, 190.786865234375 118.42626190185547, 200 100 "
"L 100 100 Z" },
{ "M 100 100 Q 150 200 200 100 Z",
"M 100 180 L 200 180 Q 150 80 100 180 Z",
OP_SYMMETRIC_DIFFERENCE,
"M 100 100 "
"C 109.21287536621094 118.42575073242188, 118.42575073242188 131.75888061523438, 127.63862609863281 139.9993896484375 "
"C 142.54672241210938 126.66645812988281, 157.45405578613281 126.66668701171875, 172.36137390136719 140.00062561035156 "
"C 181.57373046875 131.75961303710938, 190.786865234375 118.42626190185547, 200 100 "
"L 100 100 Z "
"M 172.36062622070312 140.00006103515625 "
"C 157.45327758789062 153.33355712890625, 142.54594421386719 153.33332824707031, 127.63862609863281 139.9993896484375 "
"C 118.42626190185547 148.24037170410156, 109.21312713623047 161.57373046875, 100 180 "
"L 200 180 "
"C 190.78712463378906 161.57424926757812, 181.57424926757812 148.24111938476562, 172.36137390136719 140.00062561035156 "
"Z" },
/* two polygons with near edges */
{ "M 100 100 L 100 200 L 400 200 L 400 100 Z",
"M 150 103 L 250 100 L 300 103 L 250 180 Z",
OP_UNION,
"M 100 100 L 100 200 L 400 200 L 400 100 L 250 100 L 100 100 Z" },
{ "M 100 100 L 100 200 L 400 200 L 400 100 Z",
"M 150 103 L 250 100 L 300 103 L 250 180 Z",
OP_INTERSECTION,
"M 250 100 L 150 103 L 250 180 L 300 103 L 250 100 Z" },
{ "M 100 100 L 100 200 L 400 200 L 400 100 Z",
"M 150 103 L 250 100 L 300 103 L 250 180 Z",
OP_DIFFERENCE,
"M 100 100 L 100 200 L 400 200 L 400 100 L 250 100 L 300 103 L 250 180 L 150 103 L 250 100 L 100 100 Z" },
{ "M 100 100 L 100 200 L 400 200 L 400 100 Z",
"M 150 103 L 250 100 L 300 103 L 250 180 Z",
OP_SYMMETRIC_DIFFERENCE,
"M 100 100 L 100 200 L 400 200 L 400 100 L 250 100 L 300 103 L 250 180 L 150 103 L 250 100 L 100 100 Z" },
/* Collinear line segments */
{ "M 100 100 L 200 100 L 250 100 L 100 200 Z",
"M 150 100 L 300 100 L 300 200 Z",
OP_UNION,
"M 150 100 L 100 100 L 100 200 L 200 133.33332824707031 L 300 200 L 300 100 L 250 100 "
"L 200 100 L 150 100 Z" },
{ "M 100 100 L 200 100 L 250 100 L 100 200 Z",
"M 150 100 L 300 100 L 300 200 Z",
OP_INTERSECTION,
"M 200 100 L 150 100 L 200 133.33332824707031 L 250 100 L 200 100 Z" },
{ "M 100 100 L 200 100 L 250 100 L 100 200 Z",
"M 150 100 L 300 100 L 300 200 Z",
OP_DIFFERENCE,
"M 150 100 L 100 100 L 100 200 L 200 133.33332824707031 L 150 100 Z" },
{ "M 100 100 L 200 100 L 250 100 L 100 200 Z",
"M 150 100 L 300 100 L 300 200 Z",
OP_SYMMETRIC_DIFFERENCE,
"M 150 100 L 100 100 L 100 200 L 200 133.33332824707031 L 150 100 Z "
"M 250 100 L 200 133.33332824707031 L 300 200 L 300 100 L 250 100 Z" },
/* a complicated intersection */
{ "M 175 100 L 175 400 L 300 400 L 300 100 z",
"M 100 100 C 200 200 200 300 100 400 L 0 400 C 233.35 300 233.35 200 0 100 Z",
OP_UNION,
"M 175 100 L 175 248.73167419433594 L 175 249.94822692871094 L 175 251.26628112792969 "
"L 175 400 L 300 400 L 300 100 L 175 100 Z "
"M 174.9906005859375 248.32115173339844 "
"C 174.437255859375 198.88076782226562, 149.44038391113281 149.44038391113281, 100 100 "
"L 0 100 "
"C 115.68845367431641 149.57722473144531, 174.02178955078125 199.15444946289062, 175 248.73167419433594 "
"Z "
"M 100 400 "
"C 149.44171142578125 350.55828857421875, 174.4385986328125 301.11660766601562, 174.99066162109375 251.67486572265625 "
"C 174.02340698242188 300.84420776367188, 115.69005584716797 350.42208862304688, 0 400 "
"L 100 400 "
"Z" },
};
for (int i = 0; i < G_N_ELEMENTS (tests); i++)
{
GskPath *p1, *p2, *p;
char *s;
if (g_test_verbose ())
g_test_message ("testcase %d op %d", i, tests[i].op);
p1 = gsk_path_parse (tests[i].in1);
p2 = gsk_path_parse (tests[i].in2);
switch (tests[i].op)
{
case OP_UNION:
p = gsk_path_union (p1, p2, GSK_FILL_RULE_WINDING);
break;
case OP_INTERSECTION:
p = gsk_path_intersection (p1, p2, GSK_FILL_RULE_WINDING);
break;
case OP_DIFFERENCE:
p = gsk_path_difference (p1, p2, GSK_FILL_RULE_WINDING);
break;
case OP_SYMMETRIC_DIFFERENCE:
p = gsk_path_symmetric_difference (p1, p2, GSK_FILL_RULE_WINDING);
break;
default:
g_assert_not_reached ();
}
g_assert_nonnull (p);
s = gsk_path_to_string (p);
g_assert_cmpstr (s, ==, tests[i].out);
g_free (s);
gsk_path_unref (p);
gsk_path_unref (p1);
gsk_path_unref (p2);
}
}
int
main (int argc,
char *argv[])
{
gtk_test_init (&argc, &argv, NULL);
g_test_add_func ("/ops/simple", test_ops_simple);
return g_test_run ();
}
-357
View File
@@ -1,357 +0,0 @@
/*
* 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 <gtk/gtk.h>
/* testcases from path_parser.rs in librsvg */
static void
test_rsvg_parse (void)
{
struct {
const char *in;
const char *out;
} tests[] = {
{ "", "" },
// numbers
{ "M 10 20", "M 10 20" },
{ "M -10 -20", "M -10 -20" },
{ "M .10 0.20", "M 0.1 0.2" },
{ "M -.10 -0.20", "M -0.1 -0.2" },
{ "M-.10-0.20", "M -0.1 -0.2" },
{ "M10.5.50", "M 10.5 0.5" },
{ "M.10.20", "M 0.1 0.2" },
{ "M .10E1 .20e-4", "M 1 2e-05" },
{ "M-.10E1-.20", "M -1 -0.2" },
{ "M10.10E2 -0.20e3", "M 1010 -200" },
{ "M-10.10E2-0.20e-3", "M -1010 -0.0002" },
{ "M1e2.5", "M 100 0.5" },
{ "M1e-2.5", "M 0.01 0.5" },
{ "M1e+2.5", "M 100 0.5" },
// bogus numbers
{ "M+", NULL },
{ "M-", NULL },
{ "M+x", NULL },
{ "M10e", NULL },
{ "M10ex", NULL },
{ "M10e-", NULL },
{ "M10e+x", NULL },
// numbers with comma
{ "M 10, 20", "M 10 20" },
{ "M -10,-20", "M -10 -20" },
{ "M.10 , 0.20", "M 0.1 0.2" },
{ "M -.10, -0.20 ", "M -0.1 -0.2" },
{ "M-.10-0.20", "M -0.1 -0.2" },
{ "M.10.20", "M 0.1 0.2" },
{ "M .10E1,.20e-4", "M 1 2e-05" },
{ "M-.10E-2,-.20", "M -0.001 -0.2" },
{ "M10.10E2,-0.20e3", "M 1010 -200" },
{ "M-10.10E2,-0.20e-3", "M -1010 -0.0002" },
// single moveto
{ "M 10 20 ", "M 10 20" },
{ "M10,20 ", "M 10 20" },
{ "M10 20 ", "M 10 20" },
{ " M10,20 ", "M 10 20" },
// relative moveto
{ "m10 20", "M 10 20" },
// absolute moveto with implicit lineto
{ "M10 20 30 40", "M 10 20 L 30 40" },
{ "M10,20,30,40", "M 10 20 L 30 40" },
{ "M.1-2,3E2-4", "M 0.1 -2 L 300 -4" },
// relative moveto with implicit lineto
{ "m10 20 30 40", "M 10 20 L 40 60" },
// relative moveto with relative lineto sequence
{ "m 46,447 l 0,0.5 -1,0 -1,0 0,1 0,12",
"M 46 447 L 46 447.5 L 45 447.5 L 44 447.5 L 44 448.5 L 44 460.5" },
// absolute moveto with implicit linetos
{ "M10,20 30,40,50 60", "M 10 20 L 30 40 L 50 60" },
// relative moveto with implicit linetos
{ "m10 20 30 40 50 60", "M 10 20 L 40 60 L 90 120" },
// absolute moveto moveto
{ "M10 20 M 30 40", "M 10 20 M 30 40" },
// relative moveto moveto
{ "m10 20 m 30 40", "M 10 20 M 40 60" },
// relative moveto lineto moveto
{ "m10 20 30 40 m 50 60", "M 10 20 L 40 60 M 90 120" },
// absolute moveto lineto
{ "M10 20 L30,40", "M 10 20 L 30 40" },
// relative moveto lineto
{ "m10 20 l30,40", "M 10 20 L 40 60" },
// relative moveto lineto lineto abs lineto
{ "m10 20 30 40l30,40,50 60L200,300",
"M 10 20 L 40 60 L 70 100 L 120 160 L 200 300" },
// horizontal lineto
{ "M10 20 H30", "M 10 20 L 30 20" },
{ "M 10 20 H 30 40", "M 10 20 L 30 20 L 40 20" },
{ "M10 20 H30,40-50", "M 10 20 L 30 20 L 40 20 L -50 20" },
{ "m10 20 h30,40-50", "M 10 20 L 40 20 L 80 20 L 30 20" },
// vertical lineto
{ "M10 20 V30", "M 10 20 L 10 30" },
{ "M10 20 V30 40", "M 10 20 L 10 30 L 10 40" },
{ "M10 20 V30,40-50", "M 10 20 L 10 30 L 10 40 L 10 -50" },
{ "m10 20 v30,40-50", "M 10 20 L 10 50 L 10 90 L 10 40" },
// curveto
{ "M10 20 C 30,40 50 60-70,80", "M 10 20 C 30 40, 50 60, -70 80" },
{ "M10 20 C 30,40 50 60-70,80,90 100,110 120,130,140",
"M 10 20 C 30 40, 50 60, -70 80 C 90 100, 110 120, 130 140" },
{ "m10 20 c 30,40 50 60-70,80,90 100,110 120,130,140",
"M 10 20 C 40 60, 60 80, -60 100 C 30 200, 50 220, 70 240" },
{ "m10 20 c 30,40 50 60-70,80 90 100,110 120,130,140",
"M 10 20 C 40 60, 60 80, -60 100 C 30 200, 50 220, 70 240" },
// smooth curveto
{ "M10 20 S 30,40-50,60", "M 10 20 C 10 20, 30 40, -50 60" },
{ "M10 20 S 30,40 50 60-70,80,90 100",
"M 10 20 C 10 20, 30 40, 50 60 C 70 80, -70 80, 90 100" },
// quadratic curveto
{ "M10 20 Q30 40 50 60", "M 10 20 C 23.3333333 33.3333333, 36.6666667 46.6666667, 50 60" },
{ "M10 20 Q30 40 50 60,70,80-90 100",
"M 10 20 C 23.3333333 33.3333333, 36.6666667 46.6666667, 50 60 C 63.3333333 73.3333333, 16.6666667 86.6666667, -90 100" },
{ "m10 20 q 30,40 50 60-70,80 90 100",
"M 10 20 C 30 46.6666667, 46.6666667 66.6666667, 60 80 C 13.3333333 133.3333333, 43.3333333 166.6666667, 150 180" },
// smooth quadratic curveto
{ "M10 20 T30 40", "M 10 20 C 10 20, 16.6666667 26.6666667, 30 40" },
{ "M10 20 Q30 40 50 60 T70 80",
"M 10 20 C 23.3333333 33.3333333, 36.6666667 46.6666667, 50 60 C 63.3333333 73.3333333, 70 80, 70 80" },
{ "m10 20 q 30,40 50 60t-70,80",
"M 10 20 C 30 46.6666667, 46.6666667 66.6666667, 60 80 C 73.3333333 93.3333333, 50 120, -10 160" },
// elliptical arc. Exact numbers depend on too much math, so just verify
// that these parse successfully
{ "M 1 3 A 1 2 3 00 6 7", "path" },
{ "M 1 2 A 1 2 3 016 7", "path" },
{ "M 1 2 A 1 2 3 10,6 7", "path" },
{ "M 1 2 A 1 2 3 1,1 6 7", "path" },
{ "M 1 2 A 1 2 3 1 1 6 7", "path" },
{ "M 1 2 A 1 2 3 1 16 7", "path" },
// close path
{ "M10 20 Z", "M 10 20 Z" },
{ "m10 20 30 40 m 50 60 70 80 90 100z", "M 10 20 L 40 60 M 90 120 L 160 200 L 250 300 Z" },
// must start with moveto
{ " L10 20", NULL },
// moveto args
{ "M", NULL },
{ "M,", NULL },
{ "M10", NULL },
{ "M10,", NULL },
{ "M10x", NULL },
{ "M10,x", NULL },
{ "M10-20,", NULL },
{ "M10-20-30", NULL },
{ "M10-20-30 x", NULL },
// closepath args
{ "M10-20z10", NULL },
{ "M10-20z,", NULL },
// lineto args
{ "M10-20L10", NULL },
{ "M 10,10 L 20,20,30", NULL },
{ "M 10,10 L 20,20,", NULL },
// horizontal lineto args
{ "M10-20H", NULL },
{ "M10-20H,", NULL },
{ "M10-20H30,", NULL },
// vertical lineto args
{ "M10-20v", NULL },
{ "M10-20v,", NULL },
{ "M10-20v30,", NULL },
// curveto args
{ "M10-20C1", NULL },
{ "M10-20C1,", NULL },
{ "M10-20C1 2", NULL },
{ "M10-20C1,2,", NULL },
{ "M10-20C1 2 3", NULL },
{ "M10-20C1,2,3", NULL },
{ "M10-20C1,2,3,", NULL },
{ "M10-20C1 2 3 4", NULL },
{ "M10-20C1,2,3,4", NULL },
{ "M10-20C1,2,3,4,", NULL },
{ "M10-20C1 2 3 4 5", NULL },
{ "M10-20C1,2,3,4,5", NULL },
{ "M10-20C1,2,3,4,5,", NULL },
{ "M10-20C1,2,3,4,5,6,", NULL },
// smooth curveto args
{ "M10-20S1", NULL },
{ "M10-20S1,", NULL },
{ "M10-20S1 2", NULL },
{ "M10-20S1,2,", NULL },
{ "M10-20S1 2 3", NULL },
{ "M10-20S1,2,3,", NULL },
{ "M10-20S1,2,3,4,", NULL },
// quadratic curveto args
{ "M10-20Q1", NULL },
{ "M10-20Q1,", NULL },
{ "M10-20Q1 2", NULL },
{ "M10-20Q1,2,", NULL },
{ "M10-20Q1 2 3", NULL },
{ "M10-20Q1,2,3", NULL },
{ "M10-20Q1,2,3,", NULL },
{ "M10 20 Q30 40 50 60,", NULL },
// smooth quadratic curveto args
{ "M10-20T1", NULL },
{ "M10-20T1,", NULL },
{ "M10 20 T 30 40,", NULL },
// elliptical arc args
{ "M10-20A1", NULL },
{ "M10-20A1,", NULL },
{ "M10-20A1 2", NULL },
{ "M10-20A1 2,", NULL },
{ "M10-20A1 2 3", NULL },
{ "M10-20A1 2 3,", NULL },
{ "M10-20A1 2 3 4", NULL },
{ "M10-20A1 2 3 1", NULL },
{ "M10-20A1 2 3,1,", NULL },
{ "M10-20A1 2 3 1 5", NULL },
{ "M10-20A1 2 3 1 1", NULL },
{ "M10-20A1 2 3,1,1,", NULL },
{ "M10-20A1 2 3 1 1 6", NULL },
{ "M10-20A1 2 3,1,1,6,", NULL },
{ "M 1 2 A 1 2 3 1.0 0.0 6 7", NULL },
{ "M10-20A1 2 3,1,1,6,7,", NULL },
// misc
{ "M.. 1,0 0,100000", NULL },
{ "M 10 20,M 10 20", NULL },
{ "M 10 20, M 10 20", NULL },
{ "M 10 20, M 10 20", NULL },
{ "M 10 20, ", NULL },
};
int i;
for (i = 0; i < G_N_ELEMENTS (tests); i++)
{
GskPath *path;
char *string;
char *string2;
if (g_test_verbose ())
g_print ("%d: %s\n", i, tests[i].in);
path = gsk_path_parse (tests[i].in);
if (tests[i].out)
{
g_assert_nonnull (path);
string = gsk_path_to_string (path);
gsk_path_unref (path);
if (strcmp (tests[i].out, "path") != 0)
{
/* Preferred, but doesn't work, because
* gsk_path_print() prints numbers with
* insane accuracy */
/* g_assert_cmpstr (tests[i].out, ==, string); */
path = gsk_path_parse (tests[i].out);
g_assert_nonnull (path);
string2 = gsk_path_to_string (path);
gsk_path_unref (path);
g_assert_cmpstr (string, ==, string2);
}
path = gsk_path_parse (string);
g_assert_nonnull (path);
string2 = gsk_path_to_string (path);
gsk_path_unref (path);
g_assert_cmpstr (string, ==, string2);
g_free (string);
g_free (string2);
}
else
g_assert_null (path);
}
}
/* Test that circles and rectangles serialize as expected and can be
* round-tripped through strings.
*/
static void
test_serialize_custom_contours (void)
{
GskPathBuilder *builder;
GskPath *path;
GskPath *path1;
char *string;
char *string1;
builder = gsk_path_builder_new ();
gsk_path_builder_add_circle (builder, &GRAPHENE_POINT_INIT (100, 100), 50);
gsk_path_builder_add_rect (builder, &GRAPHENE_RECT_INIT (111, 222, 333, 444));
path = gsk_path_builder_free_to_path (builder);
string = gsk_path_to_string (path);
g_assert_cmpstr ("M 150 100 A 50 50 0 0 0 50 100 A 50 50 0 0 0 150 100 z M 111 222 h 333 v 444 h -333 z", ==, string);
path1 = gsk_path_parse (string);
string1 = gsk_path_to_string (path1);
g_assert_cmpstr (string, ==, string1);
g_free (string);
g_free (string1);
gsk_path_unref (path);
gsk_path_unref (path1);
}
static void
test_path_winding (void)
{
struct {
const char *path;
graphene_point_t point;
gboolean result;
} tests[] = {
{ "M 150 103 L 250 100 L 300 103 L 250 180 Z", GRAPHENE_POINT_INIT (175, 100), FALSE },
{ "M 100 100 L 300 200 L 300 0 Z", GRAPHENE_POINT_INIT (250, 100), TRUE },
{ "M 100 100 L 300 200 L 300 0 Z", GRAPHENE_POINT_INIT (400, 100), FALSE},
{ "M 100 100 L 200 100 L 300 200 L 300 0 Z", GRAPHENE_POINT_INIT (50, 100), FALSE },
{ "M 100 100 L 200 100 L 300 200 L 300 0 Z", GRAPHENE_POINT_INIT (150, 100), TRUE },
{ "M 100 100 L 200 100 L 300 200 L 300 0 Z", GRAPHENE_POINT_INIT (250, 100), TRUE },
{ "M 100 100 L 200 100 L 300 200 L 300 0 Z", GRAPHENE_POINT_INIT (400, 100), FALSE },
/* the following check that our elementary contours wind as expected.
* our rect contour is clockwise, our circle contour is counterclockwise
*/
{ "M100,100h100v100h-100z M200,150A50,50,0,1,0,100,150A50,50,0,1,0,200,150z", GRAPHENE_POINT_INIT (150,150), FALSE },
};
GskPath *path;
GskPathMeasure *measure;
gboolean res;
for (int i = 0; i < G_N_ELEMENTS (tests); i++)
{
path = gsk_path_parse (tests[i].path);
measure = gsk_path_measure_new (path);
res = gsk_path_measure_in_fill (measure, &tests[i].point, GSK_FILL_RULE_WINDING);
g_assert_true (res == tests[i].result);
gsk_path_measure_unref (measure);
gsk_path_unref (path);
}
}
int
main (int argc,
char *argv[])
{
gtk_test_init (&argc, &argv, NULL);
g_test_add_func ("/path/rsvg-parse", test_rsvg_parse);
g_test_add_func ("/path/serialize-custom-contours", test_serialize_custom_contours);
g_test_add_func ("/path/winding", test_path_winding);
return g_test_run ();
}
-199
View File
@@ -1,199 +0,0 @@
/*
* Copyright © 2020 Red Hat, Inc.
*
* 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: Matthias Clasen <mclasen@redhat.com>
*/
#include <gtk/gtk.h>
/* Test that single-point contours don't crash the stroker */
static void
test_point_to_stroke (void)
{
GskPathBuilder *builder;
GskPath *path;
GskStroke *stroke;
GskPath *path1;
char *string;
builder = gsk_path_builder_new ();
gsk_path_builder_move_to (builder, 100, 100);
gsk_path_builder_curve_to (builder, 190, 110,
200, 120,
210, 210);
gsk_path_builder_curve_to (builder, 220, 210,
230, 200,
230, 100);
gsk_path_builder_move_to (builder, 200, 200);
path = gsk_path_builder_free_to_path (builder);
string = gsk_path_to_string (path);
g_assert_cmpstr (string, ==, "M 100 100 C 190 110, 200 120, 210 210 C 220 210, 230 200, 230 100 M 200 200");
g_free (string);
stroke = gsk_stroke_new (20.f);
path1 = gsk_path_stroke (path, stroke);
gsk_stroke_free (stroke);
g_assert_nonnull (path1);
gsk_path_unref (path1);
gsk_path_unref (path);
}
/* Test that the offset curves are generally where they need to be */
static void
check_stroke_at_position (GskPathMeasure *measure,
GskStroke *stroke,
GskPathMeasure *stroke_measure,
float position)
{
graphene_point_t p;
graphene_point_t s;
float w;
float tolerance;
float d;
w = gsk_stroke_get_line_width (stroke);
tolerance = gsk_path_measure_get_tolerance (stroke_measure);
gsk_path_measure_get_point (measure, position, &p, NULL);
gsk_path_measure_get_closest_point (stroke_measure, &p, &s);
d = graphene_point_distance (&p, &s, NULL, NULL);
g_assert_cmpfloat (d, <=, w/2 + tolerance);
}
static void
check_stroke_distance (GskPath *path,
GskPathMeasure *measure,
GskStroke *stroke,
GskPath *stroke_path)
{
GskPathMeasure *stroke_measure;
float length;
float t;
int i;
stroke_measure = gsk_path_measure_new_with_tolerance (stroke_path, 0.1);
length = gsk_path_measure_get_length (measure);
for (i = 0; i < 1000; i++)
{
t = g_test_rand_double_range (0, length);
check_stroke_at_position (measure, stroke, stroke_measure, t);
}
gsk_path_measure_unref (stroke_measure);
}
static void
test_rect_stroke_distance (void)
{
GskPathBuilder *builder;
GskPath *path;
GskPathMeasure *measure;
GskPath *stroke_path;
GskStroke *stroke;
builder = gsk_path_builder_new ();
gsk_path_builder_add_rect (builder, &GRAPHENE_RECT_INIT (0, 0, 100, 100));
path = gsk_path_builder_free_to_path (builder);
stroke = gsk_stroke_new (10);
measure = gsk_path_measure_new (path);
stroke_path = gsk_path_stroke (path, stroke);
check_stroke_distance (path, measure, stroke, stroke_path);
gsk_stroke_free (stroke);
gsk_path_unref (stroke_path);
gsk_path_measure_unref (measure);
gsk_path_unref (path);
}
static void
test_circle_stroke_distance (void)
{
GskPathBuilder *builder;
GskPath *path;
GskPathMeasure *measure;
GskPath *stroke_path;
GskStroke *stroke;
builder = gsk_path_builder_new ();
gsk_path_builder_add_circle (builder, &GRAPHENE_POINT_INIT (100, 100), 50);
path = gsk_path_builder_free_to_path (builder);
stroke = gsk_stroke_new (10);
measure = gsk_path_measure_new (path);
stroke_path = gsk_path_stroke (path, stroke);
check_stroke_distance (path, measure, stroke, stroke_path);
gsk_stroke_free (stroke);
gsk_path_unref (stroke_path);
gsk_path_measure_unref (measure);
gsk_path_unref (path);
}
static void
test_path_stroke_distance (void)
{
GskPath *path;
GskPathMeasure *measure;
GskPath *stroke_path;
GskStroke *stroke;
path = gsk_path_parse ("M 250 150 A 100 100 0 0 0 50 150 A 100 100 0 0 0 250 150 z M 100 100 h 100 v 100 h -100 z M 300 150 C 300 50, 400 50, 400 150 C 400 250, 500 250, 500 150 L 600 150 L 530 190");
stroke = gsk_stroke_new (10);
measure = gsk_path_measure_new (path);
stroke_path = gsk_path_stroke (path, stroke);
check_stroke_distance (path, measure, stroke, stroke_path);
gsk_stroke_free (stroke);
gsk_path_unref (stroke_path);
gsk_path_measure_unref (measure);
gsk_path_unref (path);
}
int
main (int argc,
char *argv[])
{
gtk_test_init (&argc, &argv, NULL);
g_test_add_func ("/stroke/point", test_point_to_stroke);
g_test_add_func ("/stroke/rect/distance", test_rect_stroke_distance);
g_test_add_func ("/stroke/circle/distance", test_circle_stroke_distance);
g_test_add_func ("/stroke/path/distance", test_path_stroke_distance);
return g_test_run ();
}
-1321
View File
File diff suppressed because it is too large Load Diff
+37 -3
View File
@@ -17,6 +17,8 @@
* Author: Matthias Clasen
*/
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include <errno.h>
@@ -46,12 +48,42 @@ do_enumerate (int *argc, const char ***argv)
GSList *list, *l;
GObject *object;
const char *name;
const char *filename;
char **filenames = NULL;
GOptionContext *context;
const GOptionEntry entries[] = {
{ G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &filenames, NULL, N_("FILE") },
{ NULL, }
};
filename = (*argv)[1];
g_set_prgname ("gtk4-builder-tool enumerate");
context = g_option_context_new (NULL);
g_option_context_set_translation_domain (context, GETTEXT_PACKAGE);
g_option_context_add_main_entries (context, entries, NULL);
g_option_context_set_summary (context, _("List all named objects."));
if (!g_option_context_parse (context, argc, (char ***)argv, &error))
{
g_printerr ("%s\n", error->message);
g_error_free (error);
exit (1);
}
g_option_context_free (context);
if (filenames == NULL)
{
g_printerr ("No .ui file specified\n");
exit (1);
}
if (g_strv_length (filenames) > 1)
{
g_printerr ("Can only enumerate a single .ui file\n");
exit (1);
}
builder = gtk_builder_new ();
ret = gtk_builder_add_from_file (builder, filename, &error);
ret = gtk_builder_add_from_file (builder, filenames[0], &error);
if (ret == 0)
{
@@ -72,4 +104,6 @@ do_enumerate (int *argc, const char ***argv)
g_slist_free (list);
g_object_unref (builder);
g_strfreev (filenames);
}
+14 -4
View File
@@ -17,6 +17,8 @@
* Author: Matthias Clasen
*/
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include <errno.h>
@@ -173,16 +175,24 @@ do_preview (int *argc,
char *css = NULL;
char **filenames = NULL;
const GOptionEntry entries[] = {
{ "id", 0, 0, G_OPTION_ARG_STRING, &id, NULL, NULL },
{ "css", 0, 0, G_OPTION_ARG_FILENAME, &css, NULL, NULL },
{ G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &filenames, NULL, NULL },
{ "id", 0, 0, G_OPTION_ARG_STRING, &id, N_("Preview only the named object"), N_("ID") },
{ "css", 0, 0, G_OPTION_ARG_FILENAME, &css, N_("Use style from CSS file"), N_("FILE") },
{ G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &filenames, NULL, N_("FILE") },
{ NULL, }
};
GError *error = NULL;
if (gdk_display_get_default () == NULL)
{
g_printerr ("Could not initialize windowing system\n");
exit (1);
}
g_set_prgname ("gtk4-builder-tool preview");
context = g_option_context_new (NULL);
g_option_context_set_help_enabled (context, FALSE);
g_option_context_set_translation_domain (context, GETTEXT_PACKAGE);
g_option_context_add_main_entries (context, entries, NULL);
g_option_context_set_summary (context, _("Preview the file."));
if (!g_option_context_parse (context, argc, (char ***)argv, &error))
{
+368
View File
@@ -0,0 +1,368 @@
/* Copyright 2015 Red Hat, Inc.
*
* GTK+ 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 of the
* License, or (at your option) any later version.
*
* GLib 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 GTK+; see the file COPYING. If not,
* see <http://www.gnu.org/licenses/>.
*
* Author: Matthias Clasen
*/
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <glib/gi18n.h>
#include <glib/gprintf.h>
#include <glib/gstdio.h>
#include <gtk/gtk.h>
#include "gtkbuilderprivate.h"
#include "gtk-builder-tool.h"
static gboolean
quit_when_idle (gpointer loop)
{
g_main_loop_quit (loop);
return G_SOURCE_REMOVE;
}
static GMainLoop *loop;
static void
draw_paintable (GdkPaintable *paintable,
gpointer out_texture)
{
GtkSnapshot *snapshot;
GskRenderNode *node;
GdkTexture *texture;
GskRenderer *renderer;
snapshot = gtk_snapshot_new ();
gdk_paintable_snapshot (paintable,
snapshot,
gdk_paintable_get_intrinsic_width (paintable),
gdk_paintable_get_intrinsic_height (paintable));
node = gtk_snapshot_free_to_node (snapshot);
/* If the window literally draws nothing, we assume it hasn't been mapped yet and as such
* the invalidations were only side effects of resizes.
*/
if (node == NULL)
return;
renderer = gtk_native_get_renderer (
gtk_widget_get_native (
gtk_widget_paintable_get_widget (GTK_WIDGET_PAINTABLE (paintable))));
texture = gsk_renderer_render_texture (renderer,
node,
&GRAPHENE_RECT_INIT (
0, 0,
gdk_paintable_get_intrinsic_width (paintable),
gdk_paintable_get_intrinsic_height (paintable)
));
g_object_set_data_full (G_OBJECT (texture),
"source-render-node",
node,
(GDestroyNotify) gsk_render_node_unref);
g_signal_handlers_disconnect_by_func (paintable, draw_paintable, out_texture);
*(GdkTexture **) out_texture = texture;
g_idle_add (quit_when_idle, loop);
}
static GdkTexture *
snapshot_widget (GtkWidget *widget)
{
GdkPaintable *paintable;
GdkTexture *texture = NULL;
g_assert_true (gtk_widget_get_realized (widget));
loop = g_main_loop_new (NULL, FALSE);
/* We wait until the widget is drawn for the first time.
*
* We also use an inhibit mechanism, to give module functions a chance
* to delay the snapshot.
*/
paintable = gtk_widget_paintable_new (widget);
g_signal_connect (paintable, "invalidate-contents", G_CALLBACK (draw_paintable), &texture);
g_main_loop_run (loop);
g_main_loop_unref (loop);
g_object_unref (paintable);
gtk_window_destroy (GTK_WINDOW (widget));
return texture;
}
static void
set_window_title (GtkWindow *window,
const char *filename,
const char *id)
{
char *name;
char *title;
name = g_path_get_basename (filename);
if (id)
title = g_strdup_printf ("%s in %s", id, name);
else
title = g_strdup (name);
gtk_window_set_title (window, title);
g_free (title);
g_free (name);
}
static char *
get_save_filename (const char *filename,
gboolean as_node)
{
int length = strlen (filename);
const char *extension = as_node ? ".node" : ".png";
char *result;
if (strcmp (filename + (length - 3), ".ui") == 0)
{
char *basename = g_strndup (filename, length - 3);
result = g_strconcat (basename, extension, NULL);
g_free (basename);
}
else
result = g_strconcat (filename, extension, NULL);
return result;
}
static void
screenshot_file (const char *filename,
const char *id,
const char *cssfile,
const char *save_file,
gboolean as_node,
gboolean force)
{
GtkBuilder *builder;
GError *error = NULL;
GObject *object;
GtkWidget *window;
GdkTexture *texture;
char *save_to;
GBytes *bytes;
if (cssfile)
{
GtkCssProvider *provider;
provider = gtk_css_provider_new ();
gtk_css_provider_load_from_path (provider, cssfile);
gtk_style_context_add_provider_for_display (gdk_display_get_default (),
GTK_STYLE_PROVIDER (provider),
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
}
builder = gtk_builder_new ();
if (!gtk_builder_add_from_file (builder, filename, &error))
{
g_printerr ("%s\n", error->message);
exit (1);
}
object = NULL;
if (id)
{
object = gtk_builder_get_object (builder, id);
}
else
{
GSList *objects, *l;
objects = gtk_builder_get_objects (builder);
for (l = objects; l; l = l->next)
{
GObject *obj = l->data;
if (GTK_IS_WINDOW (obj))
{
object = obj;
break;
}
else if (GTK_IS_WIDGET (obj))
{
if (object == NULL)
object = obj;
}
}
g_slist_free (objects);
}
if (object == NULL)
{
if (id)
g_printerr ("No object with ID '%s' found\n", id);
else
g_printerr ("No object found\n");
exit (1);
}
if (!GTK_IS_WIDGET (object))
{
g_printerr ("Objects of type %s can't be screenshot\n", G_OBJECT_TYPE_NAME (object));
exit (1);
}
if (GTK_IS_WINDOW (object))
window = GTK_WIDGET (object);
else
{
GtkWidget *widget = GTK_WIDGET (object);
window = gtk_window_new ();
if (GTK_IS_BUILDABLE (object))
id = gtk_buildable_get_buildable_id (GTK_BUILDABLE (object));
set_window_title (GTK_WINDOW (window), filename, id);
g_object_ref (widget);
if (gtk_widget_get_parent (widget) != NULL)
gtk_box_remove (GTK_BOX (gtk_widget_get_parent (widget)), widget);
gtk_window_set_child (GTK_WINDOW (window), widget);
g_object_unref (widget);
}
gtk_widget_show (window);
texture = snapshot_widget (window);
g_object_unref (builder);
if (texture == NULL)
{
g_printerr ("Failed to take a screenshot\n");
exit (1);
}
save_to = (char *)save_file;
if (save_to == NULL)
save_to = get_save_filename (filename, as_node);
if (g_file_test (save_to, G_FILE_TEST_EXISTS) && !force)
{
g_printerr ("File %s exists.\n"
"Use --force to overwrite.\n", save_to);
exit (1);
}
if (as_node)
{
GskRenderNode *node;
node = (GskRenderNode *) g_object_get_data (G_OBJECT (texture), "source-render-node");
bytes = gsk_render_node_serialize (node);
}
else
{
bytes = gdk_texture_save_to_png_bytes (texture);
}
if (g_file_set_contents (save_to,
g_bytes_get_data (bytes, NULL),
g_bytes_get_size (bytes),
&error))
{
g_print ("Output written to %s.\n", save_to);
}
else
{
g_printerr ("Failed to save %s: %s\n", save_to, error->message);
exit (1);
}
g_bytes_unref (bytes);
if (save_to != save_file)
g_free (save_to);
g_object_unref (texture);
}
void
do_screenshot (int *argc,
const char ***argv)
{
GOptionContext *context;
char *id = NULL;
char *css = NULL;
char **filenames = NULL;
gboolean as_node = FALSE;
gboolean force = FALSE;
const GOptionEntry entries[] = {
{ "id", 0, 0, G_OPTION_ARG_STRING, &id, N_("Screenshot only the named object"), N_("ID") },
{ "css", 0, 0, G_OPTION_ARG_FILENAME, &css, N_("Use style from CSS file"), N_("FILE") },
{ "node", 0, 0, G_OPTION_ARG_NONE, &as_node, N_("Save as node file instead of png"), NULL },
{ "force", 0, 0, G_OPTION_ARG_NONE, &force, N_("Overwrite existing file"), NULL },
{ G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &filenames, NULL, N_("FILE") },
{ NULL, }
};
GError *error = NULL;
if (gdk_display_get_default () == NULL)
{
g_printerr ("Could not initialize windowing system\n");
exit (1);
}
g_set_prgname ("gtk4-builder-tool screenshot");
context = g_option_context_new (NULL);
g_option_context_set_translation_domain (context, GETTEXT_PACKAGE);
g_option_context_add_main_entries (context, entries, NULL);
g_option_context_set_summary (context, _("Take a screenshot of the file."));
if (!g_option_context_parse (context, argc, (char ***)argv, &error))
{
g_printerr ("%s\n", error->message);
g_error_free (error);
exit (1);
}
g_option_context_free (context);
if (filenames == NULL)
{
g_printerr ("No .ui file specified\n");
exit (1);
}
if (g_strv_length (filenames) > 2)
{
g_printerr ("Can only screenshot a single .ui file and a single output file\n");
exit (1);
}
screenshot_file (filenames[0], id, css, filenames[1], as_node, force);
g_strfreev (filenames);
g_free (id);
g_free (css);
}
+15 -9
View File
@@ -17,6 +17,8 @@
* Author: Matthias Clasen
*/
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include <errno.h>
@@ -2351,28 +2353,30 @@ do_simplify (int *argc,
gboolean replace = FALSE;
gboolean convert3to4 = FALSE;
char **filenames = NULL;
GOptionContext *ctx;
GOptionContext *context;
const GOptionEntry entries[] = {
{ "replace", 0, 0, G_OPTION_ARG_NONE, &replace, NULL, NULL },
{ "3to4", 0, 0, G_OPTION_ARG_NONE, &convert3to4, NULL, NULL },
{ G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &filenames, NULL, NULL },
{ "replace", 0, 0, G_OPTION_ARG_NONE, &replace, N_("Replace the file"), NULL },
{ "3to4", 0, 0, G_OPTION_ARG_NONE, &convert3to4, N_("Convert from GTK 3 to GTK 4"), NULL },
{ G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &filenames, NULL, N_("FILE") },
{ NULL, }
};
GError *error = NULL;
int i;
ctx = g_option_context_new (NULL);
g_option_context_set_help_enabled (ctx, FALSE);
g_option_context_add_main_entries (ctx, entries, NULL);
g_set_prgname ("gtk4-builder-tool simplify");
context = g_option_context_new (NULL);
g_option_context_set_translation_domain (context, GETTEXT_PACKAGE);
g_option_context_add_main_entries (context, entries, NULL);
g_option_context_set_summary (context, _("Simplify the file."));
if (!g_option_context_parse (ctx, argc, (char ***)argv, &error))
if (!g_option_context_parse (context, argc, (char ***)argv, &error))
{
g_printerr ("%s\n", error->message);
g_error_free (error);
exit (1);
}
g_option_context_free (ctx);
g_option_context_free (context);
if (filenames == NULL)
{
@@ -2391,4 +2395,6 @@ do_simplify (int *argc,
if (!simplify_file (filenames[i], replace, convert3to4))
exit (1);
}
g_strfreev (filenames);
}
+28 -2
View File
@@ -17,6 +17,8 @@
* Author: Matthias Clasen
*/
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include <errno.h>
@@ -150,11 +152,35 @@ validate_file (const char *filename)
void
do_validate (int *argc, const char ***argv)
{
GError *error = NULL;
char **filenames = NULL;
GOptionContext *context;
const GOptionEntry entries[] = {
{ G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &filenames, NULL, N_("FILE") },
{ NULL, }
};
int i;
for (i = 1; i < *argc; i++)
g_set_prgname ("gtk4-builder-tool validate");
context = g_option_context_new (NULL);
g_option_context_set_translation_domain (context, GETTEXT_PACKAGE);
g_option_context_add_main_entries (context, entries, NULL);
g_option_context_set_summary (context, _("Validate the file."));
if (!g_option_context_parse (context, argc, (char ***)argv, &error))
{
if (!validate_file ((*argv)[i]))
g_printerr ("%s\n", error->message);
g_error_free (error);
exit (1);
}
g_option_context_free (context);
for (i = 0; filenames[i]; i++)
{
if (!validate_file (filenames[i]))
exit (1);
}
g_strfreev (filenames);
}
+12 -24
View File
@@ -17,6 +17,8 @@
* Author: Matthias Clasen
*/
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include <errno.h>
@@ -34,21 +36,15 @@ usage (void)
g_print (_("Usage:\n"
" gtk-builder-tool [COMMAND] [OPTION…] FILE\n"
"\n"
"Perform various tasks on GtkBuilder .ui files.\n"
"\n"
"Commands:\n"
" validate Validate the file\n"
" simplify Simplify the file\n"
" enumerate List all named objects\n"
" preview Preview the file\n"
"\n"
"Simplify Options:\n"
" --replace Replace the file\n"
" --3to4 Convert from GTK 3 to GTK 4\n"
"\n"
"Preview Options:\n"
" --id=ID Preview only the named object\n"
" --css=FILE Use style from CSS file\n"
"\n"
"Perform various tasks on GtkBuilder .ui files.\n"));
" screenshot Take a screenshot of the file\n"
"\n"));
exit (1);
}
@@ -109,20 +105,18 @@ log_writer_func (GLogLevelFlags level,
int
main (int argc, const char *argv[])
{
gboolean has_display;
g_set_prgname ("gtk-builder-tool");
g_log_set_writer_func (log_writer_func, NULL, NULL);
has_display = gtk_init_check ();
gtk_init_check ();
gtk_test_register_all_types ();
if (argc < 3)
if (argc < 2)
usage ();
if (strcmp (argv[2], "--help") == 0)
if (strcmp (argv[1], "--help") == 0)
usage ();
argv++;
@@ -135,15 +129,9 @@ main (int argc, const char *argv[])
else if (strcmp (argv[0], "enumerate") == 0)
do_enumerate (&argc, &argv);
else if (strcmp (argv[0], "preview") == 0)
{
if (!has_display)
{
g_printerr ("Could not initialize windowing system\n");
return 1;
}
do_preview (&argc, &argv);
}
do_preview (&argc, &argv);
else if (strcmp (argv[0], "screenshot") == 0)
do_screenshot (&argc, &argv);
else
usage ();
+5 -4
View File
@@ -2,9 +2,10 @@
#ifndef __GTK_BUILDER_TOOL_H__
#define __GTK_BUILDER_TOOL_H__
void do_simplify (int *argc, const char ***argv);
void do_validate (int *argc, const char ***argv);
void do_enumerate (int *argc, const char ***argv);
void do_preview (int *argc, const char ***argv);
void do_simplify (int *argc, const char ***argv);
void do_validate (int *argc, const char ***argv);
void do_enumerate (int *argc, const char ***argv);
void do_preview (int *argc, const char ***argv);
void do_screenshot (int *argc, const char ***argv);
#endif
+1
View File
@@ -28,6 +28,7 @@ gtk_tools = [
'gtk-builder-tool-simplify.c',
'gtk-builder-tool-validate.c',
'gtk-builder-tool-enumerate.c',
'gtk-builder-tool-screenshot.c',
'gtk-builder-tool-preview.c'], [libgtk_dep] ],
['gtk4-update-icon-cache', ['updateiconcache.c'] + extra_update_icon_cache_objs, [ libgtk_static_dep ] ],
['gtk4-encode-symbolic-svg', ['encodesymbolic.c'], [ libgtk_static_dep ] ],