Compare commits
21 Commits
path-itera
...
path-point
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b533dfa0da | ||
|
|
1de378655e | ||
|
|
9cdc549bf9 | ||
|
|
9725d33196 | ||
|
|
2384c57051 | ||
|
|
0d178b6a4d | ||
|
|
bb98216951 | ||
|
|
c01f3d24ed | ||
|
|
068163939e | ||
|
|
cbee6ee382 | ||
|
|
c4c6d72375 | ||
|
|
a25484bb8c | ||
|
|
0582da02b3 | ||
|
|
1dd3986b03 | ||
|
|
44119a7a57 | ||
|
|
cc60d519f3 | ||
|
|
a592e52fab | ||
|
|
366abbb930 | ||
|
|
bac372d2a4 | ||
|
|
b48ec3ad8b | ||
|
|
471cbc8c41 |
150
LOG
150
LOG
@@ -1,150 +0,0 @@
|
||||
TAP version 13
|
||||
# random seed: R02S22611f6aefc1121b0ab2dc5286960449
|
||||
# GLib-GIO-DEBUG: _g_io_module_get_default: Found default implementation dconf (DConfSettingsBackend) for ‘gsettings-backend’
|
||||
# GLib-GIO-DEBUG: Using cross-namespace EXTERNAL authentication (this will deadlock if server is GDBus < 2.73.3)
|
||||
# GLib-GIO-DEBUG: Using cross-namespace EXTERNAL authentication (this will deadlock if server is GDBus < 2.73.3)
|
||||
1..1
|
||||
# Start of ops tests
|
||||
# testcase 0 op 0
|
||||
collecting#
|
||||
Cubic 0: # M 100.000000 100.000000 C 150.000000 100.000000 200.000000 100.000000 250.000000 100.000000
|
||||
Cubic 1: # M 250.000000 100.000000 C 300.000000 100.000000 350.000000 100.000000 400.000000 100.000000
|
||||
Line 2: # M 400 100 L 200 300
|
||||
Line 3: # M 200 300 L 100 100
|
||||
Cubic 4: # M 200.000000 100.000000 C 250.000000 100.000000 300.000000 100.000000 350.000000 100.000000
|
||||
Cubic 5: # M 350.000000 100.000000 C 400.000000 100.000000 450.000000 100.000000 500.000000 100.000000
|
||||
Line 6: # M 500 100 L 300 300
|
||||
Line 7: # M 300 300 L 200 100
|
||||
splitting#
|
||||
1 intersections between Cubic 0 and Cubic 1#
|
||||
1 intersections between Cubic 0 and Line 3#
|
||||
9 intersections between Cubic 0 and Cubic 4#
|
||||
# split Cubic 0.0 from Cubic 0 at 0.666666: M 100.000000 100.000000 C 133.333298 100.000000 166.666595 100.000000 199.999893 100.000000
|
||||
split Cubic 0.1 from Cubic 0 at 0.666666: M 199.999893 100.000000 C 216.666595 100.000000 233.333298 100.000000 250.000000 100.000000split Cubic 0.4 from Cubic 0 at 0.00600814: M 200.300323 100.000000 C 216.866882 100.000000 233.433441 100.000000 250.000000 100.000000split Cubic 0.7 from Cubic 0 at 0.00604445: M 200.600708 100.000000 C 217.067139 100.000000 233.533569 100.000000 250.000000 100.000000# split Cubic 4.9 from Cubic 4 at 0.00533867: M 200.800812 100.000000 C 250.533875 100.000000 300.266937 100.000000 350.000000 100.000000
|
||||
1 intersections between Cubic 0.1 and Line 7#
|
||||
1 intersections between Cubic 1 and Line 2#
|
||||
9 intersections between Cubic 1 and Cubic 4.9#
|
||||
split Cubic 1.9 from Cubic 1 at 0.00534248: M 250.801361 100.000000 C 300.534241 100.000000 350.267120 100.000000 400.000000 100.000000# split Cubic 4.9.0 from Cubic 4.9 at 0.329756: M 200.800812 100.000000 C 217.200577 100.000000 233.600342 100.000000 250.000122 100.000000
|
||||
# split Cubic 4.9.1 from Cubic 4.9 at 0.329756: M 250.000122 100.000000 C 283.333435 100.000000 316.666718 100.000000 350.000000 100.000000
|
||||
# split Cubic 4.9.6 from Cubic 4.9 at 0.00500283: M 250.500412 100.000000 C 283.666931 100.000000 316.833466 100.000000 350.000000 100.000000
|
||||
9 intersections between Cubic 1.9 and Cubic 5#
|
||||
# split Cubic 1.9.0 from Cubic 1.9 at 0.664877: M 250.801361 100.000000 C 283.867615 100.000000 316.933868 100.000000 350.000122 100.000000
|
||||
split Cubic 1.9.1 from Cubic 1.9 at 0.664877: M 350.000122 100.000000 C 366.666748 100.000000 383.333374 100.000000 400.000000 100.000000split Cubic 1.9.4 from Cubic 1.9 at 0.00601021: M 350.300598 100.000000 C 366.867065 100.000000 383.433533 100.000000 400.000000 100.000000split Cubic 1.9.7 from Cubic 1.9 at 0.00604655: M 350.601074 100.000000 C 367.067383 100.000000 383.533691 100.000000 400.000000 100.000000# split Cubic 5.9 from Cubic 5 at 0.00534248: M 350.801361 100.000000 C 400.534241 100.000000 450.267120 100.000000 500.000000 100.000000
|
||||
1 intersections between Line 2 and Line 3#
|
||||
1 intersections between Line 2 and Line 7#
|
||||
# split Line 2.0 from Line 2 at 0.666667: M 400 100 L 266.667 233.333
|
||||
split Line 2.1 from Line 2 at 0.666667: M 266.667 233.333 L 200 300# split Line 7.0 from Line 7 at 0.333333: M 300 300 L 266.667 233.333
|
||||
# split Line 7.1 from Line 7 at 0.333333: M 266.667 233.333 L 200 100
|
||||
1 intersections between Cubic 4 and Cubic 4.9.0#
|
||||
1 intersections between Cubic 4 and Line 7.1#
|
||||
1 intersections between Cubic 4.9.0 and Cubic 4.9.1#
|
||||
1 intersections between Cubic 4.9.1 and Cubic 4.9.6#
|
||||
1 intersections between Cubic 4.9.6 and Cubic 5#
|
||||
1 intersections between Cubic 5 and Cubic 5.9#
|
||||
1 intersections between Cubic 5.9 and Line 6#
|
||||
1 intersections between Line 6 and Line 7.0#
|
||||
Cubic 0.0: # M 100.000000 100.000000 C 133.333298 100.000000 166.666595 100.000000 199.999893 100.000000
|
||||
Cubic 0.1: # M 199.999893 100.000000 C 200.100037 100.000000 200.200180 100.000000 200.300323 100.000000
|
||||
Cubic 0.4: # M 200.300323 100.000000 C 200.400452 100.000000 200.500580 100.000000 200.600708 100.000000
|
||||
Cubic 0.7: # M 200.600708 100.000000 C 217.067139 100.000000 233.533569 100.000000 250.000000 100.000000
|
||||
Cubic 1: # M 250.000000 100.000000 C 250.267120 100.000000 250.534241 100.000000 250.801361 100.000000
|
||||
Cubic 1.9.0: # M 250.801361 100.000000 C 283.867615 100.000000 316.933868 100.000000 350.000122 100.000000
|
||||
Cubic 1.9.1: # M 350.000122 100.000000 C 350.100281 100.000000 350.200439 100.000000 350.300598 100.000000
|
||||
Cubic 1.9.4: # M 350.300598 100.000000 C 350.400757 100.000000 350.500916 100.000000 350.601074 100.000000
|
||||
Cubic 1.9.7: # M 350.601074 100.000000 C 367.067383 100.000000 383.533691 100.000000 400.000000 100.000000
|
||||
Line 2.0: # M 400 100 L 266.667 233.333
|
||||
Line 2.1: # M 266.667 233.333 L 200 300
|
||||
Line 3: # M 200 300 L 100 100
|
||||
Cubic 4: # M 200.000000 100.000000 C 200.266937 100.000000 200.533875 100.000000 200.800812 100.000000
|
||||
Cubic 4.9.0: # M 200.800812 100.000000 C 217.200577 100.000000 233.600342 100.000000 250.000122 100.000000
|
||||
Cubic 4.9.1: # M 250.000122 100.000000 C 250.166885 100.000000 250.333649 100.000000 250.500412 100.000000
|
||||
Cubic 4.9.6: # M 250.500412 100.000000 C 283.666931 100.000000 316.833466 100.000000 350.000000 100.000000
|
||||
Cubic 5: # M 350.000000 100.000000 C 350.267120 100.000000 350.534241 100.000000 350.801361 100.000000
|
||||
Cubic 5.9: # M 350.801361 100.000000 C 400.534241 100.000000 450.267120 100.000000 500.000000 100.000000
|
||||
Line 6: # M 500 100 L 300 300
|
||||
Line 7.0: # M 300 300 L 266.667 233.333
|
||||
Line 7.1: # M 266.667 233.333 L 200 100
|
||||
classifying#
|
||||
01 Cubic 0.0: # M 100.000000 100.000000 C 133.333298 100.000000 166.666595 100.000000 199.999893 100.000000
|
||||
[11 Cubic 0.7: # M 200.600708 100.000000 C 217.067139 100.000000 233.533569 100.000000 250.000000 100.000000 ]
|
||||
[11 Cubic 1.9.0: # M 250.801361 100.000000 C 283.867615 100.000000 316.933868 100.000000 350.000122 100.000000 ]
|
||||
[11 Cubic 1.9.7: # M 350.601074 100.000000 C 367.067383 100.000000 383.533691 100.000000 400.000000 100.000000 ]
|
||||
[11 Line 2.0: # M 400 100 L 266.667 233.333 ]
|
||||
01 Line 2.1: # M 266.667 233.333 L 200 300
|
||||
01 Line 3: # M 200 300 L 100 100
|
||||
[11 Cubic 4: # M 200.000000 100.000000 C 200.266937 100.000000 200.533875 100.000000 200.800812 100.000000 ]
|
||||
[11 Cubic 4.9.0: # M 200.800812 100.000000 C 217.200577 100.000000 233.600342 100.000000 250.000122 100.000000 ]
|
||||
[11 Cubic 4.9.6: # M 250.500412 100.000000 C 283.666931 100.000000 316.833466 100.000000 350.000000 100.000000 ]
|
||||
[11 Cubic 5: # M 350.000000 100.000000 C 350.267120 100.000000 350.534241 100.000000 350.801361 100.000000 ]
|
||||
01 Cubic 5.9: # M 350.801361 100.000000 C 400.534241 100.000000 450.267120 100.000000 500.000000 100.000000
|
||||
01 Line 6: # M 500 100 L 300 300
|
||||
01 Line 7.0: # M 300 300 L 266.667 233.333
|
||||
[11 Line 7.1: # M 266.667 233.333 L 200 100 ]
|
||||
fixups#
|
||||
# found 3 bad nodes
|
||||
# split Cubic 0/Cubic 4 BAD 200.600723 100.000000
|
||||
# [11 Line 7.1 ] 116.565
|
||||
# [11 Cubic 0.7 ] 180
|
||||
# [11 Cubic 4 ] 180
|
||||
# [11 Cubic 4 ] 180
|
||||
# [11 Cubic 4.9.0 ] 180
|
||||
# >01 Cubic 0.0 360
|
||||
# split Cubic 1/Cubic 4.9 BAD 250.801376 100.000000
|
||||
# [11 Cubic 1.9.0 ] 180
|
||||
# [11 Cubic 4.9.6 ] 180
|
||||
# [11 Cubic 0.7 ] 360
|
||||
# [11 Cubic 4.9.0 ] 360
|
||||
# split Cubic 1.9/Cubic 5 BAD 350.601105 100.000000
|
||||
# [11 Cubic 1.9.7 ] 180
|
||||
# [11 Cubic 5 ] 180
|
||||
# [11 Cubic 5 ] 180
|
||||
# <01 Cubic 5.9 180
|
||||
# [11 Cubic 1.9.0 ] 360
|
||||
# [11 Cubic 4.9.6 ] 360
|
||||
reassembling#
|
||||
start new contour Cubic 0.0#
|
||||
# Cubic 0.0 ends at:
|
||||
# start 0 100.000000 100.000000
|
||||
# >01 Line 3 116.565
|
||||
# (10 Cubic 0.0 ) 180
|
||||
picking cw#
|
||||
append Line 3#
|
||||
# Line 3 ends at:
|
||||
# end 2 200.000000 300.000000
|
||||
# >01 Line 2.1 225
|
||||
# (10 Line 3 ) 296.565
|
||||
picking cw#
|
||||
append Line 2.1#
|
||||
# Line 2.1 ends at:
|
||||
# split Line 2/Line 7 266.666656 233.333344
|
||||
# (10 Line 2.1 ) 45
|
||||
# >01 Line 7.0 116.565
|
||||
# [11 Line 2.0 ] 225
|
||||
# [11 Line 7.1 ] 296.565
|
||||
picking cw#
|
||||
append Line 7.0#
|
||||
# Line 7.0 ends at:
|
||||
# end 6 300.000000 300.000000
|
||||
# >01 Line 6 225
|
||||
# (10 Line 7.0 ) 296.565
|
||||
picking cw#
|
||||
append Line 6#
|
||||
# Line 6 ends at:
|
||||
# end 5 500.000000 100.000000
|
||||
# (10 Line 6 ) 45
|
||||
# >01 Cubic 5.9 360
|
||||
picking cw#
|
||||
append Cubic 5.9#
|
||||
# Cubic 5.9 ends at:
|
||||
# split Cubic 1.9/Cubic 5 BAD 350.601105 100.000000
|
||||
# [11 Cubic 1.9.7 ] 180
|
||||
# [11 Cubic 5 ] 180
|
||||
# [11 Cubic 5 ] 180
|
||||
# (10 Cubic 5.9 ) 180
|
||||
# [11 Cubic 1.9.0 ] 360
|
||||
# [11 Cubic 4.9.6 ] 360
|
||||
picking cw#
|
||||
**
|
||||
ERROR:../testsuite/gsk/path-ops.c:359:test_ops_simple: assertion failed (s == tests[i].out): ("M 354.60110473632812 100 A 4 4 0 0 0 346.60110473632812 100 A 4 4 0 0 0 354.60110473632812 100 z M 252.80137634277344 100 A 2 2 0 0 0 248.80137634277344 100 A 2 2 0 0 0 252.80137634277344 100 z M 204.60072326660156 100 A 4 4 0 0 0 196.60072326660156 100 A 4 4 0 0 0 204.60072326660156 100 z" == "M 100 100 z")
|
||||
not ok /ops/simple - ERROR:../testsuite/gsk/path-ops.c:359:test_ops_simple: assertion failed (s == tests[i].out): ("M 354.60110473632812 100 A 4 4 0 0 0 346.60110473632812 100 A 4 4 0 0 0 354.60110473632812 100 z M 252.80137634277344 100 A 2 2 0 0 0 248.80137634277344 100 A 2 2 0 0 0 252.80137634277344 100 z M 204.60072326660156 100 A 4 4 0 0 0 196.60072326660156 100 A 4 4 0 0 0 204.60072326660156 100 z" == "M 100 100 z")
|
||||
Bail out!
|
||||
@@ -336,6 +336,7 @@
|
||||
<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>
|
||||
|
||||
@@ -73,6 +73,7 @@ demos = files([
|
||||
'panes.c',
|
||||
'password_entry.c',
|
||||
'path_fill.c',
|
||||
'path_maze.c',
|
||||
'path_text.c',
|
||||
'peg_solitaire.c',
|
||||
'pickers.c',
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* Path/Fill and Stroke
|
||||
/* Path/Fill
|
||||
*
|
||||
* This demo shows how to use GskPath to draw shapes that are (a bit)
|
||||
* more complex than a rounded rectangle.
|
||||
* This demo shows how to use PangoCairo to draw text with more than
|
||||
* just a single color.
|
||||
*/
|
||||
|
||||
#include <glib/gi18n.h>
|
||||
@@ -9,162 +9,305 @@
|
||||
|
||||
#include "paintable.h"
|
||||
|
||||
#define GTK_TYPE_LOGO_PAINTABLE (gtk_logo_paintable_get_type ())
|
||||
G_DECLARE_FINAL_TYPE (GtkLogoPaintable, gtk_logo_paintable, GTK, LOGO_PAINTABLE, GObject)
|
||||
#define GTK_TYPE_PATH_PAINTABLE (gtk_path_paintable_get_type ())
|
||||
G_DECLARE_FINAL_TYPE (GtkPathPaintable, gtk_path_paintable, GTK, PATH_PAINTABLE, GObject)
|
||||
|
||||
struct _GtkLogoPaintable
|
||||
struct _GtkPathPaintable
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
int width;
|
||||
int height;
|
||||
GskPath *path[3];
|
||||
GdkRGBA color[3];
|
||||
|
||||
GskPath *stroke_path;
|
||||
GskStroke *stroke1;
|
||||
GskStroke *stroke2;
|
||||
GdkRGBA stroke_color;
|
||||
GskPath *path;
|
||||
GdkPaintable *background;
|
||||
};
|
||||
|
||||
struct _GtkLogoPaintableClass
|
||||
struct _GtkPathPaintableClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
static int
|
||||
gtk_logo_paintable_get_intrinsic_width (GdkPaintable *paintable)
|
||||
gtk_path_paintable_get_intrinsic_width (GdkPaintable *paintable)
|
||||
{
|
||||
GtkLogoPaintable *self = GTK_LOGO_PAINTABLE (paintable);
|
||||
GtkPathPaintable *self = GTK_PATH_PAINTABLE (paintable);
|
||||
|
||||
return self->width;
|
||||
if (self->background)
|
||||
return MAX (gdk_paintable_get_intrinsic_width (self->background), self->width);
|
||||
else
|
||||
return self->width;
|
||||
}
|
||||
|
||||
static int
|
||||
gtk_logo_paintable_get_intrinsic_height (GdkPaintable *paintable)
|
||||
gtk_path_paintable_get_intrinsic_height (GdkPaintable *paintable)
|
||||
{
|
||||
GtkLogoPaintable *self = GTK_LOGO_PAINTABLE (paintable);
|
||||
GtkPathPaintable *self = GTK_PATH_PAINTABLE (paintable);
|
||||
|
||||
return self->height;
|
||||
if (self->background)
|
||||
return MAX (gdk_paintable_get_intrinsic_height (self->background), self->height);
|
||||
else
|
||||
return self->height;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_logo_paintable_snapshot (GdkPaintable *paintable,
|
||||
gtk_path_paintable_snapshot (GdkPaintable *paintable,
|
||||
GdkSnapshot *snapshot,
|
||||
double width,
|
||||
double height)
|
||||
{
|
||||
GtkLogoPaintable *self = GTK_LOGO_PAINTABLE (paintable);
|
||||
GtkPathPaintable *self = GTK_PATH_PAINTABLE (paintable);
|
||||
|
||||
for (unsigned int i = 0; i < 3; i++)
|
||||
#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)
|
||||
{
|
||||
gtk_snapshot_push_fill (snapshot, self->path[i], GSK_FILL_RULE_WINDING);
|
||||
gtk_snapshot_append_color (snapshot,
|
||||
&self->color[i],
|
||||
&GRAPHENE_RECT_INIT (0, 0, width, height));
|
||||
gtk_snapshot_pop (snapshot);
|
||||
gdk_paintable_snapshot (self->background, snapshot, width, height);
|
||||
}
|
||||
for (unsigned int i = 0; i < 3; i++)
|
||||
else
|
||||
{
|
||||
gtk_snapshot_push_stroke (snapshot, self->stroke_path, self->stroke1);
|
||||
gtk_snapshot_append_color (snapshot,
|
||||
&self->stroke_color,
|
||||
&GRAPHENE_RECT_INIT (0, 0, width, height));
|
||||
gtk_snapshot_pop (snapshot);
|
||||
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_push_stroke (snapshot, self->stroke_path, self->stroke2);
|
||||
gtk_snapshot_append_color (snapshot,
|
||||
&self->stroke_color,
|
||||
&GRAPHENE_RECT_INIT (0, 0, width, height));
|
||||
gtk_snapshot_pop (snapshot);
|
||||
|
||||
}
|
||||
|
||||
static GdkPaintableFlags
|
||||
gtk_logo_paintable_get_flags (GdkPaintable *paintable)
|
||||
gtk_path_paintable_get_flags (GdkPaintable *paintable)
|
||||
{
|
||||
return GDK_PAINTABLE_STATIC_CONTENTS | GDK_PAINTABLE_STATIC_SIZE;
|
||||
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_logo_paintable_paintable_init (GdkPaintableInterface *iface)
|
||||
gtk_path_paintable_paintable_init (GdkPaintableInterface *iface)
|
||||
{
|
||||
iface->get_intrinsic_width = gtk_logo_paintable_get_intrinsic_width;
|
||||
iface->get_intrinsic_height = gtk_logo_paintable_get_intrinsic_height;
|
||||
iface->snapshot = gtk_logo_paintable_snapshot;
|
||||
iface->get_flags = gtk_logo_paintable_get_flags;
|
||||
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 (GtkLogoPaintable, gtk_logo_paintable, G_TYPE_OBJECT,
|
||||
G_DEFINE_TYPE_WITH_CODE (GtkPathPaintable, gtk_path_paintable, G_TYPE_OBJECT,
|
||||
G_IMPLEMENT_INTERFACE (GDK_TYPE_PAINTABLE,
|
||||
gtk_logo_paintable_paintable_init))
|
||||
gtk_path_paintable_paintable_init))
|
||||
|
||||
static void
|
||||
gtk_logo_paintable_dispose (GObject *object)
|
||||
/* Here's the boilerplate for the GObject declaration.
|
||||
* We need to disconnect the signals here that we set up elsewhere
|
||||
*/
|
||||
static void
|
||||
gtk_path_paintable_dispose (GObject *object)
|
||||
{
|
||||
GtkLogoPaintable *self = GTK_LOGO_PAINTABLE (object);
|
||||
GtkPathPaintable *self = GTK_PATH_PAINTABLE (object);
|
||||
|
||||
for (unsigned int i = 0; i < 3; i++)
|
||||
gsk_path_unref (self->path[i]);
|
||||
if (self->background)
|
||||
{
|
||||
g_signal_handlers_disconnect_by_func (self->background, gdk_paintable_invalidate_contents, self);
|
||||
g_signal_handlers_disconnect_by_func (self->background, gdk_paintable_invalidate_size, self);
|
||||
g_clear_object (&self->background);
|
||||
}
|
||||
|
||||
gsk_path_unref (self->stroke_path);
|
||||
|
||||
gsk_stroke_free (self->stroke1);
|
||||
gsk_stroke_free (self->stroke2);
|
||||
|
||||
G_OBJECT_CLASS (gtk_logo_paintable_parent_class)->dispose (object);
|
||||
G_OBJECT_CLASS (gtk_path_paintable_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_logo_paintable_class_init (GtkLogoPaintableClass *klass)
|
||||
gtk_path_paintable_class_init (GtkPathPaintableClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->dispose = gtk_logo_paintable_dispose;
|
||||
object_class->dispose = gtk_path_paintable_dispose;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_logo_paintable_init (GtkLogoPaintable *self)
|
||||
gtk_path_paintable_init (GtkPathPaintable *self)
|
||||
{
|
||||
}
|
||||
|
||||
static GdkPaintable *
|
||||
gtk_logo_paintable_new (void)
|
||||
/* 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)
|
||||
{
|
||||
GtkLogoPaintable *self;
|
||||
graphene_rect_t bounds, bounds2;
|
||||
GtkPathPaintable *self;
|
||||
|
||||
self = g_object_new (GTK_TYPE_LOGO_PAINTABLE, NULL);
|
||||
|
||||
/* Paths and colors extracted from gtk-logo.svg */
|
||||
self->path[0] = gsk_path_parse ("m3.12,66.17 -2.06,-51.46 32.93,24.7 v55.58 l-30.87,-28.82 z");
|
||||
self->path[1] = gsk_path_parse ("m34,95 49.4,-20.58 4.12,-51.46 -53.52,16.47 v55.58 z");
|
||||
self->path[2] = gsk_path_parse ("m1.06,14.71 32.93,24.7 53.52,-16.47 -36.75,-21.88 -49.7,13.65 z");
|
||||
|
||||
gdk_rgba_parse (&self->color[0], "#e40000");
|
||||
gdk_rgba_parse (&self->color[1], "#7fe719");
|
||||
gdk_rgba_parse (&self->color[2], "#729fcf");
|
||||
|
||||
self->stroke_path = gsk_path_parse ("m50.6,51.3 -47.3,14 z l33,23 z v-50");
|
||||
self->stroke1 = gsk_stroke_new (2.12);
|
||||
self->stroke2 = gsk_stroke_new (1.25);
|
||||
gdk_rgba_parse (&self->stroke_color, "#ffffff");
|
||||
|
||||
gsk_path_get_stroke_bounds (self->path[0], self->stroke1, &bounds);
|
||||
gsk_path_get_stroke_bounds (self->path[1], self->stroke1, &bounds2);
|
||||
graphene_rect_union (&bounds, &bounds2, &bounds);
|
||||
gsk_path_get_stroke_bounds (self->path[2], self->stroke1, &bounds2);
|
||||
graphene_rect_union (&bounds, &bounds2, &bounds);
|
||||
gsk_path_get_stroke_bounds (self->stroke_path, self->stroke2, &bounds2);
|
||||
graphene_rect_union (&bounds, &bounds2, &bounds);
|
||||
|
||||
self->width = bounds.origin.x + bounds.size.width;
|
||||
self->height = bounds.origin.y + bounds.size.height;
|
||||
self = g_object_new (GTK_TYPE_PATH_PAINTABLE, NULL);
|
||||
self->path = path;
|
||||
self->background = background;
|
||||
if (self->background)
|
||||
{
|
||||
g_object_ref (self->background);
|
||||
g_signal_connect_swapped (self->background, "invalidate-contents", G_CALLBACK (gdk_paintable_invalidate_contents), self);
|
||||
g_signal_connect_swapped (self->background, "invalidate-size", G_CALLBACK (gdk_paintable_invalidate_size), self);
|
||||
}
|
||||
self->width = width;
|
||||
self->height = height;
|
||||
|
||||
return GDK_PAINTABLE (self);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_path_paintable_set_path (GtkPathPaintable *self,
|
||||
GskPath *path)
|
||||
{
|
||||
g_clear_pointer (&self->path, gsk_path_unref);
|
||||
self->path = gsk_path_ref (path);
|
||||
|
||||
gdk_paintable_invalidate_contents (GDK_PAINTABLE (self));
|
||||
}
|
||||
|
||||
static GskPath *
|
||||
create_hexagon (GtkWidget *widget)
|
||||
{
|
||||
GskPathBuilder *builder;
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
|
||||
gsk_path_builder_move_to (builder, 120, 0);
|
||||
gsk_path_builder_line_to (builder, 360, 0);
|
||||
gsk_path_builder_line_to (builder, 480, 208);
|
||||
gsk_path_builder_line_to (builder, 360, 416);
|
||||
gsk_path_builder_line_to (builder, 120, 416);
|
||||
gsk_path_builder_line_to (builder, 0, 208);
|
||||
gsk_path_builder_close (builder);
|
||||
|
||||
return gsk_path_builder_free_to_path (builder);
|
||||
}
|
||||
|
||||
static GskPath *
|
||||
create_path_from_text (GtkWidget *widget)
|
||||
{
|
||||
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_QUAD:
|
||||
gsk_path_builder_quad_to (builder, pts[1].x, pts[1].y, pts[2].x, pts[2].y);
|
||||
break;
|
||||
|
||||
case GSK_PATH_CUBIC:
|
||||
gsk_path_builder_cubic_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;
|
||||
GskPathPoint point;
|
||||
graphene_point_t pos;
|
||||
graphene_vec2_t tangent;
|
||||
GskStroke *stroke;
|
||||
|
||||
path = gsk_path_measure_get_path (measure);
|
||||
|
||||
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);
|
||||
|
||||
if (gsk_path_measure_get_point (measure,
|
||||
(progress > 1 ? (progress - 1) : progress) * gsk_path_measure_get_length (measure), &point))
|
||||
{
|
||||
gsk_path_point_get_position (&point, &pos);
|
||||
gsk_path_point_get_tangent (&point, GSK_PATH_END, &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)
|
||||
{
|
||||
@@ -174,14 +317,35 @@ do_path_fill (GtkWidget *do_widget)
|
||||
{
|
||||
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);
|
||||
|
||||
paintable = gtk_logo_paintable_new ();
|
||||
#if 0
|
||||
stream = gtk_media_file_new_for_resource ("/images/gtk-logo.webm");
|
||||
#else
|
||||
stream = gtk_nuclear_media_stream_new ();
|
||||
#endif
|
||||
gtk_media_stream_play (stream);
|
||||
gtk_media_stream_set_loop (stream, TRUE);
|
||||
|
||||
path = create_hexagon (window);
|
||||
path = create_path_from_text (window);
|
||||
gsk_path_get_bounds (path, &bounds);
|
||||
|
||||
paintable = gtk_path_paintable_new (path,
|
||||
GDK_PAINTABLE (stream),
|
||||
bounds.origin.x + bounds.size.width,
|
||||
bounds.origin.y + bounds.size.height);
|
||||
picture = gtk_picture_new_for_paintable (paintable);
|
||||
measure = gsk_path_measure_new (path);
|
||||
gtk_widget_add_tick_callback (picture, update_path, measure, (GDestroyNotify) gsk_path_measure_unref);
|
||||
gtk_picture_set_content_fit (GTK_PICTURE (picture), GTK_CONTENT_FIT_CONTAIN);
|
||||
gtk_picture_set_can_shrink (GTK_PICTURE (picture), FALSE);
|
||||
g_object_unref (paintable);
|
||||
|
||||
346
demos/gtk-demo/path_maze.c
Normal file
346
demos/gtk-demo/path_maze.c
Normal file
@@ -0,0 +1,346 @@
|
||||
/* Path/Maze
|
||||
*
|
||||
* This demo shows how to use a GskPath to create a maze and use
|
||||
* gsk_path_measure_get_closest_point() to check the mouse stays
|
||||
* on the path.
|
||||
*
|
||||
* It also shows off the performance of GskPath (or not) as this
|
||||
* is a rather complex path.
|
||||
*/
|
||||
|
||||
#include <glib/gi18n.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "paintable.h"
|
||||
|
||||
#define MAZE_GRID_SIZE 20
|
||||
#define MAZE_STROKE_SIZE_ACTIVE (MAZE_GRID_SIZE - 4)
|
||||
#define MAZE_STROKE_SIZE_INACTIVE (MAZE_GRID_SIZE - 12)
|
||||
#define MAZE_WIDTH 31
|
||||
#define MAZE_HEIGHT 21
|
||||
|
||||
#define GTK_TYPE_MAZE (gtk_maze_get_type ())
|
||||
G_DECLARE_FINAL_TYPE (GtkMaze, gtk_maze, GTK, MAZE, GtkWidget)
|
||||
|
||||
struct _GtkMaze
|
||||
{
|
||||
GtkWidget parent_instance;
|
||||
|
||||
int width;
|
||||
int height;
|
||||
GskPath *path;
|
||||
GskPathMeasure *measure;
|
||||
GdkPaintable *background;
|
||||
|
||||
gboolean active;
|
||||
};
|
||||
|
||||
struct _GtkMazeClass
|
||||
{
|
||||
GtkWidgetClass parent_class;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GtkMaze, gtk_maze, GTK_TYPE_WIDGET)
|
||||
|
||||
static void
|
||||
gtk_maze_measure (GtkWidget *widget,
|
||||
GtkOrientation orientation,
|
||||
int for_size,
|
||||
int *minimum,
|
||||
int *natural,
|
||||
int *minimum_baseline,
|
||||
int *natural_baseline)
|
||||
{
|
||||
GtkMaze *self = GTK_MAZE (widget);
|
||||
|
||||
if (orientation == GTK_ORIENTATION_HORIZONTAL)
|
||||
*minimum = *natural = self->width;
|
||||
else
|
||||
*minimum = *natural = self->height;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_maze_snapshot (GtkWidget *widget,
|
||||
GdkSnapshot *snapshot)
|
||||
{
|
||||
GtkMaze *self = GTK_MAZE (widget);
|
||||
double width = gtk_widget_get_width (widget);
|
||||
double height = gtk_widget_get_height (widget);
|
||||
GskStroke *stroke;
|
||||
|
||||
stroke = gsk_stroke_new (MAZE_STROKE_SIZE_INACTIVE);
|
||||
if (self->active)
|
||||
gsk_stroke_set_line_width (stroke, MAZE_STROKE_SIZE_ACTIVE);
|
||||
gsk_stroke_set_line_join (stroke, GSK_LINE_JOIN_ROUND);
|
||||
gsk_stroke_set_line_cap (stroke, GSK_LINE_CAP_ROUND);
|
||||
gtk_snapshot_push_stroke (snapshot, self->path, stroke);
|
||||
gsk_stroke_free (stroke);
|
||||
|
||||
if (self->background)
|
||||
{
|
||||
gdk_paintable_snapshot (self->background, snapshot, width, height);
|
||||
}
|
||||
else
|
||||
{
|
||||
gtk_snapshot_append_linear_gradient (snapshot,
|
||||
&GRAPHENE_RECT_INIT (0, 0, width, height),
|
||||
&GRAPHENE_POINT_INIT (0, 0),
|
||||
&GRAPHENE_POINT_INIT (width, height),
|
||||
(GskColorStop[8]) {
|
||||
{ 0.0, { 1.0, 0.0, 0.0, 1.0 } },
|
||||
{ 0.2, { 1.0, 0.0, 0.0, 1.0 } },
|
||||
{ 0.3, { 1.0, 1.0, 0.0, 1.0 } },
|
||||
{ 0.4, { 0.0, 1.0, 0.0, 1.0 } },
|
||||
{ 0.6, { 0.0, 1.0, 1.0, 1.0 } },
|
||||
{ 0.7, { 0.0, 0.0, 1.0, 1.0 } },
|
||||
{ 0.8, { 1.0, 0.0, 1.0, 1.0 } },
|
||||
{ 1.0, { 1.0, 0.0, 1.0, 1.0 } }
|
||||
},
|
||||
8);
|
||||
}
|
||||
|
||||
gtk_snapshot_pop (snapshot);
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_maze_dispose (GObject *object)
|
||||
{
|
||||
GtkMaze *self = GTK_MAZE (object);
|
||||
|
||||
g_clear_pointer (&self->path, gsk_path_unref);
|
||||
g_clear_pointer (&self->measure, gsk_path_measure_unref);
|
||||
if (self->background)
|
||||
{
|
||||
g_signal_handlers_disconnect_matched (self->background, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, self);
|
||||
g_clear_object (&self->background);
|
||||
}
|
||||
|
||||
G_OBJECT_CLASS (gtk_maze_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_maze_class_init (GtkMazeClass *klass)
|
||||
{
|
||||
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->dispose = gtk_maze_dispose;
|
||||
|
||||
widget_class->measure = gtk_maze_measure;
|
||||
widget_class->snapshot = gtk_maze_snapshot;
|
||||
}
|
||||
|
||||
static void
|
||||
pointer_motion (GtkEventControllerMotion *controller,
|
||||
double x,
|
||||
double y,
|
||||
GtkMaze *self)
|
||||
{
|
||||
GskPathPoint point;
|
||||
graphene_point_t pos;
|
||||
|
||||
if (!self->active)
|
||||
return;
|
||||
|
||||
if (gsk_path_get_closest_point (self->path, &GRAPHENE_POINT_INIT (x, y), INFINITY, &point))
|
||||
{
|
||||
gsk_path_point_get_position (&point, &pos);
|
||||
|
||||
if (graphene_point_distance (&pos, &GRAPHENE_POINT_INIT (x, y), NULL, NULL) <= MAZE_STROKE_SIZE_ACTIVE / 2.0f)
|
||||
return;
|
||||
|
||||
self->active = FALSE;
|
||||
gtk_widget_queue_draw (GTK_WIDGET (self));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
pointer_leave (GtkEventControllerMotion *controller,
|
||||
GtkMaze *self)
|
||||
{
|
||||
if (!self->active)
|
||||
{
|
||||
self->active = TRUE;
|
||||
gtk_widget_queue_draw (GTK_WIDGET (self));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_maze_init (GtkMaze *self)
|
||||
{
|
||||
GtkEventController *controller;
|
||||
|
||||
controller = GTK_EVENT_CONTROLLER (gtk_event_controller_motion_new ());
|
||||
g_signal_connect (controller, "motion", G_CALLBACK (pointer_motion), self);
|
||||
g_signal_connect (controller, "leave", G_CALLBACK (pointer_leave), self);
|
||||
gtk_widget_add_controller (GTK_WIDGET (self), controller);
|
||||
|
||||
self->active = TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_maze_set_path (GtkMaze *self,
|
||||
GskPath *path)
|
||||
{
|
||||
g_clear_pointer (&self->path, gsk_path_unref);
|
||||
g_clear_pointer (&self->measure, gsk_path_measure_unref);
|
||||
self->path = gsk_path_ref (path);
|
||||
self->measure = gsk_path_measure_new (path);
|
||||
|
||||
gtk_widget_queue_draw (GTK_WIDGET (self));
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
gtk_maze_new (GskPath *path,
|
||||
GdkPaintable *background,
|
||||
int width,
|
||||
int height)
|
||||
{
|
||||
GtkMaze *self;
|
||||
|
||||
self = g_object_new (GTK_TYPE_MAZE, NULL);
|
||||
|
||||
gtk_maze_set_path (self, path);
|
||||
gsk_path_unref (path);
|
||||
self->background = background;
|
||||
if (self->background)
|
||||
{
|
||||
g_signal_connect_swapped (self->background, "invalidate-contents", G_CALLBACK (gtk_widget_queue_draw), self);
|
||||
g_signal_connect_swapped (self->background, "invalidate-size", G_CALLBACK (gtk_widget_queue_resize), self);
|
||||
}
|
||||
self->width = width;
|
||||
self->height = height;
|
||||
|
||||
return GTK_WIDGET (self);
|
||||
}
|
||||
|
||||
static void
|
||||
add_point_to_maze (GtkBitset *maze,
|
||||
GskPathBuilder *builder,
|
||||
guint x,
|
||||
guint y)
|
||||
{
|
||||
gboolean set[4] = { FALSE, FALSE, FALSE, FALSE };
|
||||
guint dir;
|
||||
|
||||
gtk_bitset_add (maze, y * MAZE_WIDTH + x);
|
||||
|
||||
while (TRUE)
|
||||
{
|
||||
set[0] = set[0] || x == 0 || gtk_bitset_contains (maze, y * MAZE_WIDTH + x - 1);
|
||||
set[1] = set[1] || y == 0 || gtk_bitset_contains (maze, (y - 1) * MAZE_WIDTH + x);
|
||||
set[2] = set[2] || x + 1 == MAZE_WIDTH || gtk_bitset_contains (maze, y * MAZE_WIDTH + x + 1);
|
||||
set[3] = set[3] || y + 1 == MAZE_HEIGHT || gtk_bitset_contains (maze, (y + 1) * MAZE_WIDTH + x);
|
||||
|
||||
if (set[0] && set[1] && set[2] && set[3])
|
||||
return;
|
||||
|
||||
do
|
||||
{
|
||||
dir = g_random_int_range (0, 4);
|
||||
}
|
||||
while (set[dir]);
|
||||
|
||||
switch (dir)
|
||||
{
|
||||
case 0:
|
||||
gsk_path_builder_move_to (builder, (x + 0.5) * MAZE_GRID_SIZE, (y + 0.5) * MAZE_GRID_SIZE);
|
||||
gsk_path_builder_line_to (builder, (x - 0.5) * MAZE_GRID_SIZE, (y + 0.5) * MAZE_GRID_SIZE);
|
||||
add_point_to_maze (maze, builder, x - 1, y);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
gsk_path_builder_move_to (builder, (x + 0.5) * MAZE_GRID_SIZE, (y + 0.5) * MAZE_GRID_SIZE);
|
||||
gsk_path_builder_line_to (builder, (x + 0.5) * MAZE_GRID_SIZE, (y - 0.5) * MAZE_GRID_SIZE);
|
||||
add_point_to_maze (maze, builder, x, y - 1);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
gsk_path_builder_move_to (builder, (x + 0.5) * MAZE_GRID_SIZE, (y + 0.5) * MAZE_GRID_SIZE);
|
||||
gsk_path_builder_line_to (builder, (x + 1.5) * MAZE_GRID_SIZE, (y + 0.5) * MAZE_GRID_SIZE);
|
||||
add_point_to_maze (maze, builder, x + 1, y);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
gsk_path_builder_move_to (builder, (x + 0.5) * MAZE_GRID_SIZE, (y + 0.5) * MAZE_GRID_SIZE);
|
||||
gsk_path_builder_line_to (builder, (x + 0.5) * MAZE_GRID_SIZE, (y + 1.5) * MAZE_GRID_SIZE);
|
||||
add_point_to_maze (maze, builder, x, y + 1);
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static GskPath *
|
||||
create_path_for_maze (GtkWidget *widget)
|
||||
{
|
||||
GskPathBuilder *builder;
|
||||
GtkBitset *maze;
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
maze = gtk_bitset_new_empty ();
|
||||
/* make sure the outer lines are unreachable:
|
||||
* Set the full range, then remove the center again. */
|
||||
gtk_bitset_add_range (maze, 0, MAZE_WIDTH * MAZE_HEIGHT);
|
||||
gtk_bitset_remove_rectangle (maze, MAZE_WIDTH + 1, MAZE_WIDTH - 2, MAZE_HEIGHT - 2, MAZE_WIDTH);
|
||||
|
||||
/* Fill the maze */
|
||||
add_point_to_maze (maze, builder, MAZE_WIDTH / 2, MAZE_HEIGHT / 2);
|
||||
|
||||
/* Add start and stop lines */
|
||||
gsk_path_builder_move_to (builder, 1.5 * MAZE_GRID_SIZE, -0.5 * MAZE_GRID_SIZE);
|
||||
gsk_path_builder_line_to (builder, 1.5 * MAZE_GRID_SIZE, 1.5 * MAZE_GRID_SIZE);
|
||||
gsk_path_builder_move_to (builder, (MAZE_WIDTH - 1.5) * MAZE_GRID_SIZE, (MAZE_HEIGHT - 1.5) * MAZE_GRID_SIZE);
|
||||
gsk_path_builder_line_to (builder, (MAZE_WIDTH - 1.5) * MAZE_GRID_SIZE, (MAZE_HEIGHT + 0.5) * MAZE_GRID_SIZE);
|
||||
|
||||
|
||||
gtk_bitset_unref (maze);
|
||||
|
||||
return gsk_path_builder_free_to_path (builder);
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
do_path_maze (GtkWidget *do_widget)
|
||||
{
|
||||
static GtkWidget *window = NULL;
|
||||
|
||||
if (!window)
|
||||
{
|
||||
GtkWidget *maze;
|
||||
GtkMediaStream *stream;
|
||||
GskPath *path;
|
||||
|
||||
window = gtk_window_new ();
|
||||
gtk_window_set_resizable (GTK_WINDOW (window), TRUE);
|
||||
gtk_window_set_title (GTK_WINDOW (window), "Follow the maze with the mouse");
|
||||
g_object_add_weak_pointer (G_OBJECT (window), (gpointer *)&window);
|
||||
|
||||
#if 0
|
||||
stream = gtk_media_file_new_for_resource ("/images/gtk-logo.webm");
|
||||
#else
|
||||
stream = gtk_nuclear_media_stream_new ();
|
||||
#endif
|
||||
gtk_media_stream_play (stream);
|
||||
gtk_media_stream_set_loop (stream, TRUE);
|
||||
|
||||
path = create_path_for_maze (window);
|
||||
|
||||
maze = gtk_maze_new (path,
|
||||
GDK_PAINTABLE (stream),
|
||||
MAZE_WIDTH * MAZE_GRID_SIZE,
|
||||
MAZE_HEIGHT * MAZE_GRID_SIZE);
|
||||
|
||||
gtk_window_set_child (GTK_WINDOW (window), maze);
|
||||
}
|
||||
|
||||
if (!gtk_widget_get_visible (window))
|
||||
gtk_window_present (GTK_WINDOW (window));
|
||||
else
|
||||
gtk_window_destroy (GTK_WINDOW (window));
|
||||
|
||||
return window;
|
||||
}
|
||||
@@ -92,10 +92,8 @@ gtk_path_transform_point (GskPathMeasure *measure,
|
||||
|
||||
if (gsk_path_measure_get_point (measure, (pt->x + offset->x) * scale, &point))
|
||||
{
|
||||
GskPath *path = gsk_path_measure_get_path (measure);
|
||||
|
||||
gsk_path_point_get_position (path, &point, res);
|
||||
gsk_path_point_get_tangent (path, &point, GSK_PATH_END, &tangent);
|
||||
gsk_path_point_get_position (&point, res);
|
||||
gsk_path_point_get_tangent (&point, GSK_PATH_END, &tangent);
|
||||
|
||||
res->x -= (pt->y + offset->y) * scale * graphene_vec2_get_y (&tangent);
|
||||
res->y += (pt->y + offset->y) * scale * graphene_vec2_get_x (&tangent);
|
||||
@@ -106,6 +104,7 @@ static gboolean
|
||||
gtk_path_transform_op (GskPathOperation op,
|
||||
const graphene_point_t *pts,
|
||||
gsize n_pts,
|
||||
float weight,
|
||||
gpointer data)
|
||||
{
|
||||
GtkPathTransform *transform = data;
|
||||
@@ -147,6 +146,15 @@ gtk_path_transform_op (GskPathOperation op,
|
||||
}
|
||||
break;
|
||||
|
||||
case GSK_PATH_CONIC:
|
||||
{
|
||||
graphene_point_t res[2];
|
||||
gtk_path_transform_point (transform->measure, &pts[1], &transform->offset, transform->scale, &res[0]);
|
||||
gtk_path_transform_point (transform->measure, &pts[2], &transform->offset, 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;
|
||||
@@ -324,7 +332,7 @@ gtk_path_widget_snapshot (GtkWidget *widget,
|
||||
builder = gsk_path_builder_new ();
|
||||
if (gsk_path_measure_get_point (self->line_measure, self->line_closest, &point))
|
||||
{
|
||||
gsk_path_point_get_position (self->line_path, &point, &closest);
|
||||
gsk_path_point_get_position (&point, &closest);
|
||||
gsk_path_builder_add_circle (builder, &closest, POINT_SIZE);
|
||||
path = gsk_path_builder_free_to_path (builder);
|
||||
|
||||
@@ -521,7 +529,7 @@ pointer_motion (GtkEventControllerMotion *controller,
|
||||
INFINITY,
|
||||
&point))
|
||||
{
|
||||
gsk_path_point_get_position (self->line_path, &point, &pos);
|
||||
gsk_path_point_get_position (&point, &pos);
|
||||
self->line_closest = graphene_point_distance (&pos, &GRAPHENE_POINT_INIT (x, y), NULL, NULL);
|
||||
|
||||
gtk_widget_queue_draw (GTK_WIDGET (self));
|
||||
|
||||
121
docs/reference/gtk/gtk4-path-tool.rst
Normal file
121
docs/reference/gtk/gtk4-path-tool.rst
Normal file
@@ -0,0 +1,121 @@
|
||||
.. _gtk4-path-tool(1):
|
||||
|
||||
=================
|
||||
gtk4-path-tool
|
||||
=================
|
||||
|
||||
-----------------------
|
||||
GskPath Utility
|
||||
-----------------------
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
| **gtk4-path-tool** <COMMAND> [OPTIONS...] <PATH>
|
||||
|
|
||||
| **gtk4-path-tool** decompose [OPTIONS...] <PATH>
|
||||
| **gtk4-path-tool** restrict [OPTIONS...] <PATH>
|
||||
| **gtk4-path-tool** show [OPTIONS...] <PATH>
|
||||
| **gtk4-path-tool** render [OPTIONS...] <PATH>
|
||||
| **gtk4-path-tool** info [OPTIONS...] <PATH>
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
|
||||
``gtk4-path-tool`` can perform various tasks on paths. Paths are specified
|
||||
in SVG syntax, as strings like "M 100 100 C 100 200 200 200 200 100 Z".
|
||||
|
||||
To read a path from a file, use a filename that starts with a '.' or a '/'.
|
||||
To read a path from stdin, use '-'.
|
||||
|
||||
COMMANDS
|
||||
--------
|
||||
|
||||
Decomposing
|
||||
^^^^^^^^^^^
|
||||
|
||||
The ``decompose`` command approximates the path by one with simpler elements.
|
||||
When used without options, the curves of the path are approximated by line
|
||||
segments.
|
||||
|
||||
``--allow-curves``
|
||||
|
||||
Allow cubic Bézier curves to be used in the generated path.
|
||||
|
||||
``--allow-conics``
|
||||
|
||||
Allow rational quadratic Bézier curves to be used in the generated path.
|
||||
|
||||
Restricting
|
||||
^^^^^^^^^^^
|
||||
|
||||
The ``restrict`` command creates a path that traces a segment of the original
|
||||
path. Note that the start and the end of the segment are specified as
|
||||
path length from the beginning of the path.
|
||||
|
||||
``--start=LENGTH``
|
||||
|
||||
The distance from the beginning of the path where the segment begins. The
|
||||
default values is 0.
|
||||
|
||||
``--end=LENGTH``
|
||||
|
||||
The distance from the beginning of the path where the segment ends. The
|
||||
default value is the length of path.
|
||||
|
||||
Showing
|
||||
^^^^^^^
|
||||
|
||||
The ``show`` command displays the given path in a window. The interior
|
||||
of the path is filled.
|
||||
|
||||
``--fill-rule=VALUE``
|
||||
|
||||
The fill rule that is used to determine what areas are inside the path.
|
||||
The possible values are ``winding`` or ``even-odd``. The default is ``winding``.
|
||||
|
||||
``--fg-color=COLOR``
|
||||
|
||||
The color that is used to fill the interior of the path.
|
||||
If not specified, black is used.
|
||||
|
||||
``--bg-color=COLOR``
|
||||
|
||||
The color that is used to render the background behind the path.
|
||||
If not specified, white is used.
|
||||
|
||||
Rendering
|
||||
^^^^^^^^^
|
||||
|
||||
The ``render`` command renders the given path as a PNG image.
|
||||
The interior of the path is filled.
|
||||
|
||||
``--fill-rule=VALUE``
|
||||
|
||||
The fill rule that is used to determine what areas are inside the path.
|
||||
The possible values are ``winding`` or ``even-odd``. The default is ``winding``.
|
||||
|
||||
``--fg-color=COLOR``
|
||||
|
||||
The color that is used to fill the interior of the path.
|
||||
If not specified, black is used.
|
||||
|
||||
``--bg-color=COLOR``
|
||||
|
||||
The color that is used to render the background behind the path.
|
||||
If not specified, white is used.
|
||||
|
||||
``--output-file=FILE``
|
||||
|
||||
The file to save the PNG image to.
|
||||
If not specified, "path.png" is used.
|
||||
|
||||
Info
|
||||
^^^^
|
||||
|
||||
The ``info`` command shows various information about the given path,
|
||||
such as its bounding box and and its length.
|
||||
|
||||
REFERENCES
|
||||
----------
|
||||
|
||||
- SVG Path Specification, https://www.w3.org/TR/SVG2/paths.html
|
||||
@@ -77,6 +77,7 @@ if get_option('man-pages') and rst2man.found()
|
||||
[ 'gtk4-query-settings', '1', ],
|
||||
[ 'gtk4-rendernode-tool', '1' ],
|
||||
[ 'gtk4-update-icon-cache', '1', ],
|
||||
[ 'gtk4-path-tool', '1', ],
|
||||
]
|
||||
|
||||
if get_option('demos')
|
||||
|
||||
@@ -222,7 +222,7 @@ It is possible to set accessible attributes in UI files as well:
|
||||
<accessibility>
|
||||
<property name="label">Download</property>
|
||||
<relation name="labelled-by">label1</relation>
|
||||
</accessibility>
|
||||
/accessibility>
|
||||
</object>
|
||||
```
|
||||
|
||||
|
||||
@@ -27,7 +27,9 @@
|
||||
#include "gdkmacosdisplay.h"
|
||||
#include "gdkmacossurface.h"
|
||||
|
||||
#import <epoxy/gl.h>
|
||||
#import <OpenGL/OpenGL.h>
|
||||
#import <OpenGL/gl3.h>
|
||||
#import <AppKit/AppKit.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
#include "gdkconfig.h"
|
||||
|
||||
#include <OpenGL/gl3.h>
|
||||
#include <OpenGL/CGLIOSurface.h>
|
||||
#include <QuartzCore/QuartzCore.h>
|
||||
|
||||
@@ -498,7 +499,7 @@ gdk_macos_gl_context_begin_frame (GdkDrawContext *context,
|
||||
gdk_gl_context_make_current (GDK_GL_CONTEXT (self));
|
||||
gdk_macos_gl_context_allocate (self);
|
||||
|
||||
GDK_DRAW_CONTEXT_CLASS (gdk_macos_gl_context_parent_class)->begin_frame (context, depth, region);
|
||||
GDK_DRAW_CONTEXT_CLASS (gdk_macos_gl_context_parent_class)->begin_frame (context, prefers_high_depth, region);
|
||||
|
||||
gdk_gl_context_make_current (GDK_GL_CONTEXT (self));
|
||||
CHECK_GL (NULL, glBindFramebuffer (GL_FRAMEBUFFER, self->fbo));
|
||||
|
||||
@@ -1245,9 +1245,9 @@ gsk_gl_render_job_visit_as_fallback (GskGLRenderJob *job,
|
||||
done:
|
||||
if (scale_x < 0 || scale_y < 0)
|
||||
{
|
||||
GskTransform *transform = gsk_transform_translate (gsk_transform_scale (NULL, scale_x < 0 ? -1 : 1, scale_y < 0 ? -1 : 1),
|
||||
&GRAPHENE_POINT_INIT (scale_x < 0 ? - (node->bounds.size.width + 2 * node->bounds.origin.x) : 0,
|
||||
scale_y < 0 ? - (node->bounds.size.height + 2 * node->bounds.origin.y) : 0));
|
||||
GskTransform *transform = gsk_transform_translate (NULL,
|
||||
&GRAPHENE_POINT_INIT (scale_x < 0 ? - surface_width : 0,
|
||||
scale_y < 0 ? - surface_height : 0));
|
||||
gsk_gl_render_job_push_modelview (job, transform);
|
||||
gsk_transform_unref (transform);
|
||||
}
|
||||
|
||||
@@ -97,19 +97,4 @@ gsk_bounding_box_intersection (const GskBoundingBox *a,
|
||||
return min.x <= max.x && min.y <= max.y;
|
||||
}
|
||||
|
||||
static inline void
|
||||
gsk_bounding_box_union (const GskBoundingBox *a,
|
||||
const GskBoundingBox *b,
|
||||
GskBoundingBox *res)
|
||||
{
|
||||
graphene_point_t min, max;
|
||||
|
||||
min.x = MIN (a->min.x, b->min.x);
|
||||
min.y = MIN (a->min.y, b->min.y);
|
||||
max.x = MAX (a->max.x, b->max.x);
|
||||
max.y = MAX (a->max.y, b->max.y);
|
||||
|
||||
gsk_bounding_box_init (res, &min, &max);
|
||||
}
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
1955
gsk/gskcontour.c
1955
gsk/gskcontour.c
File diff suppressed because it is too large
Load Diff
@@ -23,10 +23,15 @@
|
||||
#include "gskpathprivate.h"
|
||||
#include "gskpathpointprivate.h"
|
||||
#include "gskpathopprivate.h"
|
||||
#include "gskboundingboxprivate.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
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,
|
||||
@@ -44,10 +49,10 @@ GskPathFlags gsk_contour_get_flags (const GskContou
|
||||
void gsk_contour_print (const GskContour *self,
|
||||
GString *string);
|
||||
gboolean gsk_contour_get_bounds (const GskContour *self,
|
||||
GskBoundingBox *bounds);
|
||||
graphene_rect_t *bounds);
|
||||
gboolean gsk_contour_get_stroke_bounds (const GskContour *self,
|
||||
const GskStroke *stroke,
|
||||
GskBoundingBox *bounds);
|
||||
graphene_rect_t *bounds);
|
||||
gboolean gsk_contour_foreach (const GskContour *self,
|
||||
float tolerance,
|
||||
GskPathForeachFunc func,
|
||||
@@ -80,32 +85,23 @@ gpointer gsk_contour_init_measure (const GskContou
|
||||
float *out_length);
|
||||
void gsk_contour_free_measure (const GskContour *self,
|
||||
gpointer data);
|
||||
int gsk_contour_point_compare (const GskContour *self,
|
||||
GskRealPathPoint *p1,
|
||||
GskRealPathPoint *p2);
|
||||
|
||||
void gsk_contour_add_segment (const GskContour *self,
|
||||
GskPathBuilder *builder,
|
||||
gpointer measure_data,
|
||||
gboolean emit_move_to,
|
||||
GskRealPathPoint *start,
|
||||
GskRealPathPoint *end);
|
||||
|
||||
float start,
|
||||
float end);
|
||||
void gsk_contour_get_point (const GskContour *self,
|
||||
gpointer measure_data,
|
||||
float offset,
|
||||
GskRealPathPoint *result);
|
||||
void gsk_contour_get_start_point (const GskContour *self,
|
||||
GskRealPathPoint *result);
|
||||
void gsk_contour_get_end_point (const GskContour *self,
|
||||
GskRealPathPoint *result);
|
||||
gboolean gsk_contour_get_previous_point (const GskContour *self,
|
||||
GskRealPathPoint *point,
|
||||
GskRealPathPoint *result);
|
||||
gboolean gsk_contour_get_next_point (const GskContour *self,
|
||||
GskRealPathPoint *point,
|
||||
GskRealPathPoint *result);
|
||||
float gsk_contour_get_distance (const GskContour *self,
|
||||
GskRealPathPoint *point,
|
||||
gpointer measure_data);
|
||||
gboolean gsk_contour_dash (const GskContour *contour,
|
||||
GskStroke *stroke,
|
||||
float tolerance,
|
||||
GskPathForeachFunc func,
|
||||
gpointer user_data);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
695
gsk/gskcurve.c
695
gsk/gskcurve.c
@@ -35,7 +35,8 @@ struct _GskCurveClass
|
||||
void (* init_foreach) (GskCurve *curve,
|
||||
GskPathOperation op,
|
||||
const graphene_point_t *pts,
|
||||
gsize n_pts);
|
||||
gsize n_pts,
|
||||
float weight);
|
||||
void (* print) (const GskCurve *curve,
|
||||
GString *string);
|
||||
gskpathop (* pathop) (const GskCurve *curve);
|
||||
@@ -156,7 +157,8 @@ static void
|
||||
gsk_line_curve_init_foreach (GskCurve *curve,
|
||||
GskPathOperation op,
|
||||
const graphene_point_t *pts,
|
||||
gsize n_pts)
|
||||
gsize n_pts,
|
||||
float weight)
|
||||
{
|
||||
GskLineCurve *self = &curve->line;
|
||||
|
||||
@@ -298,7 +300,7 @@ gsk_line_curve_decompose_curve (const GskCurve *curve,
|
||||
{
|
||||
const GskLineCurve *self = &curve->line;
|
||||
|
||||
return add_curve_func (GSK_PATH_LINE, self->points, 2, user_data);
|
||||
return add_curve_func (GSK_PATH_LINE, self->points, 2, 0.f, user_data);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -375,7 +377,8 @@ static void
|
||||
gsk_quad_curve_init_foreach (GskCurve *curve,
|
||||
GskPathOperation op,
|
||||
const graphene_point_t *pts,
|
||||
gsize n_pts)
|
||||
gsize n_pts,
|
||||
float weight)
|
||||
{
|
||||
GskQuadCurve *self = &curve->quad;
|
||||
|
||||
@@ -588,7 +591,7 @@ gsk_curve_add_line_cb (const graphene_point_t *from,
|
||||
AddLineData *data = user_data;
|
||||
graphene_point_t p[2] = { *from, *to };
|
||||
|
||||
return data->add_curve (GSK_PATH_LINE, p, 2, data->user_data);
|
||||
return data->add_curve (GSK_PATH_LINE, p, 2, 0.f, data->user_data);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
@@ -601,13 +604,20 @@ gsk_quad_curve_decompose_curve (const GskCurve *curve,
|
||||
const GskQuadCurve *self = &curve->quad;
|
||||
|
||||
if (flags & GSK_PATH_FOREACH_ALLOW_QUAD)
|
||||
return add_curve_func (GSK_PATH_QUAD, self->points, 3, user_data);
|
||||
return add_curve_func (GSK_PATH_QUAD, self->points, 3, 0.f, user_data);
|
||||
else if (flags & GSK_PATH_FOREACH_ALLOW_CUBIC)
|
||||
{
|
||||
GskCurve c;
|
||||
|
||||
gsk_curve_elevate (curve, &c);
|
||||
return add_curve_func (GSK_PATH_CUBIC, c.cubic.points, 4, user_data);
|
||||
return add_curve_func (GSK_PATH_CUBIC, c.cubic.points, 4, 0.f, user_data);
|
||||
}
|
||||
else if (flags & GSK_PATH_FOREACH_ALLOW_CONIC)
|
||||
{
|
||||
GskCurve c;
|
||||
|
||||
gsk_curve_init_foreach (&c, GSK_PATH_CONIC, self->points, 3, 1.f);
|
||||
return add_curve_func (GSK_PATH_CUBIC, c.cubic.points, 4, 0.f, user_data);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -736,7 +746,8 @@ static void
|
||||
gsk_cubic_curve_init_foreach (GskCurve *curve,
|
||||
GskPathOperation op,
|
||||
const graphene_point_t *pts,
|
||||
gsize n_pts)
|
||||
gsize n_pts,
|
||||
float weight)
|
||||
{
|
||||
GskCubicCurve *self = &curve->cubic;
|
||||
|
||||
@@ -909,6 +920,7 @@ gsk_curve_get_derivative (const GskCurve *curve,
|
||||
|
||||
case GSK_PATH_MOVE:
|
||||
case GSK_PATH_CLOSE:
|
||||
case GSK_PATH_CONIC:
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
@@ -1056,7 +1068,7 @@ gsk_cubic_curve_decompose_curve (const GskCurve *curve,
|
||||
const GskCubicCurve *self = &curve->cubic;
|
||||
|
||||
if (flags & GSK_PATH_FOREACH_ALLOW_CUBIC)
|
||||
return add_curve_func (GSK_PATH_CUBIC, self->points, 4, user_data);
|
||||
return add_curve_func (GSK_PATH_CUBIC, self->points, 4, 0.f, user_data);
|
||||
|
||||
/* FIXME: Quadratic (or conic?) approximation */
|
||||
return gsk_cubic_curve_decompose (curve,
|
||||
@@ -1171,6 +1183,665 @@ static const GskCurveClass GSK_CUBIC_CURVE_CLASS = {
|
||||
gsk_cubic_curve_get_tight_bounds,
|
||||
};
|
||||
|
||||
/* }}} */
|
||||
/* {{{ Conic */
|
||||
|
||||
static inline float
|
||||
gsk_conic_curve_get_weight (const GskConicCurve *self)
|
||||
{
|
||||
return self->points[2].x;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_conic_curve_ensure_coefficents (const GskConicCurve *curve)
|
||||
{
|
||||
GskConicCurve *self = (GskConicCurve *) curve;
|
||||
float w = gsk_conic_curve_get_weight (self);
|
||||
const graphene_point_t *pts = self->points;
|
||||
graphene_point_t pw = GRAPHENE_POINT_INIT (w * pts[1].x, w * pts[1].y);
|
||||
|
||||
if (self->has_coefficients)
|
||||
return;
|
||||
|
||||
self->num[2] = pts[0];
|
||||
self->num[1] = GRAPHENE_POINT_INIT (2 * (pw.x - pts[0].x),
|
||||
2 * (pw.y - pts[0].y));
|
||||
self->num[0] = GRAPHENE_POINT_INIT (pts[3].x - 2 * pw.x + pts[0].x,
|
||||
pts[3].y - 2 * pw.y + pts[0].y);
|
||||
|
||||
self->denom[2] = GRAPHENE_POINT_INIT (1, 1);
|
||||
self->denom[1] = GRAPHENE_POINT_INIT (2 * (w - 1), 2 * (w - 1));
|
||||
self->denom[0] = GRAPHENE_POINT_INIT (-self->denom[1].x, -self->denom[1].y);
|
||||
|
||||
self->has_coefficients = TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_conic_curve_init_from_points (GskConicCurve *self,
|
||||
const graphene_point_t pts[4])
|
||||
{
|
||||
self->op = GSK_PATH_CONIC;
|
||||
self->has_coefficients = FALSE;
|
||||
|
||||
memcpy (self->points, pts, sizeof (graphene_point_t) * 4);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_conic_curve_init (GskCurve *curve,
|
||||
gskpathop op)
|
||||
{
|
||||
GskConicCurve *self = &curve->conic;
|
||||
|
||||
gsk_conic_curve_init_from_points (self, gsk_pathop_points (op));
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_conic_curve_init_foreach (GskCurve *curve,
|
||||
GskPathOperation op,
|
||||
const graphene_point_t *pts,
|
||||
gsize n_pts,
|
||||
float weight)
|
||||
{
|
||||
GskConicCurve *self = &curve->conic;
|
||||
|
||||
g_assert (n_pts == 3);
|
||||
|
||||
gsk_conic_curve_init_from_points (self,
|
||||
(graphene_point_t[4]) {
|
||||
pts[0],
|
||||
pts[1],
|
||||
GRAPHENE_POINT_INIT (weight, 0),
|
||||
pts[2]
|
||||
});
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_conic_curve_print (const GskCurve *curve,
|
||||
GString *string)
|
||||
{
|
||||
g_string_append_printf (string,
|
||||
"M %g %g O %g %g %g %g %g",
|
||||
curve->conic.points[0].x, curve->conic.points[0].y,
|
||||
curve->conic.points[1].x, curve->conic.points[1].y,
|
||||
curve->conic.points[3].x, curve->conic.points[3].y,
|
||||
curve->conic.points[2].x);
|
||||
}
|
||||
|
||||
static gskpathop
|
||||
gsk_conic_curve_pathop (const GskCurve *curve)
|
||||
{
|
||||
const GskConicCurve *self = &curve->conic;
|
||||
|
||||
return gsk_pathop_encode (self->op, self->points);
|
||||
}
|
||||
|
||||
static const graphene_point_t *
|
||||
gsk_conic_curve_get_start_point (const GskCurve *curve)
|
||||
{
|
||||
const GskConicCurve *self = &curve->conic;
|
||||
|
||||
return &self->points[0];
|
||||
}
|
||||
|
||||
static const graphene_point_t *
|
||||
gsk_conic_curve_get_end_point (const GskCurve *curve)
|
||||
{
|
||||
const GskConicCurve *self = &curve->conic;
|
||||
|
||||
return &self->points[3];
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_conic_curve_get_start_tangent (const GskCurve *curve,
|
||||
graphene_vec2_t *tangent)
|
||||
{
|
||||
const GskConicCurve *self = &curve->conic;
|
||||
|
||||
get_tangent (&self->points[0], &self->points[1], tangent);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_conic_curve_get_end_tangent (const GskCurve *curve,
|
||||
graphene_vec2_t *tangent)
|
||||
{
|
||||
const GskConicCurve *self = &curve->conic;
|
||||
|
||||
get_tangent (&self->points[1], &self->points[3], tangent);
|
||||
}
|
||||
|
||||
static inline void
|
||||
gsk_curve_eval_quad (const graphene_point_t quad[3],
|
||||
float progress,
|
||||
graphene_point_t *result)
|
||||
{
|
||||
*result = GRAPHENE_POINT_INIT ((quad[0].x * progress + quad[1].x) * progress + quad[2].x,
|
||||
(quad[0].y * progress + quad[1].y) * progress + quad[2].y);
|
||||
}
|
||||
|
||||
static inline void
|
||||
gsk_conic_curve_eval_point (const GskConicCurve *self,
|
||||
float progress,
|
||||
graphene_point_t *point)
|
||||
{
|
||||
graphene_point_t num, denom;
|
||||
|
||||
gsk_curve_eval_quad (self->num, progress, &num);
|
||||
gsk_curve_eval_quad (self->denom, progress, &denom);
|
||||
|
||||
*point = GRAPHENE_POINT_INIT (num.x / denom.x, num.y / denom.y);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_conic_curve_get_point (const GskCurve *curve,
|
||||
float t,
|
||||
graphene_point_t *pos)
|
||||
{
|
||||
const GskConicCurve *self = &curve->conic;
|
||||
|
||||
gsk_conic_curve_ensure_coefficents (self);
|
||||
|
||||
gsk_conic_curve_eval_point (self, t, pos);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_conic_curve_get_tangent (const GskCurve *curve,
|
||||
float t,
|
||||
graphene_vec2_t *tangent)
|
||||
{
|
||||
const GskConicCurve *self = &curve->conic;
|
||||
graphene_point_t tmp;
|
||||
float w = gsk_conic_curve_get_weight (self);
|
||||
const graphene_point_t *pts = self->points;
|
||||
|
||||
/* The tangent will be 0 in these corner cases, just
|
||||
* treat it like a line here. */
|
||||
if ((t <= 0.f && graphene_point_equal (&pts[0], &pts[1])) ||
|
||||
(t >= 1.f && graphene_point_equal (&pts[1], &pts[3])))
|
||||
{
|
||||
graphene_vec2_init (tangent, pts[3].x - pts[0].x, pts[3].y - pts[0].y);
|
||||
return;
|
||||
}
|
||||
|
||||
gsk_curve_eval_quad ((graphene_point_t[3]) {
|
||||
GRAPHENE_POINT_INIT ((w - 1) * (pts[3].x - pts[0].x),
|
||||
(w - 1) * (pts[3].y - pts[0].y)),
|
||||
GRAPHENE_POINT_INIT (pts[3].x - pts[0].x - 2 * w * (pts[1].x - pts[0].x),
|
||||
pts[3].y - pts[0].y - 2 * w * (pts[1].y - pts[0].y)),
|
||||
GRAPHENE_POINT_INIT (w * (pts[1].x - pts[0].x),
|
||||
w * (pts[1].y - pts[0].y))
|
||||
},
|
||||
t,
|
||||
&tmp);
|
||||
graphene_vec2_init (tangent, tmp.x, tmp.y);
|
||||
graphene_vec2_normalize (tangent, tangent);
|
||||
}
|
||||
|
||||
/* See M. Floater, Derivatives of rational Bezier curves */
|
||||
static float
|
||||
gsk_conic_curve_get_curvature (const GskCurve *curve,
|
||||
float t)
|
||||
{
|
||||
graphene_point_t p[3], p1[2];
|
||||
float w, w1[2], w2;
|
||||
graphene_vec2_t t1, t2, t3;
|
||||
|
||||
w = curve->conic.points[2].x;
|
||||
|
||||
p[0] = curve->conic.points[0];
|
||||
p[1] = curve->conic.points[1];
|
||||
p[2] = curve->conic.points[3];
|
||||
|
||||
w1[0] = (1 - t) + t*w;
|
||||
w1[1] = (1 - t)*w + t;
|
||||
|
||||
w2 = (1 - t)*w1[0] + t*w1[1];
|
||||
|
||||
p1[0].x = ((1 - t)*p[0].x + t*w*p[1].x)/w1[0];
|
||||
p1[0].y = ((1 - t)*p[0].y + t*w*p[1].y)/w1[0];
|
||||
p1[1].x = ((1 - t)*w*p[1].x + t*p[2].x)/w1[1];
|
||||
p1[1].y = ((1 - t)*w*p[1].y + t*p[2].y)/w1[1];
|
||||
|
||||
graphene_vec2_init (&t1, p[1].x - p[0].x, p[1].y - p[0].y);
|
||||
graphene_vec2_init (&t2, p[2].x - p[1].x, p[2].y - p[1].y);
|
||||
graphene_vec2_init (&t3, p1[1].x - p1[0].x, p1[1].y - p1[0].y);
|
||||
|
||||
return 0.5 * ((w*pow3 (w2))/(pow3 (w1[0])*pow3 (w1[1]))) * (cross (&t1, &t2) / pow3 (graphene_vec2_length (&t3)));
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_conic_curve_reverse (const GskCurve *curve,
|
||||
GskCurve *reverse)
|
||||
{
|
||||
const GskConicCurve *self = &curve->conic;
|
||||
|
||||
reverse->op = GSK_PATH_CONIC;
|
||||
reverse->conic.points[0] = self->points[3];
|
||||
reverse->conic.points[1] = self->points[1];
|
||||
reverse->conic.points[2] = self->points[2];
|
||||
reverse->conic.points[3] = self->points[0];
|
||||
reverse->conic.has_coefficients = FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
split_bezier3d_recurse (const graphene_point3d_t *p,
|
||||
int l,
|
||||
float t,
|
||||
graphene_point3d_t *left,
|
||||
graphene_point3d_t *right,
|
||||
int *lpos,
|
||||
int *rpos)
|
||||
{
|
||||
if (l == 1)
|
||||
{
|
||||
left[*lpos] = p[0];
|
||||
right[*rpos] = p[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
graphene_point3d_t *np;
|
||||
int i;
|
||||
|
||||
np = g_alloca (sizeof (graphene_point3d_t) * (l - 1));
|
||||
for (i = 0; i < l - 1; i++)
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
left[*lpos] = p[i];
|
||||
(*lpos)++;
|
||||
}
|
||||
if (i + 1 == l - 1)
|
||||
{
|
||||
right[*rpos] = p[i + 1];
|
||||
(*rpos)--;
|
||||
}
|
||||
graphene_point3d_interpolate (&p[i], &p[i + 1], t, &np[i]);
|
||||
}
|
||||
split_bezier3d_recurse (np, l - 1, t, left, right, lpos, rpos);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
split_bezier3d (const graphene_point3d_t *p,
|
||||
int l,
|
||||
float t,
|
||||
graphene_point3d_t *left,
|
||||
graphene_point3d_t *right)
|
||||
{
|
||||
int lpos = 0;
|
||||
int rpos = l - 1;
|
||||
split_bezier3d_recurse (p, l, t, left, right, &lpos, &rpos);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_conic_curve_split (const GskCurve *curve,
|
||||
float progress,
|
||||
GskCurve *start,
|
||||
GskCurve *end)
|
||||
{
|
||||
const GskConicCurve *self = &curve->conic;
|
||||
graphene_point3d_t p[3];
|
||||
graphene_point3d_t l[3], r[3];
|
||||
graphene_point_t left[4], right[4];
|
||||
float w;
|
||||
|
||||
/* do de Casteljau in homogeneous coordinates... */
|
||||
w = self->points[2].x;
|
||||
p[0] = GRAPHENE_POINT3D_INIT (self->points[0].x, self->points[0].y, 1);
|
||||
p[1] = GRAPHENE_POINT3D_INIT (self->points[1].x * w, self->points[1].y * w, w);
|
||||
p[2] = GRAPHENE_POINT3D_INIT (self->points[3].x, self->points[3].y, 1);
|
||||
|
||||
split_bezier3d (p, 3, progress, l, r);
|
||||
|
||||
/* then project the control points down */
|
||||
left[0] = GRAPHENE_POINT_INIT (l[0].x / l[0].z, l[0].y / l[0].z);
|
||||
left[1] = GRAPHENE_POINT_INIT (l[1].x / l[1].z, l[1].y / l[1].z);
|
||||
left[3] = GRAPHENE_POINT_INIT (l[2].x / l[2].z, l[2].y / l[2].z);
|
||||
|
||||
right[0] = GRAPHENE_POINT_INIT (r[0].x / r[0].z, r[0].y / r[0].z);
|
||||
right[1] = GRAPHENE_POINT_INIT (r[1].x / r[1].z, r[1].y / r[1].z);
|
||||
right[3] = GRAPHENE_POINT_INIT (r[2].x / r[2].z, r[2].y / r[2].z);
|
||||
|
||||
/* normalize the outer weights to be 1 by using
|
||||
* the fact that weights w_i and c*w_i are equivalent
|
||||
* for any nonzero constant c
|
||||
*/
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
l[i].z /= l[0].z;
|
||||
r[i].z /= r[2].z;
|
||||
}
|
||||
|
||||
/* normalize the inner weight to be 1 by using
|
||||
* the fact that w_0*w_2/w_1^2 is a constant for
|
||||
* all equivalent weights.
|
||||
*/
|
||||
left[2] = GRAPHENE_POINT_INIT (l[1].z / sqrt (l[2].z), 0);
|
||||
right[2] = GRAPHENE_POINT_INIT (r[1].z / sqrt (r[0].z), 0);
|
||||
|
||||
if (start)
|
||||
gsk_curve_init (start, gsk_pathop_encode (GSK_PATH_CONIC, left));
|
||||
if (end)
|
||||
gsk_curve_init (end, gsk_pathop_encode (GSK_PATH_CONIC, right));
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_conic_curve_segment (const GskCurve *curve,
|
||||
float start,
|
||||
float end,
|
||||
GskCurve *segment)
|
||||
{
|
||||
const GskConicCurve *self = &curve->conic;
|
||||
graphene_point_t start_num, start_denom;
|
||||
graphene_point_t mid_num, mid_denom;
|
||||
graphene_point_t end_num, end_denom;
|
||||
graphene_point_t ctrl_num, ctrl_denom;
|
||||
float mid;
|
||||
|
||||
if (start <= 0.0f)
|
||||
return gsk_conic_curve_split (curve, end, segment, NULL);
|
||||
else if (end >= 1.0f)
|
||||
return gsk_conic_curve_split (curve, start, NULL, segment);
|
||||
|
||||
gsk_conic_curve_ensure_coefficents (self);
|
||||
|
||||
gsk_curve_eval_quad (self->num, start, &start_num);
|
||||
gsk_curve_eval_quad (self->denom, start, &start_denom);
|
||||
mid = (start + end) / 2;
|
||||
gsk_curve_eval_quad (self->num, mid, &mid_num);
|
||||
gsk_curve_eval_quad (self->denom, mid, &mid_denom);
|
||||
gsk_curve_eval_quad (self->num, end, &end_num);
|
||||
gsk_curve_eval_quad (self->denom, end, &end_denom);
|
||||
ctrl_num = GRAPHENE_POINT_INIT (2 * mid_num.x - (start_num.x + end_num.x) / 2,
|
||||
2 * mid_num.y - (start_num.y + end_num.y) / 2);
|
||||
ctrl_denom = GRAPHENE_POINT_INIT (2 * mid_denom.x - (start_denom.x + end_denom.x) / 2,
|
||||
2 * mid_denom.y - (start_denom.y + end_denom.y) / 2);
|
||||
|
||||
gsk_conic_curve_init_from_points (&segment->conic,
|
||||
(graphene_point_t[4]) {
|
||||
GRAPHENE_POINT_INIT (start_num.x / start_denom.x,
|
||||
start_num.y / start_denom.y),
|
||||
GRAPHENE_POINT_INIT (ctrl_num.x / ctrl_denom.x,
|
||||
ctrl_num.y / ctrl_denom.y),
|
||||
GRAPHENE_POINT_INIT (ctrl_denom.x / sqrtf (start_denom.x * end_denom.x),
|
||||
0),
|
||||
GRAPHENE_POINT_INIT (end_num.x / end_denom.x,
|
||||
end_num.y / end_denom.y)
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/* taken from Skia, including the very descriptive name */
|
||||
static gboolean
|
||||
gsk_conic_curve_too_curvy (const graphene_point_t *start,
|
||||
const graphene_point_t *mid,
|
||||
const graphene_point_t *end,
|
||||
float tolerance)
|
||||
{
|
||||
return fabs ((start->x + end->x) * 0.5 - mid->x) > tolerance
|
||||
|| fabs ((start->y + end->y) * 0.5 - mid->y) > tolerance;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gsk_conic_curve_decompose_subdivide (const GskConicCurve *self,
|
||||
float tolerance,
|
||||
const graphene_point_t *start,
|
||||
float start_progress,
|
||||
const graphene_point_t *end,
|
||||
float end_progress,
|
||||
GskCurveAddLineFunc add_line_func,
|
||||
gpointer user_data)
|
||||
{
|
||||
graphene_point_t mid;
|
||||
float mid_progress;
|
||||
|
||||
mid_progress = (start_progress + end_progress) / 2;
|
||||
gsk_conic_curve_eval_point (self, mid_progress, &mid);
|
||||
|
||||
if (!gsk_conic_curve_too_curvy (start, &mid, end, tolerance))
|
||||
return add_line_func (start, end, start_progress, end_progress, GSK_CURVE_LINE_REASON_STRAIGHT, user_data);
|
||||
if (end_progress - start_progress <= MIN_PROGRESS)
|
||||
return add_line_func (start, end, start_progress, end_progress, GSK_CURVE_LINE_REASON_SHORT, user_data);
|
||||
|
||||
return gsk_conic_curve_decompose_subdivide (self, tolerance,
|
||||
start, start_progress, &mid, mid_progress,
|
||||
add_line_func, user_data)
|
||||
&& gsk_conic_curve_decompose_subdivide (self, tolerance,
|
||||
&mid, mid_progress, end, end_progress,
|
||||
add_line_func, user_data);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gsk_conic_curve_decompose (const GskCurve *curve,
|
||||
float tolerance,
|
||||
GskCurveAddLineFunc add_line_func,
|
||||
gpointer user_data)
|
||||
{
|
||||
const GskConicCurve *self = &curve->conic;
|
||||
graphene_point_t mid;
|
||||
|
||||
gsk_conic_curve_ensure_coefficents (self);
|
||||
|
||||
gsk_conic_curve_eval_point (self, 0.5, &mid);
|
||||
|
||||
return gsk_conic_curve_decompose_subdivide (self,
|
||||
tolerance,
|
||||
&self->points[0],
|
||||
0.0f,
|
||||
&mid,
|
||||
0.5f,
|
||||
add_line_func,
|
||||
user_data)
|
||||
&& gsk_conic_curve_decompose_subdivide (self,
|
||||
tolerance,
|
||||
&mid,
|
||||
0.5f,
|
||||
&self->points[3],
|
||||
1.0f,
|
||||
add_line_func,
|
||||
user_data);
|
||||
}
|
||||
|
||||
/* See Floater, M: An analysis of cubic approximation schemes
|
||||
* for conic sections
|
||||
*/
|
||||
static void
|
||||
cubic_approximation (const GskCurve *curve,
|
||||
GskCurve *cubic)
|
||||
{
|
||||
const GskConicCurve *self = &curve->conic;
|
||||
graphene_point_t p[4];
|
||||
float w = self->points[2].x;
|
||||
float w2 = w*w;
|
||||
float lambda;
|
||||
|
||||
lambda = 2 * (6*w2 + 1 - sqrt (3*w2 + 1)) / (12*w2 + 3);
|
||||
|
||||
p[0] = self->points[0];
|
||||
p[3] = self->points[3];
|
||||
graphene_point_interpolate (&self->points[0], &self->points[1], lambda, &p[1]);
|
||||
graphene_point_interpolate (&self->points[3], &self->points[1], lambda, &p[2]);
|
||||
|
||||
gsk_curve_init (cubic, gsk_pathop_encode (GSK_PATH_CUBIC, p));
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gsk_conic_is_close_to_cubic (const GskCurve *conic,
|
||||
const GskCurve *cubic,
|
||||
float tolerance)
|
||||
{
|
||||
float t[] = { 0.1, 0.5, 0.9 };
|
||||
graphene_point_t p0, p1;
|
||||
|
||||
for (int i = 0; i < G_N_ELEMENTS (t); i++)
|
||||
{
|
||||
gsk_curve_get_point (conic, t[i], &p0);
|
||||
gsk_curve_get_point (cubic, t[i], &p1);
|
||||
if (graphene_point_distance (&p0, &p1, NULL, NULL) > tolerance)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean gsk_conic_curve_decompose_curve (const GskCurve *curve,
|
||||
GskPathForeachFlags flags,
|
||||
float tolerance,
|
||||
GskCurveAddCurveFunc add_curve_func,
|
||||
gpointer user_data);
|
||||
|
||||
static gboolean
|
||||
gsk_conic_curve_decompose_or_add (const GskCurve *curve,
|
||||
const GskCurve *cubic,
|
||||
float tolerance,
|
||||
GskCurveAddCurveFunc add_curve_func,
|
||||
gpointer user_data)
|
||||
{
|
||||
if (gsk_conic_is_close_to_cubic (curve, cubic, tolerance))
|
||||
return add_curve_func (GSK_PATH_CUBIC, cubic->cubic.points, 4, 0.f, user_data);
|
||||
else
|
||||
{
|
||||
GskCurve c1, c2;
|
||||
GskCurve cc1, cc2;
|
||||
|
||||
gsk_conic_curve_split (curve, 0.5, &c1, &c2);
|
||||
|
||||
cubic_approximation (&c1, &cc1);
|
||||
cubic_approximation (&c2, &cc2);
|
||||
|
||||
return gsk_conic_curve_decompose_or_add (&c1, &cc1, tolerance, add_curve_func, user_data) &&
|
||||
gsk_conic_curve_decompose_or_add (&c2, &cc2, tolerance, add_curve_func, user_data);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gsk_conic_curve_decompose_curve (const GskCurve *curve,
|
||||
GskPathForeachFlags flags,
|
||||
float tolerance,
|
||||
GskCurveAddCurveFunc add_curve_func,
|
||||
gpointer user_data)
|
||||
{
|
||||
const GskConicCurve *self = &curve->conic;
|
||||
GskCurve c;
|
||||
|
||||
if (flags & GSK_PATH_FOREACH_ALLOW_CONIC)
|
||||
return add_curve_func (GSK_PATH_CONIC,
|
||||
(const graphene_point_t[3]) { self->points[0],
|
||||
self->points[1],
|
||||
self->points[3] },
|
||||
3,
|
||||
self->points[2].x,
|
||||
user_data);
|
||||
|
||||
if (flags & GSK_PATH_FOREACH_ALLOW_CUBIC)
|
||||
{
|
||||
cubic_approximation (curve, &c);
|
||||
return gsk_conic_curve_decompose_or_add (curve, &c, tolerance, add_curve_func, user_data);
|
||||
}
|
||||
|
||||
/* FIXME: Quadratic (or conic?) approximation */
|
||||
return gsk_conic_curve_decompose (curve,
|
||||
tolerance,
|
||||
gsk_curve_add_line_cb,
|
||||
&(AddLineData) { add_curve_func, user_data });
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_conic_curve_get_bounds (const GskCurve *curve,
|
||||
GskBoundingBox *bounds)
|
||||
{
|
||||
const GskConicCurve *self = &curve->conic;
|
||||
const graphene_point_t *pts = self->points;
|
||||
|
||||
gsk_bounding_box_init (bounds, &pts[0], &pts[3]);
|
||||
gsk_bounding_box_expand (bounds, &pts[1]);
|
||||
}
|
||||
|
||||
/* Solve N = 0 where N is the numerator of (P/Q)', with
|
||||
* P = (1-t)^2*a + 2*t*(1-t)*w*b + t^2*c
|
||||
* Q = (1-t)^2 + 2*t*(1-t)*w + t^2
|
||||
*/
|
||||
static int
|
||||
get_conic_extrema (float a, float b, float c, float w, float t[4])
|
||||
{
|
||||
float q, tt;
|
||||
int n = 0;
|
||||
float w2 = w*w;
|
||||
float wac = (w - 1)*(a - c);
|
||||
|
||||
if (wac != 0)
|
||||
{
|
||||
q = - sqrt (a*a - 4*a*b*w2 + 4*a*c*w2 - 2*a*c + 4*b*b*w2 - 4*b*c*w2 + c*c);
|
||||
|
||||
tt = (- q + 2*a*w - a - 2*b*w + c)/(2*wac);
|
||||
|
||||
if (acceptable (tt))
|
||||
t[n++] = tt;
|
||||
|
||||
tt = (q + 2*a*w - a - 2*b*w + c)/(2*wac);
|
||||
|
||||
if (acceptable (tt))
|
||||
t[n++] = tt;
|
||||
}
|
||||
|
||||
if (w * (b - c) != 0 && a == c)
|
||||
t[n++] = 0.5;
|
||||
|
||||
if (w == 1 && a - 2*b + c != 0)
|
||||
{
|
||||
tt = (a - b) / (a - 2*b + c);
|
||||
if (acceptable (tt))
|
||||
t[n++] = tt;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_conic_curve_get_tight_bounds (const GskCurve *curve,
|
||||
GskBoundingBox *bounds)
|
||||
{
|
||||
const GskConicCurve *self = &curve->conic;
|
||||
float w = gsk_conic_curve_get_weight (self);
|
||||
const graphene_point_t *pts = self->points;
|
||||
float t[8];
|
||||
int n;
|
||||
|
||||
gsk_bounding_box_init (bounds, &pts[0], &pts[3]);
|
||||
|
||||
n = 0;
|
||||
n += get_conic_extrema (pts[0].x, pts[1].x, pts[3].x, w, &t[n]);
|
||||
n += get_conic_extrema (pts[0].y, pts[1].y, pts[3].y, w, &t[n]);
|
||||
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
graphene_point_t p;
|
||||
|
||||
gsk_conic_curve_get_point (curve, t[i], &p);
|
||||
gsk_bounding_box_expand (bounds, &p);
|
||||
}
|
||||
}
|
||||
|
||||
static const GskCurveClass GSK_CONIC_CURVE_CLASS = {
|
||||
gsk_conic_curve_init,
|
||||
gsk_conic_curve_init_foreach,
|
||||
gsk_conic_curve_print,
|
||||
gsk_conic_curve_pathop,
|
||||
gsk_conic_curve_get_start_point,
|
||||
gsk_conic_curve_get_end_point,
|
||||
gsk_conic_curve_get_start_tangent,
|
||||
gsk_conic_curve_get_end_tangent,
|
||||
gsk_conic_curve_get_point,
|
||||
gsk_conic_curve_get_tangent,
|
||||
gsk_conic_curve_reverse,
|
||||
gsk_conic_curve_get_curvature,
|
||||
gsk_conic_curve_split,
|
||||
gsk_conic_curve_segment,
|
||||
gsk_conic_curve_decompose,
|
||||
gsk_conic_curve_decompose_curve,
|
||||
gsk_conic_curve_get_bounds,
|
||||
gsk_conic_curve_get_tight_bounds,
|
||||
};
|
||||
|
||||
/* }}} */
|
||||
/* {{{ API */
|
||||
|
||||
@@ -1182,6 +1853,7 @@ get_class (GskPathOperation op)
|
||||
[GSK_PATH_LINE] = &GSK_LINE_CURVE_CLASS,
|
||||
[GSK_PATH_QUAD] = &GSK_QUAD_CURVE_CLASS,
|
||||
[GSK_PATH_CUBIC] = &GSK_CUBIC_CURVE_CLASS,
|
||||
[GSK_PATH_CONIC] = &GSK_CONIC_CURVE_CLASS,
|
||||
};
|
||||
|
||||
g_assert (op < G_N_ELEMENTS (klasses) && klasses[op] != NULL);
|
||||
@@ -1201,10 +1873,11 @@ void
|
||||
gsk_curve_init_foreach (GskCurve *curve,
|
||||
GskPathOperation op,
|
||||
const graphene_point_t *pts,
|
||||
gsize n_pts)
|
||||
gsize n_pts,
|
||||
float weight)
|
||||
{
|
||||
memset (curve, 0, sizeof (GskCurve));
|
||||
get_class (op)->init_foreach (curve, op, pts, n_pts);
|
||||
get_class (op)->init_foreach (curve, op, pts, n_pts, weight);
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -33,6 +33,7 @@ typedef union _GskCurve GskCurve;
|
||||
typedef struct _GskLineCurve GskLineCurve;
|
||||
typedef struct _GskQuadCurve GskQuadCurve;
|
||||
typedef struct _GskCubicCurve GskCubicCurve;
|
||||
typedef struct _GskConicCurve GskConicCurve;
|
||||
|
||||
struct _GskLineCurve
|
||||
{
|
||||
@@ -65,12 +66,28 @@ struct _GskCubicCurve
|
||||
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;
|
||||
GskQuadCurve quad;
|
||||
GskCubicCurve cubic;
|
||||
GskConicCurve conic;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
@@ -88,6 +105,7 @@ typedef gboolean (* GskCurveAddLineFunc) (const graphene_point_t *from,
|
||||
typedef gboolean (* GskCurveAddCurveFunc) (GskPathOperation op,
|
||||
const graphene_point_t *pts,
|
||||
gsize n_pts,
|
||||
float weight,
|
||||
gpointer user_data);
|
||||
|
||||
void gsk_curve_init (GskCurve *curve,
|
||||
@@ -95,7 +113,8 @@ void gsk_curve_init (GskCurve
|
||||
void gsk_curve_init_foreach (GskCurve *curve,
|
||||
GskPathOperation op,
|
||||
const graphene_point_t *pts,
|
||||
gsize n_pts);
|
||||
gsize n_pts,
|
||||
float weight);
|
||||
|
||||
void gsk_curve_print (const GskCurve *curve,
|
||||
GString *string);
|
||||
|
||||
@@ -233,6 +233,7 @@ typedef enum {
|
||||
/**
|
||||
* 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 join point
|
||||
* @GSK_LINE_JOIN_BEVEL: use a cut-off join, the join is cut off at half
|
||||
@@ -240,6 +241,9 @@ typedef enum {
|
||||
*
|
||||
* 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`.
|
||||
*
|
||||
* New entries may be added in future versions.
|
||||
@@ -248,6 +252,7 @@ typedef enum {
|
||||
*/
|
||||
typedef enum {
|
||||
GSK_LINE_JOIN_MITER,
|
||||
GSK_LINE_JOIN_MITER_CLIP,
|
||||
GSK_LINE_JOIN_ROUND,
|
||||
GSK_LINE_JOIN_BEVEL,
|
||||
} GskLineJoin;
|
||||
@@ -265,6 +270,9 @@ typedef enum {
|
||||
* @GSK_PATH_CUBIC: 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 are used to described segments of a `GskPath`.
|
||||
*
|
||||
@@ -278,6 +286,7 @@ typedef enum {
|
||||
GSK_PATH_LINE,
|
||||
GSK_PATH_QUAD,
|
||||
GSK_PATH_CUBIC,
|
||||
GSK_PATH_CONIC,
|
||||
} GskPathOperation;
|
||||
|
||||
/**
|
||||
|
||||
401
gsk/gskpath.c
401
gsk/gskpath.c
@@ -24,7 +24,6 @@
|
||||
#include "gskcurveprivate.h"
|
||||
#include "gskpathbuilder.h"
|
||||
#include "gskpathpointprivate.h"
|
||||
#include "gsksplineprivate.h"
|
||||
|
||||
/**
|
||||
* GskPath:
|
||||
@@ -146,17 +145,14 @@ gsk_path_unref (GskPath *self)
|
||||
}
|
||||
|
||||
const GskContour *
|
||||
gsk_path_get_contour (const GskPath *self,
|
||||
gsize i)
|
||||
gsk_path_get_contour (GskPath *path,
|
||||
gsize i)
|
||||
{
|
||||
if (i < self->n_contours)
|
||||
return self->contours[i];
|
||||
else
|
||||
return NULL;
|
||||
return path->contours[i];
|
||||
}
|
||||
|
||||
GskPathFlags
|
||||
gsk_path_get_flags (const GskPath *self)
|
||||
gsk_path_get_flags (GskPath *self)
|
||||
{
|
||||
return self->flags;
|
||||
}
|
||||
@@ -170,7 +166,10 @@ gsk_path_get_flags (const GskPath *self)
|
||||
* for printing.
|
||||
*
|
||||
* The string is compatible with
|
||||
* [SVG path syntax](https://www.w3.org/TR/SVG11/paths.html#PathData).
|
||||
* [SVG path syntax](https://www.w3.org/TR/SVG11/paths.html#PathData),
|
||||
* with the exception that conic curves will generate a string of the
|
||||
* form "O x1 y1, x2 y2, w" where x1, y1 is the control point, x2, y2
|
||||
* is the end point, and w is the weight.
|
||||
*
|
||||
* Since: 4.14
|
||||
*/
|
||||
@@ -225,6 +224,7 @@ static gboolean
|
||||
gsk_path_to_cairo_add_op (GskPathOperation op,
|
||||
const graphene_point_t *pts,
|
||||
gsize n_pts,
|
||||
float weight,
|
||||
gpointer cr)
|
||||
{
|
||||
switch (op)
|
||||
@@ -256,6 +256,7 @@ gsk_path_to_cairo_add_op (GskPathOperation op,
|
||||
cairo_curve_to (cr, pts[1].x, pts[1].y, pts[2].x, pts[2].y, pts[3].x, pts[3].y);
|
||||
break;
|
||||
|
||||
case GSK_PATH_CONIC:
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
return FALSE;
|
||||
@@ -303,9 +304,9 @@ gsk_path_to_cairo (GskPath *self,
|
||||
* Returns: the number of contours in @path
|
||||
*/
|
||||
gsize
|
||||
gsk_path_get_n_contours (const GskPath *self)
|
||||
gsk_path_get_n_contours (GskPath *path)
|
||||
{
|
||||
return self->n_contours;
|
||||
return path->n_contours;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -378,33 +379,35 @@ gboolean
|
||||
gsk_path_get_bounds (GskPath *self,
|
||||
graphene_rect_t *bounds)
|
||||
{
|
||||
GskBoundingBox b;
|
||||
gsize i;
|
||||
|
||||
g_return_val_if_fail (self != NULL, FALSE);
|
||||
g_return_val_if_fail (bounds != NULL, FALSE);
|
||||
|
||||
if (self->n_contours == 0)
|
||||
for (i = 0; i < self->n_contours; i++)
|
||||
{
|
||||
if (gsk_contour_get_bounds (self->contours[i], bounds))
|
||||
break;
|
||||
}
|
||||
|
||||
if (i >= self->n_contours)
|
||||
{
|
||||
graphene_rect_init_from_rect (bounds, graphene_rect_zero ());
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gsk_contour_get_bounds (self->contours[0], &b);
|
||||
|
||||
for (gsize i = 1; i < self->n_contours; i++)
|
||||
for (i++; i < self->n_contours; i++)
|
||||
{
|
||||
GskBoundingBox tmp;
|
||||
graphene_rect_t tmp;
|
||||
|
||||
gsk_contour_get_bounds (self->contours[i], &tmp);
|
||||
gsk_bounding_box_union (&b, &tmp, &b);
|
||||
if (gsk_contour_get_bounds (self->contours[i], &tmp))
|
||||
graphene_rect_union (bounds, &tmp, bounds);
|
||||
}
|
||||
|
||||
gsk_bounding_box_to_rect (&b, bounds);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
/*< private >
|
||||
* gsk_path_get_stroke_bounds:
|
||||
* @self: a #GtkPath
|
||||
* @stroke: stroke parameters
|
||||
@@ -419,36 +422,38 @@ gsk_path_get_bounds (GskPath *self,
|
||||
* like miters.
|
||||
*
|
||||
* Returns: `TRUE` if the path has bounds, `FALSE` if the path is known
|
||||
* to be empty and have no bounds.
|
||||
* to be empty and have no bounds.
|
||||
*/
|
||||
gboolean
|
||||
gsk_path_get_stroke_bounds (GskPath *self,
|
||||
const GskStroke *stroke,
|
||||
graphene_rect_t *bounds)
|
||||
{
|
||||
GskBoundingBox b;
|
||||
gsize i;
|
||||
|
||||
g_return_val_if_fail (self != NULL, FALSE);
|
||||
g_return_val_if_fail (bounds != NULL, FALSE);
|
||||
|
||||
if (self->n_contours == 0)
|
||||
for (i = 0; i < self->n_contours; i++)
|
||||
{
|
||||
if (gsk_contour_get_stroke_bounds (self->contours[i], stroke, bounds))
|
||||
break;
|
||||
}
|
||||
|
||||
if (i >= self->n_contours)
|
||||
{
|
||||
graphene_rect_init_from_rect (bounds, graphene_rect_zero ());
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gsk_contour_get_stroke_bounds (self->contours[0], stroke, &b);
|
||||
|
||||
for (gsize i = 1; i < self->n_contours; i++)
|
||||
for (i++; i < self->n_contours; i++)
|
||||
{
|
||||
GskBoundingBox tmp;
|
||||
graphene_rect_t tmp;
|
||||
|
||||
if (gsk_contour_get_stroke_bounds (self->contours[i], stroke, &tmp))
|
||||
gsk_bounding_box_union (&b, &tmp, &b);
|
||||
graphene_rect_union (bounds, &tmp, bounds);
|
||||
}
|
||||
|
||||
gsk_bounding_box_to_rect (&b, bounds);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -515,97 +520,27 @@ gsk_path_get_closest_point (GskPath *self,
|
||||
GskPathPoint *result)
|
||||
{
|
||||
GskRealPathPoint *res = (GskRealPathPoint *) result;
|
||||
gboolean found;
|
||||
|
||||
g_return_val_if_fail (self != NULL, FALSE);
|
||||
g_return_val_if_fail (point != NULL, FALSE);
|
||||
g_return_val_if_fail (threshold >= 0, FALSE);
|
||||
g_return_val_if_fail (result != NULL, FALSE);
|
||||
|
||||
found = FALSE;
|
||||
|
||||
for (int i = 0; i < self->n_contours; i++)
|
||||
{
|
||||
float distance;
|
||||
|
||||
if (gsk_contour_get_closest_point (self->contours[i], point, threshold, res, &distance))
|
||||
{
|
||||
found = TRUE;
|
||||
res->contour = i;
|
||||
res->path = self;
|
||||
res->contour = self->contours[i];
|
||||
threshold = distance;
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
if (res->contour != NULL)
|
||||
return TRUE;
|
||||
|
||||
/**
|
||||
* gsk_path_get_start_point:
|
||||
* @self: a `GskPath`
|
||||
* @result: (out caller-allocates): return location for point
|
||||
*
|
||||
* Gets the start point of the path.
|
||||
*
|
||||
* An empty path has no points, so `FALSE`
|
||||
* is returned in this case.
|
||||
*
|
||||
* Returns: `TRUE` if @result was filled
|
||||
*
|
||||
* Since: 4.14
|
||||
*/
|
||||
gboolean
|
||||
gsk_path_get_start_point (GskPath *self,
|
||||
GskPathPoint *result)
|
||||
{
|
||||
GskRealPathPoint *res = (GskRealPathPoint *) result;
|
||||
|
||||
g_return_val_if_fail (self != NULL, FALSE);
|
||||
g_return_val_if_fail (result != NULL, FALSE);
|
||||
|
||||
if (self->n_contours == 0)
|
||||
return FALSE;
|
||||
|
||||
gsk_contour_get_start_point (self->contours[0], res);
|
||||
|
||||
res->path = self;
|
||||
res->contour = 0;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_get_end_point:
|
||||
* @self: a `GskPath`
|
||||
* @result: (out caller-allocates): return location for point
|
||||
*
|
||||
* Gets the end point of the path.
|
||||
*
|
||||
* An empty path has no points, so `FALSE`
|
||||
* is returned in this case.
|
||||
*
|
||||
* Returns: `TRUE` if @result was filled
|
||||
*
|
||||
* Since: 4.14
|
||||
*/
|
||||
gboolean
|
||||
gsk_path_get_end_point (GskPath *self,
|
||||
GskPathPoint *result)
|
||||
{
|
||||
GskRealPathPoint *res = (GskRealPathPoint *) result;
|
||||
|
||||
g_return_val_if_fail (self != NULL, FALSE);
|
||||
g_return_val_if_fail (result!= NULL, FALSE);
|
||||
|
||||
if (self->n_contours == 0)
|
||||
return FALSE;
|
||||
|
||||
gsk_contour_get_end_point (self->contours[self->n_contours - 1], res);
|
||||
|
||||
res->path = self;
|
||||
res->contour = self->n_contours - 1;
|
||||
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -665,6 +600,7 @@ gsk_path_foreach_trampoline_add_line (const graphene_point_t *from,
|
||||
return trampoline->func (GSK_PATH_LINE,
|
||||
(graphene_point_t[2]) { *from, *to },
|
||||
2,
|
||||
0.0f,
|
||||
trampoline->user_data);
|
||||
}
|
||||
|
||||
@@ -672,17 +608,19 @@ static gboolean
|
||||
gsk_path_foreach_trampoline_add_curve (GskPathOperation op,
|
||||
const graphene_point_t *pts,
|
||||
gsize n_pts,
|
||||
float weight,
|
||||
gpointer data)
|
||||
{
|
||||
GskPathForeachTrampoline *trampoline = data;
|
||||
|
||||
return trampoline->func (op, pts, n_pts, trampoline->user_data);
|
||||
return trampoline->func (op, pts, n_pts, weight, trampoline->user_data);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gsk_path_foreach_trampoline (GskPathOperation op,
|
||||
const graphene_point_t *pts,
|
||||
gsize n_pts,
|
||||
float weight,
|
||||
gpointer data)
|
||||
{
|
||||
GskPathForeachTrampoline *trampoline = data;
|
||||
@@ -692,28 +630,14 @@ gsk_path_foreach_trampoline (GskPathOperation op,
|
||||
case GSK_PATH_MOVE:
|
||||
case GSK_PATH_CLOSE:
|
||||
case GSK_PATH_LINE:
|
||||
return trampoline->func (op, pts, n_pts, trampoline->user_data);
|
||||
return trampoline->func (op, pts, n_pts, weight, trampoline->user_data);
|
||||
|
||||
case GSK_PATH_QUAD:
|
||||
{
|
||||
GskCurve curve;
|
||||
|
||||
if (trampoline->flags & GSK_PATH_FOREACH_ALLOW_QUAD)
|
||||
return trampoline->func (op, pts, n_pts, trampoline->user_data);
|
||||
else if (trampoline->flags & GSK_PATH_FOREACH_ALLOW_CUBIC)
|
||||
{
|
||||
return trampoline->func (GSK_PATH_CUBIC,
|
||||
(graphene_point_t[4]) {
|
||||
pts[0],
|
||||
GRAPHENE_POINT_INIT ((pts[0].x + 2 * pts[1].x) / 3,
|
||||
(pts[0].y + 2 * pts[1].y) / 3),
|
||||
GRAPHENE_POINT_INIT ((pts[2].x + 2 * pts[1].x) / 3,
|
||||
(pts[2].y + 2 * pts[1].y) / 3),
|
||||
pts[2],
|
||||
},
|
||||
4,
|
||||
trampoline->user_data);
|
||||
}
|
||||
return trampoline->func (op, pts, n_pts, weight, trampoline->user_data);
|
||||
|
||||
gsk_curve_init (&curve, gsk_pathop_encode (GSK_PATH_QUAD, pts));
|
||||
return gsk_curve_decompose (&curve,
|
||||
@@ -727,10 +651,10 @@ gsk_path_foreach_trampoline (GskPathOperation op,
|
||||
GskCurve curve;
|
||||
|
||||
if (trampoline->flags & GSK_PATH_FOREACH_ALLOW_CUBIC)
|
||||
return trampoline->func (op, pts, n_pts, trampoline->user_data);
|
||||
return trampoline->func (op, pts, n_pts, weight, trampoline->user_data);
|
||||
|
||||
gsk_curve_init (&curve, gsk_pathop_encode (GSK_PATH_CUBIC, pts));
|
||||
if (trampoline->flags & GSK_PATH_FOREACH_ALLOW_QUAD)
|
||||
if (trampoline->flags & (GSK_PATH_FOREACH_ALLOW_QUAD|GSK_PATH_FOREACH_ALLOW_CONIC))
|
||||
return gsk_curve_decompose_curve (&curve,
|
||||
trampoline->flags,
|
||||
trampoline->tolerance,
|
||||
@@ -743,6 +667,28 @@ gsk_path_foreach_trampoline (GskPathOperation op,
|
||||
trampoline);
|
||||
}
|
||||
|
||||
case GSK_PATH_CONIC:
|
||||
{
|
||||
GskCurve curve;
|
||||
|
||||
if (trampoline->flags & GSK_PATH_FOREACH_ALLOW_CONIC)
|
||||
return trampoline->func (op, pts, n_pts, weight, trampoline->user_data);
|
||||
|
||||
gsk_curve_init (&curve, gsk_pathop_encode (GSK_PATH_CONIC, (graphene_point_t[4]) { pts[0], pts[1], { weight, }, pts[2] } ));
|
||||
if (trampoline->flags & (GSK_PATH_FOREACH_ALLOW_QUAD|GSK_PATH_FOREACH_ALLOW_CUBIC))
|
||||
return gsk_curve_decompose_curve (&curve,
|
||||
trampoline->flags,
|
||||
trampoline->tolerance,
|
||||
gsk_path_foreach_trampoline_add_curve,
|
||||
trampoline);
|
||||
|
||||
return gsk_curve_decompose (&curve,
|
||||
trampoline->tolerance,
|
||||
gsk_path_foreach_trampoline_add_line,
|
||||
trampoline);
|
||||
}
|
||||
|
||||
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
return FALSE;
|
||||
@@ -750,7 +696,8 @@ gsk_path_foreach_trampoline (GskPathOperation op,
|
||||
}
|
||||
|
||||
#define ALLOW_ANY (GSK_PATH_FOREACH_ALLOW_QUAD| \
|
||||
GSK_PATH_FOREACH_ALLOW_CUBIC)
|
||||
GSK_PATH_FOREACH_ALLOW_CUBIC| \
|
||||
GSK_PATH_FOREACH_ALLOW_CONIC)
|
||||
|
||||
gboolean
|
||||
gsk_path_foreach_with_tolerance (GskPath *self,
|
||||
@@ -1334,6 +1281,37 @@ gsk_path_parse (const char *string)
|
||||
}
|
||||
break;
|
||||
|
||||
case 'O':
|
||||
case 'o':
|
||||
{
|
||||
double x1, y1, x2, y2, weight;
|
||||
|
||||
if (parse_coordinate_pair (&p, &x1, &y1) &&
|
||||
parse_coordinate_pair (&p, &x2, &y2) &&
|
||||
parse_nonnegative_number (&p, &weight))
|
||||
{
|
||||
if (cmd == 'c')
|
||||
{
|
||||
x1 += x;
|
||||
y1 += y;
|
||||
x2 += x;
|
||||
y2 += y;
|
||||
}
|
||||
if (_strchr ("zZ", prev_cmd))
|
||||
{
|
||||
gsk_path_builder_move_to (builder, x, y);
|
||||
path_x = x;
|
||||
path_y = y;
|
||||
}
|
||||
gsk_path_builder_conic_to (builder, x1, y1, x2, y2, weight);
|
||||
x = x2;
|
||||
y = y2;
|
||||
}
|
||||
else
|
||||
goto error;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'A':
|
||||
case 'a':
|
||||
{
|
||||
@@ -1391,174 +1369,3 @@ error:
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_builder_add_segment:
|
||||
* @self: a `GskPathBuilder`
|
||||
* @path: the `GskPath` to take the segment to
|
||||
* @start: the point on @path to start at
|
||||
* @end: the point on @path to end at
|
||||
*
|
||||
* Adds to @self the segment of @path from @start to @end.
|
||||
*
|
||||
* If @start is after @end, 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.
|
||||
*
|
||||
* Since: 4.14
|
||||
*/
|
||||
void
|
||||
gsk_path_builder_add_segment (GskPathBuilder *self,
|
||||
GskPath *path,
|
||||
const GskPathPoint *start,
|
||||
const GskPathPoint *end)
|
||||
{
|
||||
GskRealPathPoint *s = (GskRealPathPoint *) start;
|
||||
GskRealPathPoint *e = (GskRealPathPoint *) end;
|
||||
const GskContour *contour;
|
||||
|
||||
g_return_if_fail (self != NULL);
|
||||
g_return_if_fail (path != NULL);
|
||||
g_return_if_fail (path == s->path);
|
||||
g_return_if_fail (path == e->path);
|
||||
|
||||
contour = gsk_path_get_contour (path, s->contour);
|
||||
|
||||
if (s->contour == e->contour)
|
||||
{
|
||||
if (gsk_contour_point_compare (contour, s, e) < 0)
|
||||
{
|
||||
gsk_contour_add_segment (contour, self, TRUE, s, e);
|
||||
return;
|
||||
}
|
||||
else if (path->n_contours == 1)
|
||||
{
|
||||
gsk_contour_add_segment (contour, self, TRUE, s, NULL);
|
||||
gsk_contour_add_segment (contour, self, FALSE, NULL, e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
gsk_contour_add_segment (contour, self, TRUE, s, NULL);
|
||||
|
||||
for (gsize i = (s->contour + 1) % path->n_contours; i != e->contour; i = (i + 1) % path->n_contours)
|
||||
gsk_path_builder_add_contour (self, gsk_contour_dup (gsk_path_get_contour (path, i)));
|
||||
|
||||
contour = gsk_path_get_contour (path, e->contour);
|
||||
gsk_contour_add_segment (contour, self, FALSE, NULL, e);
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_get_previous_point:
|
||||
* @self: a `GskPath`
|
||||
* @point: a point on @self
|
||||
* @result: (out caller-allocates): Return location for the result
|
||||
*
|
||||
* Gets the previous 'significant' point on @self before @point.
|
||||
*
|
||||
* The 'significant' points of a path are typically the
|
||||
* on-curve points that have been specified when the
|
||||
* path was created.
|
||||
*
|
||||
* For example, in a path with 3 Bézier segments, the
|
||||
* significant points are the start of the first segment,
|
||||
* the start point of the second segment (which coincides
|
||||
* with the end point of the first segment), the start
|
||||
* point of the third segment, and the end point of the
|
||||
* last segment.
|
||||
*
|
||||
* If @point is the start point of the path, there is no
|
||||
* prior point, and this function returns `FALSE`.
|
||||
*
|
||||
* Returns: `TRUE` if @result has been set to a point
|
||||
*/
|
||||
gboolean
|
||||
gsk_path_get_previous_point (GskPath *self,
|
||||
const GskPathPoint *point,
|
||||
GskPathPoint *result)
|
||||
{
|
||||
GskRealPathPoint *p = (GskRealPathPoint *) point;
|
||||
GskRealPathPoint *res = (GskRealPathPoint *) result;
|
||||
const GskContour *contour;
|
||||
|
||||
g_return_val_if_fail (self != NULL, FALSE);
|
||||
g_return_val_if_fail (self == p->path, FALSE);
|
||||
|
||||
contour = gsk_path_get_contour (self, p->contour);
|
||||
|
||||
if (gsk_contour_get_previous_point (contour, p, res))
|
||||
{
|
||||
res->path = self;
|
||||
res->contour = p->contour;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (p->contour > 0)
|
||||
{
|
||||
contour = gsk_path_get_contour (self, p->contour - 1);
|
||||
gsk_contour_get_end_point (contour, res);
|
||||
res->path = self;
|
||||
res->contour = p->contour - 1;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_get_next_point:
|
||||
* @self: a `GskPath`
|
||||
* @point: a point on @self
|
||||
* @result: (out caller-allocates): Return location for the result
|
||||
*
|
||||
* Gets the next 'significant' point on @self after @point.
|
||||
*
|
||||
* The 'significant' points of a path are typically the
|
||||
* on-curve points that have been specified when the
|
||||
* path was created.
|
||||
*
|
||||
* For example, in a path with 3 Bézier segments, the
|
||||
* significant points are the start of the first segment,
|
||||
* the start point of the second segment (which coincides
|
||||
* with the end point of the first segment), the start
|
||||
* point of the third segment, and the end point of the
|
||||
* last segment.
|
||||
*
|
||||
* If @point is the end point of the path, there is no
|
||||
* next point, and this function returns `FALSE`.
|
||||
*
|
||||
* Returns: `TRUE` if @result has been set to a point
|
||||
*/
|
||||
gboolean
|
||||
gsk_path_get_next_point (GskPath *self,
|
||||
const GskPathPoint *point,
|
||||
GskPathPoint *result)
|
||||
{
|
||||
GskRealPathPoint *p = (GskRealPathPoint *) point;
|
||||
GskRealPathPoint *res = (GskRealPathPoint *) result;
|
||||
const GskContour *contour;
|
||||
|
||||
g_return_val_if_fail (self != NULL, FALSE);
|
||||
g_return_val_if_fail (self == p->path, FALSE);
|
||||
|
||||
contour = gsk_path_get_contour (self, p->contour);
|
||||
|
||||
if (gsk_contour_get_next_point (contour, p, res))
|
||||
{
|
||||
res->path = self;
|
||||
res->contour = p->contour;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (p->contour < self->n_contours - 1)
|
||||
{
|
||||
contour = gsk_path_get_contour (self, p->contour + 1);
|
||||
gsk_contour_get_start_point (contour, res);
|
||||
res->path = self;
|
||||
res->contour = p->contour + 1;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@@ -30,10 +30,9 @@ G_BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* GskPathForeachFlags:
|
||||
* @GSK_PATH_FOREACH_ALLOW_ONLY_LINES: The default behavior, only allow lines.
|
||||
* @GSK_PATH_FOREACH_ALLOW_QUAD: Allow emission of `GSK_PATH_QUAD` operations
|
||||
* @GSK_PATH_FOREACH_ALLOW_CUBIC: Allow emission of `GSK_PATH_CUBIC` operations.
|
||||
* @GSK_PATH_FOREACH_ALLOW_ANY: Allow emission of any kind of operation.
|
||||
* @GSK_PATH_FOREACH_ALLOW_CONIC: Allow emission of `GSK_PATH_CONIC` operations.
|
||||
*
|
||||
* Flags that can be passed to gsk_path_foreach() to enable additional
|
||||
* features.
|
||||
@@ -44,9 +43,9 @@ G_BEGIN_DECLS
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
GSK_PATH_FOREACH_ALLOW_ONLY_LINES = 0,
|
||||
GSK_PATH_FOREACH_ALLOW_QUAD = (1 << 0),
|
||||
GSK_PATH_FOREACH_ALLOW_CUBIC = (1 << 1),
|
||||
GSK_PATH_FOREACH_ALLOW_QUAD = (1 << 0),
|
||||
GSK_PATH_FOREACH_ALLOW_CUBIC = (1 << 1),
|
||||
GSK_PATH_FOREACH_ALLOW_CONIC = (1 << 2),
|
||||
} GskPathForeachFlags;
|
||||
|
||||
/**
|
||||
@@ -54,17 +53,19 @@ typedef enum
|
||||
* @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.
|
||||
* 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 ())
|
||||
@@ -100,11 +101,6 @@ GDK_AVAILABLE_IN_4_14
|
||||
gboolean gsk_path_get_bounds (GskPath *self,
|
||||
graphene_rect_t *bounds);
|
||||
|
||||
GDK_AVAILABLE_IN_4_14
|
||||
gboolean gsk_path_get_stroke_bounds (GskPath *self,
|
||||
const GskStroke *stroke,
|
||||
graphene_rect_t *bounds);
|
||||
|
||||
GDK_AVAILABLE_IN_4_14
|
||||
gboolean gsk_path_in_fill (GskPath *self,
|
||||
const graphene_point_t *point,
|
||||
@@ -116,29 +112,19 @@ gboolean gsk_path_get_closest_point (GskPath
|
||||
float threshold,
|
||||
GskPathPoint *result);
|
||||
|
||||
GDK_AVAILABLE_IN_4_14
|
||||
gboolean gsk_path_get_previous_point (GskPath *self,
|
||||
const GskPathPoint *point,
|
||||
GskPathPoint *result);
|
||||
|
||||
GDK_AVAILABLE_IN_4_14
|
||||
gboolean gsk_path_get_next_point (GskPath *self,
|
||||
const GskPathPoint *point,
|
||||
GskPathPoint *result);
|
||||
|
||||
GDK_AVAILABLE_IN_4_14
|
||||
gboolean gsk_path_get_start_point (GskPath *self,
|
||||
GskPathPoint *result);
|
||||
GDK_AVAILABLE_IN_4_14
|
||||
gboolean gsk_path_get_end_point (GskPath *self,
|
||||
GskPathPoint *result);
|
||||
|
||||
GDK_AVAILABLE_IN_4_14
|
||||
gboolean gsk_path_foreach (GskPath *self,
|
||||
GskPathForeachFlags flags,
|
||||
GskPathForeachFunc func,
|
||||
gpointer user_data);
|
||||
|
||||
GDK_AVAILABLE_IN_4_14
|
||||
gboolean gsk_path_dash (GskPath *self,
|
||||
GskStroke *stroke,
|
||||
float tolerance,
|
||||
GskPathForeachFunc func,
|
||||
gpointer user_data);
|
||||
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GskPath, gsk_path_unref)
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
|
||||
#include "gskpathprivate.h"
|
||||
#include "gskcontourprivate.h"
|
||||
#include "gsksplineprivate.h"
|
||||
|
||||
/**
|
||||
* GskPathBuilder:
|
||||
@@ -310,8 +309,6 @@ gsk_path_builder_add_contour (GskPathBuilder *self,
|
||||
* drawing commands and updated after every operation.
|
||||
*
|
||||
* When the builder is created, the default current point is set to (0, 0).
|
||||
* Note that this is different from cairo, which starts out without
|
||||
* a current point.
|
||||
*
|
||||
* Returns: (transfer none): The current point
|
||||
*
|
||||
@@ -442,29 +439,36 @@ void
|
||||
gsk_path_builder_add_rect (GskPathBuilder *self,
|
||||
const graphene_rect_t *rect)
|
||||
{
|
||||
GskContour *contour;
|
||||
|
||||
g_return_if_fail (self != NULL);
|
||||
|
||||
gsk_path_builder_move_to (self, rect->origin.x, rect->origin.y);
|
||||
contour = gsk_rect_contour_new (rect);
|
||||
gsk_path_builder_add_contour (self, contour);
|
||||
|
||||
gsk_path_builder_rel_line_to (self, rect->size.width, 0);
|
||||
gsk_path_builder_rel_line_to (self, 0, rect->size.height);
|
||||
gsk_path_builder_rel_line_to (self, - rect->size.width, 0);
|
||||
|
||||
gsk_path_builder_close (self);
|
||||
gsk_contour_get_start_end (contour, NULL, &self->current_point);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
circle_contour_curve (const graphene_point_t pts[4],
|
||||
gpointer data)
|
||||
/**
|
||||
* gsk_path_builder_add_rounded_rect:
|
||||
* @self: a #GskPathBuilder
|
||||
* @rect: the rounded rect
|
||||
*
|
||||
* Adds @rect as a new contour to the path built in @self.
|
||||
*
|
||||
* Since: 4.14
|
||||
*/
|
||||
void
|
||||
gsk_path_builder_add_rounded_rect (GskPathBuilder *self,
|
||||
const GskRoundedRect *rect)
|
||||
{
|
||||
GskPathBuilder *self = data;
|
||||
GskContour *contour;
|
||||
|
||||
gsk_path_builder_cubic_to (self,
|
||||
pts[1].x, pts[1].y,
|
||||
pts[2].x, pts[2].y,
|
||||
pts[3].x, pts[3].y);
|
||||
g_return_if_fail (self != NULL);
|
||||
g_return_if_fail (rect != NULL);
|
||||
|
||||
return TRUE;
|
||||
contour = gsk_rounded_rect_contour_new (rect);
|
||||
gsk_path_builder_add_contour (self, contour);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -482,15 +486,57 @@ gsk_path_builder_add_circle (GskPathBuilder *self,
|
||||
const graphene_point_t *center,
|
||||
float radius)
|
||||
{
|
||||
GskContour *contour;
|
||||
|
||||
g_return_if_fail (self != NULL);
|
||||
g_return_if_fail (center != NULL);
|
||||
g_return_if_fail (radius > 0);
|
||||
|
||||
gsk_path_builder_move_to (self, center->x + radius, center->y);
|
||||
gsk_spline_decompose_arc (center, radius,
|
||||
GSK_PATH_TOLERANCE_DEFAULT,
|
||||
0, 2 * M_PI,
|
||||
circle_contour_curve, self);
|
||||
contour = gsk_circle_contour_new (center, radius, 0, 360);
|
||||
gsk_path_builder_add_contour (self, contour);
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_builder_add_ellipse:
|
||||
* @self: 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.
|
||||
*
|
||||
* Since: 4.14
|
||||
*/
|
||||
void
|
||||
gsk_path_builder_add_ellipse (GskPathBuilder *self,
|
||||
const graphene_point_t *center,
|
||||
const graphene_size_t *radius)
|
||||
{
|
||||
const float weight = M_SQRT1_2;
|
||||
graphene_point_t pts[8];
|
||||
float w, h;
|
||||
|
||||
g_return_if_fail (self != NULL);
|
||||
g_return_if_fail (center != NULL);
|
||||
g_return_if_fail (radius != NULL);
|
||||
|
||||
w = radius->width / 2;
|
||||
h = radius->height / 2;
|
||||
|
||||
pts[0] = GRAPHENE_POINT_INIT (center->x + w, center->y);
|
||||
pts[1] = GRAPHENE_POINT_INIT (center->x + w, center->y + h);
|
||||
pts[2] = GRAPHENE_POINT_INIT (center->x, center->y + h);
|
||||
pts[3] = GRAPHENE_POINT_INIT (center->x - w, center->y + h);
|
||||
pts[4] = GRAPHENE_POINT_INIT (center->x - w, center->y);
|
||||
pts[5] = GRAPHENE_POINT_INIT (center->x - w, center->y - h);
|
||||
pts[6] = GRAPHENE_POINT_INIT (center->x, center->y - h);
|
||||
pts[7] = GRAPHENE_POINT_INIT (center->x + w, center->y - h);
|
||||
|
||||
gsk_path_builder_move_to (self, pts[0].x, pts[0].y);
|
||||
gsk_path_builder_conic_to (self, pts[1].x, pts[1].y, pts[2].x, pts[2].y, weight);
|
||||
gsk_path_builder_conic_to (self, pts[3].x, pts[3].y, pts[4].x, pts[4].y, weight);
|
||||
gsk_path_builder_conic_to (self, pts[5].x, pts[5].y, pts[6].x, pts[6].y, weight);
|
||||
gsk_path_builder_conic_to (self, pts[7].x, pts[7].y, pts[0].x, pts[0].y, weight);
|
||||
gsk_path_builder_close (self);
|
||||
}
|
||||
|
||||
@@ -745,6 +791,82 @@ gsk_path_builder_rel_cubic_to (GskPathBuilder *self,
|
||||
self->current_point.y + y3);
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_builder_conic_to:
|
||||
* @self: 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.
|
||||
*
|
||||
* Since: 4.14
|
||||
*/
|
||||
void
|
||||
gsk_path_builder_conic_to (GskPathBuilder *self,
|
||||
float x1,
|
||||
float y1,
|
||||
float x2,
|
||||
float y2,
|
||||
float weight)
|
||||
{
|
||||
g_return_if_fail (self != NULL);
|
||||
g_return_if_fail (weight >= 0);
|
||||
|
||||
self->flags &= ~GSK_PATH_FLAT;
|
||||
gsk_path_builder_append_current (self,
|
||||
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:
|
||||
* @self: 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].
|
||||
*
|
||||
* Since: 4.14
|
||||
*/
|
||||
void
|
||||
gsk_path_builder_rel_conic_to (GskPathBuilder *self,
|
||||
float x1,
|
||||
float y1,
|
||||
float x2,
|
||||
float y2,
|
||||
float weight)
|
||||
{
|
||||
g_return_if_fail (self != NULL);
|
||||
g_return_if_fail (weight >= 0);
|
||||
|
||||
gsk_path_builder_conic_to (self,
|
||||
self->current_point.x + x1,
|
||||
self->current_point.y + y1,
|
||||
self->current_point.x + x2,
|
||||
self->current_point.y + y2,
|
||||
weight);
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_builder_close:
|
||||
* @self: a `GskPathBuilder`
|
||||
|
||||
@@ -60,19 +60,27 @@ void gsk_path_builder_add_cairo_path (GskPathBuilder
|
||||
GDK_AVAILABLE_IN_4_14
|
||||
void gsk_path_builder_add_layout (GskPathBuilder *self,
|
||||
PangoLayout *layout);
|
||||
|
||||
GDK_AVAILABLE_IN_4_14
|
||||
void gsk_path_builder_add_rect (GskPathBuilder *self,
|
||||
const graphene_rect_t *rect);
|
||||
GDK_AVAILABLE_IN_4_14
|
||||
void gsk_path_builder_add_rounded_rect (GskPathBuilder *self,
|
||||
const GskRoundedRect *rect);
|
||||
GDK_AVAILABLE_IN_4_14
|
||||
void gsk_path_builder_add_circle (GskPathBuilder *self,
|
||||
const graphene_point_t *center,
|
||||
float radius);
|
||||
GDK_AVAILABLE_IN_4_14
|
||||
void gsk_path_builder_add_ellipse (GskPathBuilder *self,
|
||||
const graphene_point_t *center,
|
||||
const graphene_size_t *radius);
|
||||
|
||||
GDK_AVAILABLE_IN_4_14
|
||||
void gsk_path_builder_add_segment (GskPathBuilder *self,
|
||||
GskPath *path,
|
||||
const GskPathPoint *start,
|
||||
const GskPathPoint *end);
|
||||
GskPathMeasure *measure,
|
||||
float start,
|
||||
float end);
|
||||
|
||||
GDK_AVAILABLE_IN_4_14
|
||||
void gsk_path_builder_move_to (GskPathBuilder *self,
|
||||
float x,
|
||||
@@ -118,6 +126,20 @@ void gsk_path_builder_rel_cubic_to (GskPathBuilder
|
||||
float x3,
|
||||
float y3);
|
||||
GDK_AVAILABLE_IN_4_14
|
||||
void gsk_path_builder_conic_to (GskPathBuilder *self,
|
||||
float x1,
|
||||
float y1,
|
||||
float x2,
|
||||
float y2,
|
||||
float weight);
|
||||
GDK_AVAILABLE_IN_4_14
|
||||
void gsk_path_builder_rel_conic_to (GskPathBuilder *self,
|
||||
float x1,
|
||||
float y1,
|
||||
float x2,
|
||||
float y2,
|
||||
float weight);
|
||||
GDK_AVAILABLE_IN_4_14
|
||||
void gsk_path_builder_close (GskPathBuilder *self);
|
||||
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GskPathBuilder, gsk_path_builder_unref)
|
||||
|
||||
306
gsk/gskpathdash.c
Normal file
306
gsk/gskpathdash.c
Normal file
@@ -0,0 +1,306 @@
|
||||
/*
|
||||
* 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 "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,
|
||||
GskCurveLineReason reason,
|
||||
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_QUAD:
|
||||
case GSK_PATH_CUBIC:
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_dash:
|
||||
* @self: the `GskPath` to dash
|
||||
* @stroke: the stroke containing the dash parameters
|
||||
* @tolerance: tolerance to use while dashing
|
||||
* @func: (scope call) (closure user_data): the function to call for operations
|
||||
* @user_data: (nullable): user data passed to @func
|
||||
*
|
||||
* Calls @func for every operation of the path that is the result
|
||||
* of dashing @self with the dash pattern from @stroke.
|
||||
*
|
||||
* Returns: `FALSE` if @func returned FALSE`, `TRUE` otherwise.
|
||||
*
|
||||
* Since: 4.14
|
||||
*/
|
||||
gboolean
|
||||
gsk_path_dash (GskPath *self,
|
||||
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 (self, -1, func, user_data);
|
||||
|
||||
for (i = 0; i < gsk_path_get_n_contours (self); i++)
|
||||
{
|
||||
if (!gsk_contour_dash (gsk_path_get_contour (self, i), stroke, tolerance, func, user_data))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -236,6 +236,104 @@ gsk_path_measure_clamp_distance (GskPathMeasure *self,
|
||||
return CLAMP (distance, 0, self->length);
|
||||
}
|
||||
|
||||
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 = 0; i < measure->n_contours; 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:
|
||||
* @self: a `GskPathBuilder`
|
||||
* @measure: the `GskPathMeasure` to take the segment to
|
||||
* @start: start distance into the path
|
||||
* @end: end distance into the path
|
||||
*
|
||||
* Adds to @self 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 its length for the end
|
||||
* of the path. The values will be clamped to that range. The length
|
||||
* can be obtained with [method@Gsk.PathMeasure.get_length].
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Since: 4.14
|
||||
*/
|
||||
void
|
||||
gsk_path_builder_add_segment (GskPathBuilder *self,
|
||||
GskPathMeasure *measure,
|
||||
float start,
|
||||
float end)
|
||||
{
|
||||
g_return_if_fail (self != 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 (self, measure, TRUE, start, end);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* If the path is closed, we can connect the 2 subpaths. */
|
||||
gboolean closed = gsk_path_is_closed (measure->path);
|
||||
gboolean need_move_to = !closed;
|
||||
|
||||
if (start < measure->length)
|
||||
gsk_path_builder_add_segment_chunk (self, measure,
|
||||
TRUE,
|
||||
start, measure->length);
|
||||
else
|
||||
need_move_to = TRUE;
|
||||
|
||||
if (end > 0)
|
||||
gsk_path_builder_add_segment_chunk (self, measure,
|
||||
need_move_to,
|
||||
0, end);
|
||||
if (start == end && closed)
|
||||
gsk_path_builder_close (self);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_measure_get_point:
|
||||
* @self: a `GskPathMeasure`
|
||||
@@ -284,44 +382,29 @@ gsk_path_measure_get_point (GskPathMeasure *self,
|
||||
|
||||
gsk_contour_get_point (contour, self->measures[i].contour_data, offset, res);
|
||||
|
||||
res->path = self->path;
|
||||
res->contour = i;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_point_get_distance:
|
||||
* @measure: a `GskPathMeasure`
|
||||
* @point: a `GskPathPoint on the path of @self
|
||||
*
|
||||
* Returns the distance from the beginning of the path
|
||||
* to @point.
|
||||
*
|
||||
* Returns: the distance of @point
|
||||
*
|
||||
* Since: 4.14
|
||||
*/
|
||||
float
|
||||
gsk_path_point_get_distance (GskPathMeasure *measure,
|
||||
const GskPathPoint *point)
|
||||
gsk_path_measure_get_distance (GskPathMeasure *self,
|
||||
GskPathPoint *point)
|
||||
{
|
||||
GskRealPathPoint *p = (GskRealPathPoint *)point;
|
||||
const GskContour *contour = gsk_path_get_contour (measure->path, p->contour);
|
||||
const GskContour *contour = p->contour;
|
||||
float contour_offset = 0;
|
||||
|
||||
g_return_val_if_fail (measure != NULL, 0);
|
||||
g_return_val_if_fail (measure->path == p->path, 0);
|
||||
g_return_val_if_fail (contour != NULL, 0);
|
||||
g_return_val_if_fail (self != NULL, 0);
|
||||
g_return_val_if_fail (point != NULL, 0);
|
||||
g_return_val_if_fail (self->path == p->path, 0);
|
||||
|
||||
for (gsize i = 0; i < measure->n_contours; i++)
|
||||
for (gsize i = 0; i < self->n_contours; i++)
|
||||
{
|
||||
if (contour == gsk_path_get_contour (measure->path, i))
|
||||
if (contour == gsk_path_get_contour (self->path, i))
|
||||
return contour_offset + gsk_contour_get_distance (contour,
|
||||
p,
|
||||
measure->measures[i].contour_data);
|
||||
self->measures[i].contour_data);
|
||||
|
||||
contour_offset += measure->measures[i].length;
|
||||
contour_offset += self->measures[i].length;
|
||||
}
|
||||
|
||||
g_return_val_if_reached (0);
|
||||
|
||||
@@ -55,11 +55,11 @@ float gsk_path_measure_get_length (GskPathMeasure
|
||||
GDK_AVAILABLE_IN_4_14
|
||||
gboolean gsk_path_measure_get_point (GskPathMeasure *self,
|
||||
float distance,
|
||||
GskPathPoint *result);
|
||||
GskPathPoint *point);
|
||||
|
||||
GDK_AVAILABLE_IN_4_14
|
||||
float gsk_path_measure_get_distance (GskPathMeasure *self,
|
||||
const GskPathPoint *point);
|
||||
GskPathPoint *point);
|
||||
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GskPathMeasure, gsk_path_measure_unref)
|
||||
|
||||
|
||||
@@ -82,17 +82,23 @@ gsk_pathop_foreach (gskpathop pop,
|
||||
switch (gsk_pathop_op (pop))
|
||||
{
|
||||
case GSK_PATH_MOVE:
|
||||
return func (gsk_pathop_op (pop), gsk_pathop_points (pop), 1, user_data);
|
||||
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, user_data);
|
||||
return func (gsk_pathop_op (pop), gsk_pathop_points (pop), 2, 0, user_data);
|
||||
|
||||
case GSK_PATH_QUAD:
|
||||
return func (gsk_pathop_op (pop), gsk_pathop_points (pop), 3, user_data);
|
||||
return func (gsk_pathop_op (pop), gsk_pathop_points (pop), 3, 0, user_data);
|
||||
|
||||
case GSK_PATH_CUBIC:
|
||||
return func (gsk_pathop_op (pop), gsk_pathop_points (pop), 4, user_data);
|
||||
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 ();
|
||||
@@ -128,6 +134,10 @@ gsk_path_builder_pathop_to (GskPathBuilder *builder,
|
||||
gsk_path_builder_cubic_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;
|
||||
@@ -162,6 +172,10 @@ gsk_path_builder_pathop_reverse_to (GskPathBuilder *builder,
|
||||
gsk_path_builder_cubic_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;
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
|
||||
#include "gdk/gdkprivate.h"
|
||||
|
||||
|
||||
/**
|
||||
* GskPathPoint:
|
||||
*
|
||||
@@ -66,10 +67,25 @@ gsk_path_point_free (GskPathPoint *point)
|
||||
g_free (point);
|
||||
}
|
||||
|
||||
GskPath *
|
||||
gsk_path_point_get_path (GskPathPoint *point)
|
||||
{
|
||||
GskRealPathPoint *self = (GskRealPathPoint *) point;
|
||||
|
||||
return self->path;
|
||||
}
|
||||
|
||||
const GskContour *
|
||||
gsk_path_point_get_contour (GskPathPoint *point)
|
||||
{
|
||||
GskRealPathPoint *self = (GskRealPathPoint *) point;
|
||||
|
||||
return self->contour;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_point_get_position:
|
||||
* @path: a `GskPath`
|
||||
* @point: a `GskPathPoint` on @path
|
||||
* @point: a `GskPathPoint`
|
||||
* @position: (out caller-allocates): Return location for
|
||||
* the coordinates of the point
|
||||
*
|
||||
@@ -78,23 +94,17 @@ gsk_path_point_free (GskPathPoint *point)
|
||||
* Since: 4.14
|
||||
*/
|
||||
void
|
||||
gsk_path_point_get_position (GskPath *path,
|
||||
const GskPathPoint *point,
|
||||
graphene_point_t *position)
|
||||
gsk_path_point_get_position (GskPathPoint *point,
|
||||
graphene_point_t *position)
|
||||
{
|
||||
GskRealPathPoint *self = (GskRealPathPoint *) point;
|
||||
const GskContour *contour = gsk_path_get_contour (path, self->contour);
|
||||
|
||||
g_return_if_fail (path == self->path);
|
||||
g_return_if_fail (contour != NULL);
|
||||
|
||||
gsk_contour_get_position (contour, self, position);
|
||||
gsk_contour_get_position (self->contour, self, position);
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_point_get_tangent:
|
||||
* @path: a `GskPath`
|
||||
* @point: a `GskPathPoint` on @path
|
||||
* @point: a `GskPathPoint`
|
||||
* @direction: the direction for which to return the tangent
|
||||
* @tangent: (out caller-allocates): Return location for
|
||||
* the tangent at the point
|
||||
@@ -110,24 +120,18 @@ gsk_path_point_get_position (GskPath *path,
|
||||
* Since: 4.14
|
||||
*/
|
||||
void
|
||||
gsk_path_point_get_tangent (GskPath *path,
|
||||
const GskPathPoint *point,
|
||||
GskPathDirection direction,
|
||||
graphene_vec2_t *tangent)
|
||||
gsk_path_point_get_tangent (GskPathPoint *point,
|
||||
GskPathDirection direction,
|
||||
graphene_vec2_t *tangent)
|
||||
{
|
||||
GskRealPathPoint *self = (GskRealPathPoint *) point;
|
||||
const GskContour *contour = gsk_path_get_contour (path, self->contour);
|
||||
|
||||
g_return_if_fail (path == self->path);
|
||||
g_return_if_fail (contour != NULL);
|
||||
|
||||
gsk_contour_get_tangent (contour, self, direction, tangent);
|
||||
gsk_contour_get_tangent (self->contour, self, direction, tangent);
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_point_get_curvature:
|
||||
* @path: a `GskPath`
|
||||
* @point: a `GskPathPoint` on @path
|
||||
* @point: a `GskPathPoint`
|
||||
* @center: (out caller-allocates): Return location for
|
||||
* the center of the osculating circle
|
||||
*
|
||||
@@ -143,15 +147,19 @@ gsk_path_point_get_tangent (GskPath *path,
|
||||
* Since: 4.14
|
||||
*/
|
||||
float
|
||||
gsk_path_point_get_curvature (GskPath *path,
|
||||
const GskPathPoint *point,
|
||||
graphene_point_t *center)
|
||||
gsk_path_point_get_curvature (GskPathPoint *point,
|
||||
graphene_point_t *center)
|
||||
{
|
||||
GskRealPathPoint *self = (GskRealPathPoint *) point;
|
||||
const GskContour *contour = gsk_path_get_contour (path, self->contour);
|
||||
|
||||
g_return_val_if_fail (path == self->path, 0);
|
||||
g_return_val_if_fail (contour != NULL, 0);
|
||||
|
||||
return gsk_contour_get_curvature (contour, self, center);
|
||||
return gsk_contour_get_curvature (self->contour, self, center);
|
||||
}
|
||||
|
||||
float
|
||||
gsk_path_point_get_distance (GskPathPoint *point,
|
||||
gpointer measure_data)
|
||||
{
|
||||
GskRealPathPoint *self = (GskRealPathPoint *) point;
|
||||
|
||||
return gsk_contour_get_distance (self->contour, self, measure_data);
|
||||
}
|
||||
|
||||
@@ -43,29 +43,22 @@ GDK_AVAILABLE_IN_4_14
|
||||
GType gsk_path_point_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GDK_AVAILABLE_IN_4_14
|
||||
GskPathPoint * gsk_path_point_copy (GskPathPoint *point);
|
||||
GskPathPoint * gsk_path_point_copy (GskPathPoint *point);
|
||||
|
||||
GDK_AVAILABLE_IN_4_14
|
||||
void gsk_path_point_free (GskPathPoint *point);
|
||||
void gsk_path_point_free (GskPathPoint *point);
|
||||
|
||||
GDK_AVAILABLE_IN_4_14
|
||||
void gsk_path_point_get_position (GskPath *path,
|
||||
const GskPathPoint *point,
|
||||
graphene_point_t *position);
|
||||
void gsk_path_point_get_position (GskPathPoint *point,
|
||||
graphene_point_t *position);
|
||||
|
||||
GDK_AVAILABLE_IN_4_14
|
||||
void gsk_path_point_get_tangent (GskPath *path,
|
||||
const GskPathPoint *point,
|
||||
GskPathDirection direction,
|
||||
graphene_vec2_t *tangent);
|
||||
void gsk_path_point_get_tangent (GskPathPoint *point,
|
||||
GskPathDirection direction,
|
||||
graphene_vec2_t *tangent);
|
||||
|
||||
GDK_AVAILABLE_IN_4_14
|
||||
float gsk_path_point_get_curvature (GskPath *path,
|
||||
const GskPathPoint *point,
|
||||
graphene_point_t *center);
|
||||
|
||||
GDK_AVAILABLE_IN_4_14
|
||||
float gsk_path_point_get_distance (GskPathMeasure *measure,
|
||||
const GskPathPoint *point);
|
||||
float gsk_path_point_get_curvature (GskPathPoint *point,
|
||||
graphene_point_t *center);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
@@ -8,17 +8,26 @@ G_BEGIN_DECLS
|
||||
struct _GskRealPathPoint
|
||||
{
|
||||
GskPath *path;
|
||||
gsize contour;
|
||||
const GskContour *contour;
|
||||
|
||||
union {
|
||||
struct {
|
||||
unsigned int idx;
|
||||
float t;
|
||||
} std;
|
||||
struct {
|
||||
float distance;
|
||||
} rect;
|
||||
struct {
|
||||
float angle;
|
||||
} circle;
|
||||
} data;
|
||||
};
|
||||
|
||||
G_STATIC_ASSERT (sizeof (GskRealPathPoint) <= sizeof (GskPathPoint));
|
||||
GskPath * gsk_path_point_get_path (GskPathPoint *self);
|
||||
const GskContour * gsk_path_point_get_contour (GskPathPoint *self);
|
||||
float gsk_path_point_get_distance (GskPathPoint *self,
|
||||
gpointer measure_data);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
||||
@@ -40,11 +40,11 @@ typedef struct _GskRealPathPoint GskRealPathPoint;
|
||||
|
||||
GskPath * gsk_path_new_from_contours (const GSList *contours);
|
||||
|
||||
gsize gsk_path_get_n_contours (const GskPath *self);
|
||||
const GskContour * gsk_path_get_contour (const GskPath *self,
|
||||
gsize gsk_path_get_n_contours (GskPath *path);
|
||||
const GskContour * gsk_path_get_contour (GskPath *path,
|
||||
gsize i);
|
||||
|
||||
GskPathFlags gsk_path_get_flags (const GskPath *self);
|
||||
GskPathFlags gsk_path_get_flags (GskPath *self);
|
||||
|
||||
gboolean gsk_path_foreach_with_tolerance (GskPath *self,
|
||||
GskPathForeachFlags flags,
|
||||
@@ -65,6 +65,9 @@ void gsk_path_builder_svg_arc_to (GskPathBuilder
|
||||
float x,
|
||||
float y);
|
||||
|
||||
gboolean gsk_path_get_stroke_bounds (GskPath *self,
|
||||
const GskStroke *stroke,
|
||||
graphene_rect_t *bounds);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
#include "gskdiffprivate.h"
|
||||
#include "gl/gskglrenderer.h"
|
||||
#include "gskpathprivate.h"
|
||||
#include "gskrectprivate.h"
|
||||
#include "gskrendererprivate.h"
|
||||
#include "gskroundedrectprivate.h"
|
||||
#include "gskstrokeprivate.h"
|
||||
@@ -4414,17 +4413,9 @@ gsk_fill_node_draw (GskRenderNode *node,
|
||||
break;
|
||||
}
|
||||
gsk_path_to_cairo (self->path, cr);
|
||||
if (gsk_render_node_get_node_type (self->child) == GSK_COLOR_NODE &&
|
||||
gsk_rect_contains_rect (&self->child->bounds, &node->bounds))
|
||||
{
|
||||
gdk_cairo_set_source_rgba (cr, gsk_color_node_get_color (self->child));
|
||||
cairo_fill (cr);
|
||||
}
|
||||
else
|
||||
{
|
||||
cairo_clip (cr);
|
||||
gsk_render_node_draw (self->child, cr);
|
||||
}
|
||||
cairo_clip (cr);
|
||||
|
||||
gsk_render_node_draw (self->child, cr);
|
||||
|
||||
cairo_restore (cr);
|
||||
}
|
||||
@@ -4441,10 +4432,12 @@ gsk_fill_node_diff (GskRenderNode *node1,
|
||||
{
|
||||
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);
|
||||
rectangle_init_from_graphene (&clip_rect, &node1->bounds);
|
||||
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);
|
||||
@@ -4602,19 +4595,12 @@ gsk_stroke_node_draw (GskRenderNode *node,
|
||||
|
||||
cairo_save (cr);
|
||||
|
||||
if (gsk_render_node_get_node_type (self->child) == GSK_COLOR_NODE &&
|
||||
gsk_rect_contains_rect (&self->child->bounds, &node->bounds))
|
||||
{
|
||||
gdk_cairo_set_source_rgba (cr, gsk_color_node_get_color (self->child));
|
||||
}
|
||||
else
|
||||
{
|
||||
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_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);
|
||||
|
||||
@@ -4636,12 +4622,9 @@ gsk_stroke_node_diff (GskRenderNode *node1,
|
||||
gsk_stroke_equal (&self1->stroke, &self2->stroke))
|
||||
{
|
||||
cairo_region_t *sub;
|
||||
cairo_rectangle_int_t clip_rect;
|
||||
|
||||
sub = cairo_region_create();
|
||||
gsk_render_node_diff (self1->child, self2->child, sub);
|
||||
rectangle_init_from_graphene (&clip_rect, &node1->bounds);
|
||||
cairo_region_intersect_rectangle (sub, &clip_rect);
|
||||
cairo_region_union (region, sub);
|
||||
cairo_region_destroy (sub);
|
||||
}
|
||||
|
||||
@@ -24,12 +24,10 @@
|
||||
#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"
|
||||
@@ -1178,26 +1176,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,
|
||||
Context *context)
|
||||
@@ -2121,165 +2099,6 @@ parse_rounded_clip_node (GtkCssParser *parser,
|
||||
return result;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
parse_path (GtkCssParser *parser,
|
||||
Context *context,
|
||||
gpointer out_path)
|
||||
{
|
||||
GskPath *path;
|
||||
char *str = NULL;
|
||||
|
||||
if (!parse_string (parser, context, &str))
|
||||
return FALSE;
|
||||
|
||||
path = gsk_path_parse (str);
|
||||
g_free (str);
|
||||
|
||||
if (path == NULL)
|
||||
{
|
||||
gtk_css_parser_error_value (parser, "Invalid path");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
*((GskPath **) out_path) = path;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
clear_path (gpointer inout_path)
|
||||
{
|
||||
g_clear_pointer ((GskPath **) inout_path, gsk_path_unref);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
parse_enum (GtkCssParser *parser,
|
||||
GType type,
|
||||
gpointer out_value)
|
||||
{
|
||||
GEnumClass *class;
|
||||
GEnumValue *v;
|
||||
char *enum_name;
|
||||
|
||||
enum_name = gtk_css_parser_consume_ident (parser);
|
||||
if (enum_name == NULL)
|
||||
return FALSE;
|
||||
|
||||
class = g_type_class_ref (type);
|
||||
|
||||
v = g_enum_get_value_by_nick (class, enum_name);
|
||||
if (v == NULL)
|
||||
{
|
||||
gtk_css_parser_error_value (parser, "Unknown value \"%s\" for enum \"%s\"",
|
||||
enum_name, g_type_name (type));
|
||||
g_free (enum_name);
|
||||
g_type_class_unref (class);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
*(int*)out_value = v->value;
|
||||
|
||||
g_free (enum_name);
|
||||
g_type_class_unref (class);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
parse_fill_rule (GtkCssParser *parser,
|
||||
Context *context,
|
||||
gpointer out_rule)
|
||||
{
|
||||
return parse_enum (parser, GSK_TYPE_FILL_RULE, out_rule);
|
||||
}
|
||||
|
||||
static GskRenderNode *
|
||||
parse_fill_node (GtkCssParser *parser,
|
||||
Context *context)
|
||||
{
|
||||
GskRenderNode *child = NULL;
|
||||
GskPath *path = NULL;
|
||||
int rule = GSK_FILL_RULE_WINDING;
|
||||
const Declaration declarations[] = {
|
||||
{ "child", parse_node, clear_node, &child },
|
||||
{ "path", parse_path, clear_path, &path },
|
||||
{ "fill-rule", parse_fill_rule, NULL, &rule },
|
||||
};
|
||||
GskRenderNode *result;
|
||||
|
||||
parse_declarations (parser, context, declarations, G_N_ELEMENTS (declarations));
|
||||
if (child == NULL)
|
||||
child = create_default_render_node ();
|
||||
if (path == NULL)
|
||||
path = create_default_path ();
|
||||
|
||||
result = gsk_fill_node_new (child, path, rule);
|
||||
|
||||
gsk_path_unref (path);
|
||||
|
||||
gsk_render_node_unref (child);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
parse_line_cap (GtkCssParser *parser,
|
||||
Context *context,
|
||||
gpointer out)
|
||||
{
|
||||
return parse_enum (parser, GSK_TYPE_LINE_CAP, out);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
parse_line_join (GtkCssParser *parser,
|
||||
Context *context,
|
||||
gpointer out)
|
||||
{
|
||||
return parse_enum (parser, GSK_TYPE_LINE_JOIN, out);
|
||||
}
|
||||
|
||||
static GskRenderNode *
|
||||
parse_stroke_node (GtkCssParser *parser,
|
||||
Context *context)
|
||||
{
|
||||
GskRenderNode *child = NULL;
|
||||
GskPath *path = NULL;
|
||||
double line_width = 1.0;
|
||||
int line_cap = GSK_LINE_CAP_BUTT;
|
||||
int line_join = GSK_LINE_JOIN_MITER;
|
||||
double miter_limit = 4.0;
|
||||
GskStroke *stroke;
|
||||
|
||||
const Declaration declarations[] = {
|
||||
{ "child", parse_node, clear_node, &child },
|
||||
{ "path", parse_path, clear_path, &path },
|
||||
{ "line-width", parse_positive_double, NULL, &line_width },
|
||||
{ "line-cap", parse_line_cap, NULL, &line_cap },
|
||||
{ "line-join", parse_line_join, NULL, &line_join },
|
||||
{ "miter-limit", parse_positive_double, NULL, &miter_limit }
|
||||
};
|
||||
GskRenderNode *result;
|
||||
|
||||
parse_declarations (parser, context, declarations, G_N_ELEMENTS (declarations));
|
||||
if (child == NULL)
|
||||
child = create_default_render_node ();
|
||||
if (path == NULL)
|
||||
path = create_default_path ();
|
||||
|
||||
stroke = gsk_stroke_new (line_width);
|
||||
gsk_stroke_set_line_cap (stroke, line_cap);
|
||||
gsk_stroke_set_line_join (stroke, line_join);
|
||||
gsk_stroke_set_miter_limit (stroke, miter_limit);
|
||||
|
||||
result = gsk_stroke_node_new (child, path, stroke);
|
||||
|
||||
gsk_path_unref (path);
|
||||
gsk_stroke_free (stroke);
|
||||
gsk_render_node_unref (child);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static GskRenderNode *
|
||||
parse_shadow_node (GtkCssParser *parser,
|
||||
Context *context)
|
||||
@@ -2362,8 +2181,6 @@ parse_node (GtkCssParser *parser,
|
||||
{ "repeating-linear-gradient", parse_repeating_linear_gradient_node },
|
||||
{ "repeating-radial-gradient", parse_repeating_radial_gradient_node },
|
||||
{ "rounded-clip", parse_rounded_clip_node },
|
||||
{ "fill", parse_fill_node },
|
||||
{ "stroke", parse_stroke_node },
|
||||
{ "shadow", parse_shadow_node },
|
||||
{ "text", parse_text_node },
|
||||
{ "texture", parse_texture_node },
|
||||
@@ -3026,11 +2843,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);
|
||||
}
|
||||
|
||||
@@ -3231,55 +3045,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 = '\n';
|
||||
}
|
||||
append_escaping_newlines (p->str, str);
|
||||
g_string_append (p->str, "\";\n");
|
||||
g_free (str);
|
||||
}
|
||||
|
||||
static void
|
||||
render_node_print (Printer *p,
|
||||
GskRenderNode *node)
|
||||
@@ -3455,11 +3220,14 @@ render_node_print (Printer *p,
|
||||
|
||||
case GSK_FILL_NODE:
|
||||
{
|
||||
char *path_str;
|
||||
|
||||
start_node (p, "fill", node_name);
|
||||
|
||||
append_node_param (p, "child", gsk_fill_node_get_child (node));
|
||||
append_path_param (p, "path", gsk_fill_node_get_path (node));
|
||||
append_enum_param (p, "fill-rule", GSK_TYPE_FILL_RULE, gsk_fill_node_get_fill_rule (node));
|
||||
path_str = gsk_path_to_string (gsk_fill_node_get_path (node));
|
||||
append_string_param (p, "path", path_str);
|
||||
g_free (path_str);
|
||||
|
||||
end_node (p);
|
||||
}
|
||||
@@ -3468,17 +3236,17 @@ render_node_print (Printer *p,
|
||||
case GSK_STROKE_NODE:
|
||||
{
|
||||
const GskStroke *stroke;
|
||||
char *path_str;
|
||||
|
||||
start_node (p, "stroke", node_name);
|
||||
|
||||
append_node_param (p, "child", gsk_stroke_node_get_child (node));
|
||||
append_path_param (p, "path", gsk_stroke_node_get_path (node));
|
||||
path_str = gsk_path_to_string (gsk_stroke_node_get_path (node));
|
||||
append_string_param (p, "path", path_str);
|
||||
g_free (path_str);
|
||||
|
||||
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);
|
||||
append_float_param (p, "line-width", gsk_stroke_get_line_width (stroke), 0.0);
|
||||
|
||||
end_node (p);
|
||||
}
|
||||
|
||||
@@ -138,6 +138,7 @@ gsk_stroke_to_cairo (const GskStroke *self,
|
||||
switch (self->line_join)
|
||||
{
|
||||
case GSK_LINE_JOIN_MITER:
|
||||
case GSK_LINE_JOIN_MITER_CLIP:
|
||||
cairo_set_line_join (cr, CAIRO_LINE_JOIN_MITER);
|
||||
break;
|
||||
case GSK_LINE_JOIN_ROUND:
|
||||
@@ -186,19 +187,7 @@ gsk_stroke_equal (gconstpointer stroke1,
|
||||
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 ||
|
||||
self1->n_dash != self2->n_dash ||
|
||||
self1->dash_offset != self2->dash_offset)
|
||||
return FALSE;
|
||||
|
||||
for (gsize i = 0; i < self1->n_dash; i++)
|
||||
if (self1->dash[i] != self2->dash[i])
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
return self1->line_width == self2->line_width;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -332,7 +321,8 @@ gsk_stroke_get_line_join (const GskStroke *self)
|
||||
*
|
||||
* 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`.
|
||||
* `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.
|
||||
*
|
||||
* Since: 4.14
|
||||
*/
|
||||
@@ -421,6 +411,7 @@ gsk_stroke_set_dash (GskStroke *self,
|
||||
g_free (self->dash);
|
||||
self->dash = g_memdup (dash, sizeof (gfloat) * n_dash);
|
||||
self->n_dash = n_dash;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -486,50 +477,3 @@ gsk_stroke_get_dash_offset (const GskStroke *self)
|
||||
|
||||
return self->dash_offset;
|
||||
}
|
||||
|
||||
/*< private >
|
||||
* gsk_stroke_get_join_width:
|
||||
* @stroke: a `GskStroke`
|
||||
*
|
||||
* Return a width that is sufficient to use
|
||||
* when calculating stroke bounds around joins
|
||||
* and caps.
|
||||
*
|
||||
* Returns: the join width
|
||||
*/
|
||||
float
|
||||
gsk_stroke_get_join_width (const GskStroke *stroke)
|
||||
{
|
||||
float width;
|
||||
|
||||
switch (stroke->line_cap)
|
||||
{
|
||||
case GSK_LINE_CAP_BUTT:
|
||||
width = 0;
|
||||
break;
|
||||
case GSK_LINE_CAP_ROUND:
|
||||
width = stroke->line_width;
|
||||
break;
|
||||
case GSK_LINE_CAP_SQUARE:
|
||||
width = G_SQRT2 * stroke->line_width;
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
switch (stroke->line_join)
|
||||
{
|
||||
case GSK_LINE_JOIN_MITER:
|
||||
width = MAX (width, MAX (stroke->miter_limit, 1.f) * stroke->line_width);
|
||||
break;
|
||||
case GSK_LINE_JOIN_ROUND:
|
||||
case GSK_LINE_JOIN_BEVEL:
|
||||
width = MAX (width, stroke->line_width);
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
return width;
|
||||
}
|
||||
|
||||
|
||||
@@ -53,6 +53,4 @@ gsk_stroke_clear (GskStroke *stroke)
|
||||
stroke->n_dash = 0; /* better safe than sorry */
|
||||
}
|
||||
|
||||
float gsk_stroke_get_join_width (const GskStroke *stroke);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
@@ -27,6 +27,7 @@ gsk_public_sources = files([
|
||||
'gskdiff.c',
|
||||
'gskglshader.c',
|
||||
'gskpath.c',
|
||||
'gskpathdash.c',
|
||||
'gskpathbuilder.c',
|
||||
'gskpathmeasure.c',
|
||||
'gskpathpoint.c',
|
||||
|
||||
@@ -411,7 +411,7 @@ gtk_grid_view_get_position_from_allocation (GtkListBase *base,
|
||||
}
|
||||
|
||||
pos = gtk_list_tile_get_position (self->item_manager, tile);
|
||||
if (tile->n_items > 1 && tile->area.width > 0 && tile->area.height > 0)
|
||||
if (tile->n_items > 1)
|
||||
{
|
||||
int xspacing, yspacing;
|
||||
|
||||
|
||||
@@ -1202,7 +1202,7 @@ my_pango_layout_get_width_for_height (PangoLayout *layout,
|
||||
else if (text_height > for_height)
|
||||
min = mid + 1;
|
||||
else
|
||||
max = text_width;
|
||||
max = mid;
|
||||
}
|
||||
|
||||
return min * PANGO_SCALE;
|
||||
|
||||
@@ -357,16 +357,16 @@ snapshot_frame_fill (GtkSnapshot *snapshot,
|
||||
gtk_snapshot_append_border (snapshot, outline, border_width, colors);
|
||||
}
|
||||
|
||||
static void
|
||||
set_stroke_style (cairo_t *cr,
|
||||
double line_width,
|
||||
GtkBorderStyle style,
|
||||
double length)
|
||||
static GskStroke *
|
||||
create_stroke_style (double line_width,
|
||||
GtkBorderStyle style,
|
||||
double length)
|
||||
{
|
||||
double segments[2];
|
||||
GskStroke *stroke;
|
||||
float segments[2];
|
||||
double n;
|
||||
|
||||
cairo_set_line_width (cr, line_width);
|
||||
stroke = gsk_stroke_new (line_width);
|
||||
|
||||
if (style == GTK_BORDER_STYLE_DOTTED)
|
||||
{
|
||||
@@ -374,12 +374,12 @@ set_stroke_style (cairo_t *cr,
|
||||
|
||||
segments[0] = 0;
|
||||
segments[1] = n ? length / n : 2;
|
||||
cairo_set_dash (cr, segments, G_N_ELEMENTS (segments), 0);
|
||||
gsk_stroke_set_dash (stroke, segments, 2);
|
||||
|
||||
cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
|
||||
cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
|
||||
gsk_stroke_set_line_cap (stroke, GSK_LINE_CAP_ROUND);
|
||||
gsk_stroke_set_line_join (stroke, GSK_LINE_JOIN_ROUND);
|
||||
}
|
||||
else
|
||||
else if (style == GTK_BORDER_STYLE_DASHED)
|
||||
{
|
||||
n = length / line_width;
|
||||
/* Optimize the common case of an integer-sized rectangle
|
||||
@@ -397,32 +397,33 @@ set_stroke_style (cairo_t *cr,
|
||||
segments[0] = n ? (1. / 3) * length / n : 1;
|
||||
segments[1] = 2 * segments[0];
|
||||
}
|
||||
cairo_set_dash (cr, segments, G_N_ELEMENTS (segments), 0);
|
||||
gsk_stroke_set_dash (stroke, segments, 2);
|
||||
|
||||
cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
|
||||
cairo_set_line_join (cr, CAIRO_LINE_JOIN_MITER);
|
||||
gsk_stroke_set_line_cap (stroke, GSK_LINE_CAP_SQUARE);
|
||||
gsk_stroke_set_line_join (stroke, GSK_LINE_JOIN_MITER);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
return stroke;
|
||||
}
|
||||
|
||||
static void
|
||||
render_frame_stroke (cairo_t *cr,
|
||||
const GskRoundedRect *border_box,
|
||||
const double border_width[4],
|
||||
GdkRGBA colors[4],
|
||||
guint hidden_side,
|
||||
GtkBorderStyle stroke_style)
|
||||
snapshot_frame_stroke (GtkSnapshot *snapshot,
|
||||
const GskRoundedRect *border_box,
|
||||
const float 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,
|
||||
@@ -430,32 +431,36 @@ render_frame_stroke (cairo_t *cr,
|
||||
border_width[GTK_CSS_BOTTOM] / 2.0,
|
||||
border_width[GTK_CSS_LEFT] / 2.0);
|
||||
|
||||
if (!different_colors && !different_borders && hidden_side == 0)
|
||||
if (border_width[0] == border_width[1] &&
|
||||
border_width[0] == border_width[2] &&
|
||||
border_width[0] == border_width[3] &&
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
}
|
||||
else
|
||||
{
|
||||
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]);
|
||||
const float weight = sqrtf(2)/2.0;
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
@@ -465,49 +470,111 @@ render_frame_stroke (cairo_t *cr,
|
||||
if (border_width[i] == 0)
|
||||
continue;
|
||||
|
||||
cairo_save (cr);
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
if (i == 0)
|
||||
_gtk_rounded_box_path_top (border_box, &padding_box, cr);
|
||||
{
|
||||
/* 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);
|
||||
}
|
||||
else if (i == 1)
|
||||
_gtk_rounded_box_path_right (border_box, &padding_box, cr);
|
||||
{
|
||||
/* 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);
|
||||
}
|
||||
else if (i == 2)
|
||||
_gtk_rounded_box_path_bottom (border_box, &padding_box, cr);
|
||||
{
|
||||
/* 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);
|
||||
}
|
||||
else if (i == 3)
|
||||
_gtk_rounded_box_path_left (border_box, &padding_box, cr);
|
||||
cairo_clip (cr);
|
||||
{
|
||||
/* 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_side (&stroke_box, cr, i);
|
||||
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);
|
||||
|
||||
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_append_border (snapshot, border_box, border_width, colors);
|
||||
|
||||
cairo_restore (cr);
|
||||
gtk_snapshot_pop (snapshot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
|
||||
@@ -1160,6 +1160,7 @@ gtk_snapshot_push_fill (GtkSnapshot *snapshot,
|
||||
{
|
||||
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,
|
||||
@@ -1218,10 +1219,6 @@ gtk_snapshot_clear_stroke (GtkSnapshotState *state)
|
||||
*
|
||||
* The image is recorded until the next call to [method@Gtk.Snapshot.pop].
|
||||
*
|
||||
* Note that the strokes are subject to the same transformation as
|
||||
* everything else, so uneven scaling will cause horizontal and vertical
|
||||
* strokes to have different widths.
|
||||
*
|
||||
* Since: 4.14
|
||||
*/
|
||||
void
|
||||
@@ -1231,6 +1228,7 @@ gtk_snapshot_push_stroke (GtkSnapshot *snapshot,
|
||||
{
|
||||
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,
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
#endif
|
||||
|
||||
#include <glib-object.h>
|
||||
#include <gdk/gdk.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
@@ -88,39 +87,28 @@ struct _GtkPrinterOptionClass
|
||||
void (*_gtk_reserved4) (void);
|
||||
};
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GType gtk_printer_option_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkPrinterOption *gtk_printer_option_new (const char *name,
|
||||
const char *display_text,
|
||||
GtkPrinterOptionType type);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_printer_option_set (GtkPrinterOption *option,
|
||||
const char *value);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_printer_option_set_has_conflict (GtkPrinterOption *option,
|
||||
gboolean has_conflict);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_printer_option_clear_has_conflict (GtkPrinterOption *option);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_printer_option_set_boolean (GtkPrinterOption *option,
|
||||
gboolean value);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_printer_option_allocate_choices (GtkPrinterOption *option,
|
||||
int num);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_printer_option_choices_from_array (GtkPrinterOption *option,
|
||||
int num_choices,
|
||||
const char **choices,
|
||||
const char **choices_display);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_printer_option_has_choice (GtkPrinterOption *option,
|
||||
const char *choice);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_printer_option_set_activates_default (GtkPrinterOption *option,
|
||||
gboolean activates);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_printer_option_get_activates_default (GtkPrinterOption *option);
|
||||
|
||||
|
||||
|
||||
BIN
matthiasc@master.gnome.org
Normal file
BIN
matthiasc@master.gnome.org
Normal file
Binary file not shown.
@@ -1,92 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2023 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>
|
||||
#include <gsk/gskboundingboxprivate.h>
|
||||
|
||||
static void
|
||||
init_random_rect (graphene_rect_t *r)
|
||||
{
|
||||
r->origin.x = g_test_rand_double_range (0, 1000);
|
||||
r->origin.y = g_test_rand_double_range (0, 1000);
|
||||
r->size.width = g_test_rand_double_range (0, 1000);
|
||||
r->size.height = g_test_rand_double_range (0, 1000);
|
||||
}
|
||||
|
||||
static void
|
||||
test_to_rect (void)
|
||||
{
|
||||
graphene_rect_t rect, rect2;
|
||||
GskBoundingBox bb;
|
||||
graphene_point_t p, p2;
|
||||
|
||||
for (unsigned int i = 0; i < 100; i++)
|
||||
{
|
||||
init_random_rect (&rect);
|
||||
|
||||
gsk_bounding_box_init_from_rect (&bb, &rect);
|
||||
gsk_bounding_box_to_rect (&bb, &rect2);
|
||||
|
||||
graphene_rect_get_top_left (&rect, &p);
|
||||
graphene_rect_get_top_left (&rect2, &p2);
|
||||
|
||||
/* Note: that we can't assert equality here is the reason
|
||||
* GskBoundingBox exists.
|
||||
*/
|
||||
g_assert_true (graphene_point_near (&p, &p2, 0.001));
|
||||
|
||||
graphene_rect_get_bottom_right (&rect, &p);
|
||||
graphene_rect_get_bottom_right (&rect2, &p2);
|
||||
|
||||
g_assert_true (graphene_point_near (&p, &p2, 0.001));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_contains (void)
|
||||
{
|
||||
graphene_rect_t rect;
|
||||
GskBoundingBox bb;
|
||||
graphene_point_t p;
|
||||
|
||||
for (unsigned int i = 0; i < 100; i++)
|
||||
{
|
||||
init_random_rect (&rect);
|
||||
|
||||
gsk_bounding_box_init_from_rect (&bb, &rect);
|
||||
|
||||
g_assert_true (gsk_bounding_box_contains_point (&bb, &bb.min));
|
||||
g_assert_true (gsk_bounding_box_contains_point (&bb, &bb.max));
|
||||
|
||||
graphene_point_interpolate (&bb.min, &bb.max, 0.5, &p);
|
||||
g_assert_true (gsk_bounding_box_contains_point (&bb, &p));
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc,
|
||||
char *argv[])
|
||||
{
|
||||
gtk_test_init (&argc, &argv, NULL);
|
||||
|
||||
g_test_add_func ("/bounding-box/to-rect", test_to_rect);
|
||||
g_test_add_func ("/bounding-box/contains", test_contains);
|
||||
|
||||
return g_test_run ();
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
fill {
|
||||
child: color {
|
||||
bounds: 0 0 100 100;
|
||||
color: rgb(255,0,0);
|
||||
}
|
||||
path: "\
|
||||
M 10 10\
|
||||
L 90 10\
|
||||
L 90 90\
|
||||
L 10 90\
|
||||
Z";
|
||||
fill-rule: winding;
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 250 B |
@@ -1,19 +0,0 @@
|
||||
color {
|
||||
bounds: 0 0 100 100;
|
||||
color: rgb(0,0,0);
|
||||
}
|
||||
stroke {
|
||||
child: color {
|
||||
bounds: 0 0 100 100;
|
||||
color: rgb(255,0,0);
|
||||
}
|
||||
path: "\
|
||||
M 10 10\
|
||||
L 90 10\
|
||||
L 90 90\
|
||||
L 10 90\
|
||||
Z";
|
||||
line-width: 2;
|
||||
line-cap: butt;
|
||||
line-join: miter;
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 346 B |
@@ -21,6 +21,57 @@
|
||||
|
||||
#include "gsk/gskcurveprivate.h"
|
||||
|
||||
static gboolean
|
||||
measure_segment (const graphene_point_t *from,
|
||||
const graphene_point_t *to,
|
||||
float from_t,
|
||||
float to_t,
|
||||
GskCurveLineReason reason,
|
||||
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)
|
||||
{
|
||||
@@ -46,6 +97,17 @@ test_curve_tangents (void)
|
||||
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);
|
||||
@@ -92,6 +154,7 @@ static gboolean
|
||||
pathop_cb (GskPathOperation op,
|
||||
const graphene_point_t *pts,
|
||||
gsize n_pts,
|
||||
float weight,
|
||||
gpointer user_data)
|
||||
{
|
||||
GskCurve *curve = user_data;
|
||||
@@ -101,7 +164,7 @@ pathop_cb (GskPathOperation op,
|
||||
if (op == GSK_PATH_MOVE)
|
||||
return TRUE;
|
||||
|
||||
gsk_curve_init_foreach (curve, op, pts, n_pts);
|
||||
gsk_curve_init_foreach (curve, op, pts, n_pts, weight);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@@ -151,6 +214,7 @@ main (int argc,
|
||||
{
|
||||
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/special/crossing", test_curve_crossing);
|
||||
|
||||
@@ -8,6 +8,15 @@ init_random_point (graphene_point_t *p)
|
||||
p->y = g_test_rand_double_range (0, 1000);
|
||||
}
|
||||
|
||||
static float
|
||||
random_weight (void)
|
||||
{
|
||||
if (g_test_rand_bit ())
|
||||
return g_test_rand_double_range (1, 20);
|
||||
else
|
||||
return 1.0 / g_test_rand_double_range (1, 20);
|
||||
}
|
||||
|
||||
static void
|
||||
init_random_curve_with_op (GskCurve *curve,
|
||||
GskPathOperation min_op,
|
||||
@@ -48,6 +57,18 @@ init_random_curve_with_op (GskCurve *curve,
|
||||
}
|
||||
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 (random_weight(), 0);
|
||||
init_random_point (&p[3]);
|
||||
gsk_curve_init (curve, gsk_pathop_encode (GSK_PATH_CONIC, p));
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
@@ -56,7 +77,7 @@ init_random_curve_with_op (GskCurve *curve,
|
||||
static void
|
||||
init_random_curve (GskCurve *curve)
|
||||
{
|
||||
init_random_curve_with_op (curve, GSK_PATH_LINE, GSK_PATH_CUBIC);
|
||||
init_random_curve_with_op (curve, GSK_PATH_LINE, GSK_PATH_CONIC);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -196,17 +217,77 @@ static gboolean
|
||||
add_curve_to_array (GskPathOperation op,
|
||||
const graphene_point_t *pts,
|
||||
gsize n_pts,
|
||||
float weight,
|
||||
gpointer user_data)
|
||||
{
|
||||
GArray *array = user_data;
|
||||
GskCurve c;
|
||||
|
||||
gsk_curve_init_foreach (&c, op, pts, n_pts);
|
||||
gsk_curve_init_foreach (&c, op, pts, n_pts, weight);
|
||||
g_array_append_val (array, c);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
test_curve_decompose_conic (void)
|
||||
{
|
||||
g_test_skip ("No good error bounds for decomposing conics");
|
||||
return;
|
||||
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
GArray *array;
|
||||
GskCurve c;
|
||||
GskPathBuilder *builder;
|
||||
GskPath *path;
|
||||
GskPathMeasure *measure;
|
||||
const graphene_point_t *s;
|
||||
|
||||
init_random_curve_with_op (&c, GSK_PATH_CONIC, GSK_PATH_CONIC);
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
|
||||
s = gsk_curve_get_start_point (&c);
|
||||
gsk_path_builder_move_to (builder, s->x, s->y);
|
||||
gsk_curve_builder_to (&c, builder);
|
||||
path = gsk_path_builder_free_to_path (builder);
|
||||
measure = gsk_path_measure_new_with_tolerance (path, 0.1);
|
||||
|
||||
array = g_array_new (FALSE, FALSE, sizeof (GskCurve));
|
||||
|
||||
g_assert_true (gsk_curve_decompose_curve (&c, GSK_PATH_FOREACH_ALLOW_CUBIC, 0.1, add_curve_to_array, array));
|
||||
|
||||
g_assert_cmpint (array->len, >=, 1);
|
||||
|
||||
for (int j = 0; j < array->len; j++)
|
||||
{
|
||||
GskCurve *c2 = &g_array_index (array, GskCurve, j);
|
||||
|
||||
g_assert_true (c2->op == GSK_PATH_CUBIC);
|
||||
|
||||
/* Check that the curves we got are approximating the conic */
|
||||
for (int k = 0; k < 11; k++)
|
||||
{
|
||||
GskPathPoint point;
|
||||
graphene_point_t p, q;
|
||||
|
||||
gsk_curve_get_point (c2, k/10.0, &p);
|
||||
if (gsk_path_get_closest_point (path, &p, INFINITY, &point))
|
||||
{
|
||||
gsk_path_point_get_position (&point, &q);
|
||||
g_assert_true (graphene_point_near (&p, &q, 0.5));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
g_array_unref (array);
|
||||
|
||||
gsk_path_measure_unref (measure);
|
||||
gsk_path_unref (path);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_curve_decompose_into (GskPathForeachFlags flags)
|
||||
{
|
||||
@@ -249,6 +330,9 @@ test_curve_decompose_into (GskPathForeachFlags flags)
|
||||
case GSK_PATH_CUBIC:
|
||||
g_assert_true (flags & GSK_PATH_FOREACH_ALLOW_CUBIC);
|
||||
break;
|
||||
case GSK_PATH_CONIC:
|
||||
g_assert_true (flags & GSK_PATH_FOREACH_ALLOW_CONIC);
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
@@ -328,6 +412,7 @@ main (int argc, char *argv[])
|
||||
g_test_add_func ("/curve/points", test_curve_points);
|
||||
g_test_add_func ("/curve/tangents", test_curve_tangents);
|
||||
g_test_add_func ("/curve/decompose", test_curve_decompose);
|
||||
g_test_add_func ("/curve/decompose/conic", test_curve_decompose_conic);
|
||||
g_test_add_func ("/curve/decompose/into/line", test_curve_decompose_into_line);
|
||||
g_test_add_func ("/curve/decompose/into/quad", test_curve_decompose_into_quad);
|
||||
g_test_add_func ("/curve/decompose/into/cubic", test_curve_decompose_into_cubic);
|
||||
|
||||
183
testsuite/gsk/dash.c
Normal file
183
testsuite/gsk/dash.c
Normal file
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
* 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>
|
||||
|
||||
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_QUAD:
|
||||
gsk_path_builder_quad_to (builder, pts[1].x, pts[1].y, pts[2].x, pts[2].y);
|
||||
break;
|
||||
|
||||
case GSK_PATH_CUBIC:
|
||||
gsk_path_builder_cubic_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 ();
|
||||
}
|
||||
@@ -27,7 +27,6 @@ test_bad_split (void)
|
||||
GskPathMeasure *measure, *measure1;
|
||||
GskPathBuilder *builder;
|
||||
float split, length, epsilon;
|
||||
GskPathPoint point0, point1;
|
||||
|
||||
/* An example that was isolated from the /path/segment test path.c
|
||||
* It shows how uneven parametrization of cubics can lead to bad
|
||||
@@ -38,13 +37,11 @@ test_bad_split (void)
|
||||
|
||||
measure = gsk_path_measure_new (path);
|
||||
split = 2.962588;
|
||||
gsk_path_measure_get_point (measure, 0, &point0);
|
||||
gsk_path_measure_get_point (measure, split, &point1);
|
||||
length = gsk_path_measure_get_length (measure);
|
||||
epsilon = MAX (length / 256, 1.f / 1024);
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
gsk_path_builder_add_segment (builder, path, &point0, &point1);;
|
||||
gsk_path_builder_add_segment (builder, measure, 0, split);
|
||||
path1 = gsk_path_builder_free_to_path (builder);
|
||||
measure1 = gsk_path_measure_new (path1);
|
||||
|
||||
@@ -113,34 +110,34 @@ test_rect (void)
|
||||
#define TEST_POS_AT(distance, X, Y) \
|
||||
ret = gsk_path_measure_get_point (measure, distance, &point); \
|
||||
g_assert_true (ret); \
|
||||
gsk_path_point_get_position (path, &point, &p); \
|
||||
gsk_path_point_get_position (&point, &p); \
|
||||
g_assert_true (graphene_point_near (&p, &GRAPHENE_POINT_INIT (X, Y), 0.01)); \
|
||||
ret = gsk_path_get_closest_point (path, &GRAPHENE_POINT_INIT (X, Y), INFINITY, &point); \
|
||||
g_assert_true (ret); \
|
||||
if (distance < length) \
|
||||
g_assert_true (fabs (gsk_path_point_get_distance (measure, &point) - distance) < 0.01); \
|
||||
g_assert_true (fabs (gsk_path_measure_get_distance (measure, &point) - distance) < 0.01); \
|
||||
else \
|
||||
g_assert_true (fabs (gsk_path_point_get_distance (measure, &point)) < 0.01); \
|
||||
gsk_path_point_get_position (path, &point, &p); \
|
||||
g_assert_true (fabs (gsk_path_measure_get_distance (measure, &point)) < 0.01); \
|
||||
gsk_path_point_get_position (&point, &p); \
|
||||
g_assert_true (graphene_point_near (&p, &GRAPHENE_POINT_INIT (X, Y), 0.01)); \
|
||||
|
||||
#define TEST_TANGENT_AT(distance, x1, y1, x2, y2) \
|
||||
ret = gsk_path_measure_get_point (measure, distance, &point); \
|
||||
g_assert_true (ret); \
|
||||
gsk_path_point_get_tangent (path, &point, GSK_PATH_START, &tangent); \
|
||||
gsk_path_point_get_tangent (&point, GSK_PATH_START, &tangent); \
|
||||
g_assert_true (graphene_vec2_near (&tangent, graphene_vec2_init (&expected_tangent, x1, y1), 0.01)); \
|
||||
gsk_path_point_get_tangent (path, &point, GSK_PATH_END, &tangent); \
|
||||
gsk_path_point_get_tangent (&point, GSK_PATH_END, &tangent); \
|
||||
g_assert_true (graphene_vec2_near (&tangent, graphene_vec2_init (&expected_tangent, x2, y2), 0.01)); \
|
||||
|
||||
#define TEST_POS_AT2(distance, X, Y, expected_distance) \
|
||||
ret = gsk_path_measure_get_point (measure, distance, &point); \
|
||||
g_assert_true (ret); \
|
||||
gsk_path_point_get_position (path, &point, &p); \
|
||||
gsk_path_point_get_position (&point, &p); \
|
||||
g_assert_true (graphene_point_near (&p, &GRAPHENE_POINT_INIT (X, Y), 0.01)); \
|
||||
ret = gsk_path_get_closest_point (path, &GRAPHENE_POINT_INIT (X, Y), INFINITY, &point); \
|
||||
g_assert_true (ret); \
|
||||
g_assert_true (fabs (gsk_path_point_get_distance (measure, &point) - expected_distance) < 0.01); \
|
||||
gsk_path_point_get_position (path, &point, &p); \
|
||||
g_assert_true (fabs (gsk_path_measure_get_distance (measure, &point) - expected_distance) < 0.01); \
|
||||
gsk_path_point_get_position (&point, &p); \
|
||||
g_assert_true (graphene_point_near (&p, &GRAPHENE_POINT_INIT (X, Y), 0.01)); \
|
||||
|
||||
TEST_POS_AT (0, 0, 0)
|
||||
@@ -151,7 +148,7 @@ test_rect (void)
|
||||
TEST_POS_AT (175, 75, 50)
|
||||
TEST_POS_AT (250, 0, 50)
|
||||
TEST_POS_AT (260, 0, 40)
|
||||
TEST_POS_AT2 (300, 0, 0, 0)
|
||||
TEST_POS_AT (300, 0, 0)
|
||||
|
||||
TEST_TANGENT_AT (0, 0, -1, 1, 0)
|
||||
TEST_TANGENT_AT (50, 1, 0, 1, 0)
|
||||
@@ -227,7 +224,7 @@ test_rect (void)
|
||||
TEST_POS_AT2 (0, 100, 0, 0)
|
||||
TEST_POS_AT2 (25, 75, 0, 25)
|
||||
TEST_POS_AT2 (100, 0, 0, 100)
|
||||
TEST_POS_AT2 (110, 10, 0, 110)
|
||||
TEST_POS_AT2 (110, 10, 0, 90)
|
||||
TEST_POS_AT2 (200, 100, 0, 0)
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
@@ -240,7 +237,7 @@ test_rect (void)
|
||||
|
||||
/* These cases are ambiguous */
|
||||
TEST_POS_AT2 (0, 0, 100, 0)
|
||||
TEST_POS_AT2 (25, 0, 75, 25)
|
||||
TEST_POS_AT2 (25, 0, 75, 175)
|
||||
TEST_POS_AT2 (100, 0, 0, 100)
|
||||
TEST_POS_AT2 (110, 0, 10, 110)
|
||||
TEST_POS_AT2 (200, 0, 100, 0)
|
||||
|
||||
@@ -19,10 +19,19 @@
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
static float
|
||||
random_weight (void)
|
||||
{
|
||||
if (g_test_rand_bit ())
|
||||
return g_test_rand_double_range (1, 20);
|
||||
else
|
||||
return 1.0 / g_test_rand_double_range (1, 20);
|
||||
}
|
||||
|
||||
static GskPath *
|
||||
create_random_degenerate_path (guint max_contours)
|
||||
{
|
||||
#define N_DEGENERATE_PATHS 14
|
||||
#define N_DEGENERATE_PATHS 15
|
||||
GskPathBuilder *builder;
|
||||
guint i;
|
||||
|
||||
@@ -156,6 +165,20 @@ create_random_degenerate_path (guint max_contours)
|
||||
}
|
||||
break;
|
||||
|
||||
case 14:
|
||||
/* a conic with start == end */
|
||||
{
|
||||
graphene_point_t point = GRAPHENE_POINT_INIT (g_test_rand_double_range (-1000, 1000),
|
||||
g_test_rand_double_range (-1000, 1000));
|
||||
gsk_path_builder_move_to (builder, point.x, point.y);
|
||||
gsk_path_builder_conic_to (builder,
|
||||
g_test_rand_double_range (-1000, 1000),
|
||||
g_test_rand_double_range (-1000, 1000),
|
||||
point.x, point.y,
|
||||
random_weight ());
|
||||
}
|
||||
break;
|
||||
|
||||
case N_DEGENERATE_PATHS:
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
@@ -225,7 +248,7 @@ add_standard_contour (GskPathBuilder *builder)
|
||||
n = g_test_rand_int_range (1, 20);
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
switch (g_test_rand_int_range (0, 6))
|
||||
switch (g_test_rand_int_range (0, 8))
|
||||
{
|
||||
case 0:
|
||||
gsk_path_builder_line_to (builder,
|
||||
@@ -275,6 +298,24 @@ add_standard_contour (GskPathBuilder *builder)
|
||||
g_test_rand_double_range (-1000, 1000));
|
||||
break;
|
||||
|
||||
case 6:
|
||||
gsk_path_builder_conic_to (builder,
|
||||
g_test_rand_double_range (-1000, 1000),
|
||||
g_test_rand_double_range (-1000, 1000),
|
||||
g_test_rand_double_range (-1000, 1000),
|
||||
g_test_rand_double_range (-1000, 1000),
|
||||
random_weight ());
|
||||
break;
|
||||
|
||||
case 7:
|
||||
gsk_path_builder_rel_conic_to (builder,
|
||||
g_test_rand_double_range (-1000, 1000),
|
||||
g_test_rand_double_range (-1000, 1000),
|
||||
g_test_rand_double_range (-1000, 1000),
|
||||
g_test_rand_double_range (-1000, 1000),
|
||||
random_weight ());
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
break;
|
||||
@@ -325,22 +366,18 @@ test_segment_start (void)
|
||||
GskPathBuilder *builder;
|
||||
float epsilon, length;
|
||||
guint i;
|
||||
GskPathPoint point0, point1;
|
||||
|
||||
path = create_random_path (G_MAXUINT);
|
||||
measure = gsk_path_measure_new (path);
|
||||
length = gsk_path_measure_get_length (measure);
|
||||
epsilon = MAX (length / 1024, G_MINFLOAT);
|
||||
gsk_path_measure_get_point (measure, 0, &point0);
|
||||
|
||||
for (i = 0; i < 100; i++)
|
||||
{
|
||||
float seg_length = length * i / 100.0f;
|
||||
|
||||
gsk_path_measure_get_point (measure, seg_length, &point1);
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
gsk_path_builder_add_segment (builder, path, &point0, &point1);
|
||||
gsk_path_builder_add_segment (builder, measure, 0, seg_length);
|
||||
path1 = gsk_path_builder_free_to_path (builder);
|
||||
measure1 = gsk_path_measure_new (path1);
|
||||
|
||||
@@ -365,7 +402,6 @@ test_segment_end (void)
|
||||
GskPathBuilder *builder;
|
||||
float epsilon, length;
|
||||
guint i;
|
||||
GskPathPoint point0, point1;
|
||||
|
||||
path = create_random_path (G_MAXUINT);
|
||||
measure = gsk_path_measure_new (path);
|
||||
@@ -376,10 +412,8 @@ test_segment_end (void)
|
||||
{
|
||||
float seg_length = length * i / 100.0f;
|
||||
|
||||
gsk_path_measure_get_point (measure, length - seg_length, &point0);
|
||||
gsk_path_measure_get_point (measure, length, &point1);
|
||||
builder = gsk_path_builder_new ();
|
||||
gsk_path_builder_add_segment (builder, path, &point0, &point1);
|
||||
gsk_path_builder_add_segment (builder, measure, length - seg_length, length);
|
||||
path1 = gsk_path_builder_free_to_path (builder);
|
||||
measure1 = gsk_path_measure_new (path1);
|
||||
|
||||
@@ -404,7 +438,6 @@ test_segment_chunk (void)
|
||||
GskPathBuilder *builder;
|
||||
float epsilon, length;
|
||||
guint i;
|
||||
GskPathPoint point0, point1;
|
||||
|
||||
path = create_random_path (G_MAXUINT);
|
||||
measure = gsk_path_measure_new (path);
|
||||
@@ -415,19 +448,15 @@ test_segment_chunk (void)
|
||||
{
|
||||
float seg_start = length * i / 200.0f;
|
||||
|
||||
gsk_path_measure_get_point (measure, seg_start, &point0);
|
||||
gsk_path_measure_get_point (measure, seg_start + length / 2, &point1);
|
||||
builder = gsk_path_builder_new ();
|
||||
gsk_path_builder_add_segment (builder, path, &point0, &point1);
|
||||
gsk_path_builder_add_segment (builder, measure, seg_start, seg_start + length / 2);
|
||||
path1 = gsk_path_builder_free_to_path (builder);
|
||||
measure1 = gsk_path_measure_new (path1);
|
||||
|
||||
g_assert_cmpfloat_with_epsilon (length / 2, gsk_path_measure_get_length (measure1), epsilon);
|
||||
|
||||
gsk_path_measure_get_point (measure, seg_start + length / 2, &point0);
|
||||
gsk_path_measure_get_point (measure, seg_start, &point1);
|
||||
builder = gsk_path_builder_new ();
|
||||
gsk_path_builder_add_segment (builder, path, &point0, &point1);
|
||||
gsk_path_builder_add_segment (builder, measure, seg_start + length / 2, seg_start);
|
||||
path2 = gsk_path_builder_free_to_path (builder);
|
||||
measure2 = gsk_path_measure_new (path2);
|
||||
|
||||
@@ -451,7 +480,6 @@ test_segment (void)
|
||||
GskPathBuilder *builder;
|
||||
guint i;
|
||||
float split1, split2, epsilon, length;
|
||||
GskPathPoint point0, point1, point2, point3;
|
||||
|
||||
for (i = 0; i < 1000; i++)
|
||||
{
|
||||
@@ -464,22 +492,18 @@ test_segment (void)
|
||||
split1 = g_test_rand_double_range (0, length);
|
||||
split2 = g_test_rand_double_range (split1, length);
|
||||
|
||||
gsk_path_measure_get_point (measure, 0, &point0);
|
||||
gsk_path_measure_get_point (measure, split1, &point1);
|
||||
gsk_path_measure_get_point (measure, split2, &point2);
|
||||
gsk_path_measure_get_point (measure, length, &point3);
|
||||
builder = gsk_path_builder_new ();
|
||||
gsk_path_builder_add_segment (builder, path, &point0, &point1);
|
||||
gsk_path_builder_add_segment (builder, measure, 0, split1);
|
||||
path1 = gsk_path_builder_free_to_path (builder);
|
||||
measure1 = gsk_path_measure_new (path1);
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
gsk_path_builder_add_segment (builder, path, &point1, &point2);
|
||||
gsk_path_builder_add_segment (builder, measure, split1, split2);
|
||||
path2 = gsk_path_builder_free_to_path (builder);
|
||||
measure2 = gsk_path_measure_new (path2);
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
gsk_path_builder_add_segment (builder, path, &point2, &point3);
|
||||
gsk_path_builder_add_segment (builder, measure, split2, length);
|
||||
path3 = gsk_path_builder_free_to_path (builder);
|
||||
measure3 = gsk_path_measure_new (path3);
|
||||
|
||||
@@ -523,7 +547,7 @@ test_get_point (void)
|
||||
g_assert_true (gsk_path_is_empty (path));
|
||||
continue;
|
||||
}
|
||||
gsk_path_point_get_position (path, &point, &last_point);
|
||||
gsk_path_point_get_position (&point, &last_point);
|
||||
|
||||
/* FIXME: anything we can test with tangents here? */
|
||||
last_offset = 0;
|
||||
@@ -533,7 +557,7 @@ test_get_point (void)
|
||||
offset = length * j / 1024;
|
||||
ret = gsk_path_measure_get_point (measure, offset, &point);
|
||||
g_assert_true (ret);
|
||||
gsk_path_point_get_position (path, &point, &p);
|
||||
gsk_path_point_get_position (&point, &p);
|
||||
|
||||
if (graphene_point_distance (&last_point, &p, NULL, NULL) > 2 * (offset - last_offset))
|
||||
{
|
||||
@@ -594,25 +618,25 @@ test_closest_point (void)
|
||||
ret = gsk_path_get_closest_point (path1, &test, INFINITY, &point);
|
||||
g_assert_true (ret);
|
||||
|
||||
gsk_path_point_get_position (path1, &point, &p1);
|
||||
gsk_path_point_get_tangent (path1, &point, GSK_PATH_END, &t1);
|
||||
offset1 = gsk_path_point_get_distance (measure1, &point);
|
||||
gsk_path_point_get_position (&point, &p1);
|
||||
gsk_path_point_get_tangent (&point, GSK_PATH_END, &t1);
|
||||
offset1 = gsk_path_measure_get_distance (measure1, &point);
|
||||
distance1 = graphene_point_distance (&p1, &test, NULL, NULL);
|
||||
|
||||
ret = gsk_path_get_closest_point (path2, &test, INFINITY, &point);
|
||||
g_assert_true (ret);
|
||||
|
||||
gsk_path_point_get_position (path2, &point, &p2);
|
||||
gsk_path_point_get_tangent (path2, &point, GSK_PATH_END, &t2);
|
||||
offset2 = gsk_path_point_get_distance (measure2, &point);
|
||||
gsk_path_point_get_position (&point, &p2);
|
||||
gsk_path_point_get_tangent (&point, GSK_PATH_END, &t2);
|
||||
offset2 = gsk_path_measure_get_distance (measure2, &point);
|
||||
distance2 = graphene_point_distance (&p2, &test, NULL, NULL);
|
||||
|
||||
ret = gsk_path_get_closest_point (path, &test, INFINITY, &point);
|
||||
g_assert_true (ret);
|
||||
|
||||
gsk_path_point_get_position (path, &point, &p);
|
||||
gsk_path_point_get_tangent (path, &point, GSK_PATH_END, &t);
|
||||
offset = gsk_path_point_get_distance (measure, &point);
|
||||
gsk_path_point_get_position (&point, &p);
|
||||
gsk_path_point_get_tangent (&point, GSK_PATH_END, &t);
|
||||
offset = gsk_path_measure_get_distance (measure, &point);
|
||||
distance = graphene_point_distance (&p, &test, NULL, NULL);
|
||||
|
||||
if (distance1 == distance)
|
||||
@@ -678,11 +702,11 @@ test_closest_point_for_point (void)
|
||||
offset = g_test_rand_double_range (0, length);
|
||||
ret = gsk_path_measure_get_point (measure, offset, &point);
|
||||
g_assert_true (ret);
|
||||
gsk_path_point_get_position (path, &point, &p);
|
||||
gsk_path_point_get_position (&point, &p);
|
||||
ret = gsk_path_get_closest_point (path, &p, 2 * tolerance, &point);
|
||||
g_assert_true (ret);
|
||||
gsk_path_point_get_position (path, &point, &closest_point);
|
||||
//closest_offset = gsk_path_point_get_distance (measure, &point);
|
||||
gsk_path_point_get_position (&point, &closest_point);
|
||||
//closest_offset = gsk_path_measure_get_distance (measure, &point);
|
||||
distance = graphene_point_distance (&p, &closest_point, NULL, NULL);
|
||||
|
||||
/* should be given due to the TRUE return above, but who knows... */
|
||||
@@ -779,6 +803,7 @@ static gboolean
|
||||
rotate_path_cb (GskPathOperation op,
|
||||
const graphene_point_t *pts,
|
||||
gsize n_pts,
|
||||
float weight,
|
||||
gpointer user_data)
|
||||
{
|
||||
GskPathBuilder **builders = user_data;
|
||||
@@ -810,6 +835,11 @@ rotate_path_cb (GskPathOperation op,
|
||||
gsk_path_builder_cubic_to (builders[1], pts[1].y, -pts[1].x, pts[2].y, -pts[2].x, pts[3].y, -pts[3].x);
|
||||
break;
|
||||
|
||||
case GSK_PATH_CONIC:
|
||||
gsk_path_builder_conic_to (builders[0], pts[1].x, pts[1].y, pts[2].x, pts[2].y, weight);
|
||||
gsk_path_builder_conic_to (builders[1], pts[1].y, -pts[1].x, pts[2].y, -pts[2].x, weight);
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
return FALSE;
|
||||
|
||||
@@ -59,7 +59,6 @@ compare_render_tests = [
|
||||
'empty-shadow',
|
||||
'empty-texture',
|
||||
'empty-transform',
|
||||
'fill',
|
||||
'huge-height',
|
||||
'huge-width',
|
||||
'inset-shadow-multiple',
|
||||
@@ -89,7 +88,6 @@ compare_render_tests = [
|
||||
'scaled-cairo',
|
||||
'scaled-texture',
|
||||
'shadow-in-opacity',
|
||||
'stroke',
|
||||
'texture-scale-magnify-10000x',
|
||||
'texture-scale-magnify-rotate',
|
||||
'texture-scale-stripes',
|
||||
@@ -272,8 +270,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',
|
||||
@@ -290,8 +286,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',
|
||||
@@ -300,8 +294,6 @@ node_parser_tests = [
|
||||
'empty-texture-scale.ref.node',
|
||||
'empty-transform.node',
|
||||
'empty-transform.ref.node',
|
||||
'fill.node',
|
||||
'fill.ref.node',
|
||||
'glshader.node',
|
||||
'glshader.ref.node',
|
||||
'glshader.errors',
|
||||
@@ -328,8 +320,6 @@ node_parser_tests = [
|
||||
'string-error.errors',
|
||||
'string-error.node',
|
||||
'string-error.ref.node',
|
||||
'stroke.node',
|
||||
'stroke.ref.node',
|
||||
'testswitch.node',
|
||||
'text-fail.node',
|
||||
'text-fail.ref.node',
|
||||
@@ -373,6 +363,7 @@ tests = [
|
||||
['path-special-cases'],
|
||||
['measure'],
|
||||
['measure-special-cases'],
|
||||
['dash'],
|
||||
]
|
||||
|
||||
test_cargs = []
|
||||
@@ -409,7 +400,6 @@ internal_tests = [
|
||||
[ 'half-float' ],
|
||||
['rounded-rect'],
|
||||
['misc'],
|
||||
['boundingbox'],
|
||||
]
|
||||
|
||||
foreach t : internal_tests
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
fill {
|
||||
path: "M0,0L50,0C50,50 0,50 0,0Z";
|
||||
fill-rule: winding;
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
fill {
|
||||
child: color {
|
||||
bounds: 0 0 50 50;
|
||||
color: rgb(255,0,204);
|
||||
}
|
||||
path: "\
|
||||
M 0 0\
|
||||
L 50 0\
|
||||
C 50 50, 0 50, 0 0\
|
||||
Z";
|
||||
fill-rule: winding;
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
stroke {
|
||||
child: color { bounds: 0 0 100 100; color: red; }
|
||||
path: "M10,10L90,90M90,10L10,90";
|
||||
line-width: 10;
|
||||
line-cap: round;
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
stroke {
|
||||
child: color {
|
||||
bounds: 0 0 100 100;
|
||||
color: rgb(255,0,0);
|
||||
}
|
||||
path: "\
|
||||
M 10 10\
|
||||
L 90 90\
|
||||
M 90 10\
|
||||
L 10 90";
|
||||
line-width: 10;
|
||||
line-cap: round;
|
||||
line-join: miter;
|
||||
}
|
||||
@@ -277,252 +277,44 @@ test_rsvg_parse (void)
|
||||
}
|
||||
}
|
||||
|
||||
/* Test that circles and rectangles serialize as expected and can be
|
||||
* round-tripped through strings.
|
||||
*/
|
||||
static void
|
||||
test_empty_path (void)
|
||||
test_serialize_custom_contours (void)
|
||||
{
|
||||
GskPathBuilder *builder;
|
||||
GskPath *path;
|
||||
char *s;
|
||||
graphene_rect_t bounds;
|
||||
GskPathPoint point;
|
||||
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);
|
||||
|
||||
g_assert_true (gsk_path_is_empty (path));
|
||||
g_assert_false (gsk_path_is_closed (path));
|
||||
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);
|
||||
|
||||
s = gsk_path_to_string (path);
|
||||
g_assert_cmpstr (s, ==, "");
|
||||
g_free (s);
|
||||
|
||||
g_assert_false (gsk_path_get_bounds (path, &bounds));
|
||||
g_assert_true (graphene_rect_equal (&bounds, graphene_rect_zero ()));
|
||||
|
||||
g_assert_false (gsk_path_in_fill (path, &GRAPHENE_POINT_INIT (0, 0), GSK_FILL_RULE_WINDING));
|
||||
|
||||
g_assert_false (gsk_path_get_closest_point (path, &GRAPHENE_POINT_INIT (0, 0), INFINITY, &point));
|
||||
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);
|
||||
}
|
||||
|
||||
static void
|
||||
test_rect_path (void)
|
||||
{
|
||||
GskPathBuilder *builder;
|
||||
GskPath *path;
|
||||
char *s;
|
||||
graphene_rect_t bounds;
|
||||
GskPathPoint point;
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
gsk_path_builder_add_rect (builder, &GRAPHENE_RECT_INIT (0, 0, 200, 100));
|
||||
path = gsk_path_builder_free_to_path (builder);
|
||||
|
||||
g_assert_false (gsk_path_is_empty (path));
|
||||
g_assert_true (gsk_path_is_closed (path));
|
||||
|
||||
s = gsk_path_to_string (path);
|
||||
g_assert_cmpstr (s, ==, "M 0 0 L 200 0 L 200 100 L 0 100 Z");
|
||||
g_free (s);
|
||||
|
||||
g_assert_true (gsk_path_get_bounds (path, &bounds));
|
||||
g_assert_true (graphene_rect_equal (&bounds, &GRAPHENE_RECT_INIT (0, 0, 200, 100)));
|
||||
|
||||
g_assert_true (gsk_path_in_fill (path, &GRAPHENE_POINT_INIT (50, 50), GSK_FILL_RULE_WINDING));
|
||||
g_assert_false (gsk_path_in_fill (path, &GRAPHENE_POINT_INIT (200, 200), GSK_FILL_RULE_WINDING));
|
||||
|
||||
g_assert_true (gsk_path_get_closest_point (path, &GRAPHENE_POINT_INIT (200, 200), INFINITY, &point));
|
||||
|
||||
gsk_path_unref (path);
|
||||
}
|
||||
|
||||
/* test quad <> cubic conversions */
|
||||
static gboolean
|
||||
collect_path (GskPathOperation op,
|
||||
const graphene_point_t *pts,
|
||||
gsize n_pts,
|
||||
gpointer user_data)
|
||||
{
|
||||
GskPathBuilder *builder = user_data;
|
||||
|
||||
if (op == GSK_PATH_MOVE)
|
||||
return TRUE;
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case GSK_PATH_MOVE:
|
||||
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_QUAD:
|
||||
gsk_path_builder_quad_to (builder, pts[1].x, pts[1].y,
|
||||
pts[2].x, pts[2].y);
|
||||
break;
|
||||
|
||||
case GSK_PATH_CUBIC:
|
||||
gsk_path_builder_cubic_to (builder, pts[1].x, pts[1].y,
|
||||
pts[2].x, pts[2].y,
|
||||
pts[3].x, pts[3].y);
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
test_foreach (void)
|
||||
{
|
||||
const char *s = "M 0 0 Q 9 0, 9 9 Q 99 9, 99 18 Z";
|
||||
const char *sp = "M 0 0 C 6 0, 9 3, 9 9 C 69 9, 99 12, 99 18 Z";
|
||||
GskPath *path, *path2;
|
||||
char *s2;
|
||||
GskPathBuilder *builder;
|
||||
|
||||
path = gsk_path_parse (s);
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
gsk_path_foreach (path, GSK_PATH_FOREACH_ALLOW_QUAD, collect_path, builder);
|
||||
path2 = gsk_path_builder_free_to_path (builder);
|
||||
s2 = gsk_path_to_string (path2);
|
||||
|
||||
g_assert_cmpstr (s, ==, s2);
|
||||
|
||||
gsk_path_unref (path2);
|
||||
g_free (s2);
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
gsk_path_foreach (path, GSK_PATH_FOREACH_ALLOW_QUAD|GSK_PATH_FOREACH_ALLOW_CUBIC, collect_path, builder);
|
||||
path2 = gsk_path_builder_free_to_path (builder);
|
||||
s2 = gsk_path_to_string (path2);
|
||||
|
||||
g_assert_cmpstr (s, ==, s2);
|
||||
|
||||
gsk_path_unref (path2);
|
||||
g_free (s2);
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
gsk_path_foreach (path, GSK_PATH_FOREACH_ALLOW_CUBIC, collect_path, builder);
|
||||
path2 = gsk_path_builder_free_to_path (builder);
|
||||
s2 = gsk_path_to_string (path2);
|
||||
|
||||
g_assert_cmpstr (sp, ==, s2);
|
||||
|
||||
gsk_path_unref (path2);
|
||||
g_free (s2);
|
||||
}
|
||||
|
||||
/* Test the basics of the path point api */
|
||||
static void
|
||||
test_path_point (void)
|
||||
{
|
||||
GskPath *path;
|
||||
GskPathPoint point;
|
||||
gboolean ret;
|
||||
graphene_point_t pos, center;
|
||||
graphene_vec2_t t1, t2, mx;
|
||||
float curvature;
|
||||
|
||||
path = gsk_path_parse ("M0,0L100,0L100,100L0,100Z");
|
||||
|
||||
ret = gsk_path_get_closest_point (path, &GRAPHENE_POINT_INIT (200, 200), INFINITY, &point);
|
||||
g_assert_true (ret);
|
||||
|
||||
gsk_path_point_get_position (path, &point, &pos);
|
||||
gsk_path_point_get_tangent (path, &point, GSK_PATH_START, &t1);
|
||||
gsk_path_point_get_tangent (path, &point, GSK_PATH_END, &t2);
|
||||
curvature = gsk_path_point_get_curvature (path, &point, ¢er);
|
||||
|
||||
g_assert_true (graphene_point_equal (&pos, &GRAPHENE_POINT_INIT (100, 100)));
|
||||
g_assert_true (graphene_vec2_equal (&t1, graphene_vec2_y_axis ()));
|
||||
graphene_vec2_negate (graphene_vec2_x_axis (), &mx);
|
||||
g_assert_true (graphene_vec2_equal (&t2, &mx));
|
||||
g_assert_true (curvature == 0);
|
||||
|
||||
gsk_path_unref (path);
|
||||
}
|
||||
|
||||
/* Test that gsk_path_builder_add_segment yields the expected results */
|
||||
static void
|
||||
test_path_segments (void)
|
||||
{
|
||||
struct {
|
||||
const char *path;
|
||||
graphene_point_t p1;
|
||||
graphene_point_t p2;
|
||||
const char *result;
|
||||
} tests[] = {
|
||||
{
|
||||
"M 0 0 L 100 0 L 50 50 Z",
|
||||
{ 100, 0 },
|
||||
{ 50, 50 },
|
||||
"M 100 0 L 50 50"
|
||||
},
|
||||
{
|
||||
"M 0 0 L 100 0 L 50 50 Z",
|
||||
{ 50, 0 },
|
||||
{ 70, 0 },
|
||||
"M 50 0 L 70 0"
|
||||
},
|
||||
{
|
||||
"M 0 0 L 100 0 L 50 50 Z",
|
||||
{ 70, 0 },
|
||||
{ 50, 0 },
|
||||
"M 70 0 L 100 0 L 50 50 L 0 0 L 50 0"
|
||||
},
|
||||
{
|
||||
"M 0 0 L 100 0 L 50 50 Z",
|
||||
{ 50, 0 },
|
||||
{ 50, 50 },
|
||||
"M 50 0 L 100 0 L 50 50"
|
||||
}
|
||||
};
|
||||
|
||||
for (unsigned int i = 0; i < G_N_ELEMENTS (tests); i++)
|
||||
{
|
||||
GskPath *path;
|
||||
GskPathPoint p1, p2;
|
||||
GskPathBuilder *builder;
|
||||
GskPath *result;
|
||||
char *str;
|
||||
|
||||
path = gsk_path_parse (tests[i].path);
|
||||
g_assert_true (gsk_path_get_closest_point (path, &tests[i].p1, INFINITY, &p1));
|
||||
g_assert_true (gsk_path_get_closest_point (path, &tests[i].p2, INFINITY, &p2));
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
gsk_path_builder_add_segment (builder, path, &p1, &p2);
|
||||
result = gsk_path_builder_free_to_path (builder);
|
||||
str = gsk_path_to_string (result);
|
||||
|
||||
g_assert_cmpstr (str, ==, tests[i].result);
|
||||
|
||||
g_free (str);
|
||||
gsk_path_unref (path);
|
||||
}
|
||||
gsk_path_unref (path1);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
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/empty", test_empty_path);
|
||||
g_test_add_func ("/path/rect", test_rect_path);
|
||||
g_test_add_func ("/path/foreach", test_foreach);
|
||||
g_test_add_func ("/path/point", test_path_point);
|
||||
g_test_add_func ("/path/segments", test_path_segments);
|
||||
g_test_add_func ("/path/serialize-custom-contours", test_serialize_custom_contours);
|
||||
|
||||
return g_test_run ();
|
||||
}
|
||||
|
||||
@@ -19,10 +19,19 @@
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
static float
|
||||
random_weight (void)
|
||||
{
|
||||
if (g_test_rand_bit ())
|
||||
return g_test_rand_double_range (1, 20);
|
||||
else
|
||||
return 1.0 / g_test_rand_double_range (1, 20);
|
||||
}
|
||||
|
||||
static GskPath *
|
||||
create_random_degenerate_path (guint max_contours)
|
||||
{
|
||||
#define N_DEGENERATE_PATHS 14
|
||||
#define N_DEGENERATE_PATHS 15
|
||||
GskPathBuilder *builder;
|
||||
guint i;
|
||||
|
||||
@@ -142,7 +151,7 @@ create_random_degenerate_path (guint max_contours)
|
||||
break;
|
||||
|
||||
case 13:
|
||||
/* a cubic with start == end */
|
||||
/* a curve with start == end */
|
||||
{
|
||||
graphene_point_t point = GRAPHENE_POINT_INIT (g_test_rand_double_range (-1000, 1000),
|
||||
g_test_rand_double_range (-1000, 1000));
|
||||
@@ -156,6 +165,20 @@ create_random_degenerate_path (guint max_contours)
|
||||
}
|
||||
break;
|
||||
|
||||
case 14:
|
||||
/* a conic with start == end */
|
||||
{
|
||||
graphene_point_t point = GRAPHENE_POINT_INIT (g_test_rand_double_range (-1000, 1000),
|
||||
g_test_rand_double_range (-1000, 1000));
|
||||
gsk_path_builder_move_to (builder, point.x, point.y);
|
||||
gsk_path_builder_conic_to (builder,
|
||||
g_test_rand_double_range (-1000, 1000),
|
||||
g_test_rand_double_range (-1000, 1000),
|
||||
point.x, point.y,
|
||||
random_weight ());
|
||||
}
|
||||
break;
|
||||
|
||||
case N_DEGENERATE_PATHS:
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
@@ -225,7 +248,7 @@ add_standard_contour (GskPathBuilder *builder)
|
||||
n = g_test_rand_int_range (1, 20);
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
switch (g_test_rand_int_range (0, 6))
|
||||
switch (g_test_rand_int_range (0, 8))
|
||||
{
|
||||
case 0:
|
||||
gsk_path_builder_line_to (builder,
|
||||
@@ -275,6 +298,24 @@ add_standard_contour (GskPathBuilder *builder)
|
||||
g_test_rand_double_range (-1000, 1000));
|
||||
break;
|
||||
|
||||
case 6:
|
||||
gsk_path_builder_conic_to (builder,
|
||||
g_test_rand_double_range (-1000, 1000),
|
||||
g_test_rand_double_range (-1000, 1000),
|
||||
g_test_rand_double_range (-1000, 1000),
|
||||
g_test_rand_double_range (-1000, 1000),
|
||||
random_weight ());
|
||||
break;
|
||||
|
||||
case 7:
|
||||
gsk_path_builder_rel_conic_to (builder,
|
||||
g_test_rand_double_range (-1000, 1000),
|
||||
g_test_rand_double_range (-1000, 1000),
|
||||
g_test_rand_double_range (-1000, 1000),
|
||||
g_test_rand_double_range (-1000, 1000),
|
||||
random_weight ());
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
break;
|
||||
@@ -314,6 +355,7 @@ create_random_path (guint max_contours)
|
||||
typedef struct {
|
||||
GskPathOperation op;
|
||||
graphene_point_t pts[4];
|
||||
float weight;
|
||||
} PathOperation;
|
||||
|
||||
static void
|
||||
@@ -371,6 +413,16 @@ path_operation_print (const PathOperation *p,
|
||||
_g_string_append_point (string, &p->pts[3]);
|
||||
break;
|
||||
|
||||
case GSK_PATH_CONIC:
|
||||
/* This is not valid SVG */
|
||||
g_string_append (string, " O ");
|
||||
_g_string_append_point (string, &p->pts[1]);
|
||||
g_string_append (string, ", ");
|
||||
_g_string_append_point (string, &p->pts[2]);
|
||||
g_string_append (string, ", ");
|
||||
_g_string_append_double (string, p->weight);
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
return;
|
||||
@@ -405,6 +457,11 @@ path_operation_equal (const PathOperation *p1,
|
||||
&& graphene_point_near (&p1->pts[2], &p2->pts[2], epsilon)
|
||||
&& graphene_point_near (&p1->pts[3], &p2->pts[3], epsilon);
|
||||
|
||||
case GSK_PATH_CONIC:
|
||||
return graphene_point_near (&p1->pts[1], &p2->pts[1], epsilon)
|
||||
&& graphene_point_near (&p1->pts[2], &p2->pts[2], epsilon)
|
||||
&& G_APPROX_VALUE (p1->weight, p2->weight, epsilon);
|
||||
|
||||
default:
|
||||
g_return_val_if_reached (FALSE);
|
||||
}
|
||||
@@ -414,6 +471,7 @@ static gboolean
|
||||
collect_path_operation_cb (GskPathOperation op,
|
||||
const graphene_point_t *pts,
|
||||
gsize n_pts,
|
||||
float weight,
|
||||
gpointer user_data)
|
||||
{
|
||||
g_array_append_vals (user_data,
|
||||
@@ -428,6 +486,7 @@ collect_path_operation_cb (GskPathOperation op,
|
||||
GRAPHENE_POINT_INIT(n_pts > 3 ? pts[3].x : 0,
|
||||
n_pts > 3 ? pts[3].y : 0)
|
||||
},
|
||||
weight
|
||||
} },
|
||||
1);
|
||||
return TRUE;
|
||||
|
||||
142
tools/gtk-path-tool-decompose.c
Normal file
142
tools/gtk-path-tool-decompose.c
Normal file
@@ -0,0 +1,142 @@
|
||||
/* Copyright 2023 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 <gtk/gtk.h>
|
||||
#include "gtk-path-tool.h"
|
||||
|
||||
#include <glib/gi18n-lib.h>
|
||||
|
||||
static gboolean
|
||||
foreach_cb (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_QUAD:
|
||||
gsk_path_builder_quad_to (builder, pts[1].x, pts[1].y, pts[2].x, pts[2].y);
|
||||
break;
|
||||
|
||||
case GSK_PATH_CUBIC:
|
||||
gsk_path_builder_cubic_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 ();
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
do_decompose (int *argc, const char ***argv)
|
||||
{
|
||||
GError *error = NULL;
|
||||
gboolean allow_quad = FALSE;
|
||||
gboolean allow_curve = FALSE;
|
||||
gboolean allow_conic = FALSE;
|
||||
char **args = NULL;
|
||||
GOptionContext *context;
|
||||
GOptionEntry entries[] = {
|
||||
{ "allow-quad", 0, 0, G_OPTION_ARG_NONE, &allow_quad, N_("Allow quadratic Bézier curves"), NULL },
|
||||
{ "allow-cubic", 0, 0, G_OPTION_ARG_NONE, &allow_curve, N_("Allow cubic Bézier curves"), NULL },
|
||||
{ "allow-conic", 0, 0, G_OPTION_ARG_NONE, &allow_conic, N_("Allow rational quadratic Bézier curves"), NULL },
|
||||
{ G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &args, NULL, N_("PATH") },
|
||||
{ NULL, },
|
||||
};
|
||||
GskPathForeachFlags flags;
|
||||
GskPath *path, *result;
|
||||
GskPathBuilder *builder;
|
||||
|
||||
g_set_prgname ("gtk4-path-tool decompose");
|
||||
|
||||
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, _("Decompose a path."));
|
||||
|
||||
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 (args == NULL)
|
||||
{
|
||||
g_printerr ("%s\n", _("No paths given."));
|
||||
exit (1);
|
||||
}
|
||||
|
||||
path = get_path (args[0]);
|
||||
|
||||
flags = 0;
|
||||
if (allow_quad)
|
||||
flags |= GSK_PATH_FOREACH_ALLOW_QUAD;
|
||||
if (allow_curve)
|
||||
flags |= GSK_PATH_FOREACH_ALLOW_CUBIC;
|
||||
if (allow_conic)
|
||||
flags |= GSK_PATH_FOREACH_ALLOW_CONIC;
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
|
||||
gsk_path_foreach (path, flags, foreach_cb, builder);
|
||||
|
||||
result = gsk_path_builder_free_to_path (builder);
|
||||
|
||||
if (result)
|
||||
{
|
||||
char *str = gsk_path_to_string (result);
|
||||
g_print ("%s\n", str);
|
||||
g_free (str);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_printerr ("%s\n", _("That didn't work out."));
|
||||
exit (1);
|
||||
}
|
||||
}
|
||||
80
tools/gtk-path-tool-info.c
Normal file
80
tools/gtk-path-tool-info.c
Normal file
@@ -0,0 +1,80 @@
|
||||
/* Copyright 2023 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 <gtk/gtk.h>
|
||||
#include "gtk-path-tool.h"
|
||||
|
||||
#include <glib/gi18n-lib.h>
|
||||
|
||||
void
|
||||
do_info (int *argc, const char ***argv)
|
||||
{
|
||||
GError *error = NULL;
|
||||
char **args = NULL;
|
||||
GOptionContext *context;
|
||||
GOptionEntry entries[] = {
|
||||
{ G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &args, NULL, N_("PATH") },
|
||||
{ NULL, },
|
||||
};
|
||||
GskPath *path;
|
||||
GskPathMeasure *measure;
|
||||
graphene_rect_t bounds;
|
||||
|
||||
g_set_prgname ("gtk4-path-tool info");
|
||||
|
||||
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, _("Print information about a path."));
|
||||
|
||||
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 (args == NULL)
|
||||
{
|
||||
g_printerr ("%s\n", _("No paths given."));
|
||||
exit (1);
|
||||
}
|
||||
|
||||
path = get_path (args[0]);
|
||||
measure = gsk_path_measure_new (path);
|
||||
|
||||
if (gsk_path_is_empty (path))
|
||||
g_print ("%s\n", _("Path is empty."));
|
||||
else
|
||||
{
|
||||
if (gsk_path_is_closed (path))
|
||||
g_print ("%s\n", _("Path is closed"));
|
||||
|
||||
g_print ("%s %g\n", _("Path length"), gsk_path_measure_get_length (measure));
|
||||
|
||||
if (gsk_path_get_bounds (path, &bounds))
|
||||
g_print ("%s: %g %g %g %g\n", _("Bounds"),
|
||||
bounds.origin.x, bounds.origin.y,
|
||||
bounds.size.width, bounds.size.height);
|
||||
}
|
||||
}
|
||||
143
tools/gtk-path-tool-render.c
Normal file
143
tools/gtk-path-tool-render.c
Normal file
@@ -0,0 +1,143 @@
|
||||
/* Copyright 2023 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-lib.h>
|
||||
#include <glib/gprintf.h>
|
||||
#include <glib/gstdio.h>
|
||||
#include <gtk/gtk.h>
|
||||
#include "gtk-path-tool.h"
|
||||
|
||||
|
||||
void
|
||||
do_render (int *argc,
|
||||
const char ***argv)
|
||||
{
|
||||
GError *error = NULL;
|
||||
const char *fill = "winding";
|
||||
const char *fg_color = "black";
|
||||
const char *bg_color = "white";
|
||||
const char *output_file = NULL;
|
||||
char **args = NULL;
|
||||
GOptionContext *context;
|
||||
const GOptionEntry entries[] = {
|
||||
{ "fill-rule", 0, 0, G_OPTION_ARG_STRING, &fill, N_("Fill rule (winding, even-odd)"), N_("VALUE") },
|
||||
{ "fg-color", 0, 0, G_OPTION_ARG_STRING, &fg_color, N_("Foreground color"), N_("COLOR") },
|
||||
{ "bg-color", 0, 0, G_OPTION_ARG_STRING, &bg_color, N_("Background color"), N_("COLOR") },
|
||||
{ "output", 0, 0, G_OPTION_ARG_FILENAME, &output_file, N_("The output file"), N_("FILE") },
|
||||
{ G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &args, NULL, N_("PATH") },
|
||||
{ NULL, }
|
||||
};
|
||||
GskPath *path;
|
||||
GskFillRule fill_rule;
|
||||
GdkRGBA fg, bg;
|
||||
graphene_rect_t bounds;
|
||||
GskRenderNode *fg_node, *nodes[2], *node;
|
||||
GdkSurface *surface;
|
||||
GskRenderer *renderer;
|
||||
GdkTexture *texture;
|
||||
const char *filename;
|
||||
|
||||
if (gdk_display_get_default () == NULL)
|
||||
{
|
||||
g_printerr ("%s\n", _("Could not initialize windowing system"));
|
||||
exit (1);
|
||||
}
|
||||
|
||||
g_set_prgname ("gtk4-path-tool render");
|
||||
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, _("Render the path to a png image."));
|
||||
|
||||
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 (args == NULL)
|
||||
{
|
||||
g_printerr ("%s\n", _("No path specified"));
|
||||
exit (1);
|
||||
}
|
||||
|
||||
if (g_strv_length (args) > 1)
|
||||
{
|
||||
g_printerr ("%s\n", _("Can only render a single path"));
|
||||
exit (1);
|
||||
}
|
||||
|
||||
path = get_path (args[0]);
|
||||
|
||||
fill_rule = get_enum_value (GSK_TYPE_FILL_RULE, _("fill rule"), fill);
|
||||
get_color (&fg, fg_color);
|
||||
get_color (&bg, bg_color);
|
||||
|
||||
gsk_path_get_bounds (path, &bounds);
|
||||
graphene_rect_inset (&bounds, -10, -10);
|
||||
|
||||
nodes[0] = gsk_color_node_new (&bg, &bounds);
|
||||
fg_node = gsk_color_node_new (&fg, &bounds);
|
||||
nodes[1] = gsk_fill_node_new (fg_node, path, fill_rule);
|
||||
|
||||
node = gsk_container_node_new (nodes, 2);
|
||||
|
||||
gsk_render_node_unref (fg_node);
|
||||
gsk_render_node_unref (nodes[0]);
|
||||
gsk_render_node_unref (nodes[1]);
|
||||
|
||||
surface = gdk_surface_new_toplevel (gdk_display_get_default ());
|
||||
renderer = gsk_renderer_new_for_surface (surface);
|
||||
|
||||
texture = gsk_renderer_render_texture (renderer, node, &bounds);
|
||||
|
||||
filename = output_file ? output_file : "path.png";
|
||||
if (!gdk_texture_save_to_png (texture, filename))
|
||||
{
|
||||
char *msg = g_strdup_printf (_("Saving png to '%s' failed"), filename);
|
||||
g_printerr ("%s\n", msg);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
if (output_file == NULL)
|
||||
{
|
||||
char *msg = g_strdup_printf (_("Output written to '%s'."), filename);
|
||||
g_print ("%s\n", msg);
|
||||
g_free (msg);
|
||||
}
|
||||
|
||||
g_object_unref (texture);
|
||||
gsk_renderer_unrealize (renderer);
|
||||
g_object_unref (renderer);
|
||||
g_object_unref (surface);
|
||||
gsk_render_node_unref (node);
|
||||
|
||||
gsk_path_unref (path);
|
||||
|
||||
g_strfreev (args);
|
||||
}
|
||||
93
tools/gtk-path-tool-restrict.c
Normal file
93
tools/gtk-path-tool-restrict.c
Normal file
@@ -0,0 +1,93 @@
|
||||
/* Copyright 2023 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 <gtk/gtk.h>
|
||||
#include "gtk-path-tool.h"
|
||||
|
||||
#include <glib/gi18n-lib.h>
|
||||
|
||||
void
|
||||
do_restrict (int *argc, const char ***argv)
|
||||
{
|
||||
GError *error = NULL;
|
||||
double start = G_MAXDOUBLE;
|
||||
double end = G_MAXDOUBLE;
|
||||
char **args = NULL;
|
||||
GOptionContext *context;
|
||||
GOptionEntry entries[] = {
|
||||
{ "start", 0, 0, G_OPTION_ARG_DOUBLE, &start, N_("Beginning of segment"), N_("LENGTH") },
|
||||
{ "end", 0, 0, G_OPTION_ARG_DOUBLE, &start, N_("End of segment"), N_("LENGTH") },
|
||||
{ G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &args, NULL, N_("PATH") },
|
||||
{ NULL, },
|
||||
};
|
||||
GskPath *path, *result;
|
||||
GskPathMeasure *measure;
|
||||
GskPathBuilder *builder;
|
||||
|
||||
g_set_prgname ("gtk4-path-tool restrict");
|
||||
|
||||
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, _("Restrict a path to a segment."));
|
||||
|
||||
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 (args == NULL)
|
||||
{
|
||||
g_printerr ("%s\n", _("No paths given."));
|
||||
exit (1);
|
||||
}
|
||||
|
||||
path = get_path (args[0]);
|
||||
measure = gsk_path_measure_new (path);
|
||||
|
||||
if (start == G_MAXDOUBLE)
|
||||
start = 0;
|
||||
|
||||
if (end == G_MAXDOUBLE)
|
||||
end = gsk_path_measure_get_length (measure);
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
|
||||
gsk_path_builder_add_segment (builder, measure, start, end);
|
||||
|
||||
result = gsk_path_builder_free_to_path (builder);
|
||||
|
||||
if (result)
|
||||
{
|
||||
char *str = gsk_path_to_string (result);
|
||||
g_print ("%s\n", str);
|
||||
g_free (str);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_printerr ("%s\n", _("That didn't work out."));
|
||||
exit (1);
|
||||
}
|
||||
}
|
||||
134
tools/gtk-path-tool-show.c
Normal file
134
tools/gtk-path-tool-show.c
Normal file
@@ -0,0 +1,134 @@
|
||||
/* Copyright 2023 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-lib.h>
|
||||
#include <glib/gprintf.h>
|
||||
#include <glib/gstdio.h>
|
||||
#include <gtk/gtk.h>
|
||||
#include "gtk-path-tool.h"
|
||||
|
||||
#include "path-view.h"
|
||||
|
||||
static void
|
||||
show_path (GskPath *path,
|
||||
GskFillRule fill_rule,
|
||||
const GdkRGBA *fg_color,
|
||||
const GdkRGBA *bg_color)
|
||||
{
|
||||
GtkWidget *window, *sw, *child;
|
||||
|
||||
window = gtk_window_new ();
|
||||
gtk_window_set_title (GTK_WINDOW (window), _("Path Preview"));
|
||||
|
||||
// gtk_window_set_default_size (GTK_WINDOW (window), 700, 500);
|
||||
|
||||
sw = gtk_scrolled_window_new ();
|
||||
gtk_scrolled_window_set_propagate_natural_width (GTK_SCROLLED_WINDOW (sw), TRUE);
|
||||
gtk_scrolled_window_set_propagate_natural_height (GTK_SCROLLED_WINDOW (sw), TRUE);
|
||||
gtk_window_set_child (GTK_WINDOW (window), sw);
|
||||
|
||||
child = path_view_new (path);
|
||||
g_object_set (child,
|
||||
"fill-rule", fill_rule,
|
||||
"fg-color", fg_color,
|
||||
"bg-color", bg_color,
|
||||
NULL);
|
||||
gtk_widget_set_hexpand (child, TRUE);
|
||||
gtk_widget_set_vexpand (child, TRUE);
|
||||
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), child);
|
||||
|
||||
gtk_window_present (GTK_WINDOW (window));
|
||||
|
||||
while (g_list_model_get_n_items (gtk_window_get_toplevels ()) > 0)
|
||||
g_main_context_iteration (NULL, TRUE);
|
||||
}
|
||||
|
||||
void
|
||||
do_show (int *argc,
|
||||
const char ***argv)
|
||||
{
|
||||
GError *error = NULL;
|
||||
const char *fill = "winding";
|
||||
const char *fg_color = "black";
|
||||
const char *bg_color = "white";
|
||||
char **args = NULL;
|
||||
GOptionContext *context;
|
||||
const GOptionEntry entries[] = {
|
||||
{ "fill-rule", 0, 0, G_OPTION_ARG_STRING, &fill, N_("Fill rule (winding, even-odd)"), N_("VALUE") },
|
||||
{ "fg-color", 0, 0, G_OPTION_ARG_STRING, &fg_color, N_("Foreground color"), N_("COLOR") },
|
||||
{ "bg-color", 0, 0, G_OPTION_ARG_STRING, &bg_color, N_("Background color"), N_("COLOR") },
|
||||
{ G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &args, NULL, N_("PATH") },
|
||||
{ NULL, }
|
||||
};
|
||||
GskPath *path;
|
||||
GskFillRule fill_rule;
|
||||
GdkRGBA fg;
|
||||
GdkRGBA bg;
|
||||
|
||||
if (gdk_display_get_default () == NULL)
|
||||
{
|
||||
g_printerr ("%s\n", _("Could not initialize windowing system"));
|
||||
exit (1);
|
||||
}
|
||||
|
||||
g_set_prgname ("gtk4-path-tool show");
|
||||
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, _("Display the path."));
|
||||
|
||||
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 (args == NULL)
|
||||
{
|
||||
g_printerr ("%s\n", _("No path specified"));
|
||||
exit (1);
|
||||
}
|
||||
|
||||
if (g_strv_length (args) > 1)
|
||||
{
|
||||
g_printerr ("%s\n", _("Can only show a single path"));
|
||||
exit (1);
|
||||
}
|
||||
|
||||
path = get_path (args[0]);
|
||||
|
||||
fill_rule = get_enum_value (GSK_TYPE_FILL_RULE, _("fill rule"), fill);
|
||||
get_color (&fg, fg_color);
|
||||
get_color (&bg, bg_color);
|
||||
|
||||
show_path (path, fill_rule, &fg, &bg);
|
||||
|
||||
gsk_path_unref (path);
|
||||
|
||||
g_strfreev (args);
|
||||
}
|
||||
139
tools/gtk-path-tool-utils.c
Normal file
139
tools/gtk-path-tool-utils.c
Normal file
@@ -0,0 +1,139 @@
|
||||
/* Copyright 2023 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 <gtk/gtk.h>
|
||||
#include <gio/gio.h>
|
||||
#ifdef G_OS_UNIX
|
||||
#include <gio/gunixinputstream.h>
|
||||
#endif
|
||||
#include "gtk-path-tool.h"
|
||||
|
||||
#include <glib/gi18n-lib.h>
|
||||
|
||||
GskPath *
|
||||
get_path (const char *arg)
|
||||
{
|
||||
char *buffer = NULL;
|
||||
gsize len;
|
||||
GError *error = NULL;
|
||||
GskPath *path;
|
||||
|
||||
if (arg[0] == '.' || arg[0] == '/')
|
||||
{
|
||||
if (!g_file_get_contents (arg, &buffer, &len, &error))
|
||||
{
|
||||
g_printerr ("%s\n", error->message);
|
||||
exit (1);
|
||||
}
|
||||
}
|
||||
#ifdef G_OS_UNIX
|
||||
else if (strcmp (arg, "-") == 0)
|
||||
{
|
||||
GInputStream *in;
|
||||
GOutputStream *out;
|
||||
|
||||
in = g_unix_input_stream_new (0, FALSE);
|
||||
out = g_memory_output_stream_new (NULL, 0, g_realloc, g_free);
|
||||
|
||||
if (g_output_stream_splice (out, in, 0, NULL, &error) < 0)
|
||||
{
|
||||
g_printerr (_("Failed to read from standard input: %s\n"), error->message);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
if (!g_output_stream_close (out, NULL, &error))
|
||||
{
|
||||
g_printerr (_("Error reading from standard input: %s\n"), error->message);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
buffer = g_memory_output_stream_steal_data (G_MEMORY_OUTPUT_STREAM (out));
|
||||
|
||||
g_object_unref (out);
|
||||
g_object_unref (in);
|
||||
}
|
||||
#endif
|
||||
else
|
||||
buffer = g_strdup (arg);
|
||||
|
||||
g_strstrip (buffer);
|
||||
|
||||
path = gsk_path_parse (buffer);
|
||||
|
||||
if (path == NULL)
|
||||
{
|
||||
g_printerr (_("Failed to parse '%s' as path.\n"), arg);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
g_free (buffer);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
int
|
||||
get_enum_value (GType type,
|
||||
const char *type_nick,
|
||||
const char *str)
|
||||
{
|
||||
GEnumClass *class = g_type_class_ref (type);
|
||||
GEnumValue *value;
|
||||
int val;
|
||||
|
||||
value = g_enum_get_value_by_nick (class, str);
|
||||
if (value)
|
||||
val = value->value;
|
||||
else
|
||||
{
|
||||
GString *s;
|
||||
|
||||
s = g_string_new ("");
|
||||
g_string_append_printf (s, _("Failed to parse '%s' as %s."), str, type_nick);
|
||||
g_string_append (s, "\n");
|
||||
g_string_append (s, _("Possible values: "));
|
||||
|
||||
for (unsigned int i = 0; i < class->n_values; i++)
|
||||
{
|
||||
if (i > 0)
|
||||
g_string_append (s, ", ");
|
||||
g_string_append (s, class->values[i].value_nick);
|
||||
}
|
||||
g_printerr ("%s\n", s->str);
|
||||
g_string_free (s, TRUE);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
g_type_class_unref (class);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
void
|
||||
get_color (GdkRGBA *rgba,
|
||||
const char *str)
|
||||
{
|
||||
if (!gdk_rgba_parse (rgba, str))
|
||||
{
|
||||
char *msg = g_strdup_printf (_("Could not parse '%s' as color"), str);
|
||||
g_printerr ("%s\n", msg);
|
||||
exit (1);
|
||||
}
|
||||
}
|
||||
142
tools/gtk-path-tool.c
Normal file
142
tools/gtk-path-tool.c
Normal file
@@ -0,0 +1,142 @@
|
||||
/* Copyright 2023 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-lib.h>
|
||||
#include <glib/gprintf.h>
|
||||
#include <glib/gstdio.h>
|
||||
#include <gtk/gtk.h>
|
||||
#include "gtk-path-tool.h"
|
||||
|
||||
static void G_GNUC_NORETURN
|
||||
usage (void)
|
||||
{
|
||||
g_print (_("Usage:\n"
|
||||
" gtk4-path-tool [COMMAND] [OPTION…] PATH\n"
|
||||
"\n"
|
||||
"Perform various tasks on paths.\n"
|
||||
"\n"
|
||||
"Commands:\n"
|
||||
" decompose Decompose the path\n"
|
||||
" restrict Restrict the path to a segment\n"
|
||||
" show Display the path in a window\n"
|
||||
" render Render the path as an image\n"
|
||||
" info Print information about the path\n"
|
||||
"\n"));
|
||||
exit (1);
|
||||
}
|
||||
|
||||
/* A simplified version of g_log_writer_default_would_drop(), to avoid
|
||||
* bumping up the required version of GLib to 2.68
|
||||
*/
|
||||
static gboolean
|
||||
would_drop (GLogLevelFlags level,
|
||||
const char *domain)
|
||||
{
|
||||
#if GLIB_CHECK_VERSION (2, 68, 0)
|
||||
return g_log_writer_default_would_drop (level, domain);
|
||||
#else
|
||||
return (level & (G_LOG_LEVEL_ERROR |
|
||||
G_LOG_LEVEL_CRITICAL |
|
||||
G_LOG_LEVEL_WARNING)) == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static GLogWriterOutput
|
||||
log_writer_func (GLogLevelFlags level,
|
||||
const GLogField *fields,
|
||||
gsize n_fields,
|
||||
gpointer user_data)
|
||||
{
|
||||
gsize i;
|
||||
const char *domain = NULL;
|
||||
const char *message = NULL;
|
||||
|
||||
for (i = 0; i < n_fields; i++)
|
||||
{
|
||||
if (g_strcmp0 (fields[i].key, "GLIB_DOMAIN") == 0)
|
||||
domain = fields[i].value;
|
||||
else if (g_strcmp0 (fields[i].key, "MESSAGE") == 0)
|
||||
message = fields[i].value;
|
||||
}
|
||||
|
||||
if (message != NULL && !would_drop (level, domain))
|
||||
{
|
||||
const char *prefix;
|
||||
switch (level & G_LOG_LEVEL_MASK)
|
||||
{
|
||||
case G_LOG_LEVEL_ERROR:
|
||||
prefix = "ERROR";
|
||||
break;
|
||||
case G_LOG_LEVEL_CRITICAL:
|
||||
prefix = "CRITICAL";
|
||||
break;
|
||||
case G_LOG_LEVEL_WARNING:
|
||||
prefix = "WARNING";
|
||||
break;
|
||||
default:
|
||||
prefix = "INFO";
|
||||
break;
|
||||
}
|
||||
g_printerr ("%s-%s: %s\n", domain, prefix, message);
|
||||
}
|
||||
|
||||
return G_LOG_WRITER_HANDLED;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, const char *argv[])
|
||||
{
|
||||
g_set_prgname ("gtk4-path-tool");
|
||||
|
||||
g_log_set_writer_func (log_writer_func, NULL, NULL);
|
||||
|
||||
gtk_init_check ();
|
||||
|
||||
gtk_test_register_all_types ();
|
||||
|
||||
if (argc < 2)
|
||||
usage ();
|
||||
|
||||
if (strcmp (argv[1], "--help") == 0)
|
||||
usage ();
|
||||
|
||||
argv++;
|
||||
argc--;
|
||||
|
||||
if (strcmp (argv[0], "decompose") == 0)
|
||||
do_decompose (&argc, &argv);
|
||||
else if (strcmp (argv[0], "info") == 0)
|
||||
do_info (&argc, &argv);
|
||||
else if (strcmp (argv[0], "render") == 0)
|
||||
do_render (&argc, &argv);
|
||||
else if (strcmp (argv[0], "restrict") == 0)
|
||||
do_restrict (&argc, &argv);
|
||||
else if (strcmp (argv[0], "show") == 0)
|
||||
do_show (&argc, &argv);
|
||||
else
|
||||
usage ();
|
||||
|
||||
return 0;
|
||||
}
|
||||
14
tools/gtk-path-tool.h
Normal file
14
tools/gtk-path-tool.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
void do_info (int *argc, const char ***argv);
|
||||
void do_decompose (int *argc, const char ***argv);
|
||||
void do_restrict (int *argc, const char ***argv);
|
||||
void do_render (int *argc, const char ***argv);
|
||||
void do_show (int *argc, const char ***argv);
|
||||
|
||||
GskPath *get_path (const char *arg);
|
||||
int get_enum_value (GType type,
|
||||
const char *type_nick,
|
||||
const char *str);
|
||||
void get_color (GdkRGBA *rgba,
|
||||
const char *str);
|
||||
@@ -23,6 +23,14 @@ if win32_enabled
|
||||
endif
|
||||
|
||||
gtk_tools = [
|
||||
['gtk4-path-tool', ['gtk-path-tool.c',
|
||||
'gtk-path-tool-decompose.c',
|
||||
'gtk-path-tool-info.c',
|
||||
'gtk-path-tool-render.c',
|
||||
'gtk-path-tool-restrict.c',
|
||||
'gtk-path-tool-show.c',
|
||||
'gtk-path-tool-utils.c',
|
||||
'path-view.c'], [libgtk_dep]],
|
||||
['gtk4-query-settings', ['gtk-query-settings.c'], [libgtk_dep]],
|
||||
['gtk4-builder-tool', ['gtk-builder-tool.c',
|
||||
'gtk-builder-tool-simplify.c',
|
||||
|
||||
221
tools/path-view.c
Normal file
221
tools/path-view.c
Normal file
@@ -0,0 +1,221 @@
|
||||
/* Copyright 2023 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 "path-view.h"
|
||||
|
||||
struct _PathView
|
||||
{
|
||||
GtkWidget parent_instance;
|
||||
|
||||
GskPath *path;
|
||||
graphene_rect_t bounds;
|
||||
GskFillRule fill_rule;
|
||||
GdkRGBA fg;
|
||||
GdkRGBA bg;
|
||||
int padding;
|
||||
};
|
||||
|
||||
enum {
|
||||
PROP_PATH = 1,
|
||||
PROP_FILL_RULE,
|
||||
PROP_FG_COLOR,
|
||||
PROP_BG_COLOR,
|
||||
N_PROPERTIES
|
||||
};
|
||||
|
||||
static GParamSpec *properties[N_PROPERTIES] = { NULL, };
|
||||
|
||||
struct _PathViewClass
|
||||
{
|
||||
GtkWidgetClass parent_class;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (PathView, path_view, GTK_TYPE_WIDGET)
|
||||
|
||||
static void
|
||||
path_view_init (PathView *self)
|
||||
{
|
||||
self->fill_rule = GSK_FILL_RULE_WINDING;
|
||||
self->fg = (GdkRGBA) { 0, 0, 0, 1};
|
||||
self->bg = (GdkRGBA) { 1, 1, 1, 1};
|
||||
self->padding = 10;
|
||||
}
|
||||
|
||||
static void
|
||||
path_view_dispose (GObject *object)
|
||||
{
|
||||
PathView *self = PATH_VIEW (object);
|
||||
|
||||
g_clear_pointer (&self->path, gsk_path_unref);
|
||||
|
||||
G_OBJECT_CLASS (path_view_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
path_view_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
PathView *self = PATH_VIEW (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_PATH:
|
||||
g_value_set_boxed (value, self->path);
|
||||
break;
|
||||
|
||||
case PROP_FILL_RULE:
|
||||
g_value_set_enum (value, self->fill_rule);
|
||||
break;
|
||||
|
||||
case PROP_FG_COLOR:
|
||||
g_value_set_boxed (value, &self->fg);
|
||||
break;
|
||||
|
||||
case PROP_BG_COLOR:
|
||||
g_value_set_boxed (value, &self->bg);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
path_view_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
PathView *self = PATH_VIEW (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_PATH:
|
||||
g_clear_pointer (&self->path, gsk_path_unref);
|
||||
self->path = g_value_dup_boxed (value);
|
||||
gsk_path_get_bounds (self->path, &self->bounds);
|
||||
gtk_widget_queue_resize (GTK_WIDGET (self));
|
||||
break;
|
||||
|
||||
case PROP_FILL_RULE:
|
||||
self->fill_rule = g_value_get_enum (value);
|
||||
gtk_widget_queue_draw (GTK_WIDGET (self));
|
||||
break;
|
||||
|
||||
case PROP_FG_COLOR:
|
||||
self->fg = *(GdkRGBA *) g_value_get_boxed (value);
|
||||
gtk_widget_queue_draw (GTK_WIDGET (self));
|
||||
break;
|
||||
|
||||
case PROP_BG_COLOR:
|
||||
self->bg = *(GdkRGBA *) g_value_get_boxed (value);
|
||||
gtk_widget_queue_draw (GTK_WIDGET (self));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
path_view_measure (GtkWidget *widget,
|
||||
GtkOrientation orientation,
|
||||
int for_size,
|
||||
int *minimum,
|
||||
int *natural,
|
||||
int *minimum_baseline,
|
||||
int *natural_baseline)
|
||||
{
|
||||
PathView *self = PATH_VIEW (widget);
|
||||
|
||||
if (orientation == GTK_ORIENTATION_HORIZONTAL)
|
||||
*minimum = *natural = (int) ceilf (self->bounds.size.width) + 2 * self->padding;
|
||||
else
|
||||
*minimum = *natural = (int) ceilf (self->bounds.size.height) + 2 * self->padding;
|
||||
}
|
||||
|
||||
static void
|
||||
path_view_snapshot (GtkWidget *widget,
|
||||
GtkSnapshot *snapshot)
|
||||
{
|
||||
PathView *self = PATH_VIEW (widget);
|
||||
graphene_rect_t bounds = self->bounds;
|
||||
|
||||
graphene_rect_inset (&bounds, - self->padding, - self->padding);
|
||||
|
||||
gtk_snapshot_save (snapshot);
|
||||
gtk_snapshot_append_color (snapshot, &self->bg, &self->bounds);
|
||||
gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (self->padding, self->padding));
|
||||
gtk_snapshot_push_fill (snapshot, self->path, self->fill_rule);
|
||||
gtk_snapshot_append_color (snapshot, &self->fg, &self->bounds);
|
||||
gtk_snapshot_pop (snapshot);
|
||||
gtk_snapshot_restore (snapshot);
|
||||
}
|
||||
|
||||
static void
|
||||
path_view_class_init (PathViewClass *class)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
||||
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
|
||||
|
||||
object_class->dispose = path_view_dispose;
|
||||
object_class->get_property = path_view_get_property;
|
||||
object_class->set_property = path_view_set_property;
|
||||
|
||||
widget_class->measure = path_view_measure;
|
||||
widget_class->snapshot = path_view_snapshot;
|
||||
|
||||
properties[PROP_PATH]
|
||||
= g_param_spec_boxed ("path", NULL, NULL,
|
||||
GSK_TYPE_PATH,
|
||||
G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
properties[PROP_FILL_RULE]
|
||||
= g_param_spec_enum ("fill-rule", NULL, NULL,
|
||||
GSK_TYPE_FILL_RULE,
|
||||
GSK_FILL_RULE_WINDING,
|
||||
G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
properties[PROP_FG_COLOR]
|
||||
= g_param_spec_boxed ("fg-color", NULL, NULL,
|
||||
GDK_TYPE_RGBA,
|
||||
G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
properties[PROP_BG_COLOR]
|
||||
= g_param_spec_boxed ("bg-color", NULL, NULL,
|
||||
GDK_TYPE_RGBA,
|
||||
G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
g_object_class_install_properties (object_class, N_PROPERTIES, properties);
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
path_view_new (GskPath *path)
|
||||
{
|
||||
return g_object_new (PATH_TYPE_VIEW,
|
||||
"path", path,
|
||||
NULL);
|
||||
}
|
||||
9
tools/path-view.h
Normal file
9
tools/path-view.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#define PATH_TYPE_VIEW (path_view_get_type ())
|
||||
|
||||
G_DECLARE_FINAL_TYPE (PathView, path_view, PATH, VIEW, GtkWidget)
|
||||
|
||||
GtkWidget * path_view_new (GskPath *path);
|
||||
Reference in New Issue
Block a user