Compare commits
117 Commits
css-variab
...
wip/otte/v
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e390921500 | ||
|
|
78e3024924 | ||
|
|
1382a2beaa | ||
|
|
f2c20b7ca9 | ||
|
|
538ca8e0d4 | ||
|
|
85a3931d41 | ||
|
|
560d42cf62 | ||
|
|
965b8c26bb | ||
|
|
baf62c8923 | ||
|
|
d6c095a18b | ||
|
|
9a6b1ff82b | ||
|
|
5d30b347a9 | ||
|
|
0ebcfc8bde | ||
|
|
dfb0d18290 | ||
|
|
22590d24e1 | ||
|
|
66e3082e2e | ||
|
|
d2aa139e86 | ||
|
|
9f12c5df07 | ||
|
|
6fb33c775e | ||
|
|
ff9903d47f | ||
|
|
5eb0fa8b09 | ||
|
|
7057f96c78 | ||
|
|
8b6dffa5d2 | ||
|
|
19798883c4 | ||
|
|
a7c842ee9d | ||
|
|
802216e7c1 | ||
|
|
30ed2603a9 | ||
|
|
6631df684f | ||
|
|
bc67bb3830 | ||
|
|
1d02c78e1b | ||
|
|
29bd22025d | ||
|
|
930ea4e019 | ||
|
|
7f5cca028a | ||
|
|
e88d753f64 | ||
|
|
34c52b4683 | ||
|
|
ed737beda1 | ||
|
|
5c7c452ea3 | ||
|
|
25277d4d9e | ||
|
|
a11d9cb942 | ||
|
|
49e3e8242a | ||
|
|
bae72fd737 | ||
|
|
4d4f4791e4 | ||
|
|
ea3983b9ab | ||
|
|
c410e0510d | ||
|
|
67ee3ba0a3 | ||
|
|
2f7ef4832e | ||
|
|
f728418d22 | ||
|
|
5328e6d1ca | ||
|
|
70319b0316 | ||
|
|
eb543bc58b | ||
|
|
e277bcdd12 | ||
|
|
e52d9d55a8 | ||
|
|
b66935f168 | ||
|
|
947e813d4b | ||
|
|
673d85ff16 | ||
|
|
5a85046c09 | ||
|
|
887c1ff416 | ||
|
|
5877155c40 | ||
|
|
041b9170cc | ||
|
|
968f5aea61 | ||
|
|
2badf27114 | ||
|
|
0a05d864d1 | ||
|
|
46fe43c5df | ||
|
|
74164440f6 | ||
|
|
0cea93777a | ||
|
|
70077d9a2a | ||
|
|
df34aceee1 | ||
|
|
70c6756639 | ||
|
|
63398ea869 | ||
|
|
d315e0687e | ||
|
|
89c63d1517 | ||
|
|
3c5369a9ff | ||
|
|
e81e80e069 | ||
|
|
6dba706a9a | ||
|
|
673e92645a | ||
|
|
8ef4b40e97 | ||
|
|
0ab25f0580 | ||
|
|
192bf4bbbe | ||
|
|
bddffb08cc | ||
|
|
84dbd7c46f | ||
|
|
4487cafd40 | ||
|
|
2f930f93cc | ||
|
|
0a947d9a6a | ||
|
|
46f405ac7d | ||
|
|
4f03537a28 | ||
|
|
1621b53570 | ||
|
|
7e66dfe902 | ||
|
|
7bc6fabbf7 | ||
|
|
26c8702400 | ||
|
|
1b7c4420dc | ||
|
|
ece3153b1f | ||
|
|
74dc039c7a | ||
|
|
3e1c3aca2a | ||
|
|
51bc26b314 | ||
|
|
51a1adf312 | ||
|
|
d84132824f | ||
|
|
ffecc89352 | ||
|
|
77518895ea | ||
|
|
9685a47693 | ||
|
|
7f9825919c | ||
|
|
2795fc53c1 | ||
|
|
20ba4a225c | ||
|
|
eadbfc3433 | ||
|
|
b8ee48de63 | ||
|
|
25a5a8a85d | ||
|
|
5e1170bdb2 | ||
|
|
f6bf74e2c9 | ||
|
|
fa51c2eb0e | ||
|
|
d34f1bbd6c | ||
|
|
8fd55e9f89 | ||
|
|
be4beeb8f2 | ||
|
|
09288f621a | ||
|
|
3ec3727575 | ||
|
|
77e58d4463 | ||
|
|
bb58b504a6 | ||
|
|
f6bb568e91 | ||
|
|
efbb3ab535 |
@@ -335,6 +335,9 @@
|
||||
<file>paintable_symbolic.c</file>
|
||||
<file>panes.c</file>
|
||||
<file>password_entry.c</file>
|
||||
<file>path_fill.c</file>
|
||||
<file>path_maze.c</file>
|
||||
<file>path_text.c</file>
|
||||
<file>peg_solitaire.c</file>
|
||||
<file>pickers.c</file>
|
||||
<file>printing.c</file>
|
||||
@@ -420,6 +423,9 @@
|
||||
<gresource prefix="/fontrendering">
|
||||
<file>fontrendering.ui</file>
|
||||
</gresource>
|
||||
<gresource prefix="/path_text">
|
||||
<file>path_text.ui</file>
|
||||
</gresource>
|
||||
<gresource prefix="/org/gtk/Demo4">
|
||||
<file>icons/16x16/actions/application-exit.png</file>
|
||||
<file>icons/16x16/actions/document-new.png</file>
|
||||
|
||||
@@ -72,6 +72,9 @@ demos = files([
|
||||
'paintable_symbolic.c',
|
||||
'panes.c',
|
||||
'password_entry.c',
|
||||
'path_fill.c',
|
||||
'path_maze.c',
|
||||
'path_text.c',
|
||||
'peg_solitaire.c',
|
||||
'pickers.c',
|
||||
'printing.c',
|
||||
|
||||
366
demos/gtk-demo/path_fill.c
Normal file
366
demos/gtk-demo/path_fill.c
Normal file
@@ -0,0 +1,366 @@
|
||||
/* Path/Fill
|
||||
*
|
||||
* This demo shows how to use PangoCairo to draw text with more than
|
||||
* just a single color.
|
||||
*/
|
||||
|
||||
#include <glib/gi18n.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "paintable.h"
|
||||
#include "gsk/gskpathdashprivate.h"
|
||||
|
||||
#define GTK_TYPE_PATH_PAINTABLE (gtk_path_paintable_get_type ())
|
||||
G_DECLARE_FINAL_TYPE (GtkPathPaintable, gtk_path_paintable, GTK, PATH_PAINTABLE, GObject)
|
||||
|
||||
struct _GtkPathPaintable
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
int width;
|
||||
int height;
|
||||
GskPath *path;
|
||||
GdkPaintable *background;
|
||||
};
|
||||
|
||||
struct _GtkPathPaintableClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
static int
|
||||
gtk_path_paintable_get_intrinsic_width (GdkPaintable *paintable)
|
||||
{
|
||||
GtkPathPaintable *self = GTK_PATH_PAINTABLE (paintable);
|
||||
|
||||
if (self->background)
|
||||
return MAX (gdk_paintable_get_intrinsic_width (self->background), self->width);
|
||||
else
|
||||
return self->width;
|
||||
}
|
||||
|
||||
static int
|
||||
gtk_path_paintable_get_intrinsic_height (GdkPaintable *paintable)
|
||||
{
|
||||
GtkPathPaintable *self = GTK_PATH_PAINTABLE (paintable);
|
||||
|
||||
if (self->background)
|
||||
return MAX (gdk_paintable_get_intrinsic_height (self->background), self->height);
|
||||
else
|
||||
return self->height;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_path_paintable_snapshot (GdkPaintable *paintable,
|
||||
GdkSnapshot *snapshot,
|
||||
double width,
|
||||
double height)
|
||||
{
|
||||
GtkPathPaintable *self = GTK_PATH_PAINTABLE (paintable);
|
||||
|
||||
#if 0
|
||||
gtk_snapshot_push_fill (snapshot, self->path, GSK_FILL_RULE_WINDING);
|
||||
#else
|
||||
GskStroke *stroke = gsk_stroke_new (2.0);
|
||||
gtk_snapshot_push_stroke (snapshot, self->path, stroke);
|
||||
gsk_stroke_free (stroke);
|
||||
#endif
|
||||
|
||||
if (self->background)
|
||||
{
|
||||
gdk_paintable_snapshot (self->background, snapshot, width, height);
|
||||
}
|
||||
else
|
||||
{
|
||||
gtk_snapshot_append_linear_gradient (snapshot,
|
||||
&GRAPHENE_RECT_INIT (0, 0, width, height),
|
||||
&GRAPHENE_POINT_INIT (0, 0),
|
||||
&GRAPHENE_POINT_INIT (width, height),
|
||||
(GskColorStop[8]) {
|
||||
{ 0.0, { 1.0, 0.0, 0.0, 1.0 } },
|
||||
{ 0.2, { 1.0, 0.0, 0.0, 1.0 } },
|
||||
{ 0.3, { 1.0, 1.0, 0.0, 1.0 } },
|
||||
{ 0.4, { 0.0, 1.0, 0.0, 1.0 } },
|
||||
{ 0.6, { 0.0, 1.0, 1.0, 1.0 } },
|
||||
{ 0.7, { 0.0, 0.0, 1.0, 1.0 } },
|
||||
{ 0.8, { 1.0, 0.0, 1.0, 1.0 } },
|
||||
{ 1.0, { 1.0, 0.0, 1.0, 1.0 } }
|
||||
},
|
||||
8);
|
||||
}
|
||||
|
||||
gtk_snapshot_pop (snapshot);
|
||||
|
||||
}
|
||||
|
||||
static GdkPaintableFlags
|
||||
gtk_path_paintable_get_flags (GdkPaintable *paintable)
|
||||
{
|
||||
GtkPathPaintable *self = GTK_PATH_PAINTABLE (paintable);
|
||||
|
||||
if (self->background)
|
||||
return gdk_paintable_get_flags (self->background);
|
||||
else
|
||||
return GDK_PAINTABLE_STATIC_CONTENTS | GDK_PAINTABLE_STATIC_SIZE;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_path_paintable_paintable_init (GdkPaintableInterface *iface)
|
||||
{
|
||||
iface->get_intrinsic_width = gtk_path_paintable_get_intrinsic_width;
|
||||
iface->get_intrinsic_height = gtk_path_paintable_get_intrinsic_height;
|
||||
iface->snapshot = gtk_path_paintable_snapshot;
|
||||
iface->get_flags = gtk_path_paintable_get_flags;
|
||||
}
|
||||
|
||||
/* When defining the GType, we need to implement the GdkPaintable interface */
|
||||
G_DEFINE_TYPE_WITH_CODE (GtkPathPaintable, gtk_path_paintable, G_TYPE_OBJECT,
|
||||
G_IMPLEMENT_INTERFACE (GDK_TYPE_PAINTABLE,
|
||||
gtk_path_paintable_paintable_init))
|
||||
|
||||
/* Here's the boilerplate for the GObject declaration.
|
||||
* We need to disconnect the signals here that we set up elsewhere
|
||||
*/
|
||||
static void
|
||||
gtk_path_paintable_dispose (GObject *object)
|
||||
{
|
||||
GtkPathPaintable *self = GTK_PATH_PAINTABLE (object);
|
||||
|
||||
if (self->background)
|
||||
{
|
||||
g_signal_handlers_disconnect_by_func (self->background, gdk_paintable_invalidate_contents, self);
|
||||
g_signal_handlers_disconnect_by_func (self->background, gdk_paintable_invalidate_size, self);
|
||||
g_clear_object (&self->background);
|
||||
}
|
||||
|
||||
G_OBJECT_CLASS (gtk_path_paintable_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_path_paintable_class_init (GtkPathPaintableClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->dispose = gtk_path_paintable_dispose;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_path_paintable_init (GtkPathPaintable *self)
|
||||
{
|
||||
}
|
||||
|
||||
/* And finally, we add a simple constructor.
|
||||
* It is declared in the header so that the other examples
|
||||
* can use it.
|
||||
*/
|
||||
GdkPaintable *
|
||||
gtk_path_paintable_new (GskPath *path,
|
||||
GdkPaintable *background,
|
||||
int width,
|
||||
int height)
|
||||
{
|
||||
GtkPathPaintable *self;
|
||||
|
||||
self = g_object_new (GTK_TYPE_PATH_PAINTABLE, NULL);
|
||||
self->path = path;
|
||||
self->background = background;
|
||||
if (self->background)
|
||||
{
|
||||
g_object_ref (self->background);
|
||||
g_signal_connect_swapped (self->background, "invalidate-contents", G_CALLBACK (gdk_paintable_invalidate_contents), self);
|
||||
g_signal_connect_swapped (self->background, "invalidate-size", G_CALLBACK (gdk_paintable_invalidate_size), self);
|
||||
}
|
||||
self->width = width;
|
||||
self->height = height;
|
||||
|
||||
return GDK_PAINTABLE (self);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_path_paintable_set_path (GtkPathPaintable *self,
|
||||
GskPath *path)
|
||||
{
|
||||
g_clear_pointer (&self->path, gsk_path_unref);
|
||||
self->path = gsk_path_ref (path);
|
||||
|
||||
gdk_paintable_invalidate_contents (GDK_PAINTABLE (self));
|
||||
}
|
||||
|
||||
static GskPath *
|
||||
create_hexagon (GtkWidget *widget)
|
||||
{
|
||||
GskPathBuilder *builder;
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
|
||||
gsk_path_builder_move_to (builder, 120, 0);
|
||||
gsk_path_builder_line_to (builder, 360, 0);
|
||||
gsk_path_builder_line_to (builder, 480, 208);
|
||||
gsk_path_builder_line_to (builder, 360, 416);
|
||||
gsk_path_builder_line_to (builder, 120, 416);
|
||||
gsk_path_builder_line_to (builder, 0, 208);
|
||||
gsk_path_builder_close (builder);
|
||||
|
||||
return gsk_path_builder_free_to_path (builder);
|
||||
}
|
||||
|
||||
static GskPath *
|
||||
create_path_from_text (GtkWidget *widget)
|
||||
{
|
||||
PangoLayout *layout;
|
||||
PangoFontDescription *desc;
|
||||
GskPathBuilder *builder;
|
||||
|
||||
layout = gtk_widget_create_pango_layout (widget, "Pango power!\nPango power!\nPango power!");
|
||||
desc = pango_font_description_from_string ("sans bold 36");
|
||||
pango_layout_set_font_description (layout, desc);
|
||||
pango_font_description_free (desc);
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
|
||||
gsk_path_builder_add_layout (builder, layout);
|
||||
|
||||
return gsk_path_builder_free_to_path (builder);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
build_path (GskPathOperation op,
|
||||
const graphene_point_t *pts,
|
||||
gsize n_pts,
|
||||
float weight,
|
||||
gpointer user_data)
|
||||
{
|
||||
GskPathBuilder *builder = user_data;
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case GSK_PATH_MOVE:
|
||||
gsk_path_builder_move_to (builder, pts[0].x, pts[0].y);
|
||||
break;
|
||||
|
||||
case GSK_PATH_CLOSE:
|
||||
gsk_path_builder_close (builder);
|
||||
break;
|
||||
|
||||
case GSK_PATH_LINE:
|
||||
gsk_path_builder_line_to (builder, pts[1].x, pts[1].y);
|
||||
break;
|
||||
|
||||
case GSK_PATH_CURVE:
|
||||
gsk_path_builder_curve_to (builder, pts[1].x, pts[1].y, pts[2].x, pts[2].y, pts[3].x, pts[3].y);
|
||||
break;
|
||||
|
||||
case GSK_PATH_CONIC:
|
||||
gsk_path_builder_conic_to (builder, pts[1].x, pts[1].y, pts[2].x, pts[2].y, weight);
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
break;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
update_path (GtkWidget *widget,
|
||||
GdkFrameClock *frame_clock,
|
||||
gpointer measure)
|
||||
{
|
||||
float progress = gdk_frame_clock_get_frame_time (frame_clock) % (60 * G_USEC_PER_SEC) / (float) (30 * G_USEC_PER_SEC);
|
||||
GskPathBuilder *builder;
|
||||
GskPath *path;
|
||||
graphene_point_t pos;
|
||||
graphene_vec2_t tangent;
|
||||
GskStroke *stroke;
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
gsk_path_builder_add_segment (builder,
|
||||
measure,
|
||||
#if 1
|
||||
0.0, gsk_path_measure_get_length (measure));
|
||||
#else
|
||||
progress > 1 ? (progress - 1) * gsk_path_measure_get_length (measure) : 0.0,
|
||||
(progress < 1 ? progress : 1.0) * gsk_path_measure_get_length (measure));
|
||||
#endif
|
||||
|
||||
path = gsk_path_builder_free_to_path (builder);
|
||||
|
||||
stroke = gsk_stroke_new (1);
|
||||
gsk_stroke_set_dash (stroke, (float[2]) { 10, 5 }, 2);
|
||||
gsk_stroke_set_dash_offset (stroke, - (gdk_frame_clock_get_frame_time (frame_clock) % G_USEC_PER_SEC) * 15. / G_USEC_PER_SEC);
|
||||
builder = gsk_path_builder_new ();
|
||||
gsk_path_dash (path, stroke, 0.2, build_path, builder);
|
||||
gsk_path_unref (path);
|
||||
|
||||
gsk_path_measure_get_point (measure,
|
||||
(progress > 1 ? (progress - 1) : progress) * gsk_path_measure_get_length (measure),
|
||||
&pos,
|
||||
&tangent);
|
||||
gsk_path_builder_move_to (builder, pos.x + 5 * graphene_vec2_get_x (&tangent), pos.y + 5 * graphene_vec2_get_y (&tangent));
|
||||
gsk_path_builder_line_to (builder, pos.x + 3 * graphene_vec2_get_y (&tangent), pos.y + 3 * graphene_vec2_get_x (&tangent));
|
||||
gsk_path_builder_line_to (builder, pos.x - 3 * graphene_vec2_get_y (&tangent), pos.y - 3 * graphene_vec2_get_x (&tangent));
|
||||
gsk_path_builder_close (builder);
|
||||
|
||||
path = gsk_path_builder_free_to_path (builder);
|
||||
|
||||
gtk_path_paintable_set_path (GTK_PATH_PAINTABLE (gtk_picture_get_paintable (GTK_PICTURE (widget))),
|
||||
path);
|
||||
gsk_path_unref (path);
|
||||
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
do_path_fill (GtkWidget *do_widget)
|
||||
{
|
||||
static GtkWidget *window = NULL;
|
||||
|
||||
if (!window)
|
||||
{
|
||||
GtkWidget *picture;
|
||||
GdkPaintable *paintable;
|
||||
GtkMediaStream *stream;
|
||||
GskPath *path;
|
||||
graphene_rect_t bounds;
|
||||
GskPathMeasure *measure;
|
||||
|
||||
window = gtk_window_new ();
|
||||
gtk_window_set_resizable (GTK_WINDOW (window), TRUE);
|
||||
gtk_window_set_title (GTK_WINDOW (window), "Path Fill");
|
||||
g_object_add_weak_pointer (G_OBJECT (window), (gpointer *)&window);
|
||||
|
||||
#if 0
|
||||
stream = gtk_media_file_new_for_resource ("/images/gtk-logo.webm");
|
||||
#else
|
||||
stream = gtk_nuclear_media_stream_new ();
|
||||
#endif
|
||||
gtk_media_stream_play (stream);
|
||||
gtk_media_stream_set_loop (stream, TRUE);
|
||||
|
||||
path = create_hexagon (window);
|
||||
path = create_path_from_text (window);
|
||||
gsk_path_get_bounds (path, &bounds);
|
||||
|
||||
paintable = gtk_path_paintable_new (path,
|
||||
GDK_PAINTABLE (stream),
|
||||
bounds.origin.x + bounds.size.width,
|
||||
bounds.origin.y + bounds.size.height);
|
||||
picture = gtk_picture_new_for_paintable (paintable);
|
||||
measure = gsk_path_measure_new (path);
|
||||
gtk_widget_add_tick_callback (picture, update_path, measure, (GDestroyNotify) gsk_path_measure_unref);
|
||||
gtk_picture_set_content_fit (GTK_PICTURE (picture), GTK_CONTENT_FIT_CONTAIN);
|
||||
gtk_picture_set_can_shrink (GTK_PICTURE (picture), FALSE);
|
||||
g_object_unref (paintable);
|
||||
|
||||
gtk_window_set_child (GTK_WINDOW (window), picture);
|
||||
}
|
||||
|
||||
if (!gtk_widget_get_visible (window))
|
||||
gtk_window_present (GTK_WINDOW (window));
|
||||
else
|
||||
gtk_window_destroy (GTK_WINDOW (window));
|
||||
|
||||
return window;
|
||||
}
|
||||
338
demos/gtk-demo/path_maze.c
Normal file
338
demos/gtk-demo/path_maze.c
Normal file
@@ -0,0 +1,338 @@
|
||||
/* Path/Maze
|
||||
*
|
||||
* This demo shows how to use a GskPath to create a maze and use
|
||||
* gsk_path_measure_get_closest_point() to check the mouse stays
|
||||
* on the path.
|
||||
*
|
||||
* It also shows off the performance of GskPath (or not) as this
|
||||
* is a rather complex path.
|
||||
*/
|
||||
|
||||
#include <glib/gi18n.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "paintable.h"
|
||||
|
||||
#define MAZE_GRID_SIZE 20
|
||||
#define MAZE_STROKE_SIZE_ACTIVE (MAZE_GRID_SIZE - 4)
|
||||
#define MAZE_STROKE_SIZE_INACTIVE (MAZE_GRID_SIZE - 12)
|
||||
#define MAZE_WIDTH 31
|
||||
#define MAZE_HEIGHT 21
|
||||
|
||||
#define GTK_TYPE_MAZE (gtk_maze_get_type ())
|
||||
G_DECLARE_FINAL_TYPE (GtkMaze, gtk_maze, GTK, MAZE, GtkWidget)
|
||||
|
||||
struct _GtkMaze
|
||||
{
|
||||
GtkWidget parent_instance;
|
||||
|
||||
int width;
|
||||
int height;
|
||||
GskPath *path;
|
||||
GskPathMeasure *measure;
|
||||
GdkPaintable *background;
|
||||
|
||||
gboolean active;
|
||||
};
|
||||
|
||||
struct _GtkMazeClass
|
||||
{
|
||||
GtkWidgetClass parent_class;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GtkMaze, gtk_maze, GTK_TYPE_WIDGET)
|
||||
|
||||
static void
|
||||
gtk_maze_measure (GtkWidget *widget,
|
||||
GtkOrientation orientation,
|
||||
int for_size,
|
||||
int *minimum,
|
||||
int *natural,
|
||||
int *minimum_baseline,
|
||||
int *natural_baseline)
|
||||
{
|
||||
GtkMaze *self = GTK_MAZE (widget);
|
||||
|
||||
if (orientation == GTK_ORIENTATION_HORIZONTAL)
|
||||
*minimum = *natural = self->width;
|
||||
else
|
||||
*minimum = *natural = self->height;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_maze_snapshot (GtkWidget *widget,
|
||||
GdkSnapshot *snapshot)
|
||||
{
|
||||
GtkMaze *self = GTK_MAZE (widget);
|
||||
double width = gtk_widget_get_width (widget);
|
||||
double height = gtk_widget_get_height (widget);
|
||||
GskStroke *stroke;
|
||||
|
||||
stroke = gsk_stroke_new (MAZE_STROKE_SIZE_INACTIVE);
|
||||
if (self->active)
|
||||
gsk_stroke_set_line_width (stroke, MAZE_STROKE_SIZE_ACTIVE);
|
||||
gsk_stroke_set_line_join (stroke, GSK_LINE_JOIN_ROUND);
|
||||
gsk_stroke_set_line_cap (stroke, GSK_LINE_CAP_ROUND);
|
||||
gtk_snapshot_push_stroke (snapshot, self->path, stroke);
|
||||
gsk_stroke_free (stroke);
|
||||
|
||||
if (self->background)
|
||||
{
|
||||
gdk_paintable_snapshot (self->background, snapshot, width, height);
|
||||
}
|
||||
else
|
||||
{
|
||||
gtk_snapshot_append_linear_gradient (snapshot,
|
||||
&GRAPHENE_RECT_INIT (0, 0, width, height),
|
||||
&GRAPHENE_POINT_INIT (0, 0),
|
||||
&GRAPHENE_POINT_INIT (width, height),
|
||||
(GskColorStop[8]) {
|
||||
{ 0.0, { 1.0, 0.0, 0.0, 1.0 } },
|
||||
{ 0.2, { 1.0, 0.0, 0.0, 1.0 } },
|
||||
{ 0.3, { 1.0, 1.0, 0.0, 1.0 } },
|
||||
{ 0.4, { 0.0, 1.0, 0.0, 1.0 } },
|
||||
{ 0.6, { 0.0, 1.0, 1.0, 1.0 } },
|
||||
{ 0.7, { 0.0, 0.0, 1.0, 1.0 } },
|
||||
{ 0.8, { 1.0, 0.0, 1.0, 1.0 } },
|
||||
{ 1.0, { 1.0, 0.0, 1.0, 1.0 } }
|
||||
},
|
||||
8);
|
||||
}
|
||||
|
||||
gtk_snapshot_pop (snapshot);
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_maze_dispose (GObject *object)
|
||||
{
|
||||
GtkMaze *self = GTK_MAZE (object);
|
||||
|
||||
g_clear_pointer (&self->path, gsk_path_unref);
|
||||
g_clear_pointer (&self->measure, gsk_path_measure_unref);
|
||||
if (self->background)
|
||||
{
|
||||
g_signal_handlers_disconnect_matched (self->background, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, self);
|
||||
g_clear_object (&self->background);
|
||||
}
|
||||
|
||||
G_OBJECT_CLASS (gtk_maze_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_maze_class_init (GtkMazeClass *klass)
|
||||
{
|
||||
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->dispose = gtk_maze_dispose;
|
||||
|
||||
widget_class->measure = gtk_maze_measure;
|
||||
widget_class->snapshot = gtk_maze_snapshot;
|
||||
}
|
||||
|
||||
static void
|
||||
pointer_motion (GtkEventControllerMotion *controller,
|
||||
double x,
|
||||
double y,
|
||||
GtkMaze *self)
|
||||
{
|
||||
if (!self->active)
|
||||
return;
|
||||
|
||||
if (gsk_path_measure_get_closest_point (self->measure, &GRAPHENE_POINT_INIT (x, y), NULL) <= MAZE_STROKE_SIZE_ACTIVE / 2.0f)
|
||||
return;
|
||||
|
||||
self->active = FALSE;
|
||||
gtk_widget_queue_draw (GTK_WIDGET (self));
|
||||
}
|
||||
|
||||
static void
|
||||
pointer_leave (GtkEventControllerMotion *controller,
|
||||
GtkMaze *self)
|
||||
{
|
||||
if (!self->active)
|
||||
{
|
||||
self->active = TRUE;
|
||||
gtk_widget_queue_draw (GTK_WIDGET (self));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_maze_init (GtkMaze *self)
|
||||
{
|
||||
GtkEventController *controller;
|
||||
|
||||
controller = GTK_EVENT_CONTROLLER (gtk_event_controller_motion_new ());
|
||||
g_signal_connect (controller, "motion", G_CALLBACK (pointer_motion), self);
|
||||
g_signal_connect (controller, "leave", G_CALLBACK (pointer_leave), self);
|
||||
gtk_widget_add_controller (GTK_WIDGET (self), controller);
|
||||
|
||||
self->active = TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_maze_set_path (GtkMaze *self,
|
||||
GskPath *path)
|
||||
{
|
||||
g_clear_pointer (&self->path, gsk_path_unref);
|
||||
g_clear_pointer (&self->measure, gsk_path_measure_unref);
|
||||
self->path = gsk_path_ref (path);
|
||||
self->measure = gsk_path_measure_new (path);
|
||||
|
||||
gtk_widget_queue_draw (GTK_WIDGET (self));
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
gtk_maze_new (GskPath *path,
|
||||
GdkPaintable *background,
|
||||
int width,
|
||||
int height)
|
||||
{
|
||||
GtkMaze *self;
|
||||
|
||||
self = g_object_new (GTK_TYPE_MAZE, NULL);
|
||||
|
||||
gtk_maze_set_path (self, path);
|
||||
gsk_path_unref (path);
|
||||
self->background = background;
|
||||
if (self->background)
|
||||
{
|
||||
g_signal_connect_swapped (self->background, "invalidate-contents", G_CALLBACK (gtk_widget_queue_draw), self);
|
||||
g_signal_connect_swapped (self->background, "invalidate-size", G_CALLBACK (gtk_widget_queue_resize), self);
|
||||
}
|
||||
self->width = width;
|
||||
self->height = height;
|
||||
|
||||
return GTK_WIDGET (self);
|
||||
}
|
||||
|
||||
static void
|
||||
add_point_to_maze (GtkBitset *maze,
|
||||
GskPathBuilder *builder,
|
||||
guint x,
|
||||
guint y)
|
||||
{
|
||||
gboolean set[4] = { FALSE, FALSE, FALSE, FALSE };
|
||||
guint dir;
|
||||
|
||||
gtk_bitset_add (maze, y * MAZE_WIDTH + x);
|
||||
|
||||
while (TRUE)
|
||||
{
|
||||
set[0] = set[0] || x == 0 || gtk_bitset_contains (maze, y * MAZE_WIDTH + x - 1);
|
||||
set[1] = set[1] || y == 0 || gtk_bitset_contains (maze, (y - 1) * MAZE_WIDTH + x);
|
||||
set[2] = set[2] || x + 1 == MAZE_WIDTH || gtk_bitset_contains (maze, y * MAZE_WIDTH + x + 1);
|
||||
set[3] = set[3] || y + 1 == MAZE_HEIGHT || gtk_bitset_contains (maze, (y + 1) * MAZE_WIDTH + x);
|
||||
|
||||
if (set[0] && set[1] && set[2] && set[3])
|
||||
return;
|
||||
|
||||
do
|
||||
{
|
||||
dir = g_random_int_range (0, 4);
|
||||
}
|
||||
while (set[dir]);
|
||||
|
||||
switch (dir)
|
||||
{
|
||||
case 0:
|
||||
gsk_path_builder_move_to (builder, (x + 0.5) * MAZE_GRID_SIZE, (y + 0.5) * MAZE_GRID_SIZE);
|
||||
gsk_path_builder_line_to (builder, (x - 0.5) * MAZE_GRID_SIZE, (y + 0.5) * MAZE_GRID_SIZE);
|
||||
add_point_to_maze (maze, builder, x - 1, y);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
gsk_path_builder_move_to (builder, (x + 0.5) * MAZE_GRID_SIZE, (y + 0.5) * MAZE_GRID_SIZE);
|
||||
gsk_path_builder_line_to (builder, (x + 0.5) * MAZE_GRID_SIZE, (y - 0.5) * MAZE_GRID_SIZE);
|
||||
add_point_to_maze (maze, builder, x, y - 1);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
gsk_path_builder_move_to (builder, (x + 0.5) * MAZE_GRID_SIZE, (y + 0.5) * MAZE_GRID_SIZE);
|
||||
gsk_path_builder_line_to (builder, (x + 1.5) * MAZE_GRID_SIZE, (y + 0.5) * MAZE_GRID_SIZE);
|
||||
add_point_to_maze (maze, builder, x + 1, y);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
gsk_path_builder_move_to (builder, (x + 0.5) * MAZE_GRID_SIZE, (y + 0.5) * MAZE_GRID_SIZE);
|
||||
gsk_path_builder_line_to (builder, (x + 0.5) * MAZE_GRID_SIZE, (y + 1.5) * MAZE_GRID_SIZE);
|
||||
add_point_to_maze (maze, builder, x, y + 1);
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static GskPath *
|
||||
create_path_for_maze (GtkWidget *widget)
|
||||
{
|
||||
GskPathBuilder *builder;
|
||||
GtkBitset *maze;
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
maze = gtk_bitset_new_empty ();
|
||||
/* make sure the outer lines are unreachable:
|
||||
* Set the full range, then remove the center again. */
|
||||
gtk_bitset_add_range (maze, 0, MAZE_WIDTH * MAZE_HEIGHT);
|
||||
gtk_bitset_remove_rectangle (maze, MAZE_WIDTH + 1, MAZE_WIDTH - 2, MAZE_HEIGHT - 2, MAZE_WIDTH);
|
||||
|
||||
/* Fill the maze */
|
||||
add_point_to_maze (maze, builder, MAZE_WIDTH / 2, MAZE_HEIGHT / 2);
|
||||
|
||||
/* Add start and stop lines */
|
||||
gsk_path_builder_move_to (builder, 1.5 * MAZE_GRID_SIZE, -0.5 * MAZE_GRID_SIZE);
|
||||
gsk_path_builder_line_to (builder, 1.5 * MAZE_GRID_SIZE, 1.5 * MAZE_GRID_SIZE);
|
||||
gsk_path_builder_move_to (builder, (MAZE_WIDTH - 1.5) * MAZE_GRID_SIZE, (MAZE_HEIGHT - 1.5) * MAZE_GRID_SIZE);
|
||||
gsk_path_builder_line_to (builder, (MAZE_WIDTH - 1.5) * MAZE_GRID_SIZE, (MAZE_HEIGHT + 0.5) * MAZE_GRID_SIZE);
|
||||
|
||||
|
||||
gtk_bitset_unref (maze);
|
||||
|
||||
return gsk_path_builder_free_to_path (builder);
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
do_path_maze (GtkWidget *do_widget)
|
||||
{
|
||||
static GtkWidget *window = NULL;
|
||||
|
||||
if (!window)
|
||||
{
|
||||
GtkWidget *maze;
|
||||
GtkMediaStream *stream;
|
||||
GskPath *path;
|
||||
|
||||
window = gtk_window_new ();
|
||||
gtk_window_set_resizable (GTK_WINDOW (window), TRUE);
|
||||
gtk_window_set_title (GTK_WINDOW (window), "Follow the maze with the mouse");
|
||||
g_object_add_weak_pointer (G_OBJECT (window), (gpointer *)&window);
|
||||
|
||||
#if 0
|
||||
stream = gtk_media_file_new_for_resource ("/images/gtk-logo.webm");
|
||||
#else
|
||||
stream = gtk_nuclear_media_stream_new ();
|
||||
#endif
|
||||
gtk_media_stream_play (stream);
|
||||
gtk_media_stream_set_loop (stream, TRUE);
|
||||
|
||||
path = create_path_for_maze (window);
|
||||
|
||||
maze = gtk_maze_new (path,
|
||||
GDK_PAINTABLE (stream),
|
||||
MAZE_WIDTH * MAZE_GRID_SIZE,
|
||||
MAZE_HEIGHT * MAZE_GRID_SIZE);
|
||||
|
||||
gtk_window_set_child (GTK_WINDOW (window), maze);
|
||||
}
|
||||
|
||||
if (!gtk_widget_get_visible (window))
|
||||
gtk_window_present (GTK_WINDOW (window));
|
||||
else
|
||||
gtk_window_destroy (GTK_WINDOW (window));
|
||||
|
||||
return window;
|
||||
}
|
||||
588
demos/gtk-demo/path_text.c
Normal file
588
demos/gtk-demo/path_text.c
Normal file
@@ -0,0 +1,588 @@
|
||||
/* Path/Text
|
||||
*
|
||||
* This demo shows how to use GskPath to animate a path along another path.
|
||||
*/
|
||||
|
||||
#include <glib/gi18n.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#define GTK_TYPE_PATH_WIDGET (gtk_path_widget_get_type ())
|
||||
G_DECLARE_FINAL_TYPE (GtkPathWidget, gtk_path_widget, GTK, PATH_WIDGET, GtkWidget)
|
||||
|
||||
#define POINT_SIZE 8
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_TEXT,
|
||||
PROP_EDITABLE,
|
||||
N_PROPS
|
||||
};
|
||||
|
||||
struct _GtkPathWidget
|
||||
{
|
||||
GtkWidget parent_instance;
|
||||
|
||||
char *text;
|
||||
gboolean editable;
|
||||
|
||||
graphene_point_t points[4];
|
||||
|
||||
guint active_point;
|
||||
float line_closest;
|
||||
|
||||
GskPath *line_path;
|
||||
GskPathMeasure *line_measure;
|
||||
GskPath *text_path;
|
||||
|
||||
GdkPaintable *background;
|
||||
};
|
||||
|
||||
struct _GtkPathWidgetClass
|
||||
{
|
||||
GtkWidgetClass parent_class;
|
||||
};
|
||||
|
||||
static GParamSpec *properties[N_PROPS] = { NULL, };
|
||||
|
||||
G_DEFINE_TYPE (GtkPathWidget, gtk_path_widget, GTK_TYPE_WIDGET)
|
||||
|
||||
static GskPath *
|
||||
create_path_from_text (GtkWidget *widget,
|
||||
const char *text,
|
||||
graphene_point_t *out_offset)
|
||||
{
|
||||
PangoLayout *layout;
|
||||
PangoFontDescription *desc;
|
||||
GskPathBuilder *builder;
|
||||
GskPath *result;
|
||||
|
||||
layout = gtk_widget_create_pango_layout (widget, text);
|
||||
desc = pango_font_description_from_string ("sans bold 36");
|
||||
pango_layout_set_font_description (layout, desc);
|
||||
pango_font_description_free (desc);
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
gsk_path_builder_add_layout (builder, layout);
|
||||
result = gsk_path_builder_free_to_path (builder);
|
||||
|
||||
if (out_offset)
|
||||
graphene_point_init (out_offset, 0, - pango_layout_get_baseline (layout) / (double) PANGO_SCALE);
|
||||
g_object_unref (layout);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GskPathMeasure *measure;
|
||||
GskPathBuilder *builder;
|
||||
graphene_point_t offset;
|
||||
double scale;
|
||||
} GtkPathTransform;
|
||||
|
||||
static void
|
||||
gtk_path_transform_point (GskPathMeasure *measure,
|
||||
const graphene_point_t *pt,
|
||||
const graphene_point_t *offset,
|
||||
float scale,
|
||||
graphene_point_t *res)
|
||||
{
|
||||
graphene_vec2_t tangent;
|
||||
|
||||
gsk_path_measure_get_point (measure, (pt->x + offset->x) * scale, res, &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);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_path_transform_op (GskPathOperation op,
|
||||
const graphene_point_t *pts,
|
||||
gsize n_pts,
|
||||
float weight,
|
||||
gpointer data)
|
||||
{
|
||||
GtkPathTransform *transform = data;
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case GSK_PATH_MOVE:
|
||||
{
|
||||
graphene_point_t res;
|
||||
gtk_path_transform_point (transform->measure, &pts[0], &transform->offset, transform->scale, &res);
|
||||
gsk_path_builder_move_to (transform->builder, res.x, res.y);
|
||||
}
|
||||
break;
|
||||
|
||||
case GSK_PATH_LINE:
|
||||
{
|
||||
graphene_point_t res;
|
||||
gtk_path_transform_point (transform->measure, &pts[1], &transform->offset, transform->scale, &res);
|
||||
gsk_path_builder_line_to (transform->builder, res.x, res.y);
|
||||
}
|
||||
break;
|
||||
|
||||
case GSK_PATH_CURVE:
|
||||
{
|
||||
graphene_point_t res[3];
|
||||
gtk_path_transform_point (transform->measure, &pts[1], &transform->offset, transform->scale, &res[0]);
|
||||
gtk_path_transform_point (transform->measure, &pts[2], &transform->offset, transform->scale, &res[1]);
|
||||
gtk_path_transform_point (transform->measure, &pts[3], &transform->offset, transform->scale, &res[2]);
|
||||
gsk_path_builder_curve_to (transform->builder, res[0].x, res[0].y, res[1].x, res[1].y, res[2].x, res[2].y);
|
||||
}
|
||||
break;
|
||||
|
||||
case GSK_PATH_CONIC:
|
||||
{
|
||||
graphene_point_t res[2];
|
||||
gtk_path_transform_point (transform->measure, &pts[1], &transform->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;
|
||||
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static GskPath *
|
||||
gtk_path_transform (GskPathMeasure *measure,
|
||||
GskPath *path,
|
||||
const graphene_point_t *offset)
|
||||
{
|
||||
GtkPathTransform transform = { measure, gsk_path_builder_new (), *offset };
|
||||
graphene_rect_t bounds;
|
||||
|
||||
gsk_path_get_bounds (path, &bounds);
|
||||
if (bounds.origin.x + bounds.size.width > 0)
|
||||
transform.scale = gsk_path_measure_get_length (measure) / (bounds.origin.x + bounds.size.width);
|
||||
else
|
||||
transform.scale = 1.0f;
|
||||
|
||||
gsk_path_foreach (path, GSK_PATH_FOREACH_ALLOW_CURVE, gtk_path_transform_op, &transform);
|
||||
|
||||
return gsk_path_builder_free_to_path (transform.builder);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_path_widget_clear_text_path (GtkPathWidget *self)
|
||||
{
|
||||
g_clear_pointer (&self->text_path, gsk_path_unref);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_path_widget_clear_paths (GtkPathWidget *self)
|
||||
{
|
||||
gtk_path_widget_clear_text_path (self);
|
||||
|
||||
g_clear_pointer (&self->line_path, gsk_path_unref);
|
||||
g_clear_pointer (&self->line_measure, gsk_path_measure_unref);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_path_widget_create_text_path (GtkPathWidget *self)
|
||||
{
|
||||
GskPath *path;
|
||||
graphene_point_t offset;
|
||||
|
||||
gtk_path_widget_clear_text_path (self);
|
||||
|
||||
if (self->line_measure == NULL)
|
||||
return;
|
||||
|
||||
path = create_path_from_text (GTK_WIDGET (self), self->text, &offset);
|
||||
self->text_path = gtk_path_transform (self->line_measure, path, &offset);
|
||||
|
||||
gsk_path_unref (path);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_path_widget_create_paths (GtkPathWidget *self)
|
||||
{
|
||||
double width = gtk_widget_get_width (GTK_WIDGET (self));
|
||||
double height = gtk_widget_get_height (GTK_WIDGET (self));
|
||||
GskPathBuilder *builder;
|
||||
|
||||
gtk_path_widget_clear_paths (self);
|
||||
|
||||
if (width <= 0 || height <= 0)
|
||||
return;
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
gsk_path_builder_move_to (builder,
|
||||
self->points[0].x * width, self->points[0].y * height);
|
||||
gsk_path_builder_curve_to (builder,
|
||||
self->points[1].x * width, self->points[1].y * height,
|
||||
self->points[2].x * width, self->points[2].y * height,
|
||||
self->points[3].x * width, self->points[3].y * height);
|
||||
self->line_path = gsk_path_builder_free_to_path (builder);
|
||||
|
||||
self->line_measure = gsk_path_measure_new (self->line_path);
|
||||
|
||||
gtk_path_widget_create_text_path (self);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_path_widget_allocate (GtkWidget *widget,
|
||||
int width,
|
||||
int height,
|
||||
int baseline)
|
||||
{
|
||||
GtkPathWidget *self = GTK_PATH_WIDGET (widget);
|
||||
|
||||
GTK_WIDGET_CLASS (gtk_path_widget_parent_class)->size_allocate (widget, width, height, baseline);
|
||||
|
||||
gtk_path_widget_create_paths (self);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_path_widget_snapshot (GtkWidget *widget,
|
||||
GtkSnapshot *snapshot)
|
||||
{
|
||||
GtkPathWidget *self = GTK_PATH_WIDGET (widget);
|
||||
double width = gtk_widget_get_width (widget);
|
||||
double height = gtk_widget_get_height (widget);
|
||||
GskPath *path;
|
||||
GskStroke *stroke;
|
||||
gsize i;
|
||||
|
||||
/* frosted glass the background */
|
||||
gtk_snapshot_push_blur (snapshot, 100);
|
||||
gdk_paintable_snapshot (self->background, snapshot, width, height);
|
||||
gtk_snapshot_append_color (snapshot, &(GdkRGBA) { 1, 1, 1, 0.6 }, &GRAPHENE_RECT_INIT (0, 0, width, height));
|
||||
gtk_snapshot_pop (snapshot);
|
||||
|
||||
/* draw the text */
|
||||
if (self->text_path)
|
||||
{
|
||||
gtk_snapshot_push_fill (snapshot, self->text_path, GSK_FILL_RULE_WINDING);
|
||||
gdk_paintable_snapshot (self->background, snapshot, width, height);
|
||||
|
||||
/* ... with an emboss effect */
|
||||
stroke = gsk_stroke_new (2.0);
|
||||
gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT(1, 1));
|
||||
gtk_snapshot_push_stroke (snapshot, self->text_path, stroke);
|
||||
gtk_snapshot_append_color (snapshot, &(GdkRGBA) { 0, 0, 0, 0.2 }, &GRAPHENE_RECT_INIT (0, 0, width, height));
|
||||
gsk_stroke_free (stroke);
|
||||
gtk_snapshot_pop (snapshot);
|
||||
|
||||
gtk_snapshot_pop (snapshot);
|
||||
}
|
||||
|
||||
if (self->editable && self->line_path)
|
||||
{
|
||||
GskPathBuilder *builder;
|
||||
|
||||
/* draw the control line */
|
||||
stroke = gsk_stroke_new (1.0);
|
||||
gtk_snapshot_push_stroke (snapshot, self->line_path, stroke);
|
||||
gsk_stroke_free (stroke);
|
||||
gtk_snapshot_append_color (snapshot, &(GdkRGBA) { 0, 0, 0, 1 }, &GRAPHENE_RECT_INIT (0, 0, width, height));
|
||||
gtk_snapshot_pop (snapshot);
|
||||
|
||||
/* draw the points */
|
||||
builder = gsk_path_builder_new ();
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
gsk_path_builder_add_circle (builder, &GRAPHENE_POINT_INIT (self->points[i].x * width, self->points[i].y * height), POINT_SIZE);
|
||||
}
|
||||
path = gsk_path_builder_free_to_path (builder);
|
||||
|
||||
gtk_snapshot_push_fill (snapshot, path, GSK_FILL_RULE_WINDING);
|
||||
gtk_snapshot_append_color (snapshot, &(GdkRGBA) { 1, 1, 1, 1 }, &GRAPHENE_RECT_INIT (0, 0, width, height));
|
||||
gtk_snapshot_pop (snapshot);
|
||||
|
||||
stroke = gsk_stroke_new (1.0);
|
||||
gtk_snapshot_push_stroke (snapshot, path, stroke);
|
||||
gsk_stroke_free (stroke);
|
||||
gtk_snapshot_append_color (snapshot, &(GdkRGBA) { 0, 0, 0, 1 }, &GRAPHENE_RECT_INIT (0, 0, width, height));
|
||||
gtk_snapshot_pop (snapshot);
|
||||
|
||||
gsk_path_unref (path);
|
||||
}
|
||||
|
||||
if (self->line_closest >= 0)
|
||||
{
|
||||
GskPathBuilder *builder;
|
||||
graphene_point_t closest;
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
gsk_path_measure_get_point (self->line_measure, self->line_closest, &closest, NULL);
|
||||
gsk_path_builder_add_circle (builder, &closest, POINT_SIZE);
|
||||
path = gsk_path_builder_free_to_path (builder);
|
||||
|
||||
gtk_snapshot_push_fill (snapshot, path, GSK_FILL_RULE_WINDING);
|
||||
gtk_snapshot_append_color (snapshot, &(GdkRGBA) { 0, 0, 1, 1 }, &GRAPHENE_RECT_INIT (0, 0, width, height));
|
||||
gtk_snapshot_pop (snapshot);
|
||||
|
||||
gsk_path_unref (path);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_path_widget_set_text (GtkPathWidget *self,
|
||||
const char *text)
|
||||
{
|
||||
if (g_strcmp0 (self->text, text) == 0)
|
||||
return;
|
||||
|
||||
g_free (self->text);
|
||||
self->text = g_strdup (text);
|
||||
|
||||
gtk_path_widget_create_paths (self);
|
||||
|
||||
gtk_widget_queue_draw (GTK_WIDGET (self));
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_TEXT]);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_path_widget_set_editable (GtkPathWidget *self,
|
||||
gboolean editable)
|
||||
{
|
||||
if (self->editable == editable)
|
||||
return;
|
||||
|
||||
self->editable = editable;
|
||||
|
||||
gtk_widget_queue_draw (GTK_WIDGET (self));
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_EDITABLE]);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_path_widget_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
|
||||
{
|
||||
GtkPathWidget *self = GTK_PATH_WIDGET (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_TEXT:
|
||||
gtk_path_widget_set_text (self, g_value_get_string (value));
|
||||
break;
|
||||
|
||||
case PROP_EDITABLE:
|
||||
gtk_path_widget_set_editable (self, g_value_get_boolean (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_path_widget_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkPathWidget *self = GTK_PATH_WIDGET (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_TEXT:
|
||||
g_value_set_string (value, self->text);
|
||||
break;
|
||||
|
||||
case PROP_EDITABLE:
|
||||
g_value_set_boolean (value, self->editable);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_path_widget_dispose (GObject *object)
|
||||
{
|
||||
GtkPathWidget *self = GTK_PATH_WIDGET (object);
|
||||
|
||||
gtk_path_widget_clear_paths (self);
|
||||
|
||||
G_OBJECT_CLASS (gtk_path_widget_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_path_widget_class_init (GtkPathWidgetClass *klass)
|
||||
{
|
||||
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->dispose = gtk_path_widget_dispose;
|
||||
object_class->set_property = gtk_path_widget_set_property;
|
||||
object_class->get_property = gtk_path_widget_get_property;
|
||||
|
||||
widget_class->size_allocate = gtk_path_widget_allocate;
|
||||
widget_class->snapshot = gtk_path_widget_snapshot;
|
||||
|
||||
properties[PROP_TEXT] =
|
||||
g_param_spec_string ("text",
|
||||
"text",
|
||||
"Text transformed along a path",
|
||||
NULL,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
properties[PROP_EDITABLE] =
|
||||
g_param_spec_boolean ("editable",
|
||||
"editable",
|
||||
"If the path can be edited by the user",
|
||||
FALSE,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
g_object_class_install_properties (object_class, N_PROPS, properties);
|
||||
}
|
||||
|
||||
static void
|
||||
drag_begin (GtkGestureDrag *gesture,
|
||||
double x,
|
||||
double y,
|
||||
GtkPathWidget *self)
|
||||
{
|
||||
graphene_point_t mouse = GRAPHENE_POINT_INIT (x, y);
|
||||
double width = gtk_widget_get_width (GTK_WIDGET (self));
|
||||
double height = gtk_widget_get_height (GTK_WIDGET (self));
|
||||
gsize i;
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
if (graphene_point_distance (&GRAPHENE_POINT_INIT (self->points[i].x * width, self->points[i].y * height), &mouse, NULL, NULL) <= POINT_SIZE)
|
||||
{
|
||||
self->active_point = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == 4)
|
||||
{
|
||||
gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
|
||||
return;
|
||||
}
|
||||
|
||||
gtk_widget_queue_draw (GTK_WIDGET (self));
|
||||
}
|
||||
|
||||
static void
|
||||
drag_update (GtkGestureDrag *drag,
|
||||
double offset_x,
|
||||
double offset_y,
|
||||
GtkPathWidget *self)
|
||||
{
|
||||
double width = gtk_widget_get_width (GTK_WIDGET (self));
|
||||
double height = gtk_widget_get_height (GTK_WIDGET (self));
|
||||
double start_x, start_y;
|
||||
|
||||
gtk_gesture_drag_get_start_point (drag, &start_x, &start_y);
|
||||
|
||||
self->points[self->active_point] = GRAPHENE_POINT_INIT ((start_x + offset_x) / width,
|
||||
(start_y + offset_y) / height);
|
||||
self->points[self->active_point].x = CLAMP (self->points[self->active_point].x, 0, 1);
|
||||
self->points[self->active_point].y = CLAMP (self->points[self->active_point].y, 0, 1);
|
||||
|
||||
gtk_path_widget_create_paths (self);
|
||||
|
||||
gtk_widget_queue_draw (GTK_WIDGET (self));
|
||||
}
|
||||
|
||||
static void
|
||||
pointer_motion (GtkEventControllerMotion *controller,
|
||||
double x,
|
||||
double y,
|
||||
GtkPathWidget *self)
|
||||
{
|
||||
gsk_path_measure_get_closest_point_full (self->line_measure,
|
||||
&GRAPHENE_POINT_INIT (x, y),
|
||||
INFINITY,
|
||||
&self->line_closest,
|
||||
NULL, NULL, NULL);
|
||||
|
||||
gtk_widget_queue_draw (GTK_WIDGET (self));
|
||||
}
|
||||
|
||||
static void
|
||||
pointer_leave (GtkEventControllerMotion *controller,
|
||||
GtkPathWidget *self)
|
||||
{
|
||||
self->line_closest = -1;
|
||||
|
||||
gtk_widget_queue_draw (GTK_WIDGET (self));
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_path_widget_init (GtkPathWidget *self)
|
||||
{
|
||||
GtkEventController *controller;
|
||||
|
||||
controller = GTK_EVENT_CONTROLLER (gtk_gesture_drag_new ());
|
||||
g_signal_connect (controller, "drag-begin", G_CALLBACK (drag_begin), self);
|
||||
g_signal_connect (controller, "drag-update", G_CALLBACK (drag_update), self);
|
||||
g_signal_connect (controller, "drag-end", G_CALLBACK (drag_update), self);
|
||||
gtk_widget_add_controller (GTK_WIDGET (self), controller);
|
||||
|
||||
controller = GTK_EVENT_CONTROLLER (gtk_event_controller_motion_new ());
|
||||
g_signal_connect (controller, "enter", G_CALLBACK (pointer_motion), self);
|
||||
g_signal_connect (controller, "motion", G_CALLBACK (pointer_motion), self);
|
||||
g_signal_connect (controller, "leave", G_CALLBACK (pointer_leave), self);
|
||||
gtk_widget_add_controller (GTK_WIDGET (self), controller);
|
||||
|
||||
self->line_closest = -1;
|
||||
|
||||
self->points[0] = GRAPHENE_POINT_INIT (0.1, 0.9);
|
||||
self->points[1] = GRAPHENE_POINT_INIT (0.3, 0.1);
|
||||
self->points[2] = GRAPHENE_POINT_INIT (0.7, 0.1);
|
||||
self->points[3] = GRAPHENE_POINT_INIT (0.9, 0.9);
|
||||
|
||||
self->background = GDK_PAINTABLE (gdk_texture_new_from_resource ("/sliding_puzzle/portland-rose.jpg"));
|
||||
|
||||
gtk_path_widget_set_text (self, "It's almost working");
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
gtk_path_widget_new (void)
|
||||
{
|
||||
GtkPathWidget *self;
|
||||
|
||||
self = g_object_new (GTK_TYPE_PATH_WIDGET, NULL);
|
||||
|
||||
return GTK_WIDGET (self);
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
do_path_text (GtkWidget *do_widget)
|
||||
{
|
||||
static GtkWidget *window = NULL;
|
||||
|
||||
if (!window)
|
||||
{
|
||||
GtkBuilder *builder;
|
||||
|
||||
g_type_ensure (GTK_TYPE_PATH_WIDGET);
|
||||
|
||||
builder = gtk_builder_new_from_resource ("/path_text/path_text.ui");
|
||||
window = GTK_WIDGET (gtk_builder_get_object (builder, "window"));
|
||||
gtk_window_set_display (GTK_WINDOW (window),
|
||||
gtk_widget_get_display (do_widget));
|
||||
g_object_add_weak_pointer (G_OBJECT (window), (gpointer *) &window);
|
||||
g_object_unref (builder);
|
||||
}
|
||||
|
||||
if (!gtk_widget_get_visible (window))
|
||||
gtk_window_present (GTK_WINDOW (window));
|
||||
else
|
||||
gtk_window_destroy (GTK_WINDOW (window));
|
||||
|
||||
return window;
|
||||
}
|
||||
38
demos/gtk-demo/path_text.ui
Normal file
38
demos/gtk-demo/path_text.ui
Normal file
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<object class="GtkWindow" id="window">
|
||||
<property name="title" translatable="yes">Text along a Path</property>
|
||||
<child type="titlebar">
|
||||
<object class="GtkHeaderBar">
|
||||
<child type="end">
|
||||
<object class="GtkToggleButton" id="edit-toggle">
|
||||
<property name="icon-name">document-edit-symbolic</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkRevealer">
|
||||
<property name="reveal-child" bind-source="edit-toggle" bind-property="active" bind-flags="sync-create"></property>
|
||||
<child>
|
||||
<object class="GtkEntry" id="text">
|
||||
<property name="text">Through the looking glass</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkPathWidget" id="view">
|
||||
<property name="editable" bind-source="edit-toggle" bind-property="active" bind-flags="sync-create"></property>
|
||||
<property name="text" bind-source="text" bind-property="text" bind-flags="sync-create"></property>
|
||||
<property name="hexpand">true</property>
|
||||
<property name="vexpand">true</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
||||
@@ -7,68 +7,186 @@
|
||||
#include <glib/gi18n.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
static void
|
||||
draw_text (GtkDrawingArea *da,
|
||||
cairo_t *cr,
|
||||
int width,
|
||||
int height,
|
||||
gpointer data)
|
||||
#define TEXT_TYPE_MASK_DEMO (text_mask_demo_get_type ())
|
||||
G_DECLARE_FINAL_TYPE (TextMaskDemo, text_mask_demo, TEXT, MASK_DEMO, GtkWidget)
|
||||
|
||||
struct _TextMaskDemo
|
||||
{
|
||||
cairo_pattern_t *pattern;
|
||||
PangoLayout *layout;
|
||||
GtkWidget parent_instance;
|
||||
|
||||
char *text;
|
||||
PangoFontDescription *desc;
|
||||
GskPath *path;
|
||||
};
|
||||
|
||||
cairo_save (cr);
|
||||
struct _TextMaskDemoClass
|
||||
{
|
||||
GtkWidgetClass parent_class;
|
||||
};
|
||||
|
||||
layout = gtk_widget_create_pango_layout (GTK_WIDGET (da), "Pango power!\nPango power!\nPango power!");
|
||||
desc = pango_font_description_from_string ("sans bold 34");
|
||||
pango_layout_set_font_description (layout, desc);
|
||||
pango_font_description_free (desc);
|
||||
G_DEFINE_TYPE (TextMaskDemo, text_mask_demo, GTK_TYPE_WIDGET)
|
||||
|
||||
cairo_move_to (cr, 30, 20);
|
||||
pango_cairo_layout_path (cr, layout);
|
||||
g_object_unref (layout);
|
||||
static void
|
||||
update_path (TextMaskDemo *demo)
|
||||
{
|
||||
PangoLayout *layout;
|
||||
GskPathBuilder *builder;
|
||||
|
||||
pattern = cairo_pattern_create_linear (0.0, 0.0, width, height);
|
||||
cairo_pattern_add_color_stop_rgb (pattern, 0.0, 1.0, 0.0, 0.0);
|
||||
cairo_pattern_add_color_stop_rgb (pattern, 0.2, 1.0, 0.0, 0.0);
|
||||
cairo_pattern_add_color_stop_rgb (pattern, 0.3, 1.0, 1.0, 0.0);
|
||||
cairo_pattern_add_color_stop_rgb (pattern, 0.4, 0.0, 1.0, 0.0);
|
||||
cairo_pattern_add_color_stop_rgb (pattern, 0.6, 0.0, 1.0, 1.0);
|
||||
cairo_pattern_add_color_stop_rgb (pattern, 0.7, 0.0, 0.0, 1.0);
|
||||
cairo_pattern_add_color_stop_rgb (pattern, 0.8, 1.0, 0.0, 1.0);
|
||||
cairo_pattern_add_color_stop_rgb (pattern, 1.0, 1.0, 0.0, 1.0);
|
||||
g_clear_pointer (&demo->path, gsk_path_unref);
|
||||
|
||||
cairo_set_source (cr, pattern);
|
||||
cairo_fill_preserve (cr);
|
||||
layout = gtk_widget_create_pango_layout (GTK_WIDGET (demo), demo->text);
|
||||
pango_layout_set_font_description (layout, demo->desc);
|
||||
|
||||
cairo_pattern_destroy (pattern);
|
||||
builder = gsk_path_builder_new ();
|
||||
gsk_path_builder_add_layout (builder, layout);
|
||||
demo->path = gsk_path_builder_free_to_path (builder);
|
||||
|
||||
cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
|
||||
cairo_set_line_width (cr, 0.5);
|
||||
cairo_stroke (cr);
|
||||
gtk_widget_queue_draw (GTK_WIDGET (demo));
|
||||
}
|
||||
|
||||
cairo_restore (cr);
|
||||
static void
|
||||
text_mask_demo_init (TextMaskDemo *demo)
|
||||
{
|
||||
demo->text = g_strdup ("No text. No fun");
|
||||
demo->desc = pango_font_description_from_string ("Cantarell 20");
|
||||
update_path (demo);
|
||||
}
|
||||
|
||||
static void
|
||||
text_mask_demo_snapshot (GtkWidget *widget,
|
||||
GtkSnapshot *snapshot)
|
||||
{
|
||||
TextMaskDemo *demo = TEXT_MASK_DEMO (widget);
|
||||
float width, height;
|
||||
GskColorStop stops[4];
|
||||
GskStroke *stroke;
|
||||
|
||||
gdk_rgba_parse (&stops[0].color, "red");
|
||||
gdk_rgba_parse (&stops[1].color, "green");
|
||||
gdk_rgba_parse (&stops[2].color, "yellow");
|
||||
gdk_rgba_parse (&stops[3].color, "blue");
|
||||
|
||||
width = gtk_widget_get_width (widget);
|
||||
height = gtk_widget_get_height (widget);
|
||||
|
||||
stops[0].offset = 0;
|
||||
stops[1].offset = 0.25;
|
||||
stops[2].offset = 0.50;
|
||||
stops[3].offset = 0.75;
|
||||
|
||||
gtk_snapshot_push_fill (snapshot, demo->path, GSK_FILL_RULE_WINDING);
|
||||
|
||||
gtk_snapshot_append_linear_gradient (snapshot,
|
||||
&GRAPHENE_RECT_INIT (0, 0, width, height),
|
||||
&GRAPHENE_POINT_INIT (0, 0),
|
||||
&GRAPHENE_POINT_INIT (width, height),
|
||||
stops, 4);
|
||||
gtk_snapshot_pop (snapshot);
|
||||
|
||||
stroke = gsk_stroke_new (1.0);
|
||||
gtk_snapshot_push_stroke (snapshot, demo->path, stroke);
|
||||
gsk_stroke_free (stroke);
|
||||
|
||||
gtk_snapshot_append_color (snapshot,
|
||||
&(GdkRGBA){ 0, 0, 0, 1 },
|
||||
&GRAPHENE_RECT_INIT (0, 0, width, height));
|
||||
|
||||
gtk_snapshot_pop (snapshot);
|
||||
}
|
||||
|
||||
void
|
||||
text_mask_demo_measure (GtkWidget *widget,
|
||||
GtkOrientation orientation,
|
||||
int for_size,
|
||||
int *minimum_size,
|
||||
int *natural_size,
|
||||
int *minimum_baseline,
|
||||
int *natural_baseline)
|
||||
{
|
||||
TextMaskDemo *demo = TEXT_MASK_DEMO (widget);
|
||||
graphene_rect_t rect;
|
||||
|
||||
gsk_path_get_bounds (demo->path, &rect);
|
||||
|
||||
if (orientation == GTK_ORIENTATION_HORIZONTAL)
|
||||
*minimum_size = *natural_size = rect.size.width;
|
||||
else
|
||||
*minimum_size = *natural_size = rect.size.height;
|
||||
}
|
||||
|
||||
static void
|
||||
text_mask_demo_finalize (GObject *object)
|
||||
{
|
||||
TextMaskDemo *demo = TEXT_MASK_DEMO (object);
|
||||
|
||||
g_free (demo->text);
|
||||
pango_font_description_free (demo->desc);
|
||||
gsk_path_unref (demo->path);
|
||||
|
||||
G_OBJECT_CLASS (text_mask_demo_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
text_mask_demo_class_init (TextMaskDemoClass *class)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
||||
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
|
||||
|
||||
object_class->finalize = text_mask_demo_finalize;
|
||||
|
||||
widget_class->snapshot = text_mask_demo_snapshot;
|
||||
widget_class->measure = text_mask_demo_measure;
|
||||
}
|
||||
|
||||
static GtkWidget *
|
||||
text_mask_demo_new (void)
|
||||
{
|
||||
return g_object_new (text_mask_demo_get_type (), NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
text_mask_demo_set_text (TextMaskDemo *demo,
|
||||
const char *text)
|
||||
{
|
||||
g_free (demo->text);
|
||||
demo->text = g_strdup (text);
|
||||
|
||||
update_path (demo);
|
||||
}
|
||||
|
||||
static void
|
||||
text_mask_demo_set_font (TextMaskDemo *demo,
|
||||
PangoFontDescription *desc)
|
||||
{
|
||||
pango_font_description_free (demo->desc);
|
||||
demo->desc = pango_font_description_copy (desc);
|
||||
|
||||
update_path (demo);
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
do_textmask (GtkWidget *do_widget)
|
||||
{
|
||||
static GtkWidget *window = NULL;
|
||||
static GtkWidget *da;
|
||||
static GtkWidget *demo;
|
||||
|
||||
if (!window)
|
||||
{
|
||||
PangoFontDescription *desc;
|
||||
|
||||
window = gtk_window_new ();
|
||||
gtk_window_set_resizable (GTK_WINDOW (window), TRUE);
|
||||
gtk_widget_set_size_request (window, 400, 240);
|
||||
gtk_window_set_title (GTK_WINDOW (window), "Text Mask");
|
||||
g_object_add_weak_pointer (G_OBJECT (window), (gpointer *)&window);
|
||||
|
||||
da = gtk_drawing_area_new ();
|
||||
demo = text_mask_demo_new ();
|
||||
text_mask_demo_set_text (TEXT_MASK_DEMO (demo), "Pango power!\nPango power!\nPango power!");
|
||||
desc = pango_font_description_from_string ("Sans Bold 34");
|
||||
text_mask_demo_set_font (TEXT_MASK_DEMO (demo), desc);
|
||||
pango_font_description_free (desc);
|
||||
|
||||
gtk_window_set_child (GTK_WINDOW (window), da);
|
||||
gtk_drawing_area_set_draw_func (GTK_DRAWING_AREA (da), draw_text, NULL, NULL);
|
||||
gtk_window_set_child (GTK_WINDOW (window), demo);
|
||||
}
|
||||
|
||||
if (!gtk_widget_get_visible (window))
|
||||
|
||||
@@ -3,6 +3,7 @@ node_editor_sources = [
|
||||
'main.c',
|
||||
'node-editor-application.c',
|
||||
'node-editor-window.c',
|
||||
'rendernodeutils.c'
|
||||
]
|
||||
|
||||
node_editor_resources = gnome.compile_resources('node_editor_resources',
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "node-editor-window.h"
|
||||
|
||||
#include "gtkrendererpaintableprivate.h"
|
||||
#include "rendernodeutilsprivate.h"
|
||||
|
||||
#include "gsk/gskrendernodeparserprivate.h"
|
||||
#include "gsk/gl/gskglrenderer.h"
|
||||
@@ -61,8 +62,10 @@ struct _NodeEditorWindow
|
||||
GtkWidget *scale_scale;
|
||||
|
||||
GtkWidget *renderer_listbox;
|
||||
GListStore *saved_nodes;
|
||||
GListStore *renderers;
|
||||
GskRenderNode *node;
|
||||
GtkAdjustment *compare_progress;
|
||||
|
||||
GFile *file;
|
||||
GFileMonitor *file_monitor;
|
||||
@@ -130,6 +133,7 @@ deserialize_error_func (const GskParseLocation *start_location,
|
||||
g_array_append_val (self->errors, text_view_error);
|
||||
}
|
||||
|
||||
#if 0
|
||||
static void
|
||||
text_iter_skip_alpha_backward (GtkTextIter *iter)
|
||||
{
|
||||
@@ -165,33 +169,22 @@ text_iter_skip_whitespace_backward (GtkTextIter *iter)
|
||||
gtk_text_iter_backward_char (iter);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
text_changed (GtkTextBuffer *buffer,
|
||||
NodeEditorWindow *self)
|
||||
node_changed (NodeEditorWindow *self)
|
||||
{
|
||||
char *text;
|
||||
GBytes *bytes;
|
||||
GtkTextIter iter;
|
||||
GtkTextIter start, end;
|
||||
float scale;
|
||||
GskRenderNode *big_node;
|
||||
|
||||
g_array_remove_range (self->errors, 0, self->errors->len);
|
||||
text = get_current_text (self->text_buffer);
|
||||
text_buffer_remove_all_tags (self->text_buffer);
|
||||
bytes = g_bytes_new_take (text, strlen (text));
|
||||
|
||||
g_clear_pointer (&self->node, gsk_render_node_unref);
|
||||
|
||||
/* If this is too slow, go fix the parser performance */
|
||||
self->node = gsk_render_node_deserialize (bytes, deserialize_error_func, self);
|
||||
|
||||
scale = gtk_scale_button_get_value (GTK_SCALE_BUTTON (self->scale_scale));
|
||||
if (self->node && scale != 0.)
|
||||
{
|
||||
GskTransform *transform;
|
||||
scale = pow (2., scale);
|
||||
big_node = gsk_transform_node_new (self->node, gsk_transform_scale (NULL, scale, scale));
|
||||
transform = gsk_transform_scale (NULL, scale, scale);
|
||||
big_node = gsk_transform_node_new (self->node, transform);
|
||||
gsk_transform_unref (transform);
|
||||
}
|
||||
else if (self->node)
|
||||
{
|
||||
@@ -202,14 +195,13 @@ text_changed (GtkTextBuffer *buffer,
|
||||
big_node = NULL;
|
||||
}
|
||||
|
||||
g_bytes_unref (bytes);
|
||||
if (self->node)
|
||||
{
|
||||
/* XXX: Is this code necessary or can we have API to turn nodes into paintables? */
|
||||
GtkSnapshot *snapshot;
|
||||
GdkPaintable *paintable;
|
||||
graphene_rect_t bounds;
|
||||
guint i;
|
||||
guint i, n;
|
||||
|
||||
snapshot = gtk_snapshot_new ();
|
||||
gsk_render_node_get_bounds (big_node, &bounds);
|
||||
@@ -225,6 +217,8 @@ text_changed (GtkTextBuffer *buffer,
|
||||
gtk_snapshot_append_node (snapshot, self->node);
|
||||
paintable = gtk_snapshot_free_to_paintable (snapshot, &bounds.size);
|
||||
|
||||
n = g_list_model_get_n_items (G_LIST_MODEL (self->saved_nodes));
|
||||
g_list_store_splice (self->saved_nodes, MAX (n, 1) - 1, MIN (n, 1), (gpointer[1]) { paintable }, 1);
|
||||
for (i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (self->renderers)); i++)
|
||||
{
|
||||
gpointer item = g_list_model_get_item (G_LIST_MODEL (self->renderers), i);
|
||||
@@ -240,7 +234,30 @@ text_changed (GtkTextBuffer *buffer,
|
||||
}
|
||||
|
||||
g_clear_pointer (&big_node, gsk_render_node_unref);
|
||||
}
|
||||
|
||||
static void
|
||||
text_changed (GtkTextBuffer *buffer,
|
||||
NodeEditorWindow *self)
|
||||
{
|
||||
char *text;
|
||||
GBytes *bytes;
|
||||
|
||||
g_array_remove_range (self->errors, 0, self->errors->len);
|
||||
text = get_current_text (self->text_buffer);
|
||||
text_buffer_remove_all_tags (self->text_buffer);
|
||||
bytes = g_bytes_new_take (text, strlen (text));
|
||||
|
||||
g_clear_pointer (&self->node, gsk_render_node_unref);
|
||||
|
||||
/* If this is too slow, go fix the parser performance */
|
||||
self->node = gsk_render_node_deserialize (bytes, deserialize_error_func, self);
|
||||
|
||||
node_changed (self);
|
||||
|
||||
g_bytes_unref (bytes);
|
||||
|
||||
#if 0
|
||||
gtk_text_buffer_get_start_iter (self->text_buffer, &iter);
|
||||
|
||||
while (!gtk_text_iter_is_end (&iter))
|
||||
@@ -307,6 +324,18 @@ text_changed (GtkTextBuffer *buffer,
|
||||
gtk_text_buffer_get_bounds (self->text_buffer, &start, &end);
|
||||
gtk_text_buffer_apply_tag_by_name (self->text_buffer, "no-hyphens",
|
||||
&start, &end);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
stash_current_node (NodeEditorWindow *self)
|
||||
{
|
||||
GdkPaintable *last;
|
||||
|
||||
last = g_list_model_get_item (G_LIST_MODEL (self->saved_nodes),
|
||||
g_list_model_get_n_items (G_LIST_MODEL (self->saved_nodes)) - 1);
|
||||
g_list_store_append (self->saved_nodes, last);
|
||||
g_object_unref (last);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -314,7 +343,7 @@ scale_changed (GObject *object,
|
||||
GParamSpec *pspec,
|
||||
NodeEditorWindow *self)
|
||||
{
|
||||
text_changed (self->text_buffer, self);
|
||||
node_changed (self);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
@@ -378,7 +407,8 @@ text_view_query_tooltip_cb (GtkWidget *widget,
|
||||
|
||||
static gboolean
|
||||
load_bytes (NodeEditorWindow *self,
|
||||
GBytes *bytes);
|
||||
GBytes *bytes,
|
||||
gboolean stash);
|
||||
|
||||
static void
|
||||
load_error (NodeEditorWindow *self,
|
||||
@@ -396,7 +426,7 @@ load_error (NodeEditorWindow *self,
|
||||
node = gtk_snapshot_free_to_node (snapshot);
|
||||
bytes = gsk_render_node_serialize (node);
|
||||
|
||||
load_bytes (self, bytes);
|
||||
load_bytes (self, bytes, TRUE);
|
||||
|
||||
gsk_render_node_unref (node);
|
||||
g_object_unref (layout);
|
||||
@@ -404,7 +434,8 @@ load_error (NodeEditorWindow *self,
|
||||
|
||||
static gboolean
|
||||
load_bytes (NodeEditorWindow *self,
|
||||
GBytes *bytes)
|
||||
GBytes *bytes,
|
||||
gboolean stash)
|
||||
{
|
||||
if (!g_utf8_validate (g_bytes_get_data (bytes, NULL), g_bytes_get_size (bytes), NULL))
|
||||
{
|
||||
@@ -412,6 +443,9 @@ load_bytes (NodeEditorWindow *self,
|
||||
g_bytes_unref (bytes);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (stash)
|
||||
stash_current_node (self);
|
||||
|
||||
gtk_text_buffer_set_text (self->text_buffer,
|
||||
g_bytes_get_data (bytes, NULL),
|
||||
@@ -437,7 +471,29 @@ load_file_contents (NodeEditorWindow *self,
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return load_bytes (self, bytes);
|
||||
return load_bytes (self, bytes, TRUE);
|
||||
}
|
||||
|
||||
static void
|
||||
saved_node_activate_cb (GtkListView *listview,
|
||||
guint pos,
|
||||
NodeEditorWindow *self)
|
||||
{
|
||||
GdkPaintable *paintable;
|
||||
GtkSnapshot *snapshot;
|
||||
GskRenderNode *node;
|
||||
GBytes *bytes;
|
||||
|
||||
paintable = g_list_model_get_item (G_LIST_MODEL (self->saved_nodes), pos);
|
||||
|
||||
snapshot = gtk_snapshot_new ();
|
||||
gdk_paintable_snapshot (paintable, snapshot, gdk_paintable_get_intrinsic_width (paintable), gdk_paintable_get_intrinsic_height (paintable));
|
||||
node = gtk_snapshot_free_to_node (snapshot);
|
||||
|
||||
bytes = gsk_render_node_serialize (node);
|
||||
load_bytes (self, bytes, TRUE);
|
||||
|
||||
g_object_unref (paintable);
|
||||
}
|
||||
|
||||
static GdkContentProvider *
|
||||
@@ -466,7 +522,7 @@ on_picture_drop_read_done_cb (GObject *source,
|
||||
if (g_output_stream_splice_finish (stream, res, NULL) >= 0)
|
||||
{
|
||||
bytes = g_memory_output_stream_steal_as_bytes (G_MEMORY_OUTPUT_STREAM (stream));
|
||||
if (load_bytes (self, bytes))
|
||||
if (load_bytes (self, bytes, TRUE))
|
||||
action = GDK_ACTION_COPY;
|
||||
}
|
||||
|
||||
@@ -650,6 +706,10 @@ save_response_cb (GObject *source,
|
||||
g_object_unref (alert);
|
||||
g_error_free (error);
|
||||
}
|
||||
else
|
||||
{
|
||||
stash_current_node (self);
|
||||
}
|
||||
|
||||
g_free (text);
|
||||
g_object_unref (file);
|
||||
@@ -1075,6 +1135,8 @@ testcase_save_clicked_cb (GtkWidget *button,
|
||||
gtk_editable_set_text (GTK_EDITABLE (self->testcase_name_entry), "");
|
||||
gtk_popover_popdown (GTK_POPOVER (self->testcase_popover));
|
||||
|
||||
stash_current_node (self);
|
||||
|
||||
out:
|
||||
g_free (text);
|
||||
g_free (png_file);
|
||||
@@ -1083,8 +1145,54 @@ out:
|
||||
}
|
||||
|
||||
static void
|
||||
dark_mode_cb (GtkToggleButton *button,
|
||||
GParamSpec *pspec,
|
||||
update_compare (NodeEditorWindow *self)
|
||||
{
|
||||
GdkPaintable *from, *to;
|
||||
GtkSnapshot *snapshot;
|
||||
GskRenderNode *node, *node_from, *node_to;
|
||||
GBytes *bytes;
|
||||
guint n;
|
||||
|
||||
n = g_list_model_get_n_items (G_LIST_MODEL (self->saved_nodes));
|
||||
|
||||
from = g_list_model_get_item (G_LIST_MODEL (self->saved_nodes), n - 3);
|
||||
snapshot = gtk_snapshot_new ();
|
||||
gdk_paintable_snapshot (from, snapshot, gdk_paintable_get_intrinsic_width (from), gdk_paintable_get_intrinsic_height (from));
|
||||
node_from = gtk_snapshot_free_to_node (snapshot);
|
||||
g_object_unref (from);
|
||||
|
||||
to = g_list_model_get_item (G_LIST_MODEL (self->saved_nodes), n - 2);
|
||||
snapshot = gtk_snapshot_new ();
|
||||
gdk_paintable_snapshot (to, snapshot, gdk_paintable_get_intrinsic_width (to), gdk_paintable_get_intrinsic_height (to));
|
||||
node_to = gtk_snapshot_free_to_node (snapshot);
|
||||
g_object_unref (to);
|
||||
|
||||
node = render_node_interpolate (node_from, node_to, gtk_adjustment_get_value (self->compare_progress));
|
||||
|
||||
bytes = gsk_render_node_serialize (node);
|
||||
load_bytes (self, bytes, FALSE);
|
||||
|
||||
gsk_render_node_unref (node);
|
||||
gsk_render_node_unref (node_to);
|
||||
gsk_render_node_unref (node_from);
|
||||
}
|
||||
|
||||
static void
|
||||
compare_toggled_cb (GtkToggleButton *button,
|
||||
GParamSpec *pspec,
|
||||
NodeEditorWindow *self)
|
||||
{
|
||||
if (!gtk_toggle_button_get_active (button))
|
||||
return;
|
||||
|
||||
stash_current_node (self);
|
||||
|
||||
update_compare (self);
|
||||
}
|
||||
|
||||
static void
|
||||
dark_mode_cb (GtkToggleButton *button,
|
||||
GParamSpec *pspec,
|
||||
NodeEditorWindow *self)
|
||||
{
|
||||
g_object_set (gtk_widget_get_settings (GTK_WIDGET (self)),
|
||||
@@ -1115,6 +1223,7 @@ node_editor_window_finalize (GObject *object)
|
||||
G_OBJECT_CLASS (node_editor_window_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
#if 0
|
||||
static void
|
||||
node_editor_window_add_renderer (NodeEditorWindow *self,
|
||||
GskRenderer *renderer,
|
||||
@@ -1149,11 +1258,9 @@ node_editor_window_realize (GtkWidget *widget)
|
||||
|
||||
GTK_WIDGET_CLASS (node_editor_window_parent_class)->realize (widget);
|
||||
|
||||
#if 0
|
||||
node_editor_window_add_renderer (self,
|
||||
NULL,
|
||||
"Default");
|
||||
#endif
|
||||
node_editor_window_add_renderer (self,
|
||||
gsk_gl_renderer_new (),
|
||||
"OpenGL");
|
||||
@@ -1171,6 +1278,7 @@ node_editor_window_realize (GtkWidget *widget)
|
||||
gsk_cairo_renderer_new (),
|
||||
"Cairo");
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
node_editor_window_unrealize (GtkWidget *widget)
|
||||
@@ -1553,11 +1661,12 @@ node_editor_window_class_init (NodeEditorWindowClass *class)
|
||||
gtk_widget_class_set_template_from_resource (widget_class,
|
||||
"/org/gtk/gtk4/node-editor/node-editor-window.ui");
|
||||
|
||||
widget_class->realize = node_editor_window_realize;
|
||||
//widget_class->realize = node_editor_window_realize;
|
||||
widget_class->unrealize = node_editor_window_unrealize;
|
||||
|
||||
gtk_widget_class_bind_template_child (widget_class, NodeEditorWindow, text_view);
|
||||
gtk_widget_class_bind_template_child (widget_class, NodeEditorWindow, picture);
|
||||
gtk_widget_class_bind_template_child (widget_class, NodeEditorWindow, compare_progress);
|
||||
gtk_widget_class_bind_template_child (widget_class, NodeEditorWindow, renderer_listbox);
|
||||
gtk_widget_class_bind_template_child (widget_class, NodeEditorWindow, testcase_popover);
|
||||
gtk_widget_class_bind_template_child (widget_class, NodeEditorWindow, testcase_error_label);
|
||||
@@ -1565,18 +1674,22 @@ node_editor_window_class_init (NodeEditorWindowClass *class)
|
||||
gtk_widget_class_bind_template_child (widget_class, NodeEditorWindow, testcase_name_entry);
|
||||
gtk_widget_class_bind_template_child (widget_class, NodeEditorWindow, testcase_save_button);
|
||||
gtk_widget_class_bind_template_child (widget_class, NodeEditorWindow, scale_scale);
|
||||
gtk_widget_class_bind_template_child (widget_class, NodeEditorWindow, saved_nodes);
|
||||
|
||||
gtk_widget_class_bind_template_callback (widget_class, text_view_query_tooltip_cb);
|
||||
gtk_widget_class_bind_template_callback (widget_class, saved_node_activate_cb);
|
||||
gtk_widget_class_bind_template_callback (widget_class, open_cb);
|
||||
gtk_widget_class_bind_template_callback (widget_class, save_cb);
|
||||
gtk_widget_class_bind_template_callback (widget_class, export_image_cb);
|
||||
gtk_widget_class_bind_template_callback (widget_class, clip_image_cb);
|
||||
gtk_widget_class_bind_template_callback (widget_class, testcase_save_clicked_cb);
|
||||
gtk_widget_class_bind_template_callback (widget_class, testcase_name_entry_changed_cb);
|
||||
gtk_widget_class_bind_template_callback (widget_class, compare_toggled_cb);
|
||||
gtk_widget_class_bind_template_callback (widget_class, dark_mode_cb);
|
||||
gtk_widget_class_bind_template_callback (widget_class, on_picture_drag_prepare_cb);
|
||||
gtk_widget_class_bind_template_callback (widget_class, on_picture_drop_cb);
|
||||
gtk_widget_class_bind_template_callback (widget_class, click_gesture_pressed);
|
||||
gtk_widget_class_bind_template_callback (widget_class, update_compare);
|
||||
|
||||
gtk_widget_class_install_action (widget_class, "smart-edit", NULL, edit_action_cb);
|
||||
|
||||
|
||||
@@ -147,6 +147,11 @@
|
||||
<property name="popover">testcase_popover</property>
|
||||
</object>
|
||||
</child>
|
||||
<child type="start">
|
||||
<object class="GtkSpinner">
|
||||
<property name="spinning">1</property>
|
||||
</object>
|
||||
</child>
|
||||
<child type="end">
|
||||
<object class="GtkMenuButton" id="gear_menu_button">
|
||||
<property name="focus-on-click">0</property>
|
||||
@@ -165,6 +170,16 @@
|
||||
<signal name="notify::active" handler="dark_mode_cb" swapped="0"/>
|
||||
</object>
|
||||
</child>
|
||||
<child type="end">
|
||||
<object class="GtkToggleButton" id="compare_button">
|
||||
<property name="focus-on-click">0</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="has-frame">0</property>
|
||||
<property name="icon-name">emblem-synchronizing-symbolic</property>
|
||||
<property name="tooltip-text" translatable="yes">Compare the last 2 items</property>
|
||||
<signal name="notify::active" handler="compare_toggled_cb" swapped="0"/>
|
||||
</object>
|
||||
</child>
|
||||
<child type="end">
|
||||
<object class="GtkScaleButton" id="scale_scale">
|
||||
<property name="focus-on-click">0</property>
|
||||
@@ -185,85 +200,157 @@
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkPaned">
|
||||
<property name="shrink-start-child">false</property>
|
||||
<property name="shrink-end-child">false</property>
|
||||
<property name="position">400</property>
|
||||
<property name="start-child">
|
||||
<object class="GtkScrolledWindow">
|
||||
<property name="hscrollbar-policy">never</property>
|
||||
<property name="hexpand">1</property>
|
||||
<property name="vexpand">1</property>
|
||||
<child>
|
||||
<object class="GtkTextView" id="text_view">
|
||||
<property name="wrap-mode">word</property>
|
||||
<property name="monospace">1</property>
|
||||
<property name="top-margin">6</property>
|
||||
<property name="left-margin">6</property>
|
||||
<property name="right-margin">6</property>
|
||||
<property name="bottom-margin">6</property>
|
||||
<property name="has-tooltip">1</property>
|
||||
<property name="extra-menu">extra_menu</property>
|
||||
<signal name="query-tooltip" handler="text_view_query_tooltip_cb"/>
|
||||
<style>
|
||||
<class name="editor" />
|
||||
</style>
|
||||
<object class="GtkBox">
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkListView">
|
||||
<property name="orientation">horizontal</property>
|
||||
<property name="single-click-activate">1</property>
|
||||
<property name="model">
|
||||
<object class="GtkNoSelection">
|
||||
<property name="model">
|
||||
<object class="GListStore" id="saved_nodes" />
|
||||
</property>
|
||||
</object>
|
||||
</property>
|
||||
<property name="factory">
|
||||
<object class="GtkBuilderListItemFactory">
|
||||
<property name="bytes"><![CDATA[
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<template class="GtkListItem">
|
||||
<property name="child">
|
||||
<object class="GtkImage">
|
||||
<property name="icon-size">large</property>
|
||||
<binding name="paintable">
|
||||
<lookup name="item">GtkListItem</lookup>
|
||||
</binding>
|
||||
</object>
|
||||
</property>
|
||||
</template>
|
||||
</interface>
|
||||
]]></property>
|
||||
</object>
|
||||
</property>
|
||||
<signal name="activate" handler="saved_node_activate_cb"/>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkPaned">
|
||||
<property name="shrink-start-child">false</property>
|
||||
<property name="shrink-end-child">false</property>
|
||||
<property name="position">400</property>
|
||||
<property name="start-child">
|
||||
<object class="GtkScrolledWindow">
|
||||
<property name="hscrollbar-policy">never</property>
|
||||
<property name="hexpand">1</property>
|
||||
<property name="vexpand">1</property>
|
||||
<child>
|
||||
<object class="GtkGestureClick">
|
||||
<property name="button">1</property>
|
||||
<signal name="pressed" handler="click_gesture_pressed"/>
|
||||
<object class="GtkTextView" id="text_view">
|
||||
<property name="wrap-mode">word</property>
|
||||
<property name="monospace">1</property>
|
||||
<property name="top-margin">6</property>
|
||||
<property name="left-margin">6</property>
|
||||
<property name="right-margin">6</property>
|
||||
<property name="bottom-margin">6</property>
|
||||
<property name="has-tooltip">1</property>
|
||||
<property name="extra-menu">extra_menu</property>
|
||||
<signal name="query-tooltip" handler="text_view_query_tooltip_cb"/>
|
||||
<style>
|
||||
<class name="editor" />
|
||||
</style>
|
||||
<child>
|
||||
<object class="GtkGestureClick">
|
||||
<property name="button">1</property>
|
||||
<signal name="pressed" handler="click_gesture_pressed"/>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</property>
|
||||
<property name="end-child">
|
||||
<object class="GtkBox">
|
||||
<child>
|
||||
<object class="GtkScrolledWindow">
|
||||
<property name="hexpand">1</property>
|
||||
<property name="vexpand">1</property>
|
||||
<property name="min-content-height">100</property>
|
||||
<property name="min-content-width">100</property>
|
||||
</property>
|
||||
<property name="end-child">
|
||||
<object class="GtkBox">
|
||||
<child>
|
||||
<object class="GtkViewport">
|
||||
<object class="GtkOverlay">
|
||||
<child>
|
||||
<object class="GtkPicture" id="picture">
|
||||
<property name="can-shrink">0</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="valign">center</property>
|
||||
<object class="GtkScrolledWindow">
|
||||
<property name="hexpand">1</property>
|
||||
<property name="vexpand">1</property>
|
||||
<property name="min-content-height">100</property>
|
||||
<property name="min-content-width">100</property>
|
||||
<child>
|
||||
<object class="GtkDragSource">
|
||||
<property name="actions">copy</property>
|
||||
<signal name="prepare" handler="on_picture_drag_prepare_cb" swapped="no"/>
|
||||
<object class="GtkViewport">
|
||||
<child>
|
||||
<object class="GtkPicture" id="picture">
|
||||
<property name="can-shrink">0</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="valign">center</property>
|
||||
<child>
|
||||
<object class="GtkDragSource">
|
||||
<property name="actions">copy</property>
|
||||
<signal name="prepare" handler="on_picture_drag_prepare_cb" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkDropTargetAsync">
|
||||
<property name="actions">copy</property>
|
||||
<property name="formats">application/x-gtk-render-node</property>
|
||||
<signal name="drop" handler="on_picture_drop_cb" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="overlay">
|
||||
<object class="GtkBox">
|
||||
<property name="margin-top">16</property>
|
||||
<property name="margin-start">16</property>
|
||||
<property name="margin-end">16</property>
|
||||
<property name="margin-bottom">16</property>
|
||||
<property name="halign">fill</property>
|
||||
<property name="valign">end</property>
|
||||
<property name="visible" bind-source="compare_button" bind-property="active">0</property>
|
||||
<style>
|
||||
<class name="osd" />
|
||||
</style>
|
||||
<child>
|
||||
<object class="GtkScale">
|
||||
<property name="width-request">200</property>
|
||||
<property name="draw-value">0</property>
|
||||
<property name="adjustment">
|
||||
<object class="GtkAdjustment" id="compare_progress">
|
||||
<property name="upper">1</property>
|
||||
<property name="value">0.5</property>
|
||||
<property name="step-increment">0.01</property>
|
||||
<property name="page-increment">0.1</property>
|
||||
<signal name="notify::value" handler="update_compare" swapped="yes"/>
|
||||
</object>
|
||||
</property>
|
||||
<property name="hexpand">1</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow">
|
||||
<property name="hscrollbar-policy">never</property>
|
||||
<child>
|
||||
<object class="GtkDropTargetAsync">
|
||||
<property name="actions">copy</property>
|
||||
<property name="formats">application/x-gtk-render-node</property>
|
||||
<signal name="drop" handler="on_picture_drop_cb" swapped="no"/>
|
||||
<object class="GtkListBox" id="renderer_listbox">
|
||||
<property name="selection-mode">none</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow">
|
||||
<property name="hscrollbar-policy">never</property>
|
||||
<child>
|
||||
<object class="GtkListBox" id="renderer_listbox">
|
||||
<property name="selection-mode">none</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</property>
|
||||
</object>
|
||||
</property>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</template>
|
||||
|
||||
435
demos/node-editor/rendernodeutils.c
Normal file
435
demos/node-editor/rendernodeutils.c
Normal file
@@ -0,0 +1,435 @@
|
||||
/*
|
||||
* Copyright © 2023 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 "rendernodeutilsprivate.h"
|
||||
|
||||
static inline double
|
||||
double_interpolate (double start,
|
||||
double end,
|
||||
double progress)
|
||||
{
|
||||
return start + (end - start) * progress;
|
||||
}
|
||||
|
||||
static inline float
|
||||
float_interpolate (float start,
|
||||
float end,
|
||||
double progress)
|
||||
{
|
||||
return start + (end - start) * progress;
|
||||
}
|
||||
|
||||
static void
|
||||
rgba_interpolate (GdkRGBA *result,
|
||||
const GdkRGBA *start,
|
||||
const GdkRGBA *end,
|
||||
double progress)
|
||||
{
|
||||
result->alpha = CLAMP (double_interpolate (start->alpha, end->alpha, progress), 0, 1);
|
||||
|
||||
if (result->alpha <= 0.0)
|
||||
{
|
||||
result->red = result->green = result->blue = 0.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
result->red = CLAMP (double_interpolate (start->red * start->alpha, end->red * end->alpha, progress), 0, 1) / result->alpha;
|
||||
result->green = CLAMP (double_interpolate (start->green * start->alpha, end->green * end->alpha, progress), 0, 1) / result->alpha;
|
||||
result->blue = CLAMP (double_interpolate (start->blue * start->alpha, end->blue * end->alpha, progress), 0, 1) / result->alpha;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
rounded_rect_interpolate (GskRoundedRect *dest,
|
||||
const GskRoundedRect *start,
|
||||
const GskRoundedRect *end,
|
||||
double progress)
|
||||
{
|
||||
guint i;
|
||||
|
||||
graphene_rect_interpolate (&start->bounds, &end->bounds, progress, &dest->bounds);
|
||||
for (i = 0; i < 4; i++)
|
||||
graphene_size_interpolate (&start->corner[i], &end->corner[i], progress, &dest->corner[i]);
|
||||
}
|
||||
|
||||
static GskTransform *
|
||||
transform_interpolate (GskTransform *start,
|
||||
GskTransform *end,
|
||||
double progress)
|
||||
{
|
||||
switch (MIN (gsk_transform_get_category (start), gsk_transform_get_category (end)))
|
||||
{
|
||||
case GSK_TRANSFORM_CATEGORY_IDENTITY:
|
||||
return NULL;
|
||||
|
||||
case GSK_TRANSFORM_CATEGORY_2D_TRANSLATE:
|
||||
{
|
||||
float startx, starty, endx, endy;
|
||||
|
||||
gsk_transform_to_translate (start, &startx, &starty);
|
||||
gsk_transform_to_translate (end, &endx, &endy);
|
||||
return gsk_transform_translate (NULL,
|
||||
&GRAPHENE_POINT_INIT (float_interpolate (startx, endx, progress),
|
||||
float_interpolate (starty, endy, progress)));
|
||||
}
|
||||
|
||||
case GSK_TRANSFORM_CATEGORY_2D_AFFINE:
|
||||
case GSK_TRANSFORM_CATEGORY_2D:
|
||||
{
|
||||
float start_skew_x, start_skew_y, start_scale_x, start_scale_y, start_angle, start_dx, start_dy;
|
||||
float end_skew_x, end_skew_y, end_scale_x, end_scale_y, end_angle, end_dx, end_dy;
|
||||
GskTransform *transform;
|
||||
|
||||
gsk_transform_to_2d_components (start,
|
||||
&start_skew_x, &start_skew_y,
|
||||
&start_scale_x, &start_scale_y,
|
||||
&start_angle,
|
||||
&start_dx, &start_dy);
|
||||
gsk_transform_to_2d_components (end,
|
||||
&end_skew_x, &end_skew_y,
|
||||
&end_scale_x, &end_scale_y,
|
||||
&end_angle,
|
||||
&end_dx, &end_dy);
|
||||
transform = gsk_transform_translate (NULL,
|
||||
&GRAPHENE_POINT_INIT (float_interpolate (start_dx, end_dx, progress),
|
||||
float_interpolate (start_dy, end_dy, progress)));
|
||||
transform = gsk_transform_rotate (transform,
|
||||
float_interpolate (start_angle, end_angle, progress));
|
||||
transform = gsk_transform_scale (transform,
|
||||
float_interpolate (start_scale_x, end_scale_x, progress),
|
||||
float_interpolate (start_scale_y, end_scale_y, progress));
|
||||
transform = gsk_transform_skew (transform,
|
||||
float_interpolate (start_skew_x, end_skew_x, progress),
|
||||
float_interpolate (start_skew_y, end_skew_y, progress));
|
||||
return transform;
|
||||
}
|
||||
|
||||
case GSK_TRANSFORM_CATEGORY_UNKNOWN:
|
||||
case GSK_TRANSFORM_CATEGORY_ANY:
|
||||
case GSK_TRANSFORM_CATEGORY_3D:
|
||||
{
|
||||
graphene_matrix_t start_matrix, end_matrix, matrix;
|
||||
|
||||
gsk_transform_to_matrix (start, &start_matrix);
|
||||
gsk_transform_to_matrix (end, &end_matrix);
|
||||
graphene_matrix_interpolate (&start_matrix, &end_matrix, progress, &matrix);
|
||||
return gsk_transform_matrix (NULL, &matrix);
|
||||
}
|
||||
|
||||
default:
|
||||
g_return_val_if_reached (NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
color_stops_interpolate (GskColorStop *stops,
|
||||
const GskColorStop *start_stops,
|
||||
const GskColorStop *end_stops,
|
||||
gsize n_stops,
|
||||
double progress)
|
||||
{
|
||||
guint i;
|
||||
|
||||
for (i = 0; i < n_stops; i++)
|
||||
{
|
||||
stops[i].offset = float_interpolate (start_stops[i].offset, end_stops[i].offset, progress);
|
||||
rgba_interpolate (&stops[i].color, &start_stops[i].color, &end_stops[i].color, progress);
|
||||
}
|
||||
}
|
||||
|
||||
GskRenderNode *
|
||||
render_node_interpolate (GskRenderNode *start,
|
||||
GskRenderNode *end,
|
||||
double progress)
|
||||
{
|
||||
GskRenderNodeType start_type, end_type;
|
||||
|
||||
if (progress <= 0)
|
||||
return gsk_render_node_ref (start);
|
||||
else if (progress >= 1)
|
||||
return gsk_render_node_ref (end);
|
||||
|
||||
start_type = gsk_render_node_get_node_type (start);
|
||||
end_type = gsk_render_node_get_node_type (end);
|
||||
|
||||
if (start_type == end_type)
|
||||
{
|
||||
switch (start_type)
|
||||
{
|
||||
case GSK_COLOR_NODE:
|
||||
{
|
||||
GdkRGBA rgba;
|
||||
graphene_rect_t start_bounds, end_bounds, bounds;
|
||||
|
||||
rgba_interpolate (&rgba, gsk_color_node_get_color (start), gsk_color_node_get_color (end), progress);
|
||||
gsk_render_node_get_bounds (start, &start_bounds);
|
||||
gsk_render_node_get_bounds (end, &end_bounds);
|
||||
graphene_rect_interpolate (&start_bounds, &end_bounds, progress, &bounds);
|
||||
return gsk_color_node_new (&rgba, &bounds);
|
||||
}
|
||||
|
||||
case GSK_DEBUG_NODE:
|
||||
{
|
||||
GskRenderNode *result, *child;
|
||||
|
||||
/* xxx: do we need to interpolate the message somehow? */
|
||||
child = render_node_interpolate (gsk_debug_node_get_child (start),
|
||||
gsk_debug_node_get_child (end),
|
||||
progress);
|
||||
result = gsk_debug_node_new (child, g_strdup_printf ("progress %g", progress));
|
||||
gsk_render_node_unref (child);
|
||||
return result;
|
||||
}
|
||||
|
||||
case GSK_CONTAINER_NODE:
|
||||
{
|
||||
GskRenderNode **nodes;
|
||||
GskRenderNode *result;
|
||||
gsize i, n_nodes;
|
||||
|
||||
if (gsk_container_node_get_n_children (start) != gsk_container_node_get_n_children (end))
|
||||
break;
|
||||
|
||||
n_nodes = gsk_container_node_get_n_children (start);
|
||||
nodes = g_new (GskRenderNode *, n_nodes);
|
||||
|
||||
for (i = 0; i < n_nodes; i++)
|
||||
{
|
||||
nodes[i] = render_node_interpolate (gsk_container_node_get_child (start, i),
|
||||
gsk_container_node_get_child (end, i),
|
||||
progress);
|
||||
}
|
||||
|
||||
result = gsk_container_node_new (nodes, n_nodes);
|
||||
for (i = 0; i < n_nodes; i++)
|
||||
gsk_render_node_unref (nodes[i]);
|
||||
g_free (nodes);
|
||||
return result;
|
||||
}
|
||||
|
||||
case GSK_TEXTURE_NODE:
|
||||
{
|
||||
graphene_rect_t start_bounds, end_bounds, bounds;
|
||||
|
||||
gsk_render_node_get_bounds (start, &start_bounds);
|
||||
gsk_render_node_get_bounds (end, &end_bounds);
|
||||
graphene_rect_interpolate (&start_bounds, &end_bounds, progress, &bounds);
|
||||
return gsk_texture_node_new (gsk_texture_node_get_texture (progress > 0.5 ? end : start),
|
||||
&bounds);
|
||||
}
|
||||
|
||||
case GSK_TEXTURE_SCALE_NODE:
|
||||
{
|
||||
graphene_rect_t start_bounds, end_bounds, bounds;
|
||||
|
||||
gsk_render_node_get_bounds (start, &start_bounds);
|
||||
gsk_render_node_get_bounds (end, &end_bounds);
|
||||
graphene_rect_interpolate (&start_bounds, &end_bounds, progress, &bounds);
|
||||
return gsk_texture_scale_node_new (gsk_texture_scale_node_get_texture (progress > 0.5 ? end : start),
|
||||
&bounds,
|
||||
gsk_texture_scale_node_get_filter (progress > 0.5 ? end : start));
|
||||
}
|
||||
|
||||
case GSK_TRANSFORM_NODE:
|
||||
{
|
||||
GskRenderNode *result, *child;
|
||||
GskTransform *transform;
|
||||
|
||||
child = render_node_interpolate (gsk_transform_node_get_child (start),
|
||||
gsk_transform_node_get_child (end),
|
||||
progress);
|
||||
transform = transform_interpolate (gsk_transform_node_get_transform (start),
|
||||
gsk_transform_node_get_transform (end),
|
||||
progress);
|
||||
result = gsk_transform_node_new (child, transform);
|
||||
gsk_transform_unref (transform);
|
||||
gsk_render_node_unref (child);
|
||||
return result;
|
||||
}
|
||||
|
||||
case GSK_CLIP_NODE:
|
||||
{
|
||||
GskRenderNode *result, *child;
|
||||
graphene_rect_t clip;
|
||||
|
||||
graphene_rect_interpolate (gsk_clip_node_get_clip (start),
|
||||
gsk_clip_node_get_clip (end),
|
||||
progress,
|
||||
&clip);
|
||||
child = render_node_interpolate (gsk_clip_node_get_child (start),
|
||||
gsk_clip_node_get_child (end),
|
||||
progress);
|
||||
result = gsk_clip_node_new (child, &clip);
|
||||
gsk_render_node_unref (child);
|
||||
return result;
|
||||
}
|
||||
|
||||
case GSK_ROUNDED_CLIP_NODE:
|
||||
{
|
||||
GskRenderNode *result, *child;
|
||||
GskRoundedRect clip;
|
||||
|
||||
rounded_rect_interpolate (&clip,
|
||||
gsk_rounded_clip_node_get_clip (start),
|
||||
gsk_rounded_clip_node_get_clip (end),
|
||||
progress);
|
||||
child = render_node_interpolate (gsk_rounded_clip_node_get_child (start),
|
||||
gsk_rounded_clip_node_get_child (end),
|
||||
progress);
|
||||
result = gsk_rounded_clip_node_new (child, &clip);
|
||||
gsk_render_node_unref (child);
|
||||
return result;
|
||||
}
|
||||
|
||||
case GSK_BORDER_NODE:
|
||||
{
|
||||
GdkRGBA color[4];
|
||||
const GdkRGBA *start_color, *end_color;
|
||||
float width[4];
|
||||
const float *start_width, *end_width;
|
||||
GskRoundedRect outline;
|
||||
guint i;
|
||||
|
||||
rounded_rect_interpolate (&outline,
|
||||
gsk_border_node_get_outline (start),
|
||||
gsk_border_node_get_outline (end),
|
||||
progress);
|
||||
start_color = gsk_border_node_get_colors (start);
|
||||
end_color = gsk_border_node_get_colors (end);
|
||||
start_width = gsk_border_node_get_widths (start);
|
||||
end_width = gsk_border_node_get_widths (end);
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
rgba_interpolate (&color[i], &start_color[i], &end_color[i], progress);
|
||||
width[i] = float_interpolate (start_width[i], end_width[i], progress);
|
||||
}
|
||||
return gsk_border_node_new (&outline, width, color);
|
||||
}
|
||||
|
||||
case GSK_MASK_NODE:
|
||||
{
|
||||
GskRenderNode *source, *mask, *result;
|
||||
|
||||
source = render_node_interpolate (gsk_mask_node_get_source (start),
|
||||
gsk_mask_node_get_source (end),
|
||||
progress);
|
||||
mask = render_node_interpolate (gsk_mask_node_get_mask (start),
|
||||
gsk_mask_node_get_mask (end),
|
||||
progress);
|
||||
result = gsk_mask_node_new (source,
|
||||
mask,
|
||||
gsk_mask_node_get_mask_mode (progress > 0.5 ? end : start));
|
||||
gsk_render_node_unref (source);
|
||||
gsk_render_node_unref (mask);
|
||||
return result;
|
||||
}
|
||||
|
||||
case GSK_CONIC_GRADIENT_NODE:
|
||||
{
|
||||
graphene_rect_t start_bounds, end_bounds, bounds;
|
||||
graphene_point_t center;
|
||||
float rotation;
|
||||
GskColorStop *color_stops;
|
||||
gsize n_color_stops;
|
||||
GskRenderNode *result;
|
||||
|
||||
if (gsk_conic_gradient_node_get_n_color_stops (start) != gsk_conic_gradient_node_get_n_color_stops (end))
|
||||
break;
|
||||
|
||||
n_color_stops = gsk_conic_gradient_node_get_n_color_stops (start);
|
||||
color_stops = g_new (GskColorStop, n_color_stops);
|
||||
|
||||
gsk_render_node_get_bounds (start, &start_bounds);
|
||||
gsk_render_node_get_bounds (end, &end_bounds);
|
||||
graphene_rect_interpolate (&start_bounds, &end_bounds, progress, &bounds);
|
||||
graphene_point_interpolate (gsk_conic_gradient_node_get_center (start),
|
||||
gsk_conic_gradient_node_get_center (end),
|
||||
progress,
|
||||
¢er);
|
||||
rotation = float_interpolate (gsk_conic_gradient_node_get_rotation (start),
|
||||
gsk_conic_gradient_node_get_rotation (end),
|
||||
progress);
|
||||
color_stops_interpolate (color_stops,
|
||||
gsk_conic_gradient_node_get_color_stops (start, NULL),
|
||||
gsk_conic_gradient_node_get_color_stops (end, NULL),
|
||||
n_color_stops,
|
||||
progress);
|
||||
result = gsk_conic_gradient_node_new (&bounds,
|
||||
¢er,
|
||||
rotation,
|
||||
color_stops,
|
||||
n_color_stops);
|
||||
g_free (color_stops);
|
||||
return result;
|
||||
}
|
||||
|
||||
case GSK_REPEAT_NODE:
|
||||
{
|
||||
graphene_rect_t start_bounds, end_bounds, bounds, child_bounds;
|
||||
GskRenderNode *child, *result;
|
||||
|
||||
gsk_render_node_get_bounds (start, &start_bounds);
|
||||
gsk_render_node_get_bounds (end, &end_bounds);
|
||||
graphene_rect_interpolate (&start_bounds, &end_bounds, progress, &bounds);
|
||||
graphene_rect_interpolate (gsk_repeat_node_get_child_bounds (start),
|
||||
gsk_repeat_node_get_child_bounds (end),
|
||||
progress,
|
||||
&child_bounds);
|
||||
child = render_node_interpolate (gsk_repeat_node_get_child (start),
|
||||
gsk_repeat_node_get_child (end),
|
||||
progress);
|
||||
result = gsk_repeat_node_new (&bounds, child, &child_bounds);
|
||||
gsk_render_node_unref (child);
|
||||
return result;
|
||||
}
|
||||
|
||||
case GSK_LINEAR_GRADIENT_NODE:
|
||||
case GSK_REPEATING_LINEAR_GRADIENT_NODE:
|
||||
case GSK_RADIAL_GRADIENT_NODE:
|
||||
case GSK_REPEATING_RADIAL_GRADIENT_NODE:
|
||||
case GSK_INSET_SHADOW_NODE:
|
||||
case GSK_OUTSET_SHADOW_NODE:
|
||||
case GSK_OPACITY_NODE:
|
||||
case GSK_COLOR_MATRIX_NODE:
|
||||
case GSK_BLUR_NODE:
|
||||
case GSK_SHADOW_NODE:
|
||||
case GSK_BLEND_NODE:
|
||||
case GSK_CROSS_FADE_NODE:
|
||||
case GSK_FILL_NODE:
|
||||
case GSK_STROKE_NODE:
|
||||
g_warning ("FIXME: not implemented");
|
||||
break;
|
||||
|
||||
case GSK_GL_SHADER_NODE:
|
||||
case GSK_CAIRO_NODE:
|
||||
case GSK_TEXT_NODE:
|
||||
break;
|
||||
|
||||
case GSK_NOT_A_RENDER_NODE:
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return gsk_cross_fade_node_new (start, end, progress);
|
||||
}
|
||||
|
||||
30
demos/node-editor/rendernodeutilsprivate.h
Normal file
30
demos/node-editor/rendernodeutilsprivate.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <gsk/gsk.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
GskRenderNode * render_node_interpolate (GskRenderNode *start,
|
||||
GskRenderNode *end,
|
||||
double progress);
|
||||
|
||||
G_END_DECLS
|
||||
@@ -1390,6 +1390,7 @@ gdk_display_create_vulkan_device (GdkDisplay *display,
|
||||
|
||||
device_extensions = g_ptr_array_new ();
|
||||
g_ptr_array_add (device_extensions, (gpointer) VK_KHR_SWAPCHAIN_EXTENSION_NAME);
|
||||
g_ptr_array_add (device_extensions, (gpointer) VK_KHR_SHADER_NON_SEMANTIC_INFO_EXTENSION_NAME);
|
||||
g_ptr_array_add (device_extensions, (gpointer) VK_KHR_MAINTENANCE_3_EXTENSION_NAME);
|
||||
g_ptr_array_add (device_extensions, (gpointer) VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME);
|
||||
if (has_incremental_present)
|
||||
|
||||
@@ -271,6 +271,8 @@ collect_reused_child_nodes (GskRenderer *renderer,
|
||||
case GSK_CROSS_FADE_NODE:
|
||||
case GSK_BLUR_NODE:
|
||||
case GSK_MASK_NODE:
|
||||
case GSK_FILL_NODE:
|
||||
case GSK_STROKE_NODE:
|
||||
|
||||
default:
|
||||
|
||||
@@ -859,6 +861,8 @@ gsk_broadway_renderer_add_node (GskRenderer *renderer,
|
||||
case GSK_CROSS_FADE_NODE:
|
||||
case GSK_BLUR_NODE:
|
||||
case GSK_GL_SHADER_NODE:
|
||||
case GSK_FILL_NODE:
|
||||
case GSK_STROKE_NODE:
|
||||
default:
|
||||
break; /* Fallback */
|
||||
}
|
||||
|
||||
@@ -255,6 +255,8 @@ node_supports_2d_transform (const GskRenderNode *node)
|
||||
case GSK_BLEND_NODE:
|
||||
case GSK_BLUR_NODE:
|
||||
case GSK_MASK_NODE:
|
||||
case GSK_FILL_NODE:
|
||||
case GSK_STROKE_NODE:
|
||||
return TRUE;
|
||||
|
||||
case GSK_SHADOW_NODE:
|
||||
@@ -309,6 +311,8 @@ node_supports_transform (const GskRenderNode *node)
|
||||
case GSK_BLEND_NODE:
|
||||
case GSK_BLUR_NODE:
|
||||
case GSK_MASK_NODE:
|
||||
case GSK_FILL_NODE:
|
||||
case GSK_STROKE_NODE:
|
||||
return TRUE;
|
||||
|
||||
case GSK_SHADOW_NODE:
|
||||
@@ -4089,6 +4093,14 @@ gsk_gl_render_job_visit_node (GskGLRenderJob *job,
|
||||
gsk_gl_render_job_visit_as_fallback (job, node);
|
||||
break;
|
||||
|
||||
case GSK_FILL_NODE:
|
||||
gsk_gl_render_job_visit_as_fallback (job, node);
|
||||
break;
|
||||
|
||||
case GSK_STROKE_NODE:
|
||||
gsk_gl_render_job_visit_as_fallback (job, node);
|
||||
break;
|
||||
|
||||
case GSK_NOT_A_RENDER_NODE:
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
|
||||
@@ -20,11 +20,15 @@
|
||||
#define __GSK_H_INSIDE__
|
||||
|
||||
#include <gsk/gskenums.h>
|
||||
#include <gsk/gskglshader.h>
|
||||
#include <gsk/gskpath.h>
|
||||
#include <gsk/gskpathbuilder.h>
|
||||
#include <gsk/gskpathmeasure.h>
|
||||
#include <gsk/gskrenderer.h>
|
||||
#include <gsk/gskrendernode.h>
|
||||
#include <gsk/gskroundedrect.h>
|
||||
#include <gsk/gskstroke.h>
|
||||
#include <gsk/gsktransform.h>
|
||||
#include <gsk/gskglshader.h>
|
||||
|
||||
#include <gsk/gskcairorenderer.h>
|
||||
|
||||
|
||||
1895
gsk/gskcontour.c
Normal file
1895
gsk/gskcontour.c
Normal file
File diff suppressed because it is too large
Load Diff
107
gsk/gskcontourprivate.h
Normal file
107
gsk/gskcontourprivate.h
Normal file
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright © 2020 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __GSK_CONTOUR_PRIVATE_H__
|
||||
#define __GSK_CONTOUR_PRIVATE_H__
|
||||
|
||||
#include <gskpath.h>
|
||||
|
||||
#include "gskpathopprivate.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef enum
|
||||
{
|
||||
GSK_PATH_FLAT,
|
||||
GSK_PATH_CLOSED
|
||||
} GskPathFlags;
|
||||
|
||||
typedef struct _GskContour GskContour;
|
||||
|
||||
GskContour * gsk_rect_contour_new (const graphene_rect_t *rect);
|
||||
GskContour * gsk_circle_contour_new (const graphene_point_t *center,
|
||||
float radius,
|
||||
float start_angle,
|
||||
float end_angle);
|
||||
GskContour * gsk_standard_contour_new (GskPathFlags flags,
|
||||
const graphene_point_t *points,
|
||||
gsize n_points,
|
||||
const gskpathop *ops,
|
||||
gsize n_ops,
|
||||
gssize offset);
|
||||
|
||||
void gsk_contour_copy (GskContour * dest,
|
||||
const GskContour *src);
|
||||
GskContour * gsk_contour_dup (const GskContour *src);
|
||||
|
||||
gsize gsk_contour_get_size (const GskContour *self);
|
||||
GskPathFlags gsk_contour_get_flags (const GskContour *self);
|
||||
void gsk_contour_print (const GskContour *self,
|
||||
GString *string);
|
||||
gboolean gsk_contour_get_bounds (const GskContour *self,
|
||||
graphene_rect_t *bounds);
|
||||
gpointer gsk_contour_init_measure (const GskContour *self,
|
||||
float tolerance,
|
||||
float *out_length);
|
||||
void gsk_contour_free_measure (const GskContour *self,
|
||||
gpointer data);
|
||||
gboolean gsk_contour_foreach (const GskContour *self,
|
||||
float tolerance,
|
||||
GskPathForeachFunc func,
|
||||
gpointer user_data);
|
||||
void gsk_contour_get_start_end (const GskContour *self,
|
||||
graphene_point_t *start,
|
||||
graphene_point_t *end);
|
||||
void gsk_contour_get_point (const GskContour *self,
|
||||
gpointer measure_data,
|
||||
float distance,
|
||||
graphene_point_t *pos,
|
||||
graphene_vec2_t *tangent);
|
||||
gboolean gsk_contour_get_closest_point (const GskContour *self,
|
||||
gpointer measure_data,
|
||||
float tolerance,
|
||||
const graphene_point_t *point,
|
||||
float threshold,
|
||||
float *out_distance,
|
||||
graphene_point_t *out_pos,
|
||||
float *out_offset,
|
||||
graphene_vec2_t *out_tangent);
|
||||
int gsk_contour_get_winding (const GskContour *self,
|
||||
gpointer measure_data,
|
||||
const graphene_point_t *point,
|
||||
gboolean *on_edge);
|
||||
void gsk_contour_add_segment (const GskContour *self,
|
||||
GskPathBuilder *builder,
|
||||
gpointer measure_data,
|
||||
gboolean emit_move_to,
|
||||
float start,
|
||||
float end);
|
||||
gboolean gsk_contour_get_stroke_bounds (const GskContour *self,
|
||||
const GskStroke *stroke,
|
||||
graphene_rect_t *bounds);
|
||||
|
||||
gsize gsk_contour_get_shader_size (const GskContour *self);
|
||||
void gsk_contour_to_shader (const GskContour *self,
|
||||
guchar *data);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GSK_CONTOUR_PRIVATE_H__ */
|
||||
|
||||
979
gsk/gskcurve.c
Normal file
979
gsk/gskcurve.c
Normal file
@@ -0,0 +1,979 @@
|
||||
/*
|
||||
* 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 "gskcurveprivate.h"
|
||||
|
||||
#define MIN_PROGRESS (1/1024.f)
|
||||
|
||||
typedef struct _GskCurveClass GskCurveClass;
|
||||
|
||||
struct _GskCurveClass
|
||||
{
|
||||
void (* init) (GskCurve *curve,
|
||||
gskpathop op);
|
||||
void (* init_foreach) (GskCurve *curve,
|
||||
GskPathOperation op,
|
||||
const graphene_point_t *pts,
|
||||
gsize n_pts,
|
||||
float weight);
|
||||
void (* get_point) (const GskCurve *curve,
|
||||
float t,
|
||||
graphene_point_t *pos);
|
||||
void (* get_tangent) (const GskCurve *curve,
|
||||
float t,
|
||||
graphene_vec2_t *tangent);
|
||||
void (* split) (const GskCurve *curve,
|
||||
float progress,
|
||||
GskCurve *result1,
|
||||
GskCurve *result2);
|
||||
void (* segment) (const GskCurve *curve,
|
||||
float start,
|
||||
float end,
|
||||
GskCurve *segment);
|
||||
gboolean (* decompose) (const GskCurve *curve,
|
||||
float tolerance,
|
||||
GskCurveAddLineFunc add_line_func,
|
||||
gpointer user_data);
|
||||
gskpathop (* pathop) (const GskCurve *curve);
|
||||
const graphene_point_t * (* get_start_point) (const GskCurve *curve);
|
||||
const graphene_point_t * (* get_end_point) (const GskCurve *curve);
|
||||
void (* get_start_tangent) (const GskCurve *curve,
|
||||
graphene_vec2_t *tangent);
|
||||
void (* get_end_tangent) (const GskCurve *curve,
|
||||
graphene_vec2_t *tangent);
|
||||
};
|
||||
|
||||
static void
|
||||
get_tangent (const graphene_point_t *p0,
|
||||
const graphene_point_t *p1,
|
||||
graphene_vec2_t *t)
|
||||
{
|
||||
graphene_vec2_init (t, p1->x - p0->x, p1->y - p0->y);
|
||||
graphene_vec2_normalize (t, t);
|
||||
}
|
||||
|
||||
/** LINE **/
|
||||
|
||||
static void
|
||||
gsk_line_curve_init_from_points (GskLineCurve *self,
|
||||
GskPathOperation op,
|
||||
const graphene_point_t *start,
|
||||
const graphene_point_t *end)
|
||||
{
|
||||
self->op = op;
|
||||
self->points[0] = *start;
|
||||
self->points[1] = *end;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_line_curve_init (GskCurve *curve,
|
||||
gskpathop op)
|
||||
{
|
||||
GskLineCurve *self = &curve->line;
|
||||
const graphene_point_t *pts = gsk_pathop_points (op);
|
||||
|
||||
gsk_line_curve_init_from_points (self, gsk_pathop_op (op), &pts[0], &pts[1]);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_line_curve_init_foreach (GskCurve *curve,
|
||||
GskPathOperation op,
|
||||
const graphene_point_t *pts,
|
||||
gsize n_pts,
|
||||
float weight)
|
||||
{
|
||||
GskLineCurve *self = &curve->line;
|
||||
|
||||
g_assert (n_pts == 2);
|
||||
|
||||
gsk_line_curve_init_from_points (self, op, &pts[0], &pts[1]);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_line_curve_get_point (const GskCurve *curve,
|
||||
float t,
|
||||
graphene_point_t *pos)
|
||||
{
|
||||
const GskLineCurve *self = &curve->line;
|
||||
|
||||
graphene_point_interpolate (&self->points[0], &self->points[1], t, pos);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_line_curve_get_tangent (const GskCurve *curve,
|
||||
float t,
|
||||
graphene_vec2_t *tangent)
|
||||
{
|
||||
const GskLineCurve *self = &curve->line;
|
||||
|
||||
get_tangent (&self->points[0], &self->points[1], tangent);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_line_curve_split (const GskCurve *curve,
|
||||
float progress,
|
||||
GskCurve *start,
|
||||
GskCurve *end)
|
||||
{
|
||||
const GskLineCurve *self = &curve->line;
|
||||
graphene_point_t point;
|
||||
|
||||
graphene_point_interpolate (&self->points[0], &self->points[1], progress, &point);
|
||||
|
||||
if (start)
|
||||
gsk_line_curve_init_from_points (&start->line, GSK_PATH_LINE, &self->points[0], &point);
|
||||
if (end)
|
||||
gsk_line_curve_init_from_points (&end->line, GSK_PATH_LINE, &point, &self->points[1]);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_line_curve_segment (const GskCurve *curve,
|
||||
float start,
|
||||
float end,
|
||||
GskCurve *segment)
|
||||
{
|
||||
const GskLineCurve *self = &curve->line;
|
||||
graphene_point_t start_point, end_point;
|
||||
|
||||
graphene_point_interpolate (&self->points[0], &self->points[1], start, &start_point);
|
||||
graphene_point_interpolate (&self->points[0], &self->points[1], end, &end_point);
|
||||
|
||||
gsk_line_curve_init_from_points (&segment->line, GSK_PATH_LINE, &start_point, &end_point);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gsk_line_curve_decompose (const GskCurve *curve,
|
||||
float tolerance,
|
||||
GskCurveAddLineFunc add_line_func,
|
||||
gpointer user_data)
|
||||
{
|
||||
const GskLineCurve *self = &curve->line;
|
||||
|
||||
return add_line_func (&self->points[0], &self->points[1], 0.0f, 1.0f, GSK_CURVE_LINE_REASON_STRAIGHT, user_data);
|
||||
}
|
||||
|
||||
static gskpathop
|
||||
gsk_line_curve_pathop (const GskCurve *curve)
|
||||
{
|
||||
const GskLineCurve *self = &curve->line;
|
||||
|
||||
return gsk_pathop_encode (self->op, self->points);
|
||||
}
|
||||
|
||||
static const graphene_point_t *
|
||||
gsk_line_curve_get_start_point (const GskCurve *curve)
|
||||
{
|
||||
const GskLineCurve *self = &curve->line;
|
||||
|
||||
return &self->points[0];
|
||||
}
|
||||
|
||||
static const graphene_point_t *
|
||||
gsk_line_curve_get_end_point (const GskCurve *curve)
|
||||
{
|
||||
const GskLineCurve *self = &curve->line;
|
||||
|
||||
return &self->points[1];
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_line_curve_get_start_end_tangent (const GskCurve *curve,
|
||||
graphene_vec2_t *tangent)
|
||||
{
|
||||
const GskLineCurve *self = &curve->line;
|
||||
|
||||
get_tangent (&self->points[0], &self->points[1], tangent);
|
||||
}
|
||||
|
||||
static const GskCurveClass GSK_LINE_CURVE_CLASS = {
|
||||
gsk_line_curve_init,
|
||||
gsk_line_curve_init_foreach,
|
||||
gsk_line_curve_get_point,
|
||||
gsk_line_curve_get_tangent,
|
||||
gsk_line_curve_split,
|
||||
gsk_line_curve_segment,
|
||||
gsk_line_curve_decompose,
|
||||
gsk_line_curve_pathop,
|
||||
gsk_line_curve_get_start_point,
|
||||
gsk_line_curve_get_end_point,
|
||||
gsk_line_curve_get_start_end_tangent,
|
||||
gsk_line_curve_get_start_end_tangent
|
||||
};
|
||||
|
||||
/** CURVE **/
|
||||
|
||||
static void
|
||||
gsk_curve_curve_init_from_points (GskCurveCurve *self,
|
||||
const graphene_point_t pts[4])
|
||||
{
|
||||
self->op = GSK_PATH_CURVE;
|
||||
self->has_coefficients = FALSE;
|
||||
memcpy (self->points, pts, sizeof (graphene_point_t) * 4);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_curve_curve_init (GskCurve *curve,
|
||||
gskpathop op)
|
||||
{
|
||||
GskCurveCurve *self = &curve->curve;
|
||||
|
||||
gsk_curve_curve_init_from_points (self, gsk_pathop_points (op));
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_curve_curve_init_foreach (GskCurve *curve,
|
||||
GskPathOperation op,
|
||||
const graphene_point_t *pts,
|
||||
gsize n_pts,
|
||||
float weight)
|
||||
{
|
||||
GskCurveCurve *self = &curve->curve;
|
||||
|
||||
g_assert (n_pts == 4);
|
||||
|
||||
gsk_curve_curve_init_from_points (self, pts);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_curve_curve_ensure_coefficients (const GskCurveCurve *curve)
|
||||
{
|
||||
GskCurveCurve *self = (GskCurveCurve *) curve;
|
||||
const graphene_point_t *pts = &self->points[0];
|
||||
|
||||
if (self->has_coefficients)
|
||||
return;
|
||||
|
||||
self->coeffs[0] = GRAPHENE_POINT_INIT (pts[3].x - 3.0f * pts[2].x + 3.0f * pts[1].x - pts[0].x,
|
||||
pts[3].y - 3.0f * pts[2].y + 3.0f * pts[1].y - pts[0].y);
|
||||
self->coeffs[1] = GRAPHENE_POINT_INIT (3.0f * pts[2].x - 6.0f * pts[1].x + 3.0f * pts[0].x,
|
||||
3.0f * pts[2].y - 6.0f * pts[1].y + 3.0f * pts[0].y);
|
||||
self->coeffs[2] = GRAPHENE_POINT_INIT (3.0f * pts[1].x - 3.0f * pts[0].x,
|
||||
3.0f * pts[1].y - 3.0f * pts[0].y);
|
||||
self->coeffs[3] = pts[0];
|
||||
|
||||
self->has_coefficients = TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_curve_curve_get_point (const GskCurve *curve,
|
||||
float t,
|
||||
graphene_point_t *pos)
|
||||
{
|
||||
const GskCurveCurve *self = &curve->curve;
|
||||
const graphene_point_t *c = self->coeffs;
|
||||
|
||||
gsk_curve_curve_ensure_coefficients (self);
|
||||
|
||||
*pos = GRAPHENE_POINT_INIT (((c[0].x * t + c[1].x) * t +c[2].x) * t + c[3].x,
|
||||
((c[0].y * t + c[1].y) * t +c[2].y) * t + c[3].y);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_curve_curve_get_tangent (const GskCurve *curve,
|
||||
float t,
|
||||
graphene_vec2_t *tangent)
|
||||
{
|
||||
const GskCurveCurve *self = &curve->curve;
|
||||
const graphene_point_t *c = self->coeffs;
|
||||
|
||||
gsk_curve_curve_ensure_coefficients (self);
|
||||
|
||||
graphene_vec2_init (tangent,
|
||||
(3.0f * c[0].x * t + 2.0f * c[1].x) * t + c[2].x,
|
||||
(3.0f * c[0].y * t + 2.0f * c[1].y) * t + c[2].y);
|
||||
graphene_vec2_normalize (tangent, tangent);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_curve_curve_split (const GskCurve *curve,
|
||||
float progress,
|
||||
GskCurve *start,
|
||||
GskCurve *end)
|
||||
{
|
||||
const GskCurveCurve *self = &curve->curve;
|
||||
const graphene_point_t *pts = self->points;
|
||||
graphene_point_t ab, bc, cd;
|
||||
graphene_point_t abbc, bccd;
|
||||
graphene_point_t final;
|
||||
|
||||
graphene_point_interpolate (&pts[0], &pts[1], progress, &ab);
|
||||
graphene_point_interpolate (&pts[1], &pts[2], progress, &bc);
|
||||
graphene_point_interpolate (&pts[2], &pts[3], progress, &cd);
|
||||
graphene_point_interpolate (&ab, &bc, progress, &abbc);
|
||||
graphene_point_interpolate (&bc, &cd, progress, &bccd);
|
||||
graphene_point_interpolate (&abbc, &bccd, progress, &final);
|
||||
|
||||
if (start)
|
||||
gsk_curve_curve_init_from_points (&start->curve, (graphene_point_t[4]) { pts[0], ab, abbc, final });
|
||||
if (end)
|
||||
gsk_curve_curve_init_from_points (&end->curve, (graphene_point_t[4]) { final, bccd, cd, pts[3] });
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_curve_curve_segment (const GskCurve *curve,
|
||||
float start,
|
||||
float end,
|
||||
GskCurve *segment)
|
||||
{
|
||||
GskCurve tmp;
|
||||
|
||||
gsk_curve_curve_split (curve, start, NULL, &tmp);
|
||||
gsk_curve_curve_split (&tmp, (end - start) / (1.0f - start), segment, NULL);
|
||||
}
|
||||
|
||||
/* taken from Skia, including the very descriptive name */
|
||||
static gboolean
|
||||
gsk_curve_curve_too_curvy (const GskCurveCurve *self,
|
||||
float tolerance)
|
||||
{
|
||||
const graphene_point_t *pts = self->points;
|
||||
graphene_point_t p;
|
||||
|
||||
graphene_point_interpolate (&pts[0], &pts[3], 1.0f / 3, &p);
|
||||
if (ABS (p.x - pts[1].x) + ABS (p.y - pts[1].y) > tolerance)
|
||||
return TRUE;
|
||||
|
||||
graphene_point_interpolate (&pts[0], &pts[3], 2.0f / 3, &p);
|
||||
if (ABS (p.x - pts[2].x) + ABS (p.y - pts[2].y) > tolerance)
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gsk_curce_curve_decompose_step (const GskCurve *curve,
|
||||
float start_progress,
|
||||
float end_progress,
|
||||
float tolerance,
|
||||
GskCurveAddLineFunc add_line_func,
|
||||
gpointer user_data)
|
||||
{
|
||||
const GskCurveCurve *self = &curve->curve;
|
||||
GskCurve left, right;
|
||||
float mid_progress;
|
||||
|
||||
if (!gsk_curve_curve_too_curvy (self, tolerance))
|
||||
return add_line_func (&self->points[0], &self->points[3], start_progress, end_progress, GSK_CURVE_LINE_REASON_STRAIGHT, user_data);
|
||||
if (end_progress - start_progress <= MIN_PROGRESS)
|
||||
return add_line_func (&self->points[0], &self->points[3], start_progress, end_progress, GSK_CURVE_LINE_REASON_SHORT, user_data);
|
||||
|
||||
gsk_curve_curve_split ((const GskCurve *) self, 0.5, &left, &right);
|
||||
mid_progress = (start_progress + end_progress) / 2;
|
||||
|
||||
return gsk_curce_curve_decompose_step (&left, start_progress, mid_progress, tolerance, add_line_func, user_data)
|
||||
&& gsk_curce_curve_decompose_step (&right, mid_progress, end_progress, tolerance, add_line_func, user_data);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gsk_curve_curve_decompose (const GskCurve *curve,
|
||||
float tolerance,
|
||||
GskCurveAddLineFunc add_line_func,
|
||||
gpointer user_data)
|
||||
{
|
||||
return gsk_curce_curve_decompose_step (curve, 0.0, 1.0, tolerance, add_line_func, user_data);
|
||||
}
|
||||
|
||||
static gskpathop
|
||||
gsk_curve_curve_pathop (const GskCurve *curve)
|
||||
{
|
||||
const GskCurveCurve *self = &curve->curve;
|
||||
|
||||
return gsk_pathop_encode (self->op, self->points);
|
||||
}
|
||||
|
||||
static const graphene_point_t *
|
||||
gsk_curve_curve_get_start_point (const GskCurve *curve)
|
||||
{
|
||||
const GskCurveCurve *self = &curve->curve;
|
||||
|
||||
return &self->points[0];
|
||||
}
|
||||
|
||||
static const graphene_point_t *
|
||||
gsk_curve_curve_get_end_point (const GskCurve *curve)
|
||||
{
|
||||
const GskCurveCurve *self = &curve->curve;
|
||||
|
||||
return &self->points[3];
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_curve_curve_get_start_tangent (const GskCurve *curve,
|
||||
graphene_vec2_t *tangent)
|
||||
{
|
||||
const GskCurveCurve *self = &curve->curve;
|
||||
|
||||
get_tangent (&self->points[0], &self->points[1], tangent);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_curve_curve_get_end_tangent (const GskCurve *curve,
|
||||
graphene_vec2_t *tangent)
|
||||
{
|
||||
const GskCurveCurve *self = &curve->curve;
|
||||
|
||||
get_tangent (&self->points[2], &self->points[3], tangent);
|
||||
}
|
||||
|
||||
static const GskCurveClass GSK_CURVE_CURVE_CLASS = {
|
||||
gsk_curve_curve_init,
|
||||
gsk_curve_curve_init_foreach,
|
||||
gsk_curve_curve_get_point,
|
||||
gsk_curve_curve_get_tangent,
|
||||
gsk_curve_curve_split,
|
||||
gsk_curve_curve_segment,
|
||||
gsk_curve_curve_decompose,
|
||||
gsk_curve_curve_pathop,
|
||||
gsk_curve_curve_get_start_point,
|
||||
gsk_curve_curve_get_end_point,
|
||||
gsk_curve_curve_get_start_tangent,
|
||||
gsk_curve_curve_get_end_tangent
|
||||
};
|
||||
|
||||
/** CONIC **/
|
||||
|
||||
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 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 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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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 const GskCurveClass GSK_CONIC_CURVE_CLASS = {
|
||||
gsk_conic_curve_init,
|
||||
gsk_conic_curve_init_foreach,
|
||||
gsk_conic_curve_get_point,
|
||||
gsk_conic_curve_get_tangent,
|
||||
gsk_conic_curve_split,
|
||||
gsk_conic_curve_segment,
|
||||
gsk_conic_curve_decompose,
|
||||
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
|
||||
};
|
||||
|
||||
/** API **/
|
||||
|
||||
static const GskCurveClass *
|
||||
get_class (GskPathOperation op)
|
||||
{
|
||||
const GskCurveClass *klasses[] = {
|
||||
[GSK_PATH_CLOSE] = &GSK_LINE_CURVE_CLASS,
|
||||
[GSK_PATH_LINE] = &GSK_LINE_CURVE_CLASS,
|
||||
[GSK_PATH_CURVE] = &GSK_CURVE_CURVE_CLASS,
|
||||
[GSK_PATH_CONIC] = &GSK_CONIC_CURVE_CLASS,
|
||||
};
|
||||
|
||||
g_assert (op < G_N_ELEMENTS (klasses) && klasses[op] != NULL);
|
||||
|
||||
return klasses[op];
|
||||
}
|
||||
|
||||
void
|
||||
gsk_curve_init (GskCurve *curve,
|
||||
gskpathop op)
|
||||
{
|
||||
get_class (gsk_pathop_op (op))->init (curve, op);
|
||||
}
|
||||
|
||||
void
|
||||
gsk_curve_init_foreach (GskCurve *curve,
|
||||
GskPathOperation op,
|
||||
const graphene_point_t *pts,
|
||||
gsize n_pts,
|
||||
float weight)
|
||||
{
|
||||
get_class (op)->init_foreach (curve, op, pts, n_pts, weight);
|
||||
}
|
||||
|
||||
void
|
||||
gsk_curve_get_point (const GskCurve *curve,
|
||||
float progress,
|
||||
graphene_point_t *pos)
|
||||
{
|
||||
get_class (curve->op)->get_point (curve, progress, pos);
|
||||
}
|
||||
|
||||
void
|
||||
gsk_curve_get_tangent (const GskCurve *curve,
|
||||
float progress,
|
||||
graphene_vec2_t *tangent)
|
||||
{
|
||||
get_class (curve->op)->get_tangent (curve, progress, tangent);
|
||||
}
|
||||
|
||||
void
|
||||
gsk_curve_split (const GskCurve *curve,
|
||||
float progress,
|
||||
GskCurve *start,
|
||||
GskCurve *end)
|
||||
{
|
||||
get_class (curve->op)->split (curve, progress, start, end);
|
||||
}
|
||||
|
||||
void
|
||||
gsk_curve_segment (const GskCurve *curve,
|
||||
float start,
|
||||
float end,
|
||||
GskCurve *segment)
|
||||
{
|
||||
if (start <= 0 && end >= 1)
|
||||
{
|
||||
*segment = *curve;
|
||||
return;
|
||||
}
|
||||
|
||||
get_class (curve->op)->segment (curve, start, end, segment);
|
||||
}
|
||||
|
||||
gboolean
|
||||
gsk_curve_decompose (const GskCurve *curve,
|
||||
float tolerance,
|
||||
GskCurveAddLineFunc add_line_func,
|
||||
gpointer user_data)
|
||||
{
|
||||
return get_class (curve->op)->decompose (curve, tolerance, add_line_func, user_data);
|
||||
}
|
||||
|
||||
gskpathop
|
||||
gsk_curve_pathop (const GskCurve *curve)
|
||||
{
|
||||
return get_class (curve->op)->pathop (curve);
|
||||
}
|
||||
|
||||
const graphene_point_t *
|
||||
gsk_curve_get_start_point (const GskCurve *curve)
|
||||
{
|
||||
return get_class (curve->op)->get_start_point (curve);
|
||||
}
|
||||
|
||||
const graphene_point_t *
|
||||
gsk_curve_get_end_point (const GskCurve *curve)
|
||||
{
|
||||
return get_class (curve->op)->get_end_point (curve);
|
||||
}
|
||||
|
||||
void
|
||||
gsk_curve_get_start_tangent (const GskCurve *curve,
|
||||
graphene_vec2_t *tangent)
|
||||
{
|
||||
get_class (curve->op)->get_start_tangent (curve, tangent);
|
||||
}
|
||||
|
||||
void
|
||||
gsk_curve_get_end_tangent (const GskCurve *curve,
|
||||
graphene_vec2_t *tangent)
|
||||
{
|
||||
get_class (curve->op)->get_end_tangent (curve, tangent);
|
||||
}
|
||||
|
||||
129
gsk/gskcurveprivate.h
Normal file
129
gsk/gskcurveprivate.h
Normal file
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright © 2020 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __GSK_CURVE_PRIVATE_H__
|
||||
#define __GSK_CURVE_PRIVATE_H__
|
||||
|
||||
#include "gskpathopprivate.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef gpointer gskpathop;
|
||||
|
||||
typedef union _GskCurve GskCurve;
|
||||
|
||||
typedef struct _GskLineCurve GskLineCurve;
|
||||
typedef struct _GskCurveCurve GskCurveCurve;
|
||||
typedef struct _GskConicCurve GskConicCurve;
|
||||
|
||||
struct _GskLineCurve
|
||||
{
|
||||
GskPathOperation op;
|
||||
|
||||
gboolean padding;
|
||||
|
||||
graphene_point_t points[2];
|
||||
};
|
||||
|
||||
struct _GskCurveCurve
|
||||
{
|
||||
GskPathOperation op;
|
||||
|
||||
gboolean has_coefficients;
|
||||
|
||||
graphene_point_t points[4];
|
||||
|
||||
graphene_point_t coeffs[4];
|
||||
};
|
||||
|
||||
struct _GskConicCurve
|
||||
{
|
||||
GskPathOperation op;
|
||||
|
||||
gboolean has_coefficients;
|
||||
|
||||
graphene_point_t points[4];
|
||||
|
||||
graphene_point_t num[3];
|
||||
graphene_point_t denom[3];
|
||||
};
|
||||
|
||||
union _GskCurve
|
||||
{
|
||||
GskPathOperation op;
|
||||
GskLineCurve line;
|
||||
GskCurveCurve curve;
|
||||
GskConicCurve conic;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
GSK_CURVE_LINE_REASON_STRAIGHT,
|
||||
GSK_CURVE_LINE_REASON_SHORT
|
||||
} GskCurveLineReason;
|
||||
|
||||
typedef gboolean (* GskCurveAddLineFunc) (const graphene_point_t *from,
|
||||
const graphene_point_t *to,
|
||||
float from_progress,
|
||||
float to_progress,
|
||||
GskCurveLineReason reason,
|
||||
gpointer user_data);
|
||||
|
||||
void gsk_curve_init (GskCurve *curve,
|
||||
gskpathop op);
|
||||
void gsk_curve_init_foreach (GskCurve *curve,
|
||||
GskPathOperation op,
|
||||
const graphene_point_t *pts,
|
||||
gsize n_pts,
|
||||
float weight);
|
||||
|
||||
void gsk_curve_get_point (const GskCurve *curve,
|
||||
float progress,
|
||||
graphene_point_t *pos);
|
||||
void gsk_curve_get_tangent (const GskCurve *curve,
|
||||
float progress,
|
||||
graphene_vec2_t *tangent);
|
||||
void gsk_curve_split (const GskCurve *curve,
|
||||
float progress,
|
||||
GskCurve *start,
|
||||
GskCurve *end);
|
||||
void gsk_curve_segment (const GskCurve *curve,
|
||||
float start,
|
||||
float end,
|
||||
GskCurve *segment);
|
||||
gboolean gsk_curve_decompose (const GskCurve *curve,
|
||||
float tolerance,
|
||||
GskCurveAddLineFunc add_line_func,
|
||||
gpointer user_data);
|
||||
gskpathop gsk_curve_pathop (const GskCurve *curve);
|
||||
#define gsk_curve_builder_to(curve, builder) gsk_path_builder_pathop_to ((builder), gsk_curve_pathop (curve))
|
||||
const graphene_point_t *gsk_curve_get_start_point (const GskCurve *curve);
|
||||
const graphene_point_t *gsk_curve_get_end_point (const GskCurve *curve);
|
||||
void gsk_curve_get_start_tangent (const GskCurve *curve,
|
||||
graphene_vec2_t *tangent);
|
||||
void gsk_curve_get_end_tangent (const GskCurve *curve,
|
||||
graphene_vec2_t *tangent);
|
||||
|
||||
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GSK_CURVE_PRIVATE_H__ */
|
||||
|
||||
103
gsk/gskenums.h
103
gsk/gskenums.h
@@ -42,6 +42,8 @@
|
||||
* @GSK_REPEAT_NODE: A node that repeats the child's contents
|
||||
* @GSK_CLIP_NODE: A node that clips its child to a rectangular area
|
||||
* @GSK_ROUNDED_CLIP_NODE: A node that clips its child to a rounded rectangle
|
||||
* @GSK_FILL_NODE: A node that fills a path
|
||||
* @GSK_STROKE_NODE: A node that strokes a path
|
||||
* @GSK_SHADOW_NODE: A node that draws a shadow below its child
|
||||
* @GSK_BLEND_NODE: A node that blends two children together
|
||||
* @GSK_CROSS_FADE_NODE: A node that cross-fades between two children
|
||||
@@ -74,6 +76,8 @@ typedef enum {
|
||||
GSK_REPEAT_NODE,
|
||||
GSK_CLIP_NODE,
|
||||
GSK_ROUNDED_CLIP_NODE,
|
||||
GSK_FILL_NODE,
|
||||
GSK_STROKE_NODE,
|
||||
GSK_SHADOW_NODE,
|
||||
GSK_BLEND_NODE,
|
||||
GSK_CROSS_FADE_NODE,
|
||||
@@ -170,6 +174,105 @@ typedef enum {
|
||||
GSK_CORNER_BOTTOM_LEFT
|
||||
} GskCorner;
|
||||
|
||||
/**
|
||||
* GskFillRule:
|
||||
* @GSK_FILL_RULE_WINDING: If the path crosses the ray from
|
||||
* left-to-right, counts +1. If the path crosses the ray
|
||||
* from right to left, counts -1. (Left and right are determined
|
||||
* from the perspective of looking along the ray from the starting
|
||||
* point.) If the total count is non-zero, the point will be filled.
|
||||
* @GSK_FILL_RULE_EVEN_ODD: Counts the total number of
|
||||
* intersections, without regard to the orientation of the contour. If
|
||||
* the total number of intersections is odd, the point will be
|
||||
* filled.
|
||||
*
|
||||
* #GskFillRule is used to select how paths are filled, for example in
|
||||
* gsk_fill_node_new(). Whether or not a point is included in the fill is
|
||||
* determined by taking a ray from that point to infinity and looking
|
||||
* at intersections with the path. The ray can be in any direction,
|
||||
* as long as it doesn't pass through the end point of a segment
|
||||
* or have a tricky intersection such as intersecting tangent to the path.
|
||||
* (Note that filling is not actually implemented in this way. This
|
||||
* is just a description of the rule that is applied.)
|
||||
*
|
||||
* New entries may be added in future versions.
|
||||
**/
|
||||
typedef enum {
|
||||
GSK_FILL_RULE_WINDING,
|
||||
GSK_FILL_RULE_EVEN_ODD
|
||||
} GskFillRule;
|
||||
|
||||
/**
|
||||
* GskLineCap:
|
||||
* @GSK_LINE_CAP_BUTT: Start and stop the line exactly at the start
|
||||
* and end point
|
||||
* @GSK_LINE_CAP_ROUND: Use a round ending, the center of the circle
|
||||
* is the start or end point.
|
||||
* @GSK_LINE_CAP_SQUARE: use squared ending, the center of the square
|
||||
* is the start or end point.
|
||||
*
|
||||
* Specifies how to render the start and end points of contours or
|
||||
* dashes when stroking.
|
||||
*
|
||||
* The default line cap style is %GSK_LINE_CAP_BUTT.
|
||||
*/
|
||||
typedef enum {
|
||||
GSK_LINE_CAP_BUTT,
|
||||
GSK_LINE_CAP_ROUND,
|
||||
GSK_LINE_CAP_SQUARE
|
||||
} GskLineCap;
|
||||
|
||||
/**
|
||||
* GskLineJoin:
|
||||
* @GSK_LINE_JOIN_MITER: Use a sharp, angled corner
|
||||
* @GSK_LINE_JOIN_MITER_CLIP: Use a sharp, angled corner, at a distance
|
||||
* @GSK_LINE_JOIN_ROUND: Use a round join, the center of the circle is
|
||||
* the joint point
|
||||
* @GSK_LINE_JOIN_BEVEL: Use a cut-off join, the join is cut off at half
|
||||
* the line width from the joint point
|
||||
*
|
||||
* Specifies how to render the junction of two lines when stroking.
|
||||
*
|
||||
* See gsk_stroke_set_miter_limit() for details on the difference between
|
||||
* @GSK_LINE_JOIN_MITER and @GSK_LINE_JOIN_MITER_CLIP.
|
||||
*
|
||||
* The default line join style is %GSK_LINE_JOIN_MITER.
|
||||
**/
|
||||
typedef enum {
|
||||
GSK_LINE_JOIN_MITER,
|
||||
GSK_LINE_JOIN_MITER_CLIP,
|
||||
GSK_LINE_JOIN_ROUND,
|
||||
GSK_LINE_JOIN_BEVEL
|
||||
} GskLineJoin;
|
||||
|
||||
/**
|
||||
* GskPathOperation:
|
||||
* @GSK_PATH_MOVE: A move-to operation, with 1 point describing the
|
||||
* target point.
|
||||
* @GSK_PATH_LINE: A line-to operation, with 2 points describing the
|
||||
* start and end point of a straight line.
|
||||
* @GSK_PATH_CLOSE: A close operation ending the current contour with
|
||||
* a line back to the starting point. Two points describe the start
|
||||
* and end of the line.
|
||||
* @GSK_PATH_CURVE: A curve-to operation describing a cubic Bézier curve
|
||||
* with 4 points describing the start point, the two control points
|
||||
* and the end point of the curve.
|
||||
* @GSK_PATH_CONIC: A weighted quadratic bezier curve with 3 points
|
||||
* describing the start point, control point and end point of the
|
||||
* curve. A weight for the curve will be passed, too.
|
||||
*
|
||||
* Path operations can be used to approximate a #GskPath.
|
||||
*
|
||||
* More values may be added in the future.
|
||||
**/
|
||||
typedef enum {
|
||||
GSK_PATH_MOVE,
|
||||
GSK_PATH_CLOSE,
|
||||
GSK_PATH_LINE,
|
||||
GSK_PATH_CURVE,
|
||||
GSK_PATH_CONIC,
|
||||
} GskPathOperation;
|
||||
|
||||
/**
|
||||
* GskSerializationError:
|
||||
* @GSK_SERIALIZATION_UNSUPPORTED_FORMAT: The format can not be identified
|
||||
|
||||
1167
gsk/gskpath.c
Normal file
1167
gsk/gskpath.c
Normal file
File diff suppressed because it is too large
Load Diff
115
gsk/gskpath.h
Normal file
115
gsk/gskpath.h
Normal file
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright © 2020 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __GSK_PATH_H__
|
||||
#define __GSK_PATH_H__
|
||||
|
||||
#if !defined (__GSK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gsk/gsk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
|
||||
#include <gsk/gsktypes.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* GskPathForeachFlags:
|
||||
* @GSK_PATH_FOREACH_ALLOW_CURVE: Allow emission of %GSK_PATH_CURVE
|
||||
* operations.
|
||||
* @GSK_PATH_FOREACH_ALLOW_CONIC: Allow emission of %GSK_PATH_CONIC
|
||||
* operations.
|
||||
*
|
||||
* Flags that can be passed to gsk_path_foreach() to enable additional
|
||||
* features.
|
||||
*
|
||||
* By default, gsk_path_foreach() will only emit a path with all operations
|
||||
* flattened to straight lines to allow for maximum compatibility. The only
|
||||
* operations emitted will be %GSK_PATH_MOVE, %GSK_PATH_LINE and %GSK_PATH_CLOSE.
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
GSK_PATH_FOREACH_ALLOW_CURVE = (1 << 0),
|
||||
GSK_PATH_FOREACH_ALLOW_CONIC = (1 << 1)
|
||||
} GskPathForeachFlags;
|
||||
|
||||
/**
|
||||
* GskPathForeachFunc:
|
||||
* @op: The operation to perform
|
||||
* @pts: The points of the operation
|
||||
* @n_pts: The number of points
|
||||
* @weight: The weight for conic curves, or unused if not a conic curve.
|
||||
* @user_data: The user data provided with the function
|
||||
*
|
||||
* Prototype of the callback to iterate throught the operations of
|
||||
* a path.
|
||||
*
|
||||
* Returns: %TRUE to continue evaluating the path, %FALSE to
|
||||
* immediately abort and not call the function again.
|
||||
*/
|
||||
typedef gboolean (* GskPathForeachFunc) (GskPathOperation op,
|
||||
const graphene_point_t *pts,
|
||||
gsize n_pts,
|
||||
float weight,
|
||||
gpointer user_data);
|
||||
|
||||
#define GSK_TYPE_PATH (gsk_path_get_type ())
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GType gsk_path_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskPath * gsk_path_ref (GskPath *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_path_unref (GskPath *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_path_print (GskPath *self,
|
||||
GString *string);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
char * gsk_path_to_string (GskPath *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskPath * gsk_path_parse (const char *string);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_path_to_cairo (GskPath *self,
|
||||
cairo_t *cr);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gsk_path_is_empty (GskPath *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gsk_path_get_bounds (GskPath *self,
|
||||
graphene_rect_t *bounds);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gsk_path_get_stroke_bounds (GskPath *self,
|
||||
const GskStroke *stroke,
|
||||
graphene_rect_t *bounds);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gsk_path_foreach (GskPath *self,
|
||||
GskPathForeachFlags flags,
|
||||
GskPathForeachFunc func,
|
||||
gpointer user_data);
|
||||
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GskPath, gsk_path_unref)
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GSK_PATH_H__ */
|
||||
1030
gsk/gskpathbuilder.c
Normal file
1030
gsk/gskpathbuilder.c
Normal file
File diff suppressed because it is too large
Load Diff
136
gsk/gskpathbuilder.h
Normal file
136
gsk/gskpathbuilder.h
Normal file
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Copyright © 2020 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __GSK_PATH_BUILDER_H__
|
||||
#define __GSK_PATH_BUILDER_H__
|
||||
|
||||
#if !defined (__GSK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gsk/gsk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
|
||||
#include <gsk/gskroundedrect.h>
|
||||
#include <gsk/gsktypes.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GSK_TYPE_PATH_BUILDER (gsk_path_builder_get_type ())
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GType gsk_path_builder_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskPathBuilder * gsk_path_builder_new (void);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskPathBuilder * gsk_path_builder_ref (GskPathBuilder *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_path_builder_unref (GskPathBuilder *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskPath * gsk_path_builder_free_to_path (GskPathBuilder *self) G_GNUC_WARN_UNUSED_RESULT;
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskPath * gsk_path_builder_to_path (GskPathBuilder *self) G_GNUC_WARN_UNUSED_RESULT;
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
const graphene_point_t *gsk_path_builder_get_current_point (GskPathBuilder *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_path_builder_add_path (GskPathBuilder *self,
|
||||
GskPath *path);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_path_builder_add_cairo_path (GskPathBuilder *self,
|
||||
const cairo_path_t *path);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_path_builder_add_layout (GskPathBuilder *self,
|
||||
PangoLayout *layout);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_path_builder_add_rect (GskPathBuilder *self,
|
||||
const graphene_rect_t *rect);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_path_builder_add_rounded_rect (GskPathBuilder *self,
|
||||
const GskRoundedRect *rect);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_path_builder_add_circle (GskPathBuilder *self,
|
||||
const graphene_point_t *center,
|
||||
float radius);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_path_builder_add_ellipse (GskPathBuilder *self,
|
||||
const graphene_point_t *center,
|
||||
const graphene_size_t *radius);
|
||||
/* next function implemented in gskpathmeasure.c */
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_path_builder_add_segment (GskPathBuilder *self,
|
||||
GskPathMeasure *measure,
|
||||
float start,
|
||||
float end);
|
||||
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_path_builder_move_to (GskPathBuilder *self,
|
||||
float x,
|
||||
float y);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_path_builder_rel_move_to (GskPathBuilder *self,
|
||||
float x,
|
||||
float y);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_path_builder_line_to (GskPathBuilder *self,
|
||||
float x,
|
||||
float y);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_path_builder_rel_line_to (GskPathBuilder *self,
|
||||
float x,
|
||||
float y);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_path_builder_curve_to (GskPathBuilder *self,
|
||||
float x1,
|
||||
float y1,
|
||||
float x2,
|
||||
float y2,
|
||||
float x3,
|
||||
float y3);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_path_builder_rel_curve_to (GskPathBuilder *self,
|
||||
float x1,
|
||||
float y1,
|
||||
float x2,
|
||||
float y2,
|
||||
float x3,
|
||||
float y3);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_path_builder_conic_to (GskPathBuilder *self,
|
||||
float x1,
|
||||
float y1,
|
||||
float x2,
|
||||
float y2,
|
||||
float weight);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_path_builder_rel_conic_to (GskPathBuilder *self,
|
||||
float x1,
|
||||
float y1,
|
||||
float x2,
|
||||
float y2,
|
||||
float weight);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_path_builder_close (GskPathBuilder *self);
|
||||
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GskPathBuilder, gsk_path_builder_unref)
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GSK_PATH_BUILDER_H__ */
|
||||
292
gsk/gskpathdash.c
Normal file
292
gsk/gskpathdash.c
Normal file
@@ -0,0 +1,292 @@
|
||||
/*
|
||||
* Copyright © 2020 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gskpathdashprivate.h"
|
||||
|
||||
#include "gskcontourprivate.h"
|
||||
#include "gskcurveprivate.h"
|
||||
#include "gskpathprivate.h"
|
||||
#include "gskstrokeprivate.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
float offset; /* how much of the current dash we've spent */
|
||||
gsize dash_index; /* goes from 0 to n_dash * 2, so we don't have to care about on/off
|
||||
for uneven dashes */
|
||||
gboolean on; /* If we're currently dashing or not */
|
||||
gboolean may_close; /* TRUE if we haven't turned the dash off in this contour */
|
||||
gboolean needs_move_to; /* If we have emitted the initial move_to() yet */
|
||||
enum {
|
||||
NORMAL, /* no special behavior required */
|
||||
SKIP, /* skip the next dash */
|
||||
ONLY, /* only do the first dash */
|
||||
DONE /* done with the first dash */
|
||||
} first_dash_behavior; /* How to handle the first dash in the loop. We loop closed contours
|
||||
twice to make sure the first dash and the last dash can get joined */
|
||||
|
||||
GskCurve curve; /* Curve we are currently processing */
|
||||
|
||||
float collect_start; /* We're collecting multiple line segments when decomposing. */
|
||||
float collect_length; /* No need to emit a curve for every line segment when the dash is long enough. */
|
||||
|
||||
/* from the stroke */
|
||||
float *dash;
|
||||
gsize n_dash;
|
||||
float dash_length;
|
||||
float dash_offset;
|
||||
|
||||
float tolerance;
|
||||
GskPathForeachFunc func;
|
||||
gpointer user_data;
|
||||
} GskPathDash;
|
||||
|
||||
static void
|
||||
gsk_path_dash_setup (GskPathDash *self)
|
||||
{
|
||||
self->offset = fmodf (self->dash_offset, 2 * self->dash_length);
|
||||
|
||||
self->dash_index = 0;
|
||||
self->on = TRUE;
|
||||
self->may_close = TRUE;
|
||||
while (self->offset > self->dash[self->dash_index % self->n_dash])
|
||||
{
|
||||
self->offset -= self->dash[self->dash_index % self->n_dash];
|
||||
self->dash_index++;
|
||||
self->on = !self->on;
|
||||
}
|
||||
if (self->first_dash_behavior != ONLY)
|
||||
self->needs_move_to = TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gsk_path_dash_ensure_move_to (GskPathDash *self,
|
||||
const graphene_point_t *pt)
|
||||
{
|
||||
if (!self->needs_move_to)
|
||||
return TRUE;
|
||||
|
||||
if (!self->func (GSK_PATH_MOVE, pt, 1, 0, self->user_data))
|
||||
return FALSE;
|
||||
|
||||
self->needs_move_to = FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gsk_path_dash_add_line_segment (const graphene_point_t *start,
|
||||
const graphene_point_t *end,
|
||||
float t_start,
|
||||
float t_end,
|
||||
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_CURVE:
|
||||
case GSK_PATH_CONIC:
|
||||
gsk_curve_init_foreach (&self->curve, op, pts, n_pts, weight);
|
||||
if (!gsk_curve_decompose (&self->curve, self->tolerance, gsk_path_dash_add_line_segment, self))
|
||||
return FALSE;
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
break;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gsk_contour_dash (const GskContour *contour,
|
||||
GskStroke *stroke,
|
||||
float tolerance,
|
||||
GskPathForeachFunc func,
|
||||
gpointer user_data)
|
||||
{
|
||||
GskPathDash self = {
|
||||
.offset = 0,
|
||||
.dash = stroke->dash,
|
||||
.n_dash = stroke->n_dash,
|
||||
.dash_length = stroke->dash_length,
|
||||
.dash_offset = stroke->dash_offset,
|
||||
.tolerance = tolerance,
|
||||
.func = func,
|
||||
.user_data = user_data
|
||||
};
|
||||
gboolean is_closed = gsk_contour_get_flags (contour) & GSK_PATH_CLOSED ? TRUE : FALSE;
|
||||
|
||||
self.first_dash_behavior = is_closed ? SKIP : NORMAL;
|
||||
if (!gsk_contour_foreach (contour, tolerance, gsk_path_dash_foreach, &self))
|
||||
return FALSE;
|
||||
|
||||
if (is_closed)
|
||||
{
|
||||
if (self.first_dash_behavior == NORMAL)
|
||||
self.first_dash_behavior = ONLY;
|
||||
else
|
||||
self.first_dash_behavior = NORMAL;
|
||||
self.needs_move_to = !self.on;
|
||||
if (!gsk_contour_foreach (contour, tolerance, gsk_path_dash_foreach, &self) &&
|
||||
self.first_dash_behavior != DONE)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gsk_path_dash (GskPath *path,
|
||||
GskStroke *stroke,
|
||||
float tolerance,
|
||||
GskPathForeachFunc func,
|
||||
gpointer user_data)
|
||||
{
|
||||
gsize i;
|
||||
|
||||
/* Dashing disabled, no need to do any work */
|
||||
if (stroke->dash_length <= 0)
|
||||
return gsk_path_foreach (path, -1, func, user_data);
|
||||
|
||||
for (i = 0; i < gsk_path_get_n_contours (path); i++)
|
||||
{
|
||||
if (!gsk_contour_dash (gsk_path_get_contour (path, i), stroke, tolerance, func, user_data))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
50
gsk/gskpathdashprivate.h
Normal file
50
gsk/gskpathdashprivate.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright © 2020 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __GSK_PATH_DASH_PRIVATE_H__
|
||||
#define __GSK_PATH_DASH_PRIVATE_H__
|
||||
|
||||
#include <gsk/gskpath.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gsk_path_dash (GskPath *path,
|
||||
GskStroke *stroke,
|
||||
float tolerance,
|
||||
GskPathForeachFunc func,
|
||||
gpointer user_data);
|
||||
|
||||
#ifdef GTK_COMPILATION
|
||||
|
||||
#include "gskcontourprivate.h"
|
||||
|
||||
gboolean gsk_contour_dash (const GskContour *contour,
|
||||
GskStroke *stroke,
|
||||
float tolerance,
|
||||
GskPathForeachFunc func,
|
||||
gpointer user_data);
|
||||
|
||||
#endif /* GTK_COMPILATION */
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GSK_PATH_DASH_PRIVATE_H__ */
|
||||
|
||||
628
gsk/gskpathmeasure.c
Normal file
628
gsk/gskpathmeasure.c
Normal file
@@ -0,0 +1,628 @@
|
||||
/*
|
||||
* Copyright © 2020 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gskpathmeasure.h"
|
||||
|
||||
#include "gskpathbuilder.h"
|
||||
#include "gskpathprivate.h"
|
||||
|
||||
/**
|
||||
* SECTION:gskpathmeasure
|
||||
* @Title: PathMeasure
|
||||
* @Short_description: Measuring operations on paths
|
||||
* @See_also: #GskPath
|
||||
*
|
||||
* #GskPathMeasure is an object that allows measuring operations on #GskPaths.
|
||||
* These operations are useful when implementing animations.
|
||||
*/
|
||||
|
||||
typedef struct _GskContourMeasure GskContourMeasure;
|
||||
|
||||
struct _GskContourMeasure
|
||||
{
|
||||
float length;
|
||||
gpointer contour_data;
|
||||
};
|
||||
|
||||
struct _GskPathMeasure
|
||||
{
|
||||
/*< private >*/
|
||||
guint ref_count;
|
||||
|
||||
GskPath *path;
|
||||
float tolerance;
|
||||
|
||||
gsize first;
|
||||
gsize last;
|
||||
|
||||
float length;
|
||||
gsize n_contours;
|
||||
GskContourMeasure measures[];
|
||||
};
|
||||
|
||||
/**
|
||||
* GskPathMeasure:
|
||||
*
|
||||
* A #GskPathMeasure struct is a reference counted struct
|
||||
* and should be treated as opaque.
|
||||
*/
|
||||
|
||||
G_DEFINE_BOXED_TYPE (GskPathMeasure, gsk_path_measure,
|
||||
gsk_path_measure_ref,
|
||||
gsk_path_measure_unref)
|
||||
|
||||
/**
|
||||
* gsk_path_measure_new:
|
||||
* @path: the path to measure
|
||||
*
|
||||
* Creates a measure object for the given @path.
|
||||
*
|
||||
* Returns: a new #GskPathMeasure representing @path
|
||||
**/
|
||||
GskPathMeasure *
|
||||
gsk_path_measure_new (GskPath *path)
|
||||
{
|
||||
return gsk_path_measure_new_with_tolerance (path, GSK_PATH_TOLERANCE_DEFAULT);
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_measure_new_with_tolerance:
|
||||
* @path: the path to measure
|
||||
* @tolerance: the tolerance for measuring operations
|
||||
*
|
||||
* Creates a measure object for the given @path and @tolerance.
|
||||
*
|
||||
* Returns: a new #GskPathMeasure representing @path
|
||||
**/
|
||||
GskPathMeasure *
|
||||
gsk_path_measure_new_with_tolerance (GskPath *path,
|
||||
float tolerance)
|
||||
{
|
||||
GskPathMeasure *self;
|
||||
gsize i, n_contours;
|
||||
|
||||
g_return_val_if_fail (path != NULL, NULL);
|
||||
g_return_val_if_fail (tolerance > 0, NULL);
|
||||
|
||||
n_contours = gsk_path_get_n_contours (path);
|
||||
|
||||
self = g_malloc0 (sizeof (GskPathMeasure) + n_contours * sizeof (GskContourMeasure));
|
||||
|
||||
self->ref_count = 1;
|
||||
self->path = gsk_path_ref (path);
|
||||
self->tolerance = tolerance;
|
||||
self->n_contours = n_contours;
|
||||
self->first = 0;
|
||||
self->last = n_contours;
|
||||
|
||||
for (i = 0; i < n_contours; i++)
|
||||
{
|
||||
self->measures[i].contour_data = gsk_contour_init_measure (gsk_path_get_contour (path, i),
|
||||
self->tolerance,
|
||||
&self->measures[i].length);
|
||||
self->length += self->measures[i].length;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_measure_ref:
|
||||
* @self: a #GskPathMeasure
|
||||
*
|
||||
* Increases the reference count of a #GskPathMeasure by one.
|
||||
*
|
||||
* Returns: the passed in #GskPathMeasure.
|
||||
**/
|
||||
GskPathMeasure *
|
||||
gsk_path_measure_ref (GskPathMeasure *self)
|
||||
{
|
||||
g_return_val_if_fail (self != NULL, NULL);
|
||||
|
||||
self->ref_count++;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_measure_unref:
|
||||
* @self: a #GskPathMeasure
|
||||
*
|
||||
* Decreases the reference count of a #GskPathMeasure by one.
|
||||
* If the resulting reference count is zero, frees the path_measure.
|
||||
**/
|
||||
void
|
||||
gsk_path_measure_unref (GskPathMeasure *self)
|
||||
{
|
||||
gsize i;
|
||||
|
||||
g_return_if_fail (self != NULL);
|
||||
g_return_if_fail (self->ref_count > 0);
|
||||
|
||||
self->ref_count--;
|
||||
if (self->ref_count > 0)
|
||||
return;
|
||||
|
||||
for (i = 0; i < self->n_contours; i++)
|
||||
{
|
||||
gsk_contour_free_measure (gsk_path_get_contour (self->path, i),
|
||||
self->measures[i].contour_data);
|
||||
}
|
||||
|
||||
gsk_path_unref (self->path);
|
||||
g_free (self);
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_measure_get_path:
|
||||
* @self: a #GskPathMeasure
|
||||
*
|
||||
* Returns the path that the measure was created for.
|
||||
*
|
||||
* Returns: (transfer none): the path of @self
|
||||
*/
|
||||
GskPath *
|
||||
gsk_path_measure_get_path (GskPathMeasure *self)
|
||||
{
|
||||
g_return_val_if_fail (self != NULL, NULL);
|
||||
|
||||
return self->path;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_measure_get_tolerance:
|
||||
* @self: a #GskPathMeasure
|
||||
*
|
||||
* Returns the tolerance that the measure was created with.
|
||||
*
|
||||
* Returns: the tolerance of @self
|
||||
*/
|
||||
float
|
||||
gsk_path_measure_get_tolerance (GskPathMeasure *self)
|
||||
{
|
||||
g_return_val_if_fail (self != NULL, 0.f);
|
||||
|
||||
return self->tolerance;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_measure_get_n_contours:
|
||||
* @self: a #GskPathMeasure
|
||||
*
|
||||
* Returns the number of contours in the path being measured.
|
||||
*
|
||||
* The returned value is independent of whether @self if restricted
|
||||
* or not.
|
||||
*
|
||||
* Returns: The number of contours
|
||||
**/
|
||||
gsize
|
||||
gsk_path_measure_get_n_contours (GskPathMeasure *self)
|
||||
{
|
||||
g_return_val_if_fail (self != NULL, 0);
|
||||
|
||||
return self->n_contours;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_measure_restrict_to_contour:
|
||||
* @self: a #GskPathMeasure
|
||||
* @contour: contour to restrict to or (gsize) -1 for using the
|
||||
* whole path
|
||||
*
|
||||
* Restricts all functions on the path to just the given @contour.
|
||||
*
|
||||
* If @contour >= gsk_path_measure_get_n_contours() - so in
|
||||
* particular when it is set to -1 - the whole path will be used.
|
||||
**/
|
||||
void
|
||||
gsk_path_measure_restrict_to_contour (GskPathMeasure *self,
|
||||
gsize contour)
|
||||
{
|
||||
if (contour >= self->n_contours)
|
||||
{
|
||||
/* use the whole path */
|
||||
self->first = 0;
|
||||
self->last = self->n_contours;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* use just one contour */
|
||||
self->first = contour;
|
||||
self->last = contour + 1;
|
||||
}
|
||||
|
||||
self->length = 0;
|
||||
for (gsize i = self->first; i < self->last; i++)
|
||||
{
|
||||
self->length += self->measures[i].length;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_measure_get_length:
|
||||
* @self: a #GskPathMeasure
|
||||
*
|
||||
* Gets the length of the path being measured.
|
||||
*
|
||||
* The length is cached, so this function does not do any work.
|
||||
*
|
||||
* Returns: The length of the path measured by @self
|
||||
**/
|
||||
float
|
||||
gsk_path_measure_get_length (GskPathMeasure *self)
|
||||
{
|
||||
g_return_val_if_fail (self != NULL, 0);
|
||||
|
||||
return self->length;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_measure_is_closed:
|
||||
* @self: a #GskPathMeasure
|
||||
*
|
||||
* Returns if the path being measured represents a single closed
|
||||
* contour.
|
||||
*
|
||||
* Returns: %TRUE if the current path is closed
|
||||
**/
|
||||
gboolean
|
||||
gsk_path_measure_is_closed (GskPathMeasure *self)
|
||||
{
|
||||
const GskContour *contour;
|
||||
|
||||
g_return_val_if_fail (self != NULL, FALSE);
|
||||
|
||||
/* XXX: is the empty path closed? Currently it's not */
|
||||
if (self->last - self->first != 1)
|
||||
return FALSE;
|
||||
|
||||
contour = gsk_path_get_contour (self->path, self->first);
|
||||
return gsk_contour_get_flags (contour) & GSK_PATH_CLOSED ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
static float
|
||||
gsk_path_measure_clamp_distance (GskPathMeasure *self,
|
||||
float distance)
|
||||
{
|
||||
if (isnan (distance))
|
||||
return 0;
|
||||
|
||||
return CLAMP (distance, 0, self->length);
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_measure_get_point:
|
||||
* @self: a #GskPathMeasure
|
||||
* @distance: distance into the path
|
||||
* @pos: (out caller-allocates) (optional): The coordinates
|
||||
* of the position at @distance
|
||||
* @tangent: (out caller-allocates) (optional): The tangent
|
||||
* to the position at @distance
|
||||
*
|
||||
* Calculates the coordinates and tangent of the point @distance
|
||||
* units into the path. The value will be clamped to the length
|
||||
* of the path.
|
||||
*
|
||||
* If the point is a discontinuous edge in the path, the returned
|
||||
* point and tangent will describe the line starting at that point
|
||||
* going forward.
|
||||
*
|
||||
* If @self describes an empty path, the returned point will be
|
||||
* set to `(0, 0)` and the tangent will be the x axis or `(1, 0)`.
|
||||
**/
|
||||
void
|
||||
gsk_path_measure_get_point (GskPathMeasure *self,
|
||||
float distance,
|
||||
graphene_point_t *pos,
|
||||
graphene_vec2_t *tangent)
|
||||
{
|
||||
gsize i;
|
||||
|
||||
g_return_if_fail (self != NULL);
|
||||
|
||||
if (pos == NULL && tangent == NULL)
|
||||
return;
|
||||
|
||||
distance = gsk_path_measure_clamp_distance (self, distance);
|
||||
|
||||
for (i = self->first; i < self->last; i++)
|
||||
{
|
||||
if (distance < self->measures[i].length)
|
||||
break;
|
||||
|
||||
distance -= self->measures[i].length;
|
||||
}
|
||||
|
||||
/* weird corner cases */
|
||||
if (i == self->last)
|
||||
{
|
||||
/* the empty path goes here */
|
||||
if (self->first == self->last)
|
||||
{
|
||||
if (pos)
|
||||
graphene_point_init (pos, 0.f, 0.f);
|
||||
if (tangent)
|
||||
graphene_vec2_init (tangent, 1.f, 0.f);
|
||||
return;
|
||||
}
|
||||
/* rounding errors can make this happen */
|
||||
i = self->last - 1;
|
||||
distance = self->measures[i].length;
|
||||
}
|
||||
|
||||
gsk_contour_get_point (gsk_path_get_contour (self->path, i),
|
||||
self->measures[i].contour_data,
|
||||
distance,
|
||||
pos,
|
||||
tangent);
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_measure_get_closest_point:
|
||||
* @self: a #GskPathMeasure
|
||||
* @point: the point to fond the closest point to
|
||||
* @out_pos: (out caller-allocates) (optional): return location
|
||||
* for the closest point
|
||||
*
|
||||
* Gets the point on the path that is closest to @point.
|
||||
*
|
||||
* If the path being measured is empty, return 0 and set
|
||||
* @out_pos to (0, 0).
|
||||
*
|
||||
* This is a simpler and slower version of
|
||||
* gsk_path_measure_get_closest_point_full(). Use that one if you
|
||||
* need more control.
|
||||
*
|
||||
* Returns: The offset into the path of the closest point
|
||||
**/
|
||||
float
|
||||
gsk_path_measure_get_closest_point (GskPathMeasure *self,
|
||||
const graphene_point_t *point,
|
||||
graphene_point_t *out_pos)
|
||||
{
|
||||
float result;
|
||||
|
||||
g_return_val_if_fail (self != NULL, 0.0f);
|
||||
|
||||
if (gsk_path_measure_get_closest_point_full (self,
|
||||
point,
|
||||
INFINITY,
|
||||
&result,
|
||||
out_pos,
|
||||
NULL,
|
||||
NULL))
|
||||
return result;
|
||||
|
||||
if (out_pos)
|
||||
*out_pos = GRAPHENE_POINT_INIT (0, 0);
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_measure_get_closest_point_full:
|
||||
* @self: a #GskPathMeasure
|
||||
* @point: the point to fond the closest point to
|
||||
* @threshold: The maximum allowed distance between the path and @point.
|
||||
* Use INFINITY to look for any point.
|
||||
* @out_distance: (out caller-allocates) (optional): The
|
||||
* distance between the found closest point on the path and the given
|
||||
* @point.
|
||||
* @out_pos: (out caller-allocates) (optional): return location
|
||||
* for the closest point
|
||||
* @out_offset: (out caller-allocates) (optional): The offset into
|
||||
* the path of the found point
|
||||
* @out_tangent: (out caller-allocates) (optional): return location for
|
||||
* the tangent at the closest point
|
||||
*
|
||||
* Gets the point on the path that is closest to @point. If no point on
|
||||
* path is closer to @point than @threshold, return %FALSE.
|
||||
*
|
||||
* Returns: %TRUE if a point was found, %FALSE otherwise.
|
||||
**/
|
||||
gboolean
|
||||
gsk_path_measure_get_closest_point_full (GskPathMeasure *self,
|
||||
const graphene_point_t *point,
|
||||
float threshold,
|
||||
float *out_distance,
|
||||
graphene_point_t *out_pos,
|
||||
float *out_offset,
|
||||
graphene_vec2_t *out_tangent)
|
||||
{
|
||||
gboolean result;
|
||||
gsize i;
|
||||
float distance, length;
|
||||
|
||||
g_return_val_if_fail (self != NULL, FALSE);
|
||||
g_return_val_if_fail (point != NULL, FALSE);
|
||||
|
||||
result = FALSE;
|
||||
length = 0;
|
||||
|
||||
for (i = self->first; i < self->last; i++)
|
||||
{
|
||||
if (gsk_contour_get_closest_point (gsk_path_get_contour (self->path, i),
|
||||
self->measures[i].contour_data,
|
||||
self->tolerance,
|
||||
point,
|
||||
threshold,
|
||||
&distance,
|
||||
out_pos,
|
||||
out_offset,
|
||||
out_tangent))
|
||||
{
|
||||
result = TRUE;
|
||||
if (out_offset)
|
||||
*out_offset += length;
|
||||
|
||||
if (distance < self->tolerance)
|
||||
break;
|
||||
threshold = distance - self->tolerance;
|
||||
}
|
||||
|
||||
length += self->measures[i].length;
|
||||
}
|
||||
|
||||
if (result && out_distance)
|
||||
*out_distance = distance;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_measure_in_fill:
|
||||
* @self: a #GskPathMeasure
|
||||
* @point: the point to test
|
||||
* @fill_rule: the fill rule to follow
|
||||
*
|
||||
* Returns whether the given point is inside the area that would be
|
||||
* affected if the path of @self was filled according to @fill_rule.
|
||||
*
|
||||
* Returns: %TRUE if @point is inside
|
||||
*/
|
||||
gboolean
|
||||
gsk_path_measure_in_fill (GskPathMeasure *self,
|
||||
const graphene_point_t *point,
|
||||
GskFillRule fill_rule)
|
||||
{
|
||||
int winding = 0;
|
||||
gboolean on_edge = FALSE;
|
||||
int i;
|
||||
|
||||
for (i = self->first; i < self->last; i++)
|
||||
{
|
||||
winding += gsk_contour_get_winding (gsk_path_get_contour (self->path, i),
|
||||
self->measures[i].contour_data,
|
||||
point,
|
||||
&on_edge);
|
||||
if (on_edge)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
switch (fill_rule)
|
||||
{
|
||||
case GSK_FILL_RULE_EVEN_ODD:
|
||||
return winding & 1;
|
||||
case GSK_FILL_RULE_WINDING:
|
||||
return winding != 0;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_path_builder_add_segment_chunk (GskPathBuilder *self,
|
||||
GskPathMeasure *measure,
|
||||
gboolean emit_move_to,
|
||||
float start,
|
||||
float end)
|
||||
{
|
||||
g_assert (start < end);
|
||||
|
||||
for (gsize i = measure->first; i < measure->last; i++)
|
||||
{
|
||||
if (measure->measures[i].length < start)
|
||||
{
|
||||
start -= measure->measures[i].length;
|
||||
end -= measure->measures[i].length;
|
||||
}
|
||||
else if (start > 0 || end < measure->measures[i].length)
|
||||
{
|
||||
float len = MIN (end, measure->measures[i].length);
|
||||
gsk_contour_add_segment (gsk_path_get_contour (measure->path, i),
|
||||
self,
|
||||
measure->measures[i].contour_data,
|
||||
emit_move_to,
|
||||
start,
|
||||
len);
|
||||
end -= len;
|
||||
start = 0;
|
||||
if (end <= 0)
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
end -= measure->measures[i].length;
|
||||
gsk_path_builder_add_contour (self, gsk_contour_dup (gsk_path_get_contour (measure->path, i)));
|
||||
}
|
||||
emit_move_to = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_builder_add_segment:
|
||||
* @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
|
||||
* gsk_path_measure_get_length() for the end of the path. The values
|
||||
* will be clamped to that range.
|
||||
*
|
||||
* If @start >= @end after clamping, the path will first add the segment
|
||||
* from @start to the end of the path, and then add the segment from
|
||||
* the beginning to @end. If the path is closed, these segments will
|
||||
* be connected.
|
||||
**/
|
||||
void
|
||||
gsk_path_builder_add_segment (GskPathBuilder *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_measure_is_closed (measure);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
88
gsk/gskpathmeasure.h
Normal file
88
gsk/gskpathmeasure.h
Normal file
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright © 2020 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __GSK_PATH_MEASURE_H__
|
||||
#define __GSK_PATH_MEASURE_H__
|
||||
|
||||
#if !defined (__GSK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gsk/gsk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
|
||||
#include <gsk/gskpath.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GSK_TYPE_PATH_MEASURE (gsk_path_measure_get_type ())
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GType gsk_path_measure_get_type (void) G_GNUC_CONST;
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskPathMeasure * gsk_path_measure_new (GskPath *path);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskPathMeasure * gsk_path_measure_new_with_tolerance (GskPath *path,
|
||||
float tolerance);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskPathMeasure * gsk_path_measure_ref (GskPathMeasure *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_path_measure_unref (GskPathMeasure *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskPath * gsk_path_measure_get_path (GskPathMeasure *self) G_GNUC_PURE;
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
float gsk_path_measure_get_tolerance (GskPathMeasure *self) G_GNUC_PURE;
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gsize gsk_path_measure_get_n_contours (GskPathMeasure *self) G_GNUC_PURE;
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_path_measure_restrict_to_contour (GskPathMeasure *self,
|
||||
gsize contour);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
float gsk_path_measure_get_length (GskPathMeasure *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gsk_path_measure_is_closed (GskPathMeasure *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_path_measure_get_point (GskPathMeasure *self,
|
||||
float distance,
|
||||
graphene_point_t *pos,
|
||||
graphene_vec2_t *tangent);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
float gsk_path_measure_get_closest_point (GskPathMeasure *self,
|
||||
const graphene_point_t *point,
|
||||
graphene_point_t *out_pos);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gsk_path_measure_get_closest_point_full (GskPathMeasure *self,
|
||||
const graphene_point_t *point,
|
||||
float threshold,
|
||||
float *out_distance,
|
||||
graphene_point_t *out_pos,
|
||||
float *out_offset,
|
||||
graphene_vec2_t *out_tangent);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gsk_path_measure_in_fill (GskPathMeasure *self,
|
||||
const graphene_point_t *point,
|
||||
GskFillRule fill_rule);
|
||||
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GskPathMeasure, gsk_path_measure_unref)
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GSK_PATH_MEASURE_H__ */
|
||||
179
gsk/gskpathopprivate.h
Normal file
179
gsk/gskpathopprivate.h
Normal file
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
* Copyright © 2020 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __GSK_PATHOP_PRIVATE_H__
|
||||
#define __GSK_PATHOP_PRIVATE_H__
|
||||
|
||||
#include <gsk/gskpath.h>
|
||||
#include <gsk/gskpathbuilder.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef gpointer gskpathop;
|
||||
|
||||
static inline
|
||||
gskpathop gsk_pathop_encode (GskPathOperation op,
|
||||
const graphene_point_t *pts);
|
||||
static inline
|
||||
const graphene_point_t *gsk_pathop_points (gskpathop pop);
|
||||
static inline
|
||||
GskPathOperation gsk_pathop_op (gskpathop pop);
|
||||
|
||||
static inline
|
||||
gboolean gsk_pathop_foreach (gskpathop pop,
|
||||
GskPathForeachFunc func,
|
||||
gpointer user_data);
|
||||
|
||||
/* included inline so tests can use them */
|
||||
static inline
|
||||
void gsk_path_builder_pathop_to (GskPathBuilder *builder,
|
||||
gskpathop op);
|
||||
static inline
|
||||
void gsk_path_builder_pathop_reverse_to (GskPathBuilder *builder,
|
||||
gskpathop op);
|
||||
|
||||
/* IMPLEMENTATION */
|
||||
|
||||
#define GSK_PATHOP_OPERATION_MASK (0x7)
|
||||
|
||||
static inline gskpathop
|
||||
gsk_pathop_encode (GskPathOperation op,
|
||||
const graphene_point_t *pts)
|
||||
{
|
||||
/* g_assert (op & GSK_PATHOP_OPERATION_MASK == op); */
|
||||
g_assert ((GPOINTER_TO_SIZE (pts) & GSK_PATHOP_OPERATION_MASK) == 0);
|
||||
|
||||
return GSIZE_TO_POINTER (GPOINTER_TO_SIZE (pts) | op);
|
||||
}
|
||||
|
||||
static inline const graphene_point_t *
|
||||
gsk_pathop_points (gskpathop pop)
|
||||
{
|
||||
return GSIZE_TO_POINTER (GPOINTER_TO_SIZE (pop) & ~GSK_PATHOP_OPERATION_MASK);
|
||||
}
|
||||
|
||||
static inline
|
||||
GskPathOperation gsk_pathop_op (gskpathop pop)
|
||||
{
|
||||
return GPOINTER_TO_SIZE (pop) & GSK_PATHOP_OPERATION_MASK;
|
||||
}
|
||||
|
||||
static inline gboolean
|
||||
gsk_pathop_foreach (gskpathop pop,
|
||||
GskPathForeachFunc func,
|
||||
gpointer user_data)
|
||||
{
|
||||
switch (gsk_pathop_op (pop))
|
||||
{
|
||||
case GSK_PATH_MOVE:
|
||||
return func (gsk_pathop_op (pop), gsk_pathop_points (pop), 1, 0, user_data);
|
||||
|
||||
case GSK_PATH_CLOSE:
|
||||
case GSK_PATH_LINE:
|
||||
return func (gsk_pathop_op (pop), gsk_pathop_points (pop), 2, 0, user_data);
|
||||
|
||||
case GSK_PATH_CURVE:
|
||||
return func (gsk_pathop_op (pop), gsk_pathop_points (pop), 4, 0, user_data);
|
||||
|
||||
case GSK_PATH_CONIC:
|
||||
{
|
||||
const graphene_point_t *pts = gsk_pathop_points (pop);
|
||||
return func (gsk_pathop_op (pop), (graphene_point_t[3]) { pts[0], pts[1], pts[3] }, 3, pts[2].x, user_data);
|
||||
}
|
||||
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
gsk_path_builder_pathop_to (GskPathBuilder *builder,
|
||||
gskpathop op)
|
||||
{
|
||||
const graphene_point_t *pts = gsk_pathop_points (op);
|
||||
|
||||
switch (gsk_pathop_op (op))
|
||||
{
|
||||
case GSK_PATH_MOVE:
|
||||
gsk_path_builder_move_to (builder, pts[0].x, pts[0].y);
|
||||
break;
|
||||
|
||||
case GSK_PATH_CLOSE:
|
||||
gsk_path_builder_close (builder);
|
||||
break;
|
||||
|
||||
case GSK_PATH_LINE:
|
||||
gsk_path_builder_line_to (builder, pts[1].x, pts[1].y);
|
||||
break;
|
||||
|
||||
case GSK_PATH_CURVE:
|
||||
gsk_path_builder_curve_to (builder, pts[1].x, pts[1].y, pts[2].x, pts[2].y, pts[3].x, pts[3].y);
|
||||
break;
|
||||
|
||||
case GSK_PATH_CONIC:
|
||||
gsk_path_builder_conic_to (builder, pts[1].x, pts[1].y, pts[3].x, pts[3].y, pts[2].x);
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
gsk_path_builder_pathop_reverse_to (GskPathBuilder *builder,
|
||||
gskpathop op)
|
||||
{
|
||||
const graphene_point_t *pts = gsk_pathop_points (op);
|
||||
|
||||
switch (gsk_pathop_op (op))
|
||||
{
|
||||
case GSK_PATH_MOVE:
|
||||
gsk_path_builder_move_to (builder, pts[0].x, pts[0].y);
|
||||
break;
|
||||
|
||||
case GSK_PATH_CLOSE:
|
||||
gsk_path_builder_line_to (builder, pts[0].x, pts[0].y);
|
||||
break;
|
||||
|
||||
case GSK_PATH_LINE:
|
||||
gsk_path_builder_line_to (builder, pts[1].x, pts[1].y);
|
||||
break;
|
||||
|
||||
case GSK_PATH_CURVE:
|
||||
gsk_path_builder_curve_to (builder, pts[2].x, pts[2].y, pts[1].x, pts[1].y, pts[0].x, pts[0].y);
|
||||
break;
|
||||
|
||||
case GSK_PATH_CONIC:
|
||||
gsk_path_builder_conic_to (builder, pts[1].x, pts[1].y, pts[0].x, pts[0].y, pts[2].x);
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GSK_PATHOP_PRIVATE_H__ */
|
||||
|
||||
61
gsk/gskpathprivate.h
Normal file
61
gsk/gskpathprivate.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright © 2020 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __GSK_PATH_PRIVATE_H__
|
||||
#define __GSK_PATH_PRIVATE_H__
|
||||
|
||||
#include "gskpath.h"
|
||||
|
||||
#include "gskcontourprivate.h"
|
||||
#include "gskpathopprivate.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/* Same as Skia, so looks like a good value. ¯\_(ツ)_/¯ */
|
||||
#define GSK_PATH_TOLERANCE_DEFAULT (0.5)
|
||||
|
||||
GskPath * gsk_path_new_from_contours (const GSList *contours);
|
||||
|
||||
gsize gsk_path_get_n_contours (GskPath *path);
|
||||
const GskContour * gsk_path_get_contour (GskPath *path,
|
||||
gsize i);
|
||||
gboolean gsk_path_foreach_with_tolerance (GskPath *self,
|
||||
GskPathForeachFlags flags,
|
||||
double tolerance,
|
||||
GskPathForeachFunc func,
|
||||
gpointer user_data);
|
||||
|
||||
|
||||
void gsk_path_builder_add_contour (GskPathBuilder *builder,
|
||||
GskContour *contour);
|
||||
|
||||
void gsk_path_builder_svg_arc_to (GskPathBuilder *builder,
|
||||
float rx,
|
||||
float ry,
|
||||
float x_axis_rotation,
|
||||
gboolean large_arc,
|
||||
gboolean positive_sweep,
|
||||
float x,
|
||||
float y);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GSK_PATH_PRIVATE_H__ */
|
||||
|
||||
@@ -158,6 +158,8 @@ GskRenderNode * gsk_render_node_deserialize (GBytes
|
||||
#define GSK_TYPE_REPEAT_NODE (gsk_repeat_node_get_type())
|
||||
#define GSK_TYPE_CLIP_NODE (gsk_clip_node_get_type())
|
||||
#define GSK_TYPE_ROUNDED_CLIP_NODE (gsk_rounded_clip_node_get_type())
|
||||
#define GSK_TYPE_FILL_NODE (gsk_fill_node_get_type())
|
||||
#define GSK_TYPE_STROKE_NODE (gsk_stroke_node_get_type())
|
||||
#define GSK_TYPE_SHADOW_NODE (gsk_shadow_node_get_type())
|
||||
#define GSK_TYPE_BLEND_NODE (gsk_blend_node_get_type())
|
||||
#define GSK_TYPE_CROSS_FADE_NODE (gsk_cross_fade_node_get_type())
|
||||
@@ -186,6 +188,8 @@ typedef struct _GskColorMatrixNode GskColorMatrixNode;
|
||||
typedef struct _GskRepeatNode GskRepeatNode;
|
||||
typedef struct _GskClipNode GskClipNode;
|
||||
typedef struct _GskRoundedClipNode GskRoundedClipNode;
|
||||
typedef struct _GskFillNode GskFillNode;
|
||||
typedef struct _GskStrokeNode GskStrokeNode;
|
||||
typedef struct _GskShadowNode GskShadowNode;
|
||||
typedef struct _GskBlendNode GskBlendNode;
|
||||
typedef struct _GskCrossFadeNode GskCrossFadeNode;
|
||||
@@ -459,6 +463,32 @@ GskRenderNode * gsk_rounded_clip_node_get_child (const GskRender
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
const GskRoundedRect * gsk_rounded_clip_node_get_clip (const GskRenderNode *node) G_GNUC_PURE;
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GType gsk_fill_node_get_type (void) G_GNUC_CONST;
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskRenderNode * gsk_fill_node_new (GskRenderNode *child,
|
||||
GskPath *path,
|
||||
GskFillRule fill_rule);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskRenderNode * gsk_fill_node_get_child (GskRenderNode *node);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskPath * gsk_fill_node_get_path (GskRenderNode *node);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskFillRule gsk_fill_node_get_fill_rule (GskRenderNode *node);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GType gsk_stroke_node_get_type (void) G_GNUC_CONST;
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskRenderNode * gsk_stroke_node_new (GskRenderNode *child,
|
||||
GskPath *path,
|
||||
const GskStroke *stroke);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskRenderNode * gsk_stroke_node_get_child (GskRenderNode *node);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskPath * gsk_stroke_node_get_path (GskRenderNode *node);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
const GskStroke * gsk_stroke_node_get_stroke (GskRenderNode *node);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GType gsk_shadow_node_get_type (void) G_GNUC_CONST;
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
|
||||
@@ -25,8 +25,11 @@
|
||||
#include "gskdebugprivate.h"
|
||||
#include "gskdiffprivate.h"
|
||||
#include "gl/gskglrenderer.h"
|
||||
#include "gskpath.h"
|
||||
#include "gskrectprivate.h"
|
||||
#include "gskrendererprivate.h"
|
||||
#include "gskroundedrectprivate.h"
|
||||
#include "gskstrokeprivate.h"
|
||||
#include "gsktransformprivate.h"
|
||||
|
||||
#include "gdk/gdktextureprivate.h"
|
||||
@@ -4366,6 +4369,380 @@ gsk_rounded_clip_node_get_clip (const GskRenderNode *node)
|
||||
return &self->clip;
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
/* {{{ GSK_FILL_NODE */
|
||||
|
||||
struct _GskFillNode
|
||||
{
|
||||
GskRenderNode render_node;
|
||||
|
||||
GskRenderNode *child;
|
||||
GskPath *path;
|
||||
GskFillRule fill_rule;
|
||||
};
|
||||
|
||||
static void
|
||||
gsk_fill_node_finalize (GskRenderNode *node)
|
||||
{
|
||||
GskFillNode *self = (GskFillNode *) node;
|
||||
GskRenderNodeClass *parent_class = g_type_class_peek (g_type_parent (GSK_TYPE_FILL_NODE));
|
||||
|
||||
gsk_render_node_unref (self->child);
|
||||
gsk_path_unref (self->path);
|
||||
|
||||
parent_class->finalize (node);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_fill_node_draw (GskRenderNode *node,
|
||||
cairo_t *cr)
|
||||
{
|
||||
GskFillNode *self = (GskFillNode *) node;
|
||||
|
||||
cairo_save (cr);
|
||||
|
||||
switch (self->fill_rule)
|
||||
{
|
||||
case GSK_FILL_RULE_WINDING:
|
||||
cairo_set_fill_rule (cr, CAIRO_FILL_RULE_WINDING);
|
||||
break;
|
||||
case GSK_FILL_RULE_EVEN_ODD:
|
||||
cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
break;
|
||||
}
|
||||
gsk_path_to_cairo (self->path, cr);
|
||||
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_restore (cr);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_fill_node_diff (GskRenderNode *node1,
|
||||
GskRenderNode *node2,
|
||||
cairo_region_t *region)
|
||||
{
|
||||
GskFillNode *self1 = (GskFillNode *) node1;
|
||||
GskFillNode *self2 = (GskFillNode *) node2;
|
||||
|
||||
if (self1->path == self2->path)
|
||||
{
|
||||
cairo_region_t *sub;
|
||||
cairo_rectangle_int_t clip_rect;
|
||||
graphene_rect_t rect;
|
||||
|
||||
sub = cairo_region_create();
|
||||
gsk_render_node_diff (self1->child, self2->child, sub);
|
||||
graphene_rect_union (&node1->bounds, &node2->bounds, &rect);
|
||||
rectangle_init_from_graphene (&clip_rect, &rect);
|
||||
cairo_region_intersect_rectangle (sub, &clip_rect);
|
||||
cairo_region_union (region, sub);
|
||||
cairo_region_destroy (sub);
|
||||
}
|
||||
else
|
||||
{
|
||||
gsk_render_node_diff_impossible (node1, node2, region);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_fill_node_class_init (gpointer g_class,
|
||||
gpointer class_data)
|
||||
{
|
||||
GskRenderNodeClass *node_class = g_class;
|
||||
|
||||
node_class->node_type = GSK_FILL_NODE;
|
||||
|
||||
node_class->finalize = gsk_fill_node_finalize;
|
||||
node_class->draw = gsk_fill_node_draw;
|
||||
node_class->diff = gsk_fill_node_diff;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_fill_node_new:
|
||||
* @child: The node to fill the area with
|
||||
* @path: The path describing the area to fill
|
||||
* @fill_rule: The fill rule to use
|
||||
*
|
||||
* Creates a #GskRenderNode that will fill the @child in the area
|
||||
* given by @path and @fill_rule.
|
||||
*
|
||||
* Returns: (transfer none) (type GskFillNode): A new #GskRenderNode
|
||||
*/
|
||||
GskRenderNode *
|
||||
gsk_fill_node_new (GskRenderNode *child,
|
||||
GskPath *path,
|
||||
GskFillRule fill_rule)
|
||||
{
|
||||
GskFillNode *self;
|
||||
GskRenderNode *node;
|
||||
graphene_rect_t path_bounds;
|
||||
|
||||
g_return_val_if_fail (GSK_IS_RENDER_NODE (child), NULL);
|
||||
g_return_val_if_fail (path != NULL, NULL);
|
||||
|
||||
self = gsk_render_node_alloc (GSK_FILL_NODE);
|
||||
node = (GskRenderNode *) self;
|
||||
|
||||
self->child = gsk_render_node_ref (child);
|
||||
self->path = gsk_path_ref (path);
|
||||
self->fill_rule = fill_rule;
|
||||
|
||||
if (gsk_path_get_bounds (path, &path_bounds))
|
||||
graphene_rect_intersection (&path_bounds, &child->bounds, &node->bounds);
|
||||
else
|
||||
graphene_rect_init_from_rect (&node->bounds, graphene_rect_zero ());
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_fill_node_get_child:
|
||||
* @node: (type GskFillNode): a fill #GskRenderNode
|
||||
*
|
||||
* Gets the child node that is getting drawn by the given @node.
|
||||
*
|
||||
* Returns: (transfer none): The child that is getting drawn
|
||||
**/
|
||||
GskRenderNode *
|
||||
gsk_fill_node_get_child (GskRenderNode *node)
|
||||
{
|
||||
GskFillNode *self = (GskFillNode *) node;
|
||||
|
||||
g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_FILL_NODE), NULL);
|
||||
|
||||
return self->child;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_fill_node_get_path:
|
||||
* @node: (type GskFillNode): a fill #GskRenderNode
|
||||
*
|
||||
* Retrievs the path used to describe the area filled with the contents of
|
||||
* the @node.
|
||||
*
|
||||
* Returns: (transfer none): a #GskPath
|
||||
*/
|
||||
GskPath *
|
||||
gsk_fill_node_get_path (GskRenderNode *node)
|
||||
{
|
||||
GskFillNode *self = (GskFillNode *) node;
|
||||
|
||||
g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_FILL_NODE), NULL);
|
||||
|
||||
return self->path;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_fill_node_get_fill_rule:
|
||||
* @node: (type GskFillNode): a fill #GskRenderNode
|
||||
*
|
||||
* Retrievs the fill rule used to determine how the path is filled.
|
||||
*
|
||||
* Returns: a #GskFillRule
|
||||
*/
|
||||
GskFillRule
|
||||
gsk_fill_node_get_fill_rule (GskRenderNode *node)
|
||||
{
|
||||
GskFillNode *self = (GskFillNode *) node;
|
||||
|
||||
g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_FILL_NODE), GSK_FILL_RULE_WINDING);
|
||||
|
||||
return self->fill_rule;
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
/* {{{ GSK_STROKE_NODE */
|
||||
|
||||
struct _GskStrokeNode
|
||||
{
|
||||
GskRenderNode render_node;
|
||||
|
||||
GskRenderNode *child;
|
||||
GskPath *path;
|
||||
GskStroke stroke;
|
||||
};
|
||||
|
||||
static void
|
||||
gsk_stroke_node_finalize (GskRenderNode *node)
|
||||
{
|
||||
GskStrokeNode *self = (GskStrokeNode *) node;
|
||||
GskRenderNodeClass *parent_class = g_type_class_peek (g_type_parent (GSK_TYPE_STROKE_NODE));
|
||||
|
||||
gsk_render_node_unref (self->child);
|
||||
gsk_path_unref (self->path);
|
||||
gsk_stroke_clear (&self->stroke);
|
||||
|
||||
parent_class->finalize (node);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_stroke_node_draw (GskRenderNode *node,
|
||||
cairo_t *cr)
|
||||
{
|
||||
GskStrokeNode *self = (GskStrokeNode *) node;
|
||||
|
||||
cairo_save (cr);
|
||||
|
||||
gsk_cairo_rectangle (cr, &self->child->bounds);
|
||||
cairo_clip (cr);
|
||||
|
||||
cairo_push_group (cr);
|
||||
gsk_render_node_draw (self->child, cr);
|
||||
cairo_pop_group_to_source (cr);
|
||||
|
||||
gsk_stroke_to_cairo (&self->stroke, cr);
|
||||
|
||||
gsk_path_to_cairo (self->path, cr);
|
||||
cairo_stroke (cr);
|
||||
|
||||
cairo_restore (cr);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_stroke_node_diff (GskRenderNode *node1,
|
||||
GskRenderNode *node2,
|
||||
cairo_region_t *region)
|
||||
{
|
||||
GskStrokeNode *self1 = (GskStrokeNode *) node1;
|
||||
GskStrokeNode *self2 = (GskStrokeNode *) node2;
|
||||
|
||||
if (self1->path == self2->path &&
|
||||
gsk_stroke_equal (&self1->stroke, &self2->stroke))
|
||||
{
|
||||
cairo_region_t *sub;
|
||||
|
||||
sub = cairo_region_create();
|
||||
gsk_render_node_diff (self1->child, self2->child, sub);
|
||||
cairo_region_union (region, sub);
|
||||
cairo_region_destroy (sub);
|
||||
}
|
||||
else
|
||||
{
|
||||
gsk_render_node_diff_impossible (node1, node2, region);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_stroke_node_class_init (gpointer g_class,
|
||||
gpointer class_data)
|
||||
{
|
||||
GskRenderNodeClass *node_class = g_class;
|
||||
|
||||
node_class->node_type = GSK_STROKE_NODE;
|
||||
|
||||
node_class->finalize = gsk_stroke_node_finalize;
|
||||
node_class->draw = gsk_stroke_node_draw;
|
||||
node_class->diff = gsk_stroke_node_diff;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_stroke_node_new:
|
||||
* @child: The node to stroke the area with
|
||||
* @path: (transfer none): The path describing the area to stroke
|
||||
* @stroke: (transfer none): The stroke attributes to use
|
||||
*
|
||||
* Creates a #GskRenderNode that will stroke the @child along the given
|
||||
* @path using the attributes defined in @stroke.
|
||||
*
|
||||
* Returns: (transfer none) (type GskStrokeNode): A new #GskRenderNode
|
||||
*/
|
||||
GskRenderNode *
|
||||
gsk_stroke_node_new (GskRenderNode *child,
|
||||
GskPath *path,
|
||||
const GskStroke *stroke)
|
||||
{
|
||||
GskStrokeNode *self;
|
||||
GskRenderNode *node;
|
||||
graphene_rect_t path_bounds;
|
||||
|
||||
g_return_val_if_fail (GSK_IS_RENDER_NODE (child), NULL);
|
||||
g_return_val_if_fail (path != NULL, NULL);
|
||||
g_return_val_if_fail (stroke != NULL, NULL);
|
||||
|
||||
self = gsk_render_node_alloc (GSK_STROKE_NODE);
|
||||
node = (GskRenderNode *) self;
|
||||
|
||||
self->child = gsk_render_node_ref (child);
|
||||
self->path = gsk_path_ref (path);
|
||||
gsk_stroke_init_copy (&self->stroke, stroke);
|
||||
|
||||
if (gsk_path_get_stroke_bounds (path, stroke, &path_bounds))
|
||||
graphene_rect_intersection (&path_bounds, &child->bounds, &node->bounds);
|
||||
else
|
||||
graphene_rect_init_from_rect (&node->bounds, graphene_rect_zero ());
|
||||
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_stroke_node_get_child:
|
||||
* @node: (type GskStrokeNode): a stroke #GskRenderNode
|
||||
*
|
||||
* Gets the child node that is getting drawn by the given @node.
|
||||
*
|
||||
* Returns: (transfer none): The child that is getting drawn
|
||||
**/
|
||||
GskRenderNode *
|
||||
gsk_stroke_node_get_child (GskRenderNode *node)
|
||||
{
|
||||
GskStrokeNode *self = (GskStrokeNode *) node;
|
||||
|
||||
g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_STROKE_NODE), NULL);
|
||||
|
||||
return self->child;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_stroke_node_get_path:
|
||||
* @node: (type GskStrokeNode): a stroke #GskRenderNode
|
||||
*
|
||||
* Retrievs the path that will be stroked with the contents of
|
||||
* the @node.
|
||||
*
|
||||
* Returns: (transfer none): a #GskPath
|
||||
*/
|
||||
GskPath *
|
||||
gsk_stroke_node_get_path (GskRenderNode *node)
|
||||
{
|
||||
GskStrokeNode *self = (GskStrokeNode *) node;
|
||||
|
||||
g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_STROKE_NODE), NULL);
|
||||
|
||||
return self->path;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_stroke_node_get_stroke:
|
||||
* @node: (type GskStrokeNode): a stroke #GskRenderNode
|
||||
*
|
||||
* Retrievs the stroke attributes used in this @node.
|
||||
*
|
||||
* Returns: a #GskStroke
|
||||
*/
|
||||
const GskStroke *
|
||||
gsk_stroke_node_get_stroke (GskRenderNode *node)
|
||||
{
|
||||
GskStrokeNode *self = (GskStrokeNode *) node;
|
||||
|
||||
g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_STROKE_NODE), NULL);
|
||||
|
||||
return &self->stroke;
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
/* {{{ GSK_SHADOW_NODE */
|
||||
|
||||
@@ -6259,6 +6636,8 @@ GSK_DEFINE_RENDER_NODE_TYPE (gsk_color_matrix_node, GSK_COLOR_MATRIX_NODE)
|
||||
GSK_DEFINE_RENDER_NODE_TYPE (gsk_repeat_node, GSK_REPEAT_NODE)
|
||||
GSK_DEFINE_RENDER_NODE_TYPE (gsk_clip_node, GSK_CLIP_NODE)
|
||||
GSK_DEFINE_RENDER_NODE_TYPE (gsk_rounded_clip_node, GSK_ROUNDED_CLIP_NODE)
|
||||
GSK_DEFINE_RENDER_NODE_TYPE (gsk_fill_node, GSK_FILL_NODE)
|
||||
GSK_DEFINE_RENDER_NODE_TYPE (gsk_stroke_node, GSK_STROKE_NODE)
|
||||
GSK_DEFINE_RENDER_NODE_TYPE (gsk_shadow_node, GSK_SHADOW_NODE)
|
||||
GSK_DEFINE_RENDER_NODE_TYPE (gsk_blend_node, GSK_BLEND_NODE)
|
||||
GSK_DEFINE_RENDER_NODE_TYPE (gsk_cross_fade_node, GSK_CROSS_FADE_NODE)
|
||||
@@ -6407,6 +6786,16 @@ gsk_render_node_init_types_once (void)
|
||||
sizeof (GskDebugNode),
|
||||
gsk_debug_node_class_init);
|
||||
gsk_render_node_types[GSK_DEBUG_NODE] = node_type;
|
||||
|
||||
node_type = gsk_render_node_type_register_static (I_("GskFillNode"),
|
||||
sizeof (GskFillNode),
|
||||
gsk_fill_node_class_init);
|
||||
gsk_render_node_types[GSK_FILL_NODE] = node_type;
|
||||
|
||||
node_type = gsk_render_node_type_register_static (I_("GskStrokeNode"),
|
||||
sizeof (GskStrokeNode),
|
||||
gsk_stroke_node_class_init);
|
||||
gsk_render_node_types[GSK_STROKE_NODE] = node_type;
|
||||
}
|
||||
|
||||
static void
|
||||
|
||||
@@ -23,9 +23,13 @@
|
||||
|
||||
#include "gskrendernodeparserprivate.h"
|
||||
|
||||
#include "gskpath.h"
|
||||
#include "gskpathbuilder.h"
|
||||
#include "gskroundedrectprivate.h"
|
||||
#include "gskrendernodeprivate.h"
|
||||
#include "gskstroke.h"
|
||||
#include "gsktransformprivate.h"
|
||||
#include "gskenumtypes.h"
|
||||
|
||||
#include "gdk/gdkrgbaprivate.h"
|
||||
#include "gdk/gdktextureprivate.h"
|
||||
@@ -1174,6 +1178,26 @@ create_default_render_node (void)
|
||||
return gsk_color_node_new (&GDK_RGBA("FF00CC"), &GRAPHENE_RECT_INIT (0, 0, 50, 50));
|
||||
}
|
||||
|
||||
static GskPath *
|
||||
create_default_path (void)
|
||||
{
|
||||
GskPathBuilder *builder;
|
||||
guint i;
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
|
||||
gsk_path_builder_move_to (builder, 25, 0);
|
||||
for (i = 1; i < 5; i++)
|
||||
{
|
||||
gsk_path_builder_line_to (builder,
|
||||
sin (i * G_PI * 0.8) * 25 + 25,
|
||||
-cos (i * G_PI * 0.8) * 25 + 25);
|
||||
}
|
||||
gsk_path_builder_close (builder);
|
||||
|
||||
return gsk_path_builder_free_to_path (builder);
|
||||
}
|
||||
|
||||
static GskRenderNode *
|
||||
parse_color_node (GtkCssParser *parser,
|
||||
Context *context)
|
||||
@@ -2097,6 +2121,223 @@ parse_rounded_clip_node (GtkCssParser *parser,
|
||||
return result;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
parse_path (GtkCssParser *parser,
|
||||
Context *context,
|
||||
gpointer out_path)
|
||||
{
|
||||
GskPath *path;
|
||||
char *str = NULL;
|
||||
|
||||
if (!parse_string (parser, context, &str))
|
||||
return FALSE;
|
||||
|
||||
path = gsk_path_parse (str);
|
||||
g_free (str);
|
||||
|
||||
if (path == NULL)
|
||||
{
|
||||
gtk_css_parser_error_value (parser, "Invalid path");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
*((GskPath **) out_path) = path;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
clear_path (gpointer inout_path)
|
||||
{
|
||||
g_clear_pointer ((GskPath **) inout_path, gsk_path_unref);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
parse_dash (GtkCssParser *parser,
|
||||
Context *context,
|
||||
gpointer out_dash)
|
||||
{
|
||||
GArray *dash;
|
||||
double d;
|
||||
|
||||
/* because CSS does this, too */
|
||||
if (gtk_css_parser_try_ident (parser, "none"))
|
||||
{
|
||||
*((GArray **) out_dash) = NULL;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
dash = g_array_new (FALSE, FALSE, sizeof (float));
|
||||
while (gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_SIGNLESS_NUMBER) ||
|
||||
gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_SIGNLESS_INTEGER))
|
||||
{
|
||||
if (!gtk_css_parser_consume_number (parser, &d))
|
||||
{
|
||||
g_array_free (dash, TRUE);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
g_array_append_vals (dash, (float[1]) { d }, 1);
|
||||
}
|
||||
|
||||
if (dash->len == 0)
|
||||
{
|
||||
gtk_css_parser_error_syntax (parser, "Expected a positive number");
|
||||
g_array_unref (dash);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
*((GArray **) out_dash) = dash;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
clear_dash (gpointer inout_array)
|
||||
{
|
||||
g_clear_pointer ((GArray **) inout_array, g_array_unref);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
parse_enum (GtkCssParser *parser,
|
||||
GType type,
|
||||
gpointer out_value)
|
||||
{
|
||||
GEnumClass *class;
|
||||
GEnumValue *v;
|
||||
char *enum_name;
|
||||
|
||||
enum_name = gtk_css_parser_consume_ident (parser);
|
||||
if (enum_name == NULL)
|
||||
return FALSE;
|
||||
|
||||
class = g_type_class_ref (type);
|
||||
|
||||
v = g_enum_get_value_by_nick (class, enum_name);
|
||||
if (v == NULL)
|
||||
{
|
||||
gtk_css_parser_error_value (parser, "Unknown value \"%s\" for enum \"%s\"",
|
||||
enum_name, g_type_name (type));
|
||||
g_free (enum_name);
|
||||
g_type_class_unref (class);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
*(int*)out_value = v->value;
|
||||
|
||||
g_free (enum_name);
|
||||
g_type_class_unref (class);
|
||||
|
||||
gtk_css_parser_consume_token (parser);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
parse_fill_rule (GtkCssParser *parser,
|
||||
Context *context,
|
||||
gpointer out_rule)
|
||||
{
|
||||
return parse_enum (parser, GSK_TYPE_FILL_RULE, out_rule);
|
||||
}
|
||||
|
||||
static GskRenderNode *
|
||||
parse_fill_node (GtkCssParser *parser,
|
||||
Context *context)
|
||||
{
|
||||
GskRenderNode *child = NULL;
|
||||
GskPath *path = NULL;
|
||||
int rule = GSK_FILL_RULE_WINDING;
|
||||
const Declaration declarations[] = {
|
||||
{ "child", parse_node, clear_node, &child },
|
||||
{ "path", parse_path, clear_path, &path },
|
||||
{ "fill-rule", parse_fill_rule, NULL, &rule },
|
||||
};
|
||||
GskRenderNode *result;
|
||||
|
||||
parse_declarations (parser, context, declarations, G_N_ELEMENTS (declarations));
|
||||
if (child == NULL)
|
||||
child = create_default_render_node ();
|
||||
if (path == NULL)
|
||||
path = create_default_path ();
|
||||
|
||||
result = gsk_fill_node_new (child, path, rule);
|
||||
|
||||
gsk_path_unref (path);
|
||||
|
||||
gsk_render_node_unref (child);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
parse_line_cap (GtkCssParser *parser,
|
||||
Context *context,
|
||||
gpointer out)
|
||||
{
|
||||
return parse_enum (parser, GSK_TYPE_LINE_CAP, out);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
parse_line_join (GtkCssParser *parser,
|
||||
Context *context,
|
||||
gpointer out)
|
||||
{
|
||||
return parse_enum (parser, GSK_TYPE_LINE_JOIN, out);
|
||||
}
|
||||
|
||||
static GskRenderNode *
|
||||
parse_stroke_node (GtkCssParser *parser,
|
||||
Context *context)
|
||||
{
|
||||
GskRenderNode *child = NULL;
|
||||
GskPath *path = NULL;
|
||||
double line_width = 1.0;
|
||||
int line_cap = GSK_LINE_CAP_BUTT;
|
||||
int line_join = GSK_LINE_JOIN_MITER;
|
||||
double miter_limit = 4.0;
|
||||
GArray *dash = NULL;
|
||||
double dash_offset = 0.0;
|
||||
GskStroke *stroke;
|
||||
|
||||
const Declaration declarations[] = {
|
||||
{ "child", parse_node, clear_node, &child },
|
||||
{ "path", parse_path, clear_path, &path },
|
||||
{ "line-width", parse_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 },
|
||||
{ "dash", parse_dash, clear_dash, &dash },
|
||||
{ "dash-offset", parse_double, NULL, &dash_offset},
|
||||
};
|
||||
GskRenderNode *result;
|
||||
|
||||
parse_declarations (parser, context, declarations, G_N_ELEMENTS (declarations));
|
||||
if (child == NULL)
|
||||
child = create_default_render_node ();
|
||||
if (path == NULL)
|
||||
path = create_default_path ();
|
||||
|
||||
stroke = gsk_stroke_new (line_width);
|
||||
gsk_stroke_set_line_cap (stroke, line_cap);
|
||||
gsk_stroke_set_line_join (stroke, line_join);
|
||||
gsk_stroke_set_miter_limit (stroke, miter_limit);
|
||||
if (dash)
|
||||
{
|
||||
gsk_stroke_set_dash (stroke, (float *) dash->data, dash->len);
|
||||
g_array_free (dash, TRUE);
|
||||
}
|
||||
gsk_stroke_set_dash_offset (stroke, dash_offset);
|
||||
|
||||
result = gsk_stroke_node_new (child, path, stroke);
|
||||
|
||||
gsk_path_unref (path);
|
||||
gsk_stroke_free (stroke);
|
||||
gsk_render_node_unref (child);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static GskRenderNode *
|
||||
parse_shadow_node (GtkCssParser *parser,
|
||||
Context *context)
|
||||
@@ -2179,6 +2420,8 @@ parse_node (GtkCssParser *parser,
|
||||
{ "repeating-linear-gradient", parse_repeating_linear_gradient_node },
|
||||
{ "repeating-radial-gradient", parse_repeating_radial_gradient_node },
|
||||
{ "rounded-clip", parse_rounded_clip_node },
|
||||
{ "fill", parse_fill_node },
|
||||
{ "stroke", parse_stroke_node },
|
||||
{ "shadow", parse_shadow_node },
|
||||
{ "text", parse_text_node },
|
||||
{ "texture", parse_texture_node },
|
||||
@@ -2433,6 +2676,14 @@ printer_init_duplicates_for_node (Printer *printer,
|
||||
printer_init_duplicates_for_node (printer, gsk_debug_node_get_child (node));
|
||||
break;
|
||||
|
||||
case GSK_FILL_NODE:
|
||||
printer_init_duplicates_for_node (printer, gsk_fill_node_get_child (node));
|
||||
break;
|
||||
|
||||
case GSK_STROKE_NODE:
|
||||
printer_init_duplicates_for_node (printer, gsk_stroke_node_get_child (node));
|
||||
break;
|
||||
|
||||
case GSK_BLEND_NODE:
|
||||
printer_init_duplicates_for_node (printer, gsk_blend_node_get_bottom_child (node));
|
||||
printer_init_duplicates_for_node (printer, gsk_blend_node_get_top_child (node));
|
||||
@@ -2658,7 +2909,7 @@ append_float_param (Printer *p,
|
||||
float value,
|
||||
float default_value)
|
||||
{
|
||||
/* Don't approximate-compare here, better be topo verbose */
|
||||
/* Don't approximate-compare here, better be too verbose */
|
||||
if (value == default_value)
|
||||
return;
|
||||
|
||||
@@ -2833,8 +3084,11 @@ append_escaping_newlines (GString *str,
|
||||
len = strcspn (string, "\n");
|
||||
g_string_append_len (str, string, len);
|
||||
string += len;
|
||||
g_string_append (str, "\\\n");
|
||||
string++;
|
||||
if (*string)
|
||||
{
|
||||
g_string_append (str, "\\\n");
|
||||
string++;
|
||||
}
|
||||
} while (*string);
|
||||
}
|
||||
|
||||
@@ -3035,6 +3289,83 @@ gsk_text_node_serialize_glyphs (GskRenderNode *node,
|
||||
pango_glyph_string_free (ascii);
|
||||
}
|
||||
|
||||
static const char *
|
||||
enum_to_nick (GType type,
|
||||
int value)
|
||||
{
|
||||
GEnumClass *class;
|
||||
GEnumValue *v;
|
||||
|
||||
class = g_type_class_ref (type);
|
||||
v = g_enum_get_value (class, value);
|
||||
g_type_class_unref (class);
|
||||
|
||||
return v->value_nick;
|
||||
}
|
||||
|
||||
static void
|
||||
append_enum_param (Printer *p,
|
||||
const char *param_name,
|
||||
GType type,
|
||||
int value)
|
||||
{
|
||||
_indent (p);
|
||||
g_string_append_printf (p->str, "%s: ", param_name);
|
||||
g_string_append (p->str, enum_to_nick (type, value));
|
||||
g_string_append_c (p->str, ';');
|
||||
g_string_append_c (p->str, '\n');
|
||||
}
|
||||
|
||||
static void
|
||||
append_path_param (Printer *p,
|
||||
const char *param_name,
|
||||
GskPath *path)
|
||||
{
|
||||
char *str, *s;
|
||||
|
||||
_indent (p);
|
||||
g_string_append (p->str, "path: \"\\\n");
|
||||
str = gsk_path_to_string (path);
|
||||
/* Put each command on a new line */
|
||||
for (s = str; *s; s++)
|
||||
{
|
||||
if (*s == ' ' &&
|
||||
(s[1] == 'M' || s[1] == 'C' || s[1] == 'Z' || s[1] == 'L' || s[1] == 'O'))
|
||||
*s = '\n';
|
||||
}
|
||||
append_escaping_newlines (p->str, str);
|
||||
g_string_append (p->str, "\";\n");
|
||||
g_free (str);
|
||||
}
|
||||
|
||||
static void
|
||||
append_dash_param (Printer *p,
|
||||
const char *param_name,
|
||||
const float *dash,
|
||||
gsize n_dash)
|
||||
{
|
||||
_indent (p);
|
||||
g_string_append (p->str, "dash: ");
|
||||
|
||||
if (n_dash == 0)
|
||||
{
|
||||
g_string_append (p->str, "none");
|
||||
}
|
||||
else
|
||||
{
|
||||
gsize i;
|
||||
|
||||
string_append_double (p->str, dash[0]);
|
||||
for (i = 1; i < n_dash; i++)
|
||||
{
|
||||
g_string_append_c (p->str, ' ');
|
||||
string_append_double (p->str, dash[i]);
|
||||
}
|
||||
}
|
||||
|
||||
g_string_append (p->str, ";\n");
|
||||
}
|
||||
|
||||
static void
|
||||
render_node_print (Printer *p,
|
||||
GskRenderNode *node)
|
||||
@@ -3204,6 +3535,42 @@ render_node_print (Printer *p,
|
||||
append_rounded_rect_param (p, "clip", gsk_rounded_clip_node_get_clip (node));
|
||||
append_node_param (p, "child", gsk_rounded_clip_node_get_child (node));
|
||||
|
||||
end_node (p);
|
||||
}
|
||||
break;
|
||||
|
||||
case GSK_FILL_NODE:
|
||||
{
|
||||
start_node (p, "fill", node_name);
|
||||
|
||||
append_node_param (p, "child", gsk_fill_node_get_child (node));
|
||||
append_path_param (p, "path", gsk_fill_node_get_path (node));
|
||||
append_enum_param (p, "fill-rule", GSK_TYPE_FILL_RULE, gsk_fill_node_get_fill_rule (node));
|
||||
|
||||
end_node (p);
|
||||
}
|
||||
break;
|
||||
|
||||
case GSK_STROKE_NODE:
|
||||
{
|
||||
const GskStroke *stroke;
|
||||
const float *dash;
|
||||
gsize n_dash;
|
||||
|
||||
start_node (p, "stroke", node_name);
|
||||
|
||||
append_node_param (p, "child", gsk_stroke_node_get_child (node));
|
||||
append_path_param (p, "path", gsk_stroke_node_get_path (node));
|
||||
|
||||
stroke = gsk_stroke_node_get_stroke (node);
|
||||
append_float_param (p, "line-width", gsk_stroke_get_line_width (stroke), 0.0f);
|
||||
append_enum_param (p, "line-cap", GSK_TYPE_LINE_CAP, gsk_stroke_get_line_cap (stroke));
|
||||
append_enum_param (p, "line-join", GSK_TYPE_LINE_JOIN, gsk_stroke_get_line_join (stroke));
|
||||
append_float_param (p, "miter-limit", gsk_stroke_get_miter_limit (stroke), 4.0f);
|
||||
dash = gsk_stroke_get_dash (stroke, &n_dash);
|
||||
if (dash)
|
||||
append_dash_param (p, "dash", dash, n_dash);
|
||||
append_float_param (p, "dash-offset", gsk_stroke_get_dash_offset (stroke), 0.0f);
|
||||
|
||||
end_node (p);
|
||||
}
|
||||
|
||||
208
gsk/gskspline.c
Normal file
208
gsk/gskspline.c
Normal file
@@ -0,0 +1,208 @@
|
||||
/*
|
||||
* Copyright © 2002 University of Southern California
|
||||
* 2020 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
* Carl D. Worth <cworth@cworth.org>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gsksplineprivate.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
/* Spline deviation from the circle in radius would be given by:
|
||||
|
||||
error = sqrt (x**2 + y**2) - 1
|
||||
|
||||
A simpler error function to work with is:
|
||||
|
||||
e = x**2 + y**2 - 1
|
||||
|
||||
From "Good approximation of circles by curvature-continuous Bezier
|
||||
curves", Tor Dokken and Morten Daehlen, Computer Aided Geometric
|
||||
Design 8 (1990) 22-41, we learn:
|
||||
|
||||
abs (max(e)) = 4/27 * sin**6(angle/4) / cos**2(angle/4)
|
||||
|
||||
and
|
||||
abs (error) =~ 1/2 * e
|
||||
|
||||
Of course, this error value applies only for the particular spline
|
||||
approximation that is used in _cairo_gstate_arc_segment.
|
||||
*/
|
||||
static float
|
||||
arc_error_normalized (float angle)
|
||||
{
|
||||
return 2.0/27.0 * pow (sin (angle / 4), 6) / pow (cos (angle / 4), 2);
|
||||
}
|
||||
|
||||
static float
|
||||
arc_max_angle_for_tolerance_normalized (float tolerance)
|
||||
{
|
||||
float angle, error;
|
||||
guint i;
|
||||
|
||||
/* Use table lookup to reduce search time in most cases. */
|
||||
struct {
|
||||
float angle;
|
||||
float error;
|
||||
} table[] = {
|
||||
{ G_PI / 1.0, 0.0185185185185185036127 },
|
||||
{ G_PI / 2.0, 0.000272567143730179811158 },
|
||||
{ G_PI / 3.0, 2.38647043651461047433e-05 },
|
||||
{ G_PI / 4.0, 4.2455377443222443279e-06 },
|
||||
{ G_PI / 5.0, 1.11281001494389081528e-06 },
|
||||
{ G_PI / 6.0, 3.72662000942734705475e-07 },
|
||||
{ G_PI / 7.0, 1.47783685574284411325e-07 },
|
||||
{ G_PI / 8.0, 6.63240432022601149057e-08 },
|
||||
{ G_PI / 9.0, 3.2715520137536980553e-08 },
|
||||
{ G_PI / 10.0, 1.73863223499021216974e-08 },
|
||||
{ G_PI / 11.0, 9.81410988043554039085e-09 },
|
||||
};
|
||||
|
||||
for (i = 0; i < G_N_ELEMENTS (table); i++)
|
||||
{
|
||||
if (table[i].error < tolerance)
|
||||
return table[i].angle;
|
||||
}
|
||||
|
||||
i++;
|
||||
do {
|
||||
angle = G_PI / i++;
|
||||
error = arc_error_normalized (angle);
|
||||
} while (error > tolerance);
|
||||
|
||||
return angle;
|
||||
}
|
||||
|
||||
static guint
|
||||
arc_segments_needed (float angle,
|
||||
float radius,
|
||||
float tolerance)
|
||||
{
|
||||
float max_angle;
|
||||
|
||||
/* the error is amplified by at most the length of the
|
||||
* major axis of the circle; see cairo-pen.c for a more detailed analysis
|
||||
* of this. */
|
||||
max_angle = arc_max_angle_for_tolerance_normalized (tolerance / radius);
|
||||
|
||||
return ceil (fabs (angle) / max_angle);
|
||||
}
|
||||
|
||||
/* We want to draw a single spline approximating a circular arc radius
|
||||
R from angle A to angle B. Since we want a symmetric spline that
|
||||
matches the endpoints of the arc in position and slope, we know
|
||||
that the spline control points must be:
|
||||
|
||||
(R * cos(A), R * sin(A))
|
||||
(R * cos(A) - h * sin(A), R * sin(A) + h * cos (A))
|
||||
(R * cos(B) + h * sin(B), R * sin(B) - h * cos (B))
|
||||
(R * cos(B), R * sin(B))
|
||||
|
||||
for some value of h.
|
||||
|
||||
"Approximation of circular arcs by cubic polynomials", Michael
|
||||
Goldapp, Computer Aided Geometric Design 8 (1991) 227-238, provides
|
||||
various values of h along with error analysis for each.
|
||||
|
||||
From that paper, a very practical value of h is:
|
||||
|
||||
h = 4/3 * R * tan(angle/4)
|
||||
|
||||
This value does not give the spline with minimal error, but it does
|
||||
provide a very good approximation, (6th-order convergence), and the
|
||||
error expression is quite simple, (see the comment for
|
||||
_arc_error_normalized).
|
||||
*/
|
||||
static gboolean
|
||||
gsk_spline_decompose_arc_segment (const graphene_point_t *center,
|
||||
float radius,
|
||||
float angle_A,
|
||||
float angle_B,
|
||||
GskSplineAddCurveFunc curve_func,
|
||||
gpointer user_data)
|
||||
{
|
||||
float r_sin_A, r_cos_A;
|
||||
float r_sin_B, r_cos_B;
|
||||
float h;
|
||||
|
||||
r_sin_A = radius * sin (angle_A);
|
||||
r_cos_A = radius * cos (angle_A);
|
||||
r_sin_B = radius * sin (angle_B);
|
||||
r_cos_B = radius * cos (angle_B);
|
||||
|
||||
h = 4.0/3.0 * tan ((angle_B - angle_A) / 4.0);
|
||||
|
||||
return curve_func ((graphene_point_t[4]) {
|
||||
GRAPHENE_POINT_INIT (
|
||||
center->x + r_cos_A,
|
||||
center->y + r_sin_A
|
||||
),
|
||||
GRAPHENE_POINT_INIT (
|
||||
center->x + r_cos_A - h * r_sin_A,
|
||||
center->y + r_sin_A + h * r_cos_A
|
||||
),
|
||||
GRAPHENE_POINT_INIT (
|
||||
center->x + r_cos_B + h * r_sin_B,
|
||||
center->y + r_sin_B - h * r_cos_B
|
||||
),
|
||||
GRAPHENE_POINT_INIT (
|
||||
center->x + r_cos_B,
|
||||
center->y + r_sin_B
|
||||
)
|
||||
},
|
||||
user_data);
|
||||
}
|
||||
|
||||
gboolean
|
||||
gsk_spline_decompose_arc (const graphene_point_t *center,
|
||||
float radius,
|
||||
float tolerance,
|
||||
float start_angle,
|
||||
float end_angle,
|
||||
GskSplineAddCurveFunc curve_func,
|
||||
gpointer user_data)
|
||||
{
|
||||
float step = start_angle - end_angle;
|
||||
guint i, n_segments;
|
||||
|
||||
/* Recurse if drawing arc larger than pi */
|
||||
if (ABS (step) > G_PI)
|
||||
{
|
||||
float mid_angle = (start_angle + end_angle) / 2.0;
|
||||
|
||||
return gsk_spline_decompose_arc (center, radius, tolerance, start_angle, mid_angle, curve_func, user_data)
|
||||
&& gsk_spline_decompose_arc (center, radius, tolerance, mid_angle, end_angle, curve_func, user_data);
|
||||
}
|
||||
else if (ABS (step) < tolerance)
|
||||
{
|
||||
return gsk_spline_decompose_arc_segment (center, radius, start_angle, end_angle, curve_func, user_data);
|
||||
}
|
||||
|
||||
n_segments = arc_segments_needed (ABS (step), radius, tolerance);
|
||||
step = (end_angle - start_angle) / n_segments;
|
||||
|
||||
for (i = 0; i < n_segments - 1; i++, start_angle += step)
|
||||
{
|
||||
if (!gsk_spline_decompose_arc_segment (center, radius, start_angle, start_angle + step, curve_func, user_data))
|
||||
return FALSE;
|
||||
}
|
||||
return gsk_spline_decompose_arc_segment (center, radius, start_angle, end_angle, curve_func, user_data);
|
||||
}
|
||||
|
||||
41
gsk/gsksplineprivate.h
Normal file
41
gsk/gsksplineprivate.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright © 2020 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __GSK_SPLINE_PRIVATE_H__
|
||||
#define __GSK_SPLINE_PRIVATE_H__
|
||||
|
||||
#include "gskpath.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef gboolean (* GskSplineAddCurveFunc) (const graphene_point_t curve[4],
|
||||
gpointer user_data);
|
||||
gboolean gsk_spline_decompose_arc (const graphene_point_t *center,
|
||||
float radius,
|
||||
float tolerance,
|
||||
float start_angle,
|
||||
float end_angle,
|
||||
GskSplineAddCurveFunc curve_func,
|
||||
gpointer user_data);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GSK_SPLINE_PRIVATE_H__ */
|
||||
|
||||
441
gsk/gskstroke.c
Normal file
441
gsk/gskstroke.c
Normal file
@@ -0,0 +1,441 @@
|
||||
/*
|
||||
* Copyright © 2020 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gskstrokeprivate.h"
|
||||
|
||||
/**
|
||||
* SECTION:gskstroke
|
||||
* @Title: Stroke
|
||||
* @Short_description: Properties of a stroke operation
|
||||
* @See_also: #GskPath, gsk_stroke_node_new()
|
||||
*
|
||||
* This section describes the #GskStroke structure that is used to
|
||||
* describe lines and curves that are more complex than simple rectangles.
|
||||
*
|
||||
* #GskStroke is an immutable struct. After creation, you cannot change
|
||||
* the types it represents. Instead, new #GskStroke have to be created.
|
||||
* The #GskStrokeBuilder structure is meant to help in this endeavor.
|
||||
*/
|
||||
|
||||
/**
|
||||
* GskStroke:
|
||||
*
|
||||
* A #GskStroke struct is an opaque struct that should be copied
|
||||
* on use.
|
||||
*/
|
||||
|
||||
G_DEFINE_BOXED_TYPE (GskStroke, gsk_stroke,
|
||||
gsk_stroke_copy,
|
||||
gsk_stroke_free)
|
||||
|
||||
|
||||
/**
|
||||
* gsk_stroke_new:
|
||||
* @line_width: line width of the stroke. Must be > 0
|
||||
*
|
||||
* Creates a new #GskStroke with the given @line_width.
|
||||
*
|
||||
* Returns: a new #GskStroke
|
||||
**/
|
||||
GskStroke *
|
||||
gsk_stroke_new (float line_width)
|
||||
{
|
||||
GskStroke *self;
|
||||
|
||||
g_return_val_if_fail (line_width > 0, NULL);
|
||||
|
||||
self = g_new0 (GskStroke, 1);
|
||||
|
||||
self->line_width = line_width;
|
||||
self->line_cap = GSK_LINE_CAP_BUTT;
|
||||
self->line_join = GSK_LINE_JOIN_MITER;
|
||||
self->miter_limit = 4.f; /* following svg */
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_stroke_copy:
|
||||
* @other: #GskStroke to copy
|
||||
*
|
||||
* Creates a copy of the given @other stroke.
|
||||
*
|
||||
* Returns: a new #GskStroke. Use gsk_stroke_free() to free it.
|
||||
**/
|
||||
GskStroke *
|
||||
gsk_stroke_copy (const GskStroke *other)
|
||||
{
|
||||
GskStroke *self;
|
||||
|
||||
g_return_val_if_fail (other != NULL, NULL);
|
||||
|
||||
self = g_new (GskStroke, 1);
|
||||
|
||||
gsk_stroke_init_copy (self, other);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_stroke_free:
|
||||
* @self: a #GskStroke
|
||||
*
|
||||
* Frees a #GskStroke.
|
||||
**/
|
||||
void
|
||||
gsk_stroke_free (GskStroke *self)
|
||||
{
|
||||
if (self == NULL)
|
||||
return;
|
||||
|
||||
gsk_stroke_clear (self);
|
||||
|
||||
g_free (self);
|
||||
}
|
||||
|
||||
void
|
||||
gsk_stroke_to_cairo (const GskStroke *self,
|
||||
cairo_t *cr)
|
||||
{
|
||||
cairo_set_line_width (cr, self->line_width);
|
||||
|
||||
/* gcc can optimize that to a direct case. This catches later additions to the enum */
|
||||
switch (self->line_cap)
|
||||
{
|
||||
case GSK_LINE_CAP_BUTT:
|
||||
cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
|
||||
break;
|
||||
case GSK_LINE_CAP_ROUND:
|
||||
cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
|
||||
break;
|
||||
case GSK_LINE_CAP_SQUARE:
|
||||
cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
break;
|
||||
}
|
||||
|
||||
/* gcc can optimize that to a direct case. This catches later additions to the enum */
|
||||
switch (self->line_join)
|
||||
{
|
||||
case GSK_LINE_JOIN_MITER:
|
||||
case GSK_LINE_JOIN_MITER_CLIP:
|
||||
cairo_set_line_join (cr, CAIRO_LINE_JOIN_MITER);
|
||||
break;
|
||||
case GSK_LINE_JOIN_ROUND:
|
||||
cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
|
||||
break;
|
||||
case GSK_LINE_JOIN_BEVEL:
|
||||
cairo_set_line_join (cr, CAIRO_LINE_JOIN_BEVEL);
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
break;
|
||||
}
|
||||
|
||||
cairo_set_miter_limit (cr, self->miter_limit);
|
||||
|
||||
if (self->dash_length)
|
||||
{
|
||||
gsize i;
|
||||
double *dash = g_newa (double, self->n_dash);
|
||||
|
||||
for (i = 0; i < self->n_dash; i++)
|
||||
{
|
||||
dash[i] = self->dash[i];
|
||||
}
|
||||
cairo_set_dash (cr, dash, self->n_dash, self->dash_offset);
|
||||
}
|
||||
else
|
||||
cairo_set_dash (cr, NULL, 0, 0.0);
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_stroke_equal:
|
||||
* @stroke1: the first #GskStroke
|
||||
* @stroke2: the second #GskStroke
|
||||
*
|
||||
* Checks if 2 strokes are identical.
|
||||
*
|
||||
* Returns: %TRUE if the 2 strokes are equal, %FALSE otherwise
|
||||
**/
|
||||
gboolean
|
||||
gsk_stroke_equal (gconstpointer stroke1,
|
||||
gconstpointer stroke2)
|
||||
{
|
||||
const GskStroke *self1 = stroke1;
|
||||
const GskStroke *self2 = stroke2;
|
||||
|
||||
return self1->line_width == self2->line_width;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_stroke_set_line_width:
|
||||
* @self: a #GskStroke
|
||||
* @line_width: width of the line in pixels
|
||||
*
|
||||
* Sets the line width to be used when stroking. The line width
|
||||
* must be > 0.
|
||||
**/
|
||||
void
|
||||
gsk_stroke_set_line_width (GskStroke *self,
|
||||
float line_width)
|
||||
{
|
||||
g_return_if_fail (self != NULL);
|
||||
g_return_if_fail (line_width > 0);
|
||||
|
||||
self->line_width = line_width;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_stroke_get_line_width:
|
||||
* @self: a #GskStroke
|
||||
*
|
||||
* Gets the line width used.
|
||||
*
|
||||
* Returns: The line width
|
||||
**/
|
||||
float
|
||||
gsk_stroke_get_line_width (const GskStroke *self)
|
||||
{
|
||||
g_return_val_if_fail (self != NULL, 0.0);
|
||||
|
||||
return self->line_width;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_stroke_set_line_cap:
|
||||
* @self: a #GskStroke
|
||||
* @line_cap: the #GskLineCap
|
||||
*
|
||||
* Sets the line cap to be used when stroking.
|
||||
* See #GskLineCap for details.
|
||||
**/
|
||||
void
|
||||
gsk_stroke_set_line_cap (GskStroke *self,
|
||||
GskLineCap line_cap)
|
||||
{
|
||||
g_return_if_fail (self != NULL);
|
||||
|
||||
self->line_cap = line_cap;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_stroke_get_line_cap:
|
||||
* @self: a #GskStroke
|
||||
*
|
||||
* Gets the line cap used. See #GskLineCap for details.
|
||||
*
|
||||
* Returns: The line cap
|
||||
**/
|
||||
GskLineCap
|
||||
gsk_stroke_get_line_cap (const GskStroke *self)
|
||||
{
|
||||
g_return_val_if_fail (self != NULL, 0.0);
|
||||
|
||||
return self->line_cap;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_stroke_set_line_join:
|
||||
* @self: a #GskStroke
|
||||
* @line_join: The line join to use
|
||||
*
|
||||
* Sets the line join to be used when stroking.
|
||||
* See #GskLineJoin for details.
|
||||
**/
|
||||
void
|
||||
gsk_stroke_set_line_join (GskStroke *self,
|
||||
GskLineJoin line_join)
|
||||
{
|
||||
g_return_if_fail (self != NULL);
|
||||
|
||||
self->line_join = line_join;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_stroke_get_line_join:
|
||||
* @self: a #GskStroke
|
||||
*
|
||||
* Gets the line join used. See #GskLineJoin for details.
|
||||
*
|
||||
* Returns: The line join
|
||||
**/
|
||||
GskLineJoin
|
||||
gsk_stroke_get_line_join (const GskStroke *self)
|
||||
{
|
||||
g_return_val_if_fail (self != NULL, 0.0);
|
||||
|
||||
return self->line_join;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_stroke_set_miter_limit:
|
||||
* @self: a #GskStroke
|
||||
* @limit: the miter limit, must be non-negative
|
||||
*
|
||||
* Sets the limit for the distance from the corner where sharp
|
||||
* turns of joins get cut off. The miter limit is in units of
|
||||
* line width.
|
||||
*
|
||||
* For joins of type %GSK_LINE_JOIN_MITER that exceed the miter
|
||||
* limit, the join gets rendered as if it was of type
|
||||
* %GSK_LINE_JOIN_BEVEL. For joins of type %GSK_LINE_JOIN_MITER_CLIP,
|
||||
* the miter is clipped at a distance of half the miter limit.
|
||||
*/
|
||||
void
|
||||
gsk_stroke_set_miter_limit (GskStroke *self,
|
||||
float limit)
|
||||
{
|
||||
g_return_if_fail (self != NULL);
|
||||
g_return_if_fail (limit >= 0);
|
||||
|
||||
self->miter_limit = limit;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_stroke_get_miter_limit:
|
||||
* @self: a #GskStroke
|
||||
*
|
||||
* Returns the miter limit of a #GskStroke.
|
||||
*/
|
||||
float
|
||||
gsk_stroke_get_miter_limit (const GskStroke *self)
|
||||
{
|
||||
g_return_val_if_fail (self != NULL, 4.f);
|
||||
|
||||
return self->miter_limit;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_stroke_set_dash:
|
||||
* @self: a #GskStroke
|
||||
* @dash: (array length=n_dash) (transfer none) (nullable):
|
||||
* the array of dashes
|
||||
* @n_dash: number of elements in @dash
|
||||
*
|
||||
* Sets the dash pattern to use by this stroke. A dash pattern is specified by
|
||||
* an array of alternating non-negative values. Each value provides the length
|
||||
* of alternate "on" and "off" portions of the stroke.
|
||||
*
|
||||
* Each "on" segment will have caps applied as if the segment were a separate
|
||||
* contour. In particular, it is valid to use an "on" length of 0 with
|
||||
* @GSK_LINE_CAP_ROUND or @GSK_LINE_CAP_SQUARE to draw dots or squares along
|
||||
* a path.
|
||||
*
|
||||
* If @n_dash is 0, if all elements in @dash are 0, or if there are negative
|
||||
* values in @dash, then dashing is disabled.
|
||||
*
|
||||
* If @n_dash is 1, an alternating "on" and "off" pattern with the single
|
||||
* dash length provided is assumed.
|
||||
*
|
||||
* If @n_dash is uneven, the dash array will be used with the first element
|
||||
* in @dash defining an "on" or "off" in alternating passes through the array.
|
||||
*
|
||||
* You can specify a starting offset into the dash with
|
||||
* @gsk_stroke_set_dash_offset().
|
||||
**/
|
||||
void
|
||||
gsk_stroke_set_dash (GskStroke *self,
|
||||
const float *dash,
|
||||
gsize n_dash)
|
||||
{
|
||||
float dash_length;
|
||||
gsize i;
|
||||
|
||||
g_return_if_fail (self != NULL);
|
||||
g_return_if_fail (dash != NULL || n_dash == 0);
|
||||
|
||||
dash_length = 0;
|
||||
for (i = 0; i < n_dash; i++)
|
||||
{
|
||||
if (!(dash[i] >= 0)) /* should catch NaN */
|
||||
{
|
||||
g_critical ("invalid value in dash array at position %zu", i);
|
||||
return;
|
||||
}
|
||||
dash_length += dash[i];
|
||||
}
|
||||
|
||||
self->dash_length = dash_length;
|
||||
g_free (self->dash);
|
||||
self->dash = g_memdup (dash, sizeof (gfloat) * n_dash);
|
||||
self->n_dash = n_dash;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_stroke_get_dash:
|
||||
* @self: a #GskStroke
|
||||
* @n_dash: (out caller-allocates): number of elements
|
||||
* in the array returned
|
||||
*
|
||||
* Gets the dash array in use or %NULL if dashing is disabled.
|
||||
*
|
||||
* Returns: (array length=n_dash) (transfer none) (nullable):
|
||||
* The dash array or %NULL if the dash array is empty.
|
||||
**/
|
||||
const float *
|
||||
gsk_stroke_get_dash (const GskStroke *self,
|
||||
gsize *n_dash)
|
||||
{
|
||||
g_return_val_if_fail (self != NULL, NULL);
|
||||
g_return_val_if_fail (n_dash != NULL, NULL);
|
||||
|
||||
*n_dash = self->n_dash;
|
||||
|
||||
return self->dash;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_stroke_set_dash_offset:
|
||||
* @self: a #GskStroke
|
||||
* @offset: offset into the dash pattern
|
||||
*
|
||||
* Sets the offset into the dash pattern set via gsk_stroke_set_dash() where
|
||||
* dashing should begin.
|
||||
*
|
||||
* This is an offset into the length of the path, not an index into the array values of
|
||||
* the dash array.
|
||||
**/
|
||||
void
|
||||
gsk_stroke_set_dash_offset (GskStroke *self,
|
||||
float offset)
|
||||
{
|
||||
g_return_if_fail (self != NULL);
|
||||
|
||||
self->dash_offset = offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_stroke_get_dash_offset:
|
||||
* @self: a #GskStroke
|
||||
*
|
||||
* Returns the dash_offset of a #GskStroke.
|
||||
*/
|
||||
float
|
||||
gsk_stroke_get_dash_offset (const GskStroke *self)
|
||||
{
|
||||
g_return_val_if_fail (self != NULL, 4.f);
|
||||
|
||||
return self->dash_offset;
|
||||
}
|
||||
87
gsk/gskstroke.h
Normal file
87
gsk/gskstroke.h
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright © 2020 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __GSK_STROKE_H__
|
||||
#define __GSK_STROKE_H__
|
||||
|
||||
#if !defined (__GSK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gsk/gsk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
|
||||
#include <gsk/gsktypes.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GSK_TYPE_STROKE (gsk_stroke_get_type ())
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GType gsk_stroke_get_type (void) G_GNUC_CONST;
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskStroke * gsk_stroke_new (float line_width);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskStroke * gsk_stroke_copy (const GskStroke *other);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_stroke_free (GskStroke *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gsk_stroke_equal (gconstpointer stroke1,
|
||||
gconstpointer stroke2);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_stroke_set_line_width (GskStroke *self,
|
||||
float line_width);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
float gsk_stroke_get_line_width (const GskStroke *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_stroke_set_line_cap (GskStroke *self,
|
||||
GskLineCap line_cap);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskLineCap gsk_stroke_get_line_cap (const GskStroke *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_stroke_set_line_join (GskStroke *self,
|
||||
GskLineJoin line_join);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskLineJoin gsk_stroke_get_line_join (const GskStroke *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_stroke_set_miter_limit (GskStroke *self,
|
||||
float limit);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
float gsk_stroke_get_miter_limit (const GskStroke *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_stroke_set_dash (GskStroke *self,
|
||||
const float *dash,
|
||||
gsize n_dash);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
const float * gsk_stroke_get_dash (const GskStroke *self,
|
||||
gsize *n_dash);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_stroke_set_dash_offset (GskStroke *self,
|
||||
float offset);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
float gsk_stroke_get_dash_offset (const GskStroke *self);
|
||||
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GSK_STROKE_H__ */
|
||||
63
gsk/gskstrokeprivate.h
Normal file
63
gsk/gskstrokeprivate.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright © 2020 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __GSK_STROKE_PRIVATE_H__
|
||||
#define __GSK_STROKE_PRIVATE_H__
|
||||
|
||||
#include "gskstroke.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
struct _GskStroke
|
||||
{
|
||||
float line_width;
|
||||
GskLineCap line_cap;
|
||||
GskLineJoin line_join;
|
||||
float miter_limit;
|
||||
|
||||
float *dash;
|
||||
gsize n_dash;
|
||||
float dash_length; /* sum of all dashes in the array */
|
||||
float dash_offset;
|
||||
};
|
||||
|
||||
static inline void
|
||||
gsk_stroke_init_copy (GskStroke *stroke,
|
||||
const GskStroke *other)
|
||||
{
|
||||
*stroke = *other;
|
||||
|
||||
stroke->dash = g_memdup (other->dash, stroke->n_dash * sizeof (float));
|
||||
}
|
||||
|
||||
static inline void
|
||||
gsk_stroke_clear (GskStroke *stroke)
|
||||
{
|
||||
g_clear_pointer (&stroke->dash, g_free);
|
||||
stroke->n_dash = 0; /* better safe than sorry */
|
||||
}
|
||||
|
||||
void gsk_stroke_to_cairo (const GskStroke *self,
|
||||
cairo_t *cr);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GSK_STROKE_PRIVATE_H__ */
|
||||
|
||||
@@ -25,6 +25,10 @@
|
||||
#include <gdk/gdk.h>
|
||||
#include <gsk/gskenums.h>
|
||||
|
||||
typedef struct _GskPath GskPath;
|
||||
typedef struct _GskPathBuilder GskPathBuilder;
|
||||
typedef struct _GskPathMeasure GskPathMeasure;
|
||||
typedef struct _GskRenderer GskRenderer;
|
||||
typedef struct _GskStroke GskStroke;
|
||||
typedef struct _GskTransform GskTransform;
|
||||
|
||||
|
||||
@@ -23,23 +23,31 @@ gsk_private_gl_shaders = [
|
||||
]
|
||||
|
||||
gsk_public_sources = files([
|
||||
'gskdiff.c',
|
||||
'gskcairorenderer.c',
|
||||
'gskdiff.c',
|
||||
'gskglshader.c',
|
||||
'gskpath.c',
|
||||
'gskpathbuilder.c',
|
||||
'gskpathdash.c',
|
||||
'gskpathmeasure.c',
|
||||
'gskrenderer.c',
|
||||
'gskrendernode.c',
|
||||
'gskrendernodeimpl.c',
|
||||
'gskrendernodeparser.c',
|
||||
'gskroundedrect.c',
|
||||
'gskstroke.c',
|
||||
'gsktransform.c',
|
||||
'gl/gskglrenderer.c',
|
||||
])
|
||||
|
||||
gsk_private_sources = files([
|
||||
'gskcairoblur.c',
|
||||
'gskcontour.c',
|
||||
'gskcurve.c',
|
||||
'gskdebug.c',
|
||||
'gskprivate.c',
|
||||
'gskprofiler.c',
|
||||
'gskspline.c',
|
||||
'gl/gskglattachmentstate.c',
|
||||
'gl/gskglbuffer.c',
|
||||
'gl/gskglcommandqueue.c',
|
||||
@@ -66,9 +74,13 @@ gsk_public_headers = files([
|
||||
'gskcairorenderer.h',
|
||||
'gskenums.h',
|
||||
'gskglshader.h',
|
||||
'gskpath.h',
|
||||
'gskpathbuilder.h',
|
||||
'gskpathmeasure.h',
|
||||
'gskrenderer.h',
|
||||
'gskrendernode.h',
|
||||
'gskroundedrect.h',
|
||||
'gskstroke.h',
|
||||
'gsktransform.h',
|
||||
'gsktypes.h',
|
||||
])
|
||||
@@ -118,7 +130,9 @@ if have_vulkan
|
||||
'vulkan/gskvulkancommandpool.c',
|
||||
'vulkan/gskvulkanconvertop.c',
|
||||
'vulkan/gskvulkancrossfadeop.c',
|
||||
'vulkan/gskvulkandevice.c',
|
||||
'vulkan/gskvulkandownloadop.c',
|
||||
'vulkan/gskvulkanfillop.c',
|
||||
'vulkan/gskvulkanglyphcache.c',
|
||||
'vulkan/gskvulkanglyphop.c',
|
||||
'vulkan/gskvulkanimage.c',
|
||||
@@ -135,6 +149,7 @@ if have_vulkan
|
||||
'vulkan/gskvulkanrenderpassop.c',
|
||||
'vulkan/gskvulkanscissorop.c',
|
||||
'vulkan/gskvulkanshaderop.c',
|
||||
'vulkan/gskvulkanstrokeop.c',
|
||||
'vulkan/gskvulkantextureop.c',
|
||||
'vulkan/gskvulkanuploadop.c',
|
||||
])
|
||||
|
||||
@@ -7,17 +7,16 @@
|
||||
|
||||
struct _GskVulkanBuffer
|
||||
{
|
||||
GdkVulkanContext *vulkan;
|
||||
|
||||
gsize size;
|
||||
GskVulkanDevice *device;
|
||||
|
||||
VkBuffer vk_buffer;
|
||||
|
||||
GskVulkanMemory *memory;
|
||||
GskVulkanAllocator *allocator;
|
||||
GskVulkanAllocation allocation;
|
||||
};
|
||||
|
||||
static GskVulkanBuffer *
|
||||
gsk_vulkan_buffer_new_internal (GdkVulkanContext *context,
|
||||
gsk_vulkan_buffer_new_internal (GskVulkanDevice *device,
|
||||
gsize size,
|
||||
VkBufferUsageFlags usage)
|
||||
{
|
||||
@@ -26,10 +25,9 @@ gsk_vulkan_buffer_new_internal (GdkVulkanContext *context,
|
||||
|
||||
self = g_new0 (GskVulkanBuffer, 1);
|
||||
|
||||
self->vulkan = g_object_ref (context);
|
||||
self->size = size;
|
||||
self->device = g_object_ref (device);
|
||||
|
||||
GSK_VK_CHECK (vkCreateBuffer, gdk_vulkan_context_get_device (context),
|
||||
GSK_VK_CHECK (vkCreateBuffer, gsk_vulkan_device_get_vk_device (device),
|
||||
&(VkBufferCreateInfo) {
|
||||
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
||||
.size = size,
|
||||
@@ -40,44 +38,49 @@ gsk_vulkan_buffer_new_internal (GdkVulkanContext *context,
|
||||
NULL,
|
||||
&self->vk_buffer);
|
||||
|
||||
vkGetBufferMemoryRequirements (gdk_vulkan_context_get_device (context),
|
||||
vkGetBufferMemoryRequirements (gsk_vulkan_device_get_vk_device (device),
|
||||
self->vk_buffer,
|
||||
&requirements);
|
||||
|
||||
self->allocator = gsk_vulkan_device_find_allocator (device,
|
||||
requirements.memoryTypeBits,
|
||||
GSK_VULKAN_MEMORY_MAPPABLE,
|
||||
GSK_VULKAN_MEMORY_MAPPABLE);
|
||||
gsk_vulkan_alloc (self->allocator,
|
||||
requirements.size,
|
||||
requirements.alignment,
|
||||
&self->allocation);
|
||||
|
||||
self->memory = gsk_vulkan_memory_new (context,
|
||||
requirements.memoryTypeBits,
|
||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
|
||||
requirements.size);
|
||||
|
||||
GSK_VK_CHECK (vkBindBufferMemory, gdk_vulkan_context_get_device (context),
|
||||
GSK_VK_CHECK (vkBindBufferMemory, gsk_vulkan_device_get_vk_device (device),
|
||||
self->vk_buffer,
|
||||
gsk_vulkan_memory_get_device_memory (self->memory),
|
||||
0);
|
||||
self->allocation.vk_memory,
|
||||
self->allocation.offset);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
GskVulkanBuffer *
|
||||
gsk_vulkan_buffer_new (GdkVulkanContext *context,
|
||||
gsize size)
|
||||
gsk_vulkan_buffer_new (GskVulkanDevice *device,
|
||||
gsize size)
|
||||
{
|
||||
return gsk_vulkan_buffer_new_internal (context, size,
|
||||
return gsk_vulkan_buffer_new_internal (device, size,
|
||||
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT
|
||||
| VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);
|
||||
}
|
||||
|
||||
GskVulkanBuffer *
|
||||
gsk_vulkan_buffer_new_storage (GdkVulkanContext *context,
|
||||
gsize size)
|
||||
gsk_vulkan_buffer_new_storage (GskVulkanDevice *device,
|
||||
gsize size)
|
||||
{
|
||||
return gsk_vulkan_buffer_new_internal (context, size, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT);
|
||||
return gsk_vulkan_buffer_new_internal (device, size, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT);
|
||||
}
|
||||
|
||||
GskVulkanBuffer *
|
||||
gsk_vulkan_buffer_new_map (GdkVulkanContext *context,
|
||||
gsize size,
|
||||
GskVulkanMapMode mode)
|
||||
gsk_vulkan_buffer_new_map (GskVulkanDevice *device,
|
||||
gsize size,
|
||||
GskVulkanMapMode mode)
|
||||
{
|
||||
return gsk_vulkan_buffer_new_internal (context,
|
||||
return gsk_vulkan_buffer_new_internal (device,
|
||||
size,
|
||||
(mode & GSK_VULKAN_READ ? VK_BUFFER_USAGE_TRANSFER_DST_BIT : 0) |
|
||||
(mode & GSK_VULKAN_WRITE ? VK_BUFFER_USAGE_TRANSFER_SRC_BIT : 0));
|
||||
@@ -86,13 +89,13 @@ gsk_vulkan_buffer_new_map (GdkVulkanContext *context,
|
||||
void
|
||||
gsk_vulkan_buffer_free (GskVulkanBuffer *self)
|
||||
{
|
||||
vkDestroyBuffer (gdk_vulkan_context_get_device (self->vulkan),
|
||||
vkDestroyBuffer (gsk_vulkan_device_get_vk_device (self->device),
|
||||
self->vk_buffer,
|
||||
NULL);
|
||||
|
||||
gsk_vulkan_memory_free (self->memory);
|
||||
gsk_vulkan_free (self->allocator, &self->allocation);
|
||||
|
||||
g_object_unref (self->vulkan);
|
||||
g_object_unref (self->device);
|
||||
|
||||
g_free (self);
|
||||
}
|
||||
@@ -106,17 +109,12 @@ gsk_vulkan_buffer_get_buffer (GskVulkanBuffer *self)
|
||||
gsize
|
||||
gsk_vulkan_buffer_get_size (GskVulkanBuffer *self)
|
||||
{
|
||||
return self->size;
|
||||
return self->allocation.size;
|
||||
}
|
||||
|
||||
guchar *
|
||||
gsk_vulkan_buffer_map (GskVulkanBuffer *self)
|
||||
gsk_vulkan_buffer_get_data (GskVulkanBuffer *self)
|
||||
{
|
||||
return gsk_vulkan_memory_map (self->memory);
|
||||
return self->allocation.map;
|
||||
}
|
||||
|
||||
void
|
||||
gsk_vulkan_buffer_unmap (GskVulkanBuffer *self)
|
||||
{
|
||||
gsk_vulkan_memory_unmap (self->memory);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <gdk/gdk.h>
|
||||
#include "gskvulkandeviceprivate.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
@@ -13,20 +13,18 @@ typedef enum
|
||||
GSK_VULKAN_READWRITE = GSK_VULKAN_READ | GSK_VULKAN_WRITE
|
||||
} GskVulkanMapMode;
|
||||
|
||||
GskVulkanBuffer * gsk_vulkan_buffer_new (GdkVulkanContext *context,
|
||||
GskVulkanBuffer * gsk_vulkan_buffer_new (GskVulkanDevice *device,
|
||||
gsize size);
|
||||
GskVulkanBuffer * gsk_vulkan_buffer_new_storage (GdkVulkanContext *context,
|
||||
GskVulkanBuffer * gsk_vulkan_buffer_new_storage (GskVulkanDevice *device,
|
||||
gsize size);
|
||||
GskVulkanBuffer * gsk_vulkan_buffer_new_map (GdkVulkanContext *context,
|
||||
GskVulkanBuffer * gsk_vulkan_buffer_new_map (GskVulkanDevice *device,
|
||||
gsize size,
|
||||
GskVulkanMapMode mode);
|
||||
void gsk_vulkan_buffer_free (GskVulkanBuffer *buffer);
|
||||
|
||||
VkBuffer gsk_vulkan_buffer_get_buffer (GskVulkanBuffer *self);
|
||||
gsize gsk_vulkan_buffer_get_size (GskVulkanBuffer *self);
|
||||
|
||||
guchar * gsk_vulkan_buffer_map (GskVulkanBuffer *self);
|
||||
void gsk_vulkan_buffer_unmap (GskVulkanBuffer *self);
|
||||
guchar * gsk_vulkan_buffer_get_data (GskVulkanBuffer *self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
||||
138
gsk/vulkan/gskvulkandevice.c
Normal file
138
gsk/vulkan/gskvulkandevice.c
Normal file
@@ -0,0 +1,138 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "gskvulkandeviceprivate.h"
|
||||
|
||||
#include "gdk/gdkdisplayprivate.h"
|
||||
#include "gdk/gdkvulkancontextprivate.h"
|
||||
|
||||
struct _GskVulkanDevice
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
GdkDisplay *display;
|
||||
|
||||
GskVulkanAllocator *allocators[VK_MAX_MEMORY_TYPES];
|
||||
};
|
||||
|
||||
struct _GskVulkanDeviceClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GskVulkanDevice, gsk_vulkan_device, G_TYPE_OBJECT)
|
||||
|
||||
static void
|
||||
gsk_vulkan_device_finalize (GObject *object)
|
||||
{
|
||||
GskVulkanDevice *self = GSK_VULKAN_DEVICE (object);
|
||||
gsize i;
|
||||
|
||||
g_object_steal_data (G_OBJECT (self->display), "-gsk-vulkan-device");
|
||||
|
||||
for (i = 0; i < VK_MAX_MEMORY_TYPES; i++)
|
||||
g_clear_pointer (&self->allocators[i], gsk_vulkan_allocator_free);
|
||||
|
||||
gdk_display_unref_vulkan (self->display);
|
||||
g_object_unref (self->display);
|
||||
|
||||
G_OBJECT_CLASS (gsk_vulkan_device_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_device_class_init (GskVulkanDeviceClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->finalize = gsk_vulkan_device_finalize;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_device_init (GskVulkanDevice *self)
|
||||
{
|
||||
}
|
||||
|
||||
GskVulkanDevice *
|
||||
gsk_vulkan_device_get_for_display (GdkDisplay *display,
|
||||
GError **error)
|
||||
{
|
||||
GskVulkanDevice *self;
|
||||
|
||||
self = g_object_get_data (G_OBJECT (display), "-gsk-vulkan-device");
|
||||
if (self)
|
||||
return g_object_ref (self);
|
||||
|
||||
if (!gdk_display_ref_vulkan (display, error))
|
||||
return NULL;
|
||||
|
||||
self = g_object_new (GSK_TYPE_VULKAN_DEVICE, NULL);
|
||||
self->display = display;
|
||||
|
||||
g_object_set_data (G_OBJECT (display), "-gsk-vulkan-device", self);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
VkDevice
|
||||
gsk_vulkan_device_get_vk_device (GskVulkanDevice *self)
|
||||
{
|
||||
return self->display->vk_device;
|
||||
}
|
||||
|
||||
VkPhysicalDevice
|
||||
gsk_vulkan_device_get_vk_physical_device (GskVulkanDevice *self)
|
||||
{
|
||||
return self->display->vk_physical_device;
|
||||
}
|
||||
|
||||
static GskVulkanAllocator *
|
||||
gsk_vulkan_device_get_allocator (GskVulkanDevice *self,
|
||||
gsize index,
|
||||
const VkMemoryType *type)
|
||||
{
|
||||
if (self->allocators[index] == NULL)
|
||||
{
|
||||
self->allocators[index] = gsk_vulkan_direct_allocator_new (gsk_vulkan_device_get_vk_device (self),
|
||||
index,
|
||||
type);
|
||||
self->allocators[index] = gsk_vulkan_buddy_allocator_new (self->allocators[index],
|
||||
1024 * 1024);
|
||||
//allocators[index] = gsk_vulkan_stats_allocator_new (allocators[index]);
|
||||
}
|
||||
|
||||
return self->allocators[index];
|
||||
}
|
||||
|
||||
/* following code found in
|
||||
* https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPhysicalDeviceMemoryProperties.html */
|
||||
GskVulkanAllocator *
|
||||
gsk_vulkan_device_find_allocator (GskVulkanDevice *self,
|
||||
uint32_t allowed_types,
|
||||
VkMemoryPropertyFlags required_flags,
|
||||
VkMemoryPropertyFlags desired_flags)
|
||||
{
|
||||
VkPhysicalDeviceMemoryProperties properties;
|
||||
uint32_t i, found;
|
||||
|
||||
vkGetPhysicalDeviceMemoryProperties (gsk_vulkan_device_get_vk_physical_device (self),
|
||||
&properties);
|
||||
|
||||
found = properties.memoryTypeCount;
|
||||
for (i = 0; i < properties.memoryTypeCount; i++)
|
||||
{
|
||||
if (!(allowed_types & (1 << i)))
|
||||
continue;
|
||||
|
||||
if ((properties.memoryTypes[i].propertyFlags & required_flags) != required_flags)
|
||||
continue;
|
||||
|
||||
found = MIN (i, found);
|
||||
|
||||
if ((properties.memoryTypes[i].propertyFlags & desired_flags) == desired_flags)
|
||||
break;
|
||||
}
|
||||
|
||||
g_assert (found < properties.memoryTypeCount);
|
||||
|
||||
return gsk_vulkan_device_get_allocator (self, i, &properties.memoryTypes[i]);
|
||||
}
|
||||
|
||||
24
gsk/vulkan/gskvulkandeviceprivate.h
Normal file
24
gsk/vulkan/gskvulkandeviceprivate.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "gskvulkanmemoryprivate.h"
|
||||
|
||||
#include <gdk/gdkvulkancontext.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GSK_TYPE_VULKAN_DEVICE (gsk_vulkan_device_get_type ())
|
||||
|
||||
G_DECLARE_FINAL_TYPE(GskVulkanDevice, gsk_vulkan_device, GSK, VULKAN_DEVICE, GObject)
|
||||
|
||||
GskVulkanDevice * gsk_vulkan_device_get_for_display (GdkDisplay *display,
|
||||
GError **error);
|
||||
|
||||
VkDevice gsk_vulkan_device_get_vk_device (GskVulkanDevice *self);
|
||||
VkPhysicalDevice gsk_vulkan_device_get_vk_physical_device (GskVulkanDevice *self);
|
||||
|
||||
GskVulkanAllocator * gsk_vulkan_device_find_allocator (GskVulkanDevice *self,
|
||||
uint32_t allowed_types,
|
||||
VkMemoryPropertyFlags required_flags,
|
||||
VkMemoryPropertyFlags desired_flags);
|
||||
|
||||
G_END_DECLS
|
||||
@@ -36,7 +36,7 @@ gsk_vulkan_download_op_command_with_area (GskVulkanOp *op,
|
||||
gsize stride;
|
||||
|
||||
stride = area->width * gdk_memory_format_bytes_per_pixel (gsk_vulkan_image_get_format (image));
|
||||
*buffer = gsk_vulkan_buffer_new_map (gsk_vulkan_render_get_context (render),
|
||||
*buffer = gsk_vulkan_buffer_new_map (gsk_vulkan_render_get_device (render),
|
||||
area->height * stride,
|
||||
GSK_VULKAN_READ);
|
||||
|
||||
@@ -115,7 +115,7 @@ gsk_vulkan_download_op_finish (GskVulkanOp *op)
|
||||
guchar *data;
|
||||
gsize stride;
|
||||
|
||||
data = gsk_vulkan_buffer_map (self->buffer);
|
||||
data = gsk_vulkan_buffer_get_data (self->buffer);
|
||||
stride = gsk_vulkan_image_get_width (self->image) *
|
||||
gdk_memory_format_bytes_per_pixel (gsk_vulkan_image_get_format (self->image));
|
||||
self->func (self->user_data,
|
||||
@@ -124,7 +124,6 @@ gsk_vulkan_download_op_finish (GskVulkanOp *op)
|
||||
gsk_vulkan_image_get_width (self->image),
|
||||
gsk_vulkan_image_get_height (self->image),
|
||||
stride);
|
||||
gsk_vulkan_buffer_unmap (self->buffer);
|
||||
|
||||
g_object_unref (self->image);
|
||||
g_clear_pointer (&self->buffer, gsk_vulkan_buffer_free);
|
||||
|
||||
129
gsk/vulkan/gskvulkanfillop.c
Normal file
129
gsk/vulkan/gskvulkanfillop.c
Normal file
@@ -0,0 +1,129 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "gskvulkanfillopprivate.h"
|
||||
|
||||
#include "gskvulkanprivate.h"
|
||||
#include "gskvulkanshaderopprivate.h"
|
||||
|
||||
#include "gskpathprivate.h"
|
||||
|
||||
#include "vulkan/resources/fill.vert.h"
|
||||
|
||||
typedef struct _GskVulkanFillOp GskVulkanFillOp;
|
||||
|
||||
struct _GskVulkanFillOp
|
||||
{
|
||||
GskVulkanShaderOp op;
|
||||
|
||||
graphene_point_t offset;
|
||||
graphene_rect_t rect;
|
||||
GskPath *path;
|
||||
GskFillRule fill_rule;
|
||||
GdkRGBA color;
|
||||
|
||||
gsize buffer_offset;
|
||||
};
|
||||
|
||||
static void
|
||||
gsk_vulkan_fill_op_finish (GskVulkanOp *op)
|
||||
{
|
||||
GskVulkanFillOp *self = (GskVulkanFillOp *) op;
|
||||
|
||||
gsk_path_unref (self->path);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_fill_op_print (GskVulkanOp *op,
|
||||
GString *string,
|
||||
guint indent)
|
||||
{
|
||||
GskVulkanFillOp *self = (GskVulkanFillOp *) op;
|
||||
|
||||
print_indent (string, indent);
|
||||
print_rect (string, &self->rect);
|
||||
g_string_append_printf (string, "fill ");
|
||||
print_newline (string);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_fill_op_collect_vertex_data (GskVulkanOp *op,
|
||||
guchar *data)
|
||||
{
|
||||
GskVulkanFillOp *self = (GskVulkanFillOp *) op;
|
||||
GskVulkanFillInstance *instance = (GskVulkanFillInstance *) (data + ((GskVulkanShaderOp *) op)->vertex_offset);
|
||||
|
||||
gsk_vulkan_rect_to_float (&self->rect, instance->rect);
|
||||
gsk_vulkan_rgba_to_float (&self->color, instance->color);
|
||||
gsk_vulkan_point_to_float (&self->offset, instance->offset);
|
||||
instance->points_id = self->buffer_offset;
|
||||
instance->fill_rule = self->fill_rule;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_fill_op_reserve_descriptor_sets (GskVulkanOp *op,
|
||||
GskVulkanRender *render)
|
||||
{
|
||||
GskVulkanFillOp *self = (GskVulkanFillOp *) op;
|
||||
const GskContour *contour;
|
||||
gsize size, i, n;
|
||||
guchar *mem;
|
||||
|
||||
size = sizeof (guint32);
|
||||
n = gsk_path_get_n_contours (self->path);
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
contour = gsk_path_get_contour (self->path, i);
|
||||
size += gsk_contour_get_shader_size (contour);
|
||||
}
|
||||
|
||||
mem = gsk_vulkan_render_get_buffer_memory (render,
|
||||
size,
|
||||
G_ALIGNOF (float),
|
||||
&self->buffer_offset);
|
||||
|
||||
*(guint32*) mem = n;
|
||||
mem += sizeof (guint32);
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
contour = gsk_path_get_contour (self->path, i);
|
||||
gsk_contour_to_shader (contour, mem);
|
||||
mem += gsk_contour_get_shader_size (contour);
|
||||
}
|
||||
}
|
||||
|
||||
static const GskVulkanShaderOpClass GSK_VULKAN_FILL_OP_CLASS = {
|
||||
{
|
||||
GSK_VULKAN_OP_SIZE (GskVulkanFillOp),
|
||||
GSK_VULKAN_STAGE_SHADER,
|
||||
gsk_vulkan_fill_op_finish,
|
||||
gsk_vulkan_fill_op_print,
|
||||
gsk_vulkan_shader_op_count_vertex_data,
|
||||
gsk_vulkan_fill_op_collect_vertex_data,
|
||||
gsk_vulkan_fill_op_reserve_descriptor_sets,
|
||||
gsk_vulkan_shader_op_command
|
||||
},
|
||||
"fill",
|
||||
0,
|
||||
&gsk_vulkan_fill_info,
|
||||
};
|
||||
|
||||
void
|
||||
gsk_vulkan_fill_op (GskVulkanRender *render,
|
||||
GskVulkanShaderClip clip,
|
||||
const graphene_point_t *offset,
|
||||
const graphene_rect_t *rect,
|
||||
GskPath *path,
|
||||
GskFillRule fill_rule,
|
||||
const GdkRGBA *color)
|
||||
{
|
||||
GskVulkanFillOp *self;
|
||||
|
||||
self = (GskVulkanFillOp *) gsk_vulkan_shader_op_alloc (render, &GSK_VULKAN_FILL_OP_CLASS, clip, NULL);
|
||||
|
||||
self->offset = *offset;
|
||||
self->rect = *rect;
|
||||
self->path = gsk_path_ref (path);
|
||||
self->fill_rule = fill_rule;
|
||||
self->color = *color;
|
||||
}
|
||||
17
gsk/vulkan/gskvulkanfillopprivate.h
Normal file
17
gsk/vulkan/gskvulkanfillopprivate.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include "gskvulkanopprivate.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
void gsk_vulkan_fill_op (GskVulkanRender *render,
|
||||
GskVulkanShaderClip clip,
|
||||
const graphene_point_t *offset,
|
||||
const graphene_rect_t *rect,
|
||||
GskPath *path,
|
||||
GskFillRule fill_rule,
|
||||
const GdkRGBA *color);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
@@ -36,8 +36,7 @@ typedef struct {
|
||||
struct _GskVulkanGlyphCache {
|
||||
GObject parent_instance;
|
||||
|
||||
GdkVulkanContext *vulkan;
|
||||
GskRenderer *renderer;
|
||||
GskVulkanDevice *device;
|
||||
|
||||
GHashTable *hash_table;
|
||||
GPtrArray *atlases;
|
||||
@@ -70,7 +69,7 @@ create_atlas (GskVulkanGlyphCache *cache)
|
||||
atlas->x = 0;
|
||||
atlas->image = NULL;
|
||||
|
||||
atlas->image = gsk_vulkan_image_new_for_atlas (cache->vulkan, atlas->width, atlas->height);
|
||||
atlas->image = gsk_vulkan_image_new_for_atlas (cache->device, atlas->width, atlas->height);
|
||||
|
||||
return atlas;
|
||||
}
|
||||
@@ -245,12 +244,12 @@ add_to_cache (GskVulkanGlyphCache *cache,
|
||||
}
|
||||
|
||||
GskVulkanGlyphCache *
|
||||
gsk_vulkan_glyph_cache_new (GdkVulkanContext *vulkan)
|
||||
gsk_vulkan_glyph_cache_new (GskVulkanDevice *device)
|
||||
{
|
||||
GskVulkanGlyphCache *cache;
|
||||
|
||||
cache = GSK_VULKAN_GLYPH_CACHE (g_object_new (GSK_TYPE_VULKAN_GLYPH_CACHE, NULL));
|
||||
cache->vulkan = vulkan;
|
||||
cache->device = device;
|
||||
g_ptr_array_add (cache->atlases, create_atlas (cache));
|
||||
|
||||
return cache;
|
||||
|
||||
@@ -31,7 +31,7 @@ typedef struct
|
||||
guint64 timestamp;
|
||||
} GskVulkanCachedGlyph;
|
||||
|
||||
GskVulkanGlyphCache *gsk_vulkan_glyph_cache_new (GdkVulkanContext *vulkan);
|
||||
GskVulkanGlyphCache *gsk_vulkan_glyph_cache_new (GskVulkanDevice *device);
|
||||
|
||||
GskVulkanCachedGlyph *gsk_vulkan_glyph_cache_lookup (GskVulkanGlyphCache *cache,
|
||||
GskVulkanRender *render,
|
||||
|
||||
@@ -14,7 +14,7 @@ struct _GskVulkanImage
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
GdkVulkanContext *vulkan;
|
||||
GskVulkanDevice *device;
|
||||
|
||||
GdkMemoryFormat format;
|
||||
VkFormat vk_format;
|
||||
@@ -31,7 +31,8 @@ struct _GskVulkanImage
|
||||
VkImageLayout vk_image_layout;
|
||||
VkAccessFlags vk_access;
|
||||
|
||||
GskVulkanMemory *memory;
|
||||
GskVulkanAllocator *allocator;
|
||||
GskVulkanAllocation allocation;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GskVulkanImage, gsk_vulkan_image, G_TYPE_OBJECT)
|
||||
@@ -316,6 +317,21 @@ gsk_memory_format_get_vk_format_infos (GdkMemoryFormat format)
|
||||
#undef SWIZZLE
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gsk_memory_format_info_is_framebuffer_compatible (const GskMemoryFormatInfo *format)
|
||||
{
|
||||
if (format->postprocess)
|
||||
return FALSE;
|
||||
|
||||
if (format->components.r != VK_COMPONENT_SWIZZLE_R ||
|
||||
format->components.g != VK_COMPONENT_SWIZZLE_G ||
|
||||
format->components.b != VK_COMPONENT_SWIZZLE_B ||
|
||||
format->components.a != VK_COMPONENT_SWIZZLE_A)
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static GdkMemoryFormat
|
||||
gsk_memory_format_get_fallback (GdkMemoryFormat format)
|
||||
{
|
||||
@@ -385,19 +401,22 @@ gsk_memory_format_get_fallback (GdkMemoryFormat format)
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gsk_vulkan_context_supports_format (GdkVulkanContext *context,
|
||||
VkFormat format,
|
||||
VkImageTiling tiling,
|
||||
VkImageUsageFlags usage,
|
||||
gsize width,
|
||||
gsize height)
|
||||
gsk_vulkan_device_supports_format (GskVulkanDevice *device,
|
||||
VkFormat format,
|
||||
VkImageTiling tiling,
|
||||
VkImageUsageFlags usage,
|
||||
gsize width,
|
||||
gsize height)
|
||||
{
|
||||
VkPhysicalDevice vk_phys_device;
|
||||
VkFormatProperties properties;
|
||||
VkImageFormatProperties image_properties;
|
||||
VkFormatFeatureFlags features, required;
|
||||
VkResult res;
|
||||
|
||||
vkGetPhysicalDeviceFormatProperties (gdk_vulkan_context_get_physical_device (context),
|
||||
vk_phys_device = gsk_vulkan_device_get_vk_physical_device (device);
|
||||
|
||||
vkGetPhysicalDeviceFormatProperties (vk_phys_device,
|
||||
format,
|
||||
&properties);
|
||||
|
||||
@@ -421,7 +440,7 @@ gsk_vulkan_context_supports_format (GdkVulkanContext *context,
|
||||
if ((features & required) != required)
|
||||
return FALSE;
|
||||
|
||||
res = vkGetPhysicalDeviceImageFormatProperties (gdk_vulkan_context_get_physical_device (context),
|
||||
res = vkGetPhysicalDeviceImageFormatProperties (vk_phys_device,
|
||||
format,
|
||||
VK_IMAGE_TYPE_2D,
|
||||
tiling,
|
||||
@@ -442,7 +461,7 @@ static void
|
||||
gsk_vulkan_image_create_view (GskVulkanImage *self,
|
||||
const GskMemoryFormatInfo *format)
|
||||
{
|
||||
GSK_VK_CHECK (vkCreateImageView, gdk_vulkan_context_get_device (self->vulkan),
|
||||
GSK_VK_CHECK (vkCreateImageView, gsk_vulkan_device_get_vk_device (self->device),
|
||||
&(VkImageViewCreateInfo) {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||||
.image = self->vk_image,
|
||||
@@ -462,7 +481,7 @@ gsk_vulkan_image_create_view (GskVulkanImage *self,
|
||||
}
|
||||
|
||||
static GskVulkanImage *
|
||||
gsk_vulkan_image_new (GdkVulkanContext *context,
|
||||
gsk_vulkan_image_new (GskVulkanDevice *device,
|
||||
GdkMemoryFormat format,
|
||||
gsize width,
|
||||
gsize height,
|
||||
@@ -476,6 +495,7 @@ gsk_vulkan_image_new (GdkVulkanContext *context,
|
||||
{
|
||||
VkMemoryRequirements requirements;
|
||||
GskVulkanImage *self;
|
||||
VkDevice vk_device;
|
||||
const GskMemoryFormatInfo *vk_format;
|
||||
|
||||
g_assert (width > 0 && height > 0);
|
||||
@@ -489,17 +509,21 @@ gsk_vulkan_image_new (GdkVulkanContext *context,
|
||||
if (vk_format->postprocess & ~allowed_postprocess)
|
||||
continue;
|
||||
|
||||
if (gsk_vulkan_context_supports_format (context,
|
||||
vk_format->format,
|
||||
tiling, usage,
|
||||
width, height))
|
||||
if (usage & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT &&
|
||||
!gsk_memory_format_info_is_framebuffer_compatible (vk_format))
|
||||
continue;
|
||||
|
||||
if (gsk_vulkan_device_supports_format (device,
|
||||
vk_format->format,
|
||||
tiling, usage,
|
||||
width, height))
|
||||
break;
|
||||
|
||||
if (tiling != VK_IMAGE_TILING_OPTIMAL &&
|
||||
gsk_vulkan_context_supports_format (context,
|
||||
vk_format->format,
|
||||
VK_IMAGE_TILING_OPTIMAL, usage,
|
||||
width, height))
|
||||
gsk_vulkan_device_supports_format (device,
|
||||
vk_format->format,
|
||||
VK_IMAGE_TILING_OPTIMAL, usage,
|
||||
width, height))
|
||||
{
|
||||
tiling = VK_IMAGE_TILING_OPTIMAL;
|
||||
break;
|
||||
@@ -511,9 +535,11 @@ gsk_vulkan_image_new (GdkVulkanContext *context,
|
||||
format = gsk_memory_format_get_fallback (format);
|
||||
}
|
||||
|
||||
vk_device = gsk_vulkan_device_get_vk_device (device);
|
||||
|
||||
self = g_object_new (GSK_TYPE_VULKAN_IMAGE, NULL);
|
||||
|
||||
self->vulkan = g_object_ref (context);
|
||||
self->device = g_object_ref (device);
|
||||
self->format = format;
|
||||
self->vk_format = vk_format->format;
|
||||
self->postprocess = vk_format->postprocess;
|
||||
@@ -525,7 +551,7 @@ gsk_vulkan_image_new (GdkVulkanContext *context,
|
||||
self->vk_image_layout = layout;
|
||||
self->vk_access = access;
|
||||
|
||||
GSK_VK_CHECK (vkCreateImage, gdk_vulkan_context_get_device (context),
|
||||
GSK_VK_CHECK (vkCreateImage, vk_device,
|
||||
&(VkImageCreateInfo) {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
||||
.flags = 0,
|
||||
@@ -543,19 +569,23 @@ gsk_vulkan_image_new (GdkVulkanContext *context,
|
||||
NULL,
|
||||
&self->vk_image);
|
||||
|
||||
vkGetImageMemoryRequirements (gdk_vulkan_context_get_device (context),
|
||||
vkGetImageMemoryRequirements (vk_device,
|
||||
self->vk_image,
|
||||
&requirements);
|
||||
|
||||
self->memory = gsk_vulkan_memory_new (context,
|
||||
requirements.memoryTypeBits,
|
||||
memory,
|
||||
requirements.size);
|
||||
self->allocator = gsk_vulkan_device_find_allocator (device,
|
||||
requirements.memoryTypeBits,
|
||||
0,
|
||||
tiling == VK_IMAGE_TILING_LINEAR ? GSK_VULKAN_MEMORY_MAPPABLE : 0);
|
||||
gsk_vulkan_alloc (self->allocator,
|
||||
requirements.size,
|
||||
requirements.alignment,
|
||||
&self->allocation);
|
||||
|
||||
GSK_VK_CHECK (vkBindImageMemory, gdk_vulkan_context_get_device (context),
|
||||
GSK_VK_CHECK (vkBindImageMemory, vk_device,
|
||||
self->vk_image,
|
||||
gsk_vulkan_memory_get_device_memory (self->memory),
|
||||
0);
|
||||
self->allocation.vk_memory,
|
||||
self->allocation.offset);
|
||||
|
||||
gsk_vulkan_image_create_view (self, vk_format);
|
||||
|
||||
@@ -563,14 +593,14 @@ gsk_vulkan_image_new (GdkVulkanContext *context,
|
||||
}
|
||||
|
||||
GskVulkanImage *
|
||||
gsk_vulkan_image_new_for_upload (GdkVulkanContext *context,
|
||||
gsk_vulkan_image_new_for_upload (GskVulkanDevice *device,
|
||||
GdkMemoryFormat format,
|
||||
gsize width,
|
||||
gsize height)
|
||||
{
|
||||
GskVulkanImage *self;
|
||||
|
||||
self = gsk_vulkan_image_new (context,
|
||||
self = gsk_vulkan_image_new (device,
|
||||
format,
|
||||
width,
|
||||
height,
|
||||
@@ -599,44 +629,33 @@ gsk_vulkan_image_can_map (GskVulkanImage *self)
|
||||
self->vk_image_layout != VK_IMAGE_LAYOUT_GENERAL)
|
||||
return FALSE;
|
||||
|
||||
return gsk_vulkan_memory_can_map (self->memory, TRUE);
|
||||
return self->allocation.map != NULL;
|
||||
}
|
||||
|
||||
guchar *
|
||||
gsk_vulkan_image_try_map (GskVulkanImage *self,
|
||||
gsize *out_stride)
|
||||
gsk_vulkan_image_get_data (GskVulkanImage *self,
|
||||
gsize *out_stride)
|
||||
{
|
||||
VkImageSubresource image_res;
|
||||
VkSubresourceLayout image_layout;
|
||||
guchar *result;
|
||||
|
||||
if (!gsk_vulkan_image_can_map (self))
|
||||
return NULL;
|
||||
|
||||
result = gsk_vulkan_memory_map (self->memory);
|
||||
if (result == NULL)
|
||||
return NULL;
|
||||
|
||||
image_res.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
image_res.mipLevel = 0;
|
||||
image_res.arrayLayer = 0;
|
||||
|
||||
vkGetImageSubresourceLayout (gdk_vulkan_context_get_device (self->vulkan),
|
||||
vkGetImageSubresourceLayout (gsk_vulkan_device_get_vk_device (self->device),
|
||||
self->vk_image, &image_res, &image_layout);
|
||||
|
||||
*out_stride = image_layout.rowPitch;
|
||||
|
||||
return result + image_layout.offset;
|
||||
}
|
||||
|
||||
void
|
||||
gsk_vulkan_image_unmap (GskVulkanImage *self)
|
||||
{
|
||||
gsk_vulkan_memory_unmap (self->memory);
|
||||
return self->allocation.map + image_layout.offset;
|
||||
}
|
||||
|
||||
GskVulkanImage *
|
||||
gsk_vulkan_image_new_for_swapchain (GdkVulkanContext *context,
|
||||
gsk_vulkan_image_new_for_swapchain (GskVulkanDevice *device,
|
||||
VkImage image,
|
||||
VkFormat format,
|
||||
gsize width,
|
||||
@@ -646,7 +665,7 @@ gsk_vulkan_image_new_for_swapchain (GdkVulkanContext *context,
|
||||
|
||||
self = g_object_new (GSK_TYPE_VULKAN_IMAGE, NULL);
|
||||
|
||||
self->vulkan = g_object_ref (context);
|
||||
self->device = g_object_ref (device);
|
||||
self->width = width;
|
||||
self->height = height;
|
||||
self->vk_tiling = VK_IMAGE_TILING_OPTIMAL;
|
||||
@@ -670,13 +689,13 @@ gsk_vulkan_image_new_for_swapchain (GdkVulkanContext *context,
|
||||
}
|
||||
|
||||
GskVulkanImage *
|
||||
gsk_vulkan_image_new_for_atlas (GdkVulkanContext *context,
|
||||
gsize width,
|
||||
gsize height)
|
||||
gsk_vulkan_image_new_for_atlas (GskVulkanDevice *device,
|
||||
gsize width,
|
||||
gsize height)
|
||||
{
|
||||
GskVulkanImage *self;
|
||||
|
||||
self = gsk_vulkan_image_new (context,
|
||||
self = gsk_vulkan_image_new (device,
|
||||
GDK_MEMORY_DEFAULT,
|
||||
width,
|
||||
height,
|
||||
@@ -692,19 +711,19 @@ gsk_vulkan_image_new_for_atlas (GdkVulkanContext *context,
|
||||
}
|
||||
|
||||
GskVulkanImage *
|
||||
gsk_vulkan_image_new_for_offscreen (GdkVulkanContext *context,
|
||||
GdkMemoryFormat preferred_format,
|
||||
gsize width,
|
||||
gsize height)
|
||||
gsk_vulkan_image_new_for_offscreen (GskVulkanDevice *device,
|
||||
GdkMemoryFormat preferred_format,
|
||||
gsize width,
|
||||
gsize height)
|
||||
{
|
||||
GskVulkanImage *self;
|
||||
|
||||
self = gsk_vulkan_image_new (context,
|
||||
self = gsk_vulkan_image_new (device,
|
||||
preferred_format,
|
||||
width,
|
||||
height,
|
||||
0,
|
||||
VK_IMAGE_TILING_LINEAR,
|
||||
VK_IMAGE_TILING_OPTIMAL,
|
||||
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
|
||||
VK_IMAGE_USAGE_SAMPLED_BIT |
|
||||
VK_IMAGE_USAGE_TRANSFER_SRC_BIT,
|
||||
@@ -722,7 +741,7 @@ gsk_vulkan_image_finalize (GObject *object)
|
||||
GskVulkanImage *self = GSK_VULKAN_IMAGE (object);
|
||||
VkDevice device;
|
||||
|
||||
device = gdk_vulkan_context_get_device (self->vulkan);
|
||||
device = gsk_vulkan_device_get_vk_device (self->device);
|
||||
|
||||
if (self->vk_framebuffer != VK_NULL_HANDLE)
|
||||
vkDestroyFramebuffer (device, self->vk_framebuffer, NULL);
|
||||
@@ -732,12 +751,13 @@ gsk_vulkan_image_finalize (GObject *object)
|
||||
|
||||
/* memory is NULL for for_swapchain() images, where we don't own
|
||||
* the VkImage */
|
||||
if (self->memory)
|
||||
vkDestroyImage (device, self->vk_image, NULL);
|
||||
if (self->allocator)
|
||||
{
|
||||
vkDestroyImage (device, self->vk_image, NULL);
|
||||
gsk_vulkan_free (self->allocator, &self->allocation);
|
||||
}
|
||||
|
||||
g_clear_pointer (&self->memory, gsk_vulkan_memory_free);
|
||||
|
||||
g_object_unref (self->vulkan);
|
||||
g_object_unref (self->device);
|
||||
|
||||
G_OBJECT_CLASS (gsk_vulkan_image_parent_class)->finalize (object);
|
||||
}
|
||||
@@ -760,7 +780,7 @@ gsk_vulkan_image_get_framebuffer (GskVulkanImage *self,
|
||||
if (self->vk_framebuffer)
|
||||
return self->vk_framebuffer;
|
||||
|
||||
GSK_VK_CHECK (vkCreateFramebuffer, gdk_vulkan_context_get_device (self->vulkan),
|
||||
GSK_VK_CHECK (vkCreateFramebuffer, gsk_vulkan_device_get_vk_device (self->device),
|
||||
&(VkFramebufferCreateInfo) {
|
||||
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
|
||||
.renderPass = render_pass,
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "gskvulkanbufferprivate.h"
|
||||
#include "gskvulkancommandpoolprivate.h"
|
||||
#include "gskvulkandeviceprivate.h"
|
||||
|
||||
|
||||
G_BEGIN_DECLS
|
||||
@@ -18,27 +19,26 @@ typedef enum
|
||||
|
||||
G_DECLARE_FINAL_TYPE (GskVulkanImage, gsk_vulkan_image, GSK, VULKAN_IMAGE, GObject)
|
||||
|
||||
GskVulkanImage * gsk_vulkan_image_new_for_swapchain (GdkVulkanContext *context,
|
||||
GskVulkanImage * gsk_vulkan_image_new_for_swapchain (GskVulkanDevice *device,
|
||||
VkImage image,
|
||||
VkFormat format,
|
||||
gsize width,
|
||||
gsize height);
|
||||
|
||||
GskVulkanImage * gsk_vulkan_image_new_for_atlas (GdkVulkanContext *context,
|
||||
GskVulkanImage * gsk_vulkan_image_new_for_atlas (GskVulkanDevice *device,
|
||||
gsize width,
|
||||
gsize height);
|
||||
GskVulkanImage * gsk_vulkan_image_new_for_offscreen (GdkVulkanContext *context,
|
||||
GskVulkanImage * gsk_vulkan_image_new_for_offscreen (GskVulkanDevice *device,
|
||||
GdkMemoryFormat preferred_format,
|
||||
gsize width,
|
||||
gsize height);
|
||||
|
||||
GskVulkanImage * gsk_vulkan_image_new_for_upload (GdkVulkanContext *context,
|
||||
GskVulkanImage * gsk_vulkan_image_new_for_upload (GskVulkanDevice *device,
|
||||
GdkMemoryFormat format,
|
||||
gsize width,
|
||||
gsize height);
|
||||
guchar * gsk_vulkan_image_try_map (GskVulkanImage *self,
|
||||
guchar * gsk_vulkan_image_get_data (GskVulkanImage *self,
|
||||
gsize *out_stride);
|
||||
void gsk_vulkan_image_unmap (GskVulkanImage *self);
|
||||
|
||||
gsize gsk_vulkan_image_get_width (GskVulkanImage *self);
|
||||
gsize gsk_vulkan_image_get_height (GskVulkanImage *self);
|
||||
|
||||
@@ -4,117 +4,363 @@
|
||||
|
||||
#include "gskvulkanprivate.h"
|
||||
|
||||
struct _GskVulkanMemory
|
||||
/* {{{ direct allocator ***/
|
||||
|
||||
typedef struct _GskVulkanDirectAllocator GskVulkanDirectAllocator;
|
||||
|
||||
struct _GskVulkanDirectAllocator
|
||||
{
|
||||
GdkVulkanContext *vulkan;
|
||||
|
||||
gsize size;
|
||||
GskVulkanAllocator allocator_class;
|
||||
|
||||
VkDevice device; /* no reference held */
|
||||
uint32_t vk_memory_type_index;
|
||||
VkMemoryType vk_memory_type;
|
||||
VkDeviceMemory vk_memory;
|
||||
};
|
||||
|
||||
GskVulkanMemory *
|
||||
gsk_vulkan_memory_new (GdkVulkanContext *context,
|
||||
uint32_t allowed_types,
|
||||
VkMemoryPropertyFlags flags,
|
||||
gsize size)
|
||||
static void
|
||||
gsk_vulkan_direct_allocator_free_allocator (GskVulkanAllocator *allocator)
|
||||
{
|
||||
VkPhysicalDeviceMemoryProperties properties;
|
||||
GskVulkanMemory *self;
|
||||
uint32_t i;
|
||||
g_free (allocator);
|
||||
}
|
||||
|
||||
self = g_new0 (GskVulkanMemory, 1);
|
||||
static void
|
||||
gsk_vulkan_direct_allocator_alloc (GskVulkanAllocator *allocator,
|
||||
VkDeviceSize size,
|
||||
VkDeviceSize alignment,
|
||||
GskVulkanAllocation *alloc)
|
||||
{
|
||||
GskVulkanDirectAllocator *self = (GskVulkanDirectAllocator *) allocator;
|
||||
|
||||
self->vulkan = g_object_ref (context);
|
||||
self->size = size;
|
||||
|
||||
vkGetPhysicalDeviceMemoryProperties (gdk_vulkan_context_get_physical_device (context),
|
||||
&properties);
|
||||
|
||||
for (i = 0; i < properties.memoryTypeCount; i++)
|
||||
{
|
||||
if (!(allowed_types & (1 << i)))
|
||||
continue;
|
||||
|
||||
if ((properties.memoryTypes[i].propertyFlags & flags) == flags)
|
||||
break;
|
||||
}
|
||||
|
||||
g_assert (i < properties.memoryTypeCount);
|
||||
|
||||
self->vk_memory_type = properties.memoryTypes[i];
|
||||
GSK_VK_CHECK (vkAllocateMemory, gdk_vulkan_context_get_device (context),
|
||||
GSK_VK_CHECK (vkAllocateMemory, self->device,
|
||||
&(VkMemoryAllocateInfo) {
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
||||
.allocationSize = size,
|
||||
.memoryTypeIndex = i
|
||||
.memoryTypeIndex = self->vk_memory_type_index
|
||||
},
|
||||
NULL,
|
||||
&self->vk_memory);
|
||||
&alloc->vk_memory);
|
||||
|
||||
return self;
|
||||
if ((self->vk_memory_type.propertyFlags & GSK_VULKAN_MEMORY_MAPPABLE) == GSK_VULKAN_MEMORY_MAPPABLE)
|
||||
{
|
||||
GSK_VK_CHECK (vkMapMemory, self->device,
|
||||
alloc->vk_memory,
|
||||
0,
|
||||
size,
|
||||
0,
|
||||
(void **) &alloc->map);
|
||||
}
|
||||
|
||||
alloc->offset = 0;
|
||||
alloc->size = size;
|
||||
}
|
||||
|
||||
void
|
||||
gsk_vulkan_memory_free (GskVulkanMemory *self)
|
||||
static void
|
||||
gsk_vulkan_direct_allocator_free (GskVulkanAllocator *allocator,
|
||||
GskVulkanAllocation *alloc)
|
||||
{
|
||||
vkFreeMemory (gdk_vulkan_context_get_device (self->vulkan),
|
||||
self->vk_memory,
|
||||
NULL);
|
||||
GskVulkanDirectAllocator *self = (GskVulkanDirectAllocator *) allocator;
|
||||
|
||||
g_object_unref (self->vulkan);
|
||||
if (alloc->map)
|
||||
vkUnmapMemory (self->device, alloc->vk_memory);
|
||||
|
||||
vkFreeMemory (self->device,
|
||||
alloc->vk_memory,
|
||||
NULL);
|
||||
}
|
||||
|
||||
GskVulkanAllocator *
|
||||
gsk_vulkan_direct_allocator_new (VkDevice device,
|
||||
uint32_t vk_type_index,
|
||||
const VkMemoryType *vk_type)
|
||||
{
|
||||
GskVulkanDirectAllocator *self;
|
||||
|
||||
self = g_new0 (GskVulkanDirectAllocator, 1);
|
||||
self->allocator_class.free_allocator = gsk_vulkan_direct_allocator_free_allocator;
|
||||
self->allocator_class.alloc = gsk_vulkan_direct_allocator_alloc;
|
||||
self->allocator_class.free = gsk_vulkan_direct_allocator_free;
|
||||
self->device = device;
|
||||
self->vk_memory_type_index = vk_type_index;
|
||||
self->vk_memory_type = *vk_type;
|
||||
|
||||
return (GskVulkanAllocator *) self;
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
/* {{{ buddy allocator ***/
|
||||
|
||||
#define GDK_ARRAY_NAME gsk_vulkan_allocation_list
|
||||
#define GDK_ARRAY_TYPE_NAME GskVulkanAllocationList
|
||||
#define GDK_ARRAY_ELEMENT_TYPE GskVulkanAllocation
|
||||
#define GDK_ARRAY_BY_VALUE 1
|
||||
#define GDK_ARRAY_PREALLOC 4
|
||||
#define GDK_ARRAY_NO_MEMSET 1
|
||||
#include "gdk/gdkarrayimpl.c"
|
||||
|
||||
#define N_SUBDIVISIONS 10
|
||||
|
||||
typedef struct _GskVulkanBuddyAllocator GskVulkanBuddyAllocator;
|
||||
|
||||
struct _GskVulkanBuddyAllocator
|
||||
{
|
||||
GskVulkanAllocator allocator_class;
|
||||
|
||||
GskVulkanAllocator *allocator;
|
||||
|
||||
gsize block_size_slot;
|
||||
|
||||
GskVulkanAllocation cache;
|
||||
GskVulkanAllocationList free_lists[N_SUBDIVISIONS];
|
||||
};
|
||||
|
||||
static void
|
||||
gsk_vulkan_buddy_allocator_free_allocator (GskVulkanAllocator *allocator)
|
||||
{
|
||||
GskVulkanBuddyAllocator *self = (GskVulkanBuddyAllocator *) allocator;
|
||||
gsize i;
|
||||
|
||||
if (self->cache.vk_memory)
|
||||
gsk_vulkan_free (self->allocator, &self->cache);
|
||||
|
||||
for (i = 0; i < N_SUBDIVISIONS; i++)
|
||||
{
|
||||
gsk_vulkan_allocation_list_clear (&self->free_lists[i]);
|
||||
}
|
||||
|
||||
gsk_vulkan_allocator_free (self->allocator);
|
||||
|
||||
g_free (self);
|
||||
}
|
||||
|
||||
VkDeviceMemory
|
||||
gsk_vulkan_memory_get_device_memory (GskVulkanMemory *self)
|
||||
/* must not be 0:
|
||||
* gets exponent for next power of 2 that's >= num.
|
||||
* So num=1234 gets 11, because 2048 = 2^11 */
|
||||
static gsize
|
||||
find_slot (gsize num)
|
||||
{
|
||||
return self->vk_memory;
|
||||
return g_bit_storage (num - 1);
|
||||
}
|
||||
|
||||
gboolean
|
||||
gsk_vulkan_memory_can_map (GskVulkanMemory *self,
|
||||
gboolean fast)
|
||||
static void
|
||||
gsk_vulkan_buddy_allocator_alloc (GskVulkanAllocator *allocator,
|
||||
VkDeviceSize size,
|
||||
VkDeviceSize align,
|
||||
GskVulkanAllocation *alloc)
|
||||
{
|
||||
if (!(self->vk_memory_type.propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT))
|
||||
return FALSE;
|
||||
GskVulkanBuddyAllocator *self = (GskVulkanBuddyAllocator *) allocator;
|
||||
gsize slot;
|
||||
int i;
|
||||
|
||||
/* FIXME: no support implemented for this */
|
||||
if (!(self->vk_memory_type.propertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT))
|
||||
return FALSE;
|
||||
size = MAX (size, align);
|
||||
|
||||
if (!fast)
|
||||
return TRUE;
|
||||
slot = find_slot (size);
|
||||
if (slot >= self->block_size_slot)
|
||||
{
|
||||
gsk_vulkan_alloc (self->allocator, size, align, alloc);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(self->vk_memory_type.propertyFlags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT))
|
||||
return FALSE;
|
||||
slot = MIN (self->block_size_slot - slot, N_SUBDIVISIONS) - 1;
|
||||
for (i = slot; i >= 0; i--)
|
||||
{
|
||||
if (gsk_vulkan_allocation_list_get_size (&self->free_lists[i]) > 0)
|
||||
break;
|
||||
}
|
||||
if (i < 0)
|
||||
{
|
||||
if (self->cache.vk_memory)
|
||||
{
|
||||
*alloc = self->cache;
|
||||
self->cache.vk_memory = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* We force alignment to our size, so that we can use offset
|
||||
* to find the buddy allocation.
|
||||
*/
|
||||
gsk_vulkan_alloc (self->allocator, 1 << self->block_size_slot, 1 << self->block_size_slot, alloc);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
gsize n = gsk_vulkan_allocation_list_get_size (&self->free_lists[i]);
|
||||
*alloc = *gsk_vulkan_allocation_list_get (&self->free_lists[i], n - 1);
|
||||
gsk_vulkan_allocation_list_set_size (&self->free_lists[i], n - 1);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
while (i != slot)
|
||||
{
|
||||
i++;
|
||||
alloc->size >>= 1;
|
||||
gsk_vulkan_allocation_list_append (&self->free_lists[i], alloc);
|
||||
alloc->offset += alloc->size;
|
||||
if (alloc->map)
|
||||
alloc->map += alloc->size;
|
||||
}
|
||||
|
||||
g_assert (alloc->size >= size);
|
||||
}
|
||||
|
||||
guchar *
|
||||
gsk_vulkan_memory_map (GskVulkanMemory *self)
|
||||
static void
|
||||
gsk_vulkan_buddy_allocator_free (GskVulkanAllocator *allocator,
|
||||
GskVulkanAllocation *alloc)
|
||||
{
|
||||
void *data;
|
||||
GskVulkanBuddyAllocator *self = (GskVulkanBuddyAllocator *) allocator;
|
||||
gsize slot, i, n;
|
||||
|
||||
g_assert (gsk_vulkan_memory_can_map (self, FALSE));
|
||||
slot = find_slot (alloc->size);
|
||||
if (slot >= self->block_size_slot)
|
||||
{
|
||||
gsk_vulkan_free (self->allocator, alloc);
|
||||
return;
|
||||
}
|
||||
|
||||
GSK_VK_CHECK (vkMapMemory, gdk_vulkan_context_get_device (self->vulkan),
|
||||
self->vk_memory,
|
||||
0,
|
||||
self->size,
|
||||
0,
|
||||
&data);
|
||||
|
||||
return data;
|
||||
slot = MIN (self->block_size_slot - slot, N_SUBDIVISIONS) - 1;
|
||||
restart:
|
||||
n = gsk_vulkan_allocation_list_get_size (&self->free_lists[slot]);
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
GskVulkanAllocation *maybe_buddy = gsk_vulkan_allocation_list_index (&self->free_lists[slot], i);
|
||||
if (maybe_buddy->vk_memory == alloc->vk_memory &&
|
||||
maybe_buddy->offset == (alloc->offset ^ alloc->size))
|
||||
{
|
||||
if (i < n - 1)
|
||||
*maybe_buddy = *gsk_vulkan_allocation_list_get (&self->free_lists[slot], n - 1);
|
||||
gsk_vulkan_allocation_list_set_size (&self->free_lists[slot], n - 1);
|
||||
if (alloc->map && alloc->offset & alloc->size)
|
||||
alloc->map -= alloc->size;
|
||||
alloc->offset &= ~alloc->size;
|
||||
alloc->size <<= 1;
|
||||
if (slot == 0)
|
||||
{
|
||||
if (self->cache.vk_memory == NULL)
|
||||
self->cache = *alloc;
|
||||
else
|
||||
gsk_vulkan_free (self->allocator, alloc);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
slot--;
|
||||
/* no idea how to make this look good with loops */
|
||||
goto restart;
|
||||
}
|
||||
}
|
||||
}
|
||||
gsk_vulkan_allocation_list_append (&self->free_lists[slot], alloc);
|
||||
}
|
||||
|
||||
void
|
||||
gsk_vulkan_memory_unmap (GskVulkanMemory *self)
|
||||
GskVulkanAllocator *
|
||||
gsk_vulkan_buddy_allocator_new (GskVulkanAllocator *allocator,
|
||||
gsize block_size)
|
||||
{
|
||||
vkUnmapMemory (gdk_vulkan_context_get_device (self->vulkan),
|
||||
self->vk_memory);
|
||||
GskVulkanBuddyAllocator *self;
|
||||
gsize i;
|
||||
|
||||
self = g_new0 (GskVulkanBuddyAllocator, 1);
|
||||
self->allocator_class.free_allocator = gsk_vulkan_buddy_allocator_free_allocator;
|
||||
self->allocator_class.alloc = gsk_vulkan_buddy_allocator_alloc;
|
||||
self->allocator_class.free = gsk_vulkan_buddy_allocator_free;
|
||||
self->allocator = allocator;
|
||||
self->block_size_slot = find_slot (block_size);
|
||||
|
||||
for (i = 0; i < N_SUBDIVISIONS; i++)
|
||||
{
|
||||
gsk_vulkan_allocation_list_init (&self->free_lists[i]);
|
||||
}
|
||||
|
||||
return (GskVulkanAllocator *) self;
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
/* {{{ stats allocator ***/
|
||||
|
||||
typedef struct _GskVulkanStatsAllocator GskVulkanStatsAllocator;
|
||||
|
||||
struct _GskVulkanStatsAllocator
|
||||
{
|
||||
GskVulkanAllocator allocator_class;
|
||||
|
||||
GskVulkanAllocator *allocator;
|
||||
|
||||
gsize n_alloc;
|
||||
gsize n_free;
|
||||
|
||||
gsize n_bytes_requested;
|
||||
gsize n_bytes_allocated;
|
||||
gsize n_bytes_freed;
|
||||
};
|
||||
|
||||
static void
|
||||
gsk_vulkan_stats_allocator_dump_stats (GskVulkanStatsAllocator *self,
|
||||
const char *reason)
|
||||
{
|
||||
g_printerr ("%s\n", reason);
|
||||
g_printerr (" %zu bytes requested in %zu allocations\n", self->n_bytes_requested, self->n_alloc);
|
||||
g_printerr (" %zu bytes allocated (%.2f%% overhead)\n", self->n_bytes_allocated,
|
||||
(self->n_bytes_allocated - self->n_bytes_requested) * 100. / self->n_bytes_requested);
|
||||
g_printerr (" %zu bytes freed in %zu frees\n", self->n_bytes_freed , self->n_free);
|
||||
g_printerr (" %zu bytes remaining in %zu allocations\n",
|
||||
self->n_bytes_allocated - self->n_bytes_freed, self->n_alloc - self->n_free);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_stats_allocator_free_allocator (GskVulkanAllocator *allocator)
|
||||
{
|
||||
GskVulkanStatsAllocator *self = (GskVulkanStatsAllocator *) allocator;
|
||||
|
||||
g_assert (self->n_alloc == self->n_free);
|
||||
g_assert (self->n_bytes_allocated == self->n_bytes_freed);
|
||||
|
||||
gsk_vulkan_allocator_free (self->allocator);
|
||||
|
||||
g_free (self);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_stats_allocator_alloc (GskVulkanAllocator *allocator,
|
||||
VkDeviceSize size,
|
||||
VkDeviceSize align,
|
||||
GskVulkanAllocation *alloc)
|
||||
{
|
||||
GskVulkanStatsAllocator *self = (GskVulkanStatsAllocator *) allocator;
|
||||
|
||||
gsk_vulkan_alloc (self->allocator, size, align, alloc);
|
||||
|
||||
self->n_alloc++;
|
||||
self->n_bytes_requested += size;
|
||||
self->n_bytes_allocated += alloc->size;
|
||||
|
||||
gsk_vulkan_stats_allocator_dump_stats (self, "alloc()");
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_stats_allocator_free (GskVulkanAllocator *allocator,
|
||||
GskVulkanAllocation *alloc)
|
||||
{
|
||||
GskVulkanStatsAllocator *self = (GskVulkanStatsAllocator *) allocator;
|
||||
|
||||
self->n_free++;
|
||||
self->n_bytes_freed += alloc->size;
|
||||
|
||||
gsk_vulkan_free (self->allocator, alloc);
|
||||
|
||||
gsk_vulkan_stats_allocator_dump_stats (self, "free()");
|
||||
}
|
||||
|
||||
GskVulkanAllocator *
|
||||
gsk_vulkan_stats_allocator_new (GskVulkanAllocator *allocator)
|
||||
{
|
||||
GskVulkanStatsAllocator *self;
|
||||
|
||||
self = g_new0 (GskVulkanStatsAllocator, 1);
|
||||
self->allocator_class.free_allocator = gsk_vulkan_stats_allocator_free_allocator;
|
||||
self->allocator_class.alloc = gsk_vulkan_stats_allocator_alloc;
|
||||
self->allocator_class.free = gsk_vulkan_stats_allocator_free;
|
||||
self->allocator = allocator;
|
||||
|
||||
return (GskVulkanAllocator *) self;
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
|
||||
|
||||
@@ -4,20 +4,71 @@
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GSK_VULKAN_MEMORY_MAPPABLE (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | \
|
||||
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | \
|
||||
VK_MEMORY_PROPERTY_HOST_CACHED_BIT)
|
||||
|
||||
typedef struct _GskVulkanAllocator GskVulkanAllocator;
|
||||
typedef struct _GskVulkanAllocation GskVulkanAllocation;
|
||||
typedef struct _GskVulkanMemory GskVulkanMemory;
|
||||
|
||||
GskVulkanMemory * gsk_vulkan_memory_new (GdkVulkanContext *context,
|
||||
uint32_t allowed_types,
|
||||
VkMemoryPropertyFlags properties,
|
||||
gsize size);
|
||||
void gsk_vulkan_memory_free (GskVulkanMemory *memory);
|
||||
struct _GskVulkanAllocation
|
||||
{
|
||||
VkDeviceMemory vk_memory;
|
||||
guchar *map;
|
||||
VkDeviceSize offset;
|
||||
VkDeviceSize size;
|
||||
};
|
||||
|
||||
VkDeviceMemory gsk_vulkan_memory_get_device_memory (GskVulkanMemory *self);
|
||||
struct _GskVulkanAllocator
|
||||
{
|
||||
void (* free_allocator) (GskVulkanAllocator *allocator);
|
||||
|
||||
gboolean gsk_vulkan_memory_can_map (GskVulkanMemory *self,
|
||||
gboolean fast);
|
||||
guchar * gsk_vulkan_memory_map (GskVulkanMemory *self);
|
||||
void gsk_vulkan_memory_unmap (GskVulkanMemory *self);
|
||||
void (* alloc) (GskVulkanAllocator *allocator,
|
||||
VkDeviceSize size,
|
||||
VkDeviceSize alignment,
|
||||
GskVulkanAllocation *out_alloc);
|
||||
void (* free) (GskVulkanAllocator *allocator,
|
||||
GskVulkanAllocation *alloc);
|
||||
};
|
||||
|
||||
static inline void gsk_vulkan_alloc (GskVulkanAllocator *allocator,
|
||||
VkDeviceSize size,
|
||||
VkDeviceSize alignment,
|
||||
GskVulkanAllocation *out_alloc);
|
||||
static inline void gsk_vulkan_free (GskVulkanAllocator *allocator,
|
||||
GskVulkanAllocation *alloc);
|
||||
|
||||
static inline void gsk_vulkan_allocator_free (GskVulkanAllocator *allocator);
|
||||
|
||||
GskVulkanAllocator * gsk_vulkan_direct_allocator_new (VkDevice device,
|
||||
uint32_t vk_type_index,
|
||||
const VkMemoryType *vk_type);
|
||||
GskVulkanAllocator * gsk_vulkan_buddy_allocator_new (GskVulkanAllocator *allocator,
|
||||
gsize block_size);
|
||||
GskVulkanAllocator * gsk_vulkan_stats_allocator_new (GskVulkanAllocator *allocator);
|
||||
|
||||
static inline void
|
||||
gsk_vulkan_alloc (GskVulkanAllocator *allocator,
|
||||
VkDeviceSize size,
|
||||
VkDeviceSize alignment,
|
||||
GskVulkanAllocation *out_alloc)
|
||||
{
|
||||
allocator->alloc (allocator, size, alignment, out_alloc);
|
||||
}
|
||||
|
||||
static inline void
|
||||
gsk_vulkan_free (GskVulkanAllocator *allocator,
|
||||
GskVulkanAllocation *alloc)
|
||||
{
|
||||
allocator->free (allocator, alloc);
|
||||
}
|
||||
|
||||
static inline void
|
||||
gsk_vulkan_allocator_free (GskVulkanAllocator *allocator)
|
||||
{
|
||||
allocator->free_allocator (allocator);
|
||||
}
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
|
||||
#define DESCRIPTOR_POOL_MAXITEMS 50000
|
||||
#define VERTEX_BUFFER_SIZE_STEP 128 * 1024 /* 128kB */
|
||||
#define STORAGE_BUFFER_SIZE_STEP 32 * 1024
|
||||
|
||||
#define GDK_ARRAY_NAME gsk_descriptor_image_infos
|
||||
#define GDK_ARRAY_TYPE_NAME GskDescriptorImageInfos
|
||||
@@ -49,6 +50,7 @@ struct _GskVulkanRender
|
||||
{
|
||||
GskRenderer *renderer;
|
||||
GdkVulkanContext *vulkan;
|
||||
GskVulkanDevice *device;
|
||||
|
||||
graphene_rect_t viewport;
|
||||
cairo_region_t *clip;
|
||||
@@ -193,22 +195,24 @@ gsk_vulkan_render_setup (GskVulkanRender *self,
|
||||
|
||||
GskVulkanRender *
|
||||
gsk_vulkan_render_new (GskRenderer *renderer,
|
||||
GdkVulkanContext *context)
|
||||
GdkVulkanContext *context,
|
||||
GskVulkanDevice *device)
|
||||
{
|
||||
GskVulkanRender *self;
|
||||
VkDevice device;
|
||||
VkDevice vk_device;
|
||||
|
||||
self = g_new0 (GskVulkanRender, 1);
|
||||
|
||||
self->vulkan = context;
|
||||
self->device = device;
|
||||
self->renderer = renderer;
|
||||
gsk_descriptor_image_infos_init (&self->descriptor_images);
|
||||
gsk_descriptor_buffer_infos_init (&self->descriptor_buffers);
|
||||
|
||||
device = gdk_vulkan_context_get_device (self->vulkan);
|
||||
vk_device = gdk_vulkan_context_get_device (self->vulkan);
|
||||
|
||||
self->command_pool = gsk_vulkan_command_pool_new (self->vulkan);
|
||||
GSK_VK_CHECK (vkCreateFence, device,
|
||||
GSK_VK_CHECK (vkCreateFence, vk_device,
|
||||
&(VkFenceCreateInfo) {
|
||||
.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
|
||||
.flags = VK_FENCE_CREATE_SIGNALED_BIT
|
||||
@@ -216,7 +220,7 @@ gsk_vulkan_render_new (GskRenderer *renderer,
|
||||
NULL,
|
||||
&self->fence);
|
||||
|
||||
GSK_VK_CHECK (vkCreateDescriptorPool, device,
|
||||
GSK_VK_CHECK (vkCreateDescriptorPool, vk_device,
|
||||
&(VkDescriptorPoolCreateInfo) {
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
|
||||
.flags = VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT,
|
||||
@@ -236,7 +240,7 @@ gsk_vulkan_render_new (GskRenderer *renderer,
|
||||
NULL,
|
||||
&self->descriptor_pool);
|
||||
|
||||
GSK_VK_CHECK (vkCreateDescriptorSetLayout, device,
|
||||
GSK_VK_CHECK (vkCreateDescriptorSetLayout, vk_device,
|
||||
&(VkDescriptorSetLayoutCreateInfo) {
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
|
||||
.bindingCount = 1,
|
||||
@@ -262,7 +266,7 @@ gsk_vulkan_render_new (GskRenderer *renderer,
|
||||
NULL,
|
||||
&self->descriptor_set_layouts[0]);
|
||||
|
||||
GSK_VK_CHECK (vkCreateDescriptorSetLayout, device,
|
||||
GSK_VK_CHECK (vkCreateDescriptorSetLayout, vk_device,
|
||||
&(VkDescriptorSetLayoutCreateInfo) {
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
|
||||
.bindingCount = 1,
|
||||
@@ -288,7 +292,7 @@ gsk_vulkan_render_new (GskRenderer *renderer,
|
||||
NULL,
|
||||
&self->descriptor_set_layouts[1]);
|
||||
|
||||
GSK_VK_CHECK (vkCreatePipelineLayout, device,
|
||||
GSK_VK_CHECK (vkCreatePipelineLayout, vk_device,
|
||||
&(VkPipelineLayoutCreateInfo) {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
|
||||
.setLayoutCount = G_N_ELEMENTS (self->descriptor_set_layouts),
|
||||
@@ -299,7 +303,7 @@ gsk_vulkan_render_new (GskRenderer *renderer,
|
||||
NULL,
|
||||
&self->pipeline_layout);
|
||||
|
||||
GSK_VK_CHECK (vkCreateSampler, device,
|
||||
GSK_VK_CHECK (vkCreateSampler, vk_device,
|
||||
&(VkSamplerCreateInfo) {
|
||||
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
|
||||
.magFilter = VK_FILTER_LINEAR,
|
||||
@@ -314,7 +318,7 @@ gsk_vulkan_render_new (GskRenderer *renderer,
|
||||
NULL,
|
||||
&self->samplers[GSK_VULKAN_SAMPLER_DEFAULT]);
|
||||
|
||||
GSK_VK_CHECK (vkCreateSampler, device,
|
||||
GSK_VK_CHECK (vkCreateSampler, vk_device,
|
||||
&(VkSamplerCreateInfo) {
|
||||
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
|
||||
.magFilter = VK_FILTER_LINEAR,
|
||||
@@ -329,7 +333,7 @@ gsk_vulkan_render_new (GskRenderer *renderer,
|
||||
NULL,
|
||||
&self->samplers[GSK_VULKAN_SAMPLER_REPEAT]);
|
||||
|
||||
GSK_VK_CHECK (vkCreateSampler, device,
|
||||
GSK_VK_CHECK (vkCreateSampler, vk_device,
|
||||
&(VkSamplerCreateInfo) {
|
||||
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
|
||||
.magFilter = VK_FILTER_NEAREST,
|
||||
@@ -726,13 +730,9 @@ gsk_vulkan_render_ensure_storage_buffer (GskVulkanRender *self)
|
||||
return;
|
||||
|
||||
if (self->storage_buffer == NULL)
|
||||
{
|
||||
self->storage_buffer = gsk_vulkan_buffer_new_storage (self->vulkan,
|
||||
/* random */
|
||||
sizeof (float) * 1024 * 1024);
|
||||
}
|
||||
self->storage_buffer = gsk_vulkan_buffer_new_storage (self->device, STORAGE_BUFFER_SIZE_STEP);
|
||||
|
||||
self->storage_buffer_memory = gsk_vulkan_buffer_map (self->storage_buffer);
|
||||
self->storage_buffer_memory = gsk_vulkan_buffer_get_data (self->storage_buffer);
|
||||
|
||||
if (gsk_vulkan_render_get_buffer_descriptor (self, self->storage_buffer) != 0)
|
||||
{
|
||||
@@ -780,9 +780,20 @@ gsk_vulkan_render_get_buffer_memory (GskVulkanRender *self,
|
||||
gsk_vulkan_render_ensure_storage_buffer (self);
|
||||
|
||||
self->storage_buffer_used = round_up (self->storage_buffer_used, alignment);
|
||||
if (self->storage_buffer_used + size > gsk_vulkan_buffer_get_size (self->storage_buffer))
|
||||
{
|
||||
gsize larger_size = 1 << g_bit_storage (self->storage_buffer_used + size - 1);
|
||||
GskVulkanBuffer *larger_buffer = gsk_vulkan_buffer_new_storage (self->device, larger_size);
|
||||
guchar *larger_memory = gsk_vulkan_buffer_get_data (larger_buffer);
|
||||
memcpy (larger_memory, self->storage_buffer_memory, self->storage_buffer_used);
|
||||
gsk_vulkan_buffer_free (self->storage_buffer);
|
||||
self->storage_buffer = larger_buffer;
|
||||
self->storage_buffer_memory = larger_memory;
|
||||
gsk_descriptor_buffer_infos_index (&self->descriptor_buffers, 0)->buffer = gsk_vulkan_buffer_get_buffer (larger_buffer);
|
||||
}
|
||||
|
||||
result = self->storage_buffer_memory + self->storage_buffer_used;
|
||||
*out_offset = self->storage_buffer_used / sizeof (float);
|
||||
|
||||
self->storage_buffer_used += size;
|
||||
|
||||
return result;
|
||||
@@ -805,7 +816,6 @@ gsk_vulkan_render_prepare_descriptor_sets (GskVulkanRender *self)
|
||||
|
||||
if (self->storage_buffer_memory)
|
||||
{
|
||||
gsk_vulkan_buffer_unmap (self->storage_buffer);
|
||||
self->storage_buffer_memory = NULL;
|
||||
self->storage_buffer_used = 0;
|
||||
}
|
||||
@@ -878,14 +888,13 @@ gsk_vulkan_render_collect_vertex_buffer (GskVulkanRender *self)
|
||||
g_clear_pointer (&self->vertex_buffer, gsk_vulkan_buffer_free);
|
||||
|
||||
if (self->vertex_buffer == NULL)
|
||||
self->vertex_buffer = gsk_vulkan_buffer_new (self->vulkan, round_up (n_bytes, VERTEX_BUFFER_SIZE_STEP));
|
||||
self->vertex_buffer = gsk_vulkan_buffer_new (self->device, round_up (n_bytes, VERTEX_BUFFER_SIZE_STEP));
|
||||
|
||||
data = gsk_vulkan_buffer_map (self->vertex_buffer);
|
||||
data = gsk_vulkan_buffer_get_data (self->vertex_buffer);
|
||||
for (op = self->first_op; op; op = op->next)
|
||||
{
|
||||
gsk_vulkan_op_collect_vertex_data (op, data);
|
||||
}
|
||||
gsk_vulkan_buffer_unmap (self->vertex_buffer);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -1095,6 +1104,12 @@ gsk_vulkan_render_get_context (GskVulkanRender *self)
|
||||
return self->vulkan;
|
||||
}
|
||||
|
||||
GskVulkanDevice *
|
||||
gsk_vulkan_render_get_device (GskVulkanRender *self)
|
||||
{
|
||||
return self->device;
|
||||
}
|
||||
|
||||
gpointer
|
||||
gsk_vulkan_render_alloc_op (GskVulkanRender *self,
|
||||
gsize size)
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include "gskprivate.h"
|
||||
#include "gskrendererprivate.h"
|
||||
#include "gskrendernodeprivate.h"
|
||||
#include "gskvulkanbufferprivate.h"
|
||||
#include "gskvulkandeviceprivate.h"
|
||||
#include "gskvulkanimageprivate.h"
|
||||
#include "gskvulkanprivate.h"
|
||||
#include "gskvulkanrenderprivate.h"
|
||||
@@ -52,6 +52,7 @@ struct _GskVulkanRenderer
|
||||
GskRenderer parent_instance;
|
||||
|
||||
GdkVulkanContext *vulkan;
|
||||
GskVulkanDevice *device;
|
||||
|
||||
guint n_targets;
|
||||
GskVulkanImage **targets;
|
||||
@@ -162,7 +163,7 @@ gsk_vulkan_renderer_update_images_cb (GdkVulkanContext *context,
|
||||
|
||||
for (i = 0; i < self->n_targets; i++)
|
||||
{
|
||||
self->targets[i] = gsk_vulkan_image_new_for_swapchain (self->vulkan,
|
||||
self->targets[i] = gsk_vulkan_image_new_for_swapchain (self->device,
|
||||
gdk_vulkan_context_get_image (context, i),
|
||||
gdk_vulkan_context_get_image_format (self->vulkan),
|
||||
width, height);
|
||||
@@ -184,7 +185,7 @@ gsk_vulkan_renderer_get_render (GskVulkanRenderer *self)
|
||||
{
|
||||
if (self->renders[i] == NULL)
|
||||
{
|
||||
self->renders[i] = gsk_vulkan_render_new (GSK_RENDERER (self), self->vulkan);
|
||||
self->renders[i] = gsk_vulkan_render_new (GSK_RENDERER (self), self->vulkan, self->device);
|
||||
return self->renders[i];
|
||||
}
|
||||
|
||||
@@ -193,6 +194,7 @@ gsk_vulkan_renderer_get_render (GskVulkanRenderer *self)
|
||||
return self->renders[i];
|
||||
}
|
||||
|
||||
g_print ("gotta wait, oh no!\n");
|
||||
GSK_VK_CHECK (vkWaitForFences, device,
|
||||
G_N_ELEMENTS (fences),
|
||||
fences,
|
||||
@@ -216,13 +218,20 @@ gsk_vulkan_renderer_realize (GskRenderer *renderer,
|
||||
if (self->vulkan == NULL)
|
||||
return FALSE;
|
||||
|
||||
self->device = gsk_vulkan_device_get_for_display (gdk_draw_context_get_display (GDK_DRAW_CONTEXT (self->vulkan)), error);
|
||||
if (self->device == NULL)
|
||||
{
|
||||
g_clear_object (&self->vulkan);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
g_signal_connect (self->vulkan,
|
||||
"images-updated",
|
||||
G_CALLBACK (gsk_vulkan_renderer_update_images_cb),
|
||||
self);
|
||||
gsk_vulkan_renderer_update_images_cb (self->vulkan, self);
|
||||
|
||||
self->glyph_cache = gsk_vulkan_glyph_cache_new (self->vulkan);
|
||||
self->glyph_cache = gsk_vulkan_glyph_cache_new (self->device);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
@@ -235,6 +244,7 @@ gsk_vulkan_renderer_unrealize (GskRenderer *renderer)
|
||||
guint i;
|
||||
|
||||
g_clear_object (&self->glyph_cache);
|
||||
g_clear_object (&self->device);
|
||||
|
||||
for (l = self->textures; l; l = l->next)
|
||||
{
|
||||
@@ -299,13 +309,13 @@ gsk_vulkan_renderer_render_texture (GskRenderer *renderer,
|
||||
gsk_profiler_timer_begin (profiler, self->profile_timers.cpu_time);
|
||||
#endif
|
||||
|
||||
render = gsk_vulkan_render_new (renderer, self->vulkan);
|
||||
render = gsk_vulkan_render_new (renderer, self->vulkan, self->device);
|
||||
|
||||
rounded_viewport = GRAPHENE_RECT_INIT (viewport->origin.x,
|
||||
viewport->origin.y,
|
||||
ceil (viewport->size.width),
|
||||
ceil (viewport->size.height));
|
||||
image = gsk_vulkan_image_new_for_offscreen (self->vulkan,
|
||||
image = gsk_vulkan_image_new_for_offscreen (self->device,
|
||||
gdk_vulkan_context_get_offscreen_format (self->vulkan,
|
||||
gsk_render_node_get_preferred_depth (root)),
|
||||
rounded_viewport.size.width,
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "gskrenderer.h"
|
||||
#include "gskrendererprivate.h"
|
||||
#include "gskroundedrectprivate.h"
|
||||
#include "gskstrokeprivate.h"
|
||||
#include "gsktransform.h"
|
||||
#include "gskvulkanblendmodeopprivate.h"
|
||||
#include "gskvulkanbluropprivate.h"
|
||||
@@ -18,6 +19,7 @@
|
||||
#include "gskvulkancoloropprivate.h"
|
||||
#include "gskvulkanconvertopprivate.h"
|
||||
#include "gskvulkancrossfadeopprivate.h"
|
||||
#include "gskvulkanfillopprivate.h"
|
||||
#include "gskvulkanglyphopprivate.h"
|
||||
#include "gskvulkaninsetshadowopprivate.h"
|
||||
#include "gskvulkanlineargradientopprivate.h"
|
||||
@@ -30,6 +32,7 @@
|
||||
#include "gskvulkanoutsetshadowopprivate.h"
|
||||
#include "gskvulkanpushconstantsopprivate.h"
|
||||
#include "gskvulkanscissoropprivate.h"
|
||||
#include "gskvulkanstrokeopprivate.h"
|
||||
#include "gskvulkantextureopprivate.h"
|
||||
#include "gskvulkanuploadopprivate.h"
|
||||
#include "gskprivate.h"
|
||||
@@ -72,6 +75,33 @@ gsk_vulkan_render_pass_free (GskVulkanRenderPass *self)
|
||||
g_free (self);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gsk_vulkan_render_pass_is_color_node (GskRenderNode *node,
|
||||
GdkRGBA *out_color)
|
||||
{
|
||||
GskRenderNodeType node_type;
|
||||
|
||||
node_type = gsk_render_node_get_node_type (node);
|
||||
|
||||
if (node_type == GSK_COLOR_NODE)
|
||||
{
|
||||
*out_color = *gsk_color_node_get_color (node);
|
||||
return TRUE;
|
||||
}
|
||||
else if (node_type == GSK_OPACITY_NODE)
|
||||
{
|
||||
if (!gsk_vulkan_render_pass_is_color_node (gsk_opacity_node_get_child (node),
|
||||
out_color))
|
||||
return FALSE;
|
||||
out_color->alpha *= gsk_opacity_node_get_opacity (node);
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_render_pass_append_scissor (GskVulkanRender *render,
|
||||
GskRenderNode *node,
|
||||
@@ -158,7 +188,7 @@ gsk_vulkan_render_pass_upload_texture (GskVulkanRender *render,
|
||||
|
||||
width = gdk_texture_get_width (texture);
|
||||
height = gdk_texture_get_height (texture);
|
||||
better_image = gsk_vulkan_image_new_for_offscreen (gsk_vulkan_render_get_context (render),
|
||||
better_image = gsk_vulkan_image_new_for_offscreen (gsk_vulkan_render_get_device (render),
|
||||
gdk_texture_get_format (texture),
|
||||
width, height);
|
||||
gsk_vulkan_render_pass_begin_op (render,
|
||||
@@ -1158,6 +1188,7 @@ gsk_vulkan_render_pass_add_mask_node (GskVulkanRenderPass *self,
|
||||
GskVulkanImage *source_image, *mask_image;
|
||||
graphene_rect_t source_tex_rect, mask_tex_rect;
|
||||
GskRenderNode *source, *mask;
|
||||
GdkRGBA source_color;
|
||||
GskMaskMode mode;
|
||||
|
||||
mode = gsk_mask_node_get_mask_mode (node);
|
||||
@@ -1178,7 +1209,7 @@ gsk_vulkan_render_pass_add_mask_node (GskVulkanRenderPass *self,
|
||||
|
||||
/* Use the glyph shader as an optimization */
|
||||
if (mode == GSK_MASK_MODE_ALPHA &&
|
||||
gsk_render_node_get_node_type (source) == GSK_COLOR_NODE)
|
||||
gsk_vulkan_render_pass_is_color_node (source, &source_color))
|
||||
{
|
||||
graphene_rect_t bounds;
|
||||
if (graphene_rect_intersection (&source->bounds, &mask->bounds, &bounds))
|
||||
@@ -1188,7 +1219,7 @@ gsk_vulkan_render_pass_add_mask_node (GskVulkanRenderPass *self,
|
||||
&bounds,
|
||||
&state->offset,
|
||||
&mask_tex_rect,
|
||||
gsk_color_node_get_color (source));
|
||||
&source_color);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -1213,6 +1244,122 @@ gsk_vulkan_render_pass_add_mask_node (GskVulkanRenderPass *self,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static inline gboolean
|
||||
gsk_vulkan_render_pass_add_fill_node (GskVulkanRenderPass *self,
|
||||
GskVulkanRender *render,
|
||||
const GskVulkanParseState *state,
|
||||
GskRenderNode *node)
|
||||
{
|
||||
GskRenderNode *child;
|
||||
graphene_rect_t clipped, child_tex_rect;
|
||||
GskVulkanImage *child_image, *mask_image;
|
||||
graphene_matrix_t projection;
|
||||
GdkRGBA color;
|
||||
int width, height;
|
||||
|
||||
child = gsk_fill_node_get_child (node);
|
||||
|
||||
if (gsk_vulkan_render_pass_is_color_node (child, &color))
|
||||
{
|
||||
gsk_vulkan_fill_op (render,
|
||||
gsk_vulkan_clip_get_shader_clip (&state->clip, &state->offset, &node->bounds),
|
||||
&state->offset,
|
||||
&node->bounds,
|
||||
gsk_fill_node_get_path (node),
|
||||
gsk_fill_node_get_fill_rule (node),
|
||||
&color);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
graphene_rect_offset_r (&state->clip.rect.bounds, - state->offset.x, - state->offset.y, &clipped);
|
||||
graphene_rect_intersection (&clipped, &node->bounds, &clipped);
|
||||
if (clipped.size.width == 0 || clipped.size.height == 0)
|
||||
return TRUE;
|
||||
|
||||
child_image = gsk_vulkan_render_pass_get_node_as_image (self,
|
||||
render,
|
||||
state,
|
||||
child,
|
||||
&child_tex_rect);
|
||||
if (child_image == NULL)
|
||||
return TRUE;
|
||||
|
||||
width = ceil (graphene_vec2_get_x (&state->scale) * clipped.size.width);
|
||||
height = ceil (graphene_vec2_get_y (&state->scale) * clipped.size.height);
|
||||
mask_image = gsk_vulkan_image_new_for_offscreen (gsk_vulkan_render_get_device (render),
|
||||
gdk_memory_depth_get_alpha_format (gsk_render_node_get_preferred_depth (child)),
|
||||
width, height);
|
||||
|
||||
gsk_vulkan_render_pass_begin_op (render,
|
||||
mask_image,
|
||||
&(cairo_rectangle_int_t) { 0, 0, width, height },
|
||||
VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
gsk_vulkan_scissor_op (render, &(cairo_rectangle_int_t) { 0, 0, width, height });
|
||||
graphene_matrix_init_ortho (&projection,
|
||||
0, width,
|
||||
0, height,
|
||||
2 * ORTHO_NEAR_PLANE - ORTHO_FAR_PLANE,
|
||||
ORTHO_FAR_PLANE);
|
||||
gsk_vulkan_push_constants_op (render, &state->scale, &projection, &GSK_ROUNDED_RECT_INIT_FROM_RECT (clipped));
|
||||
gsk_vulkan_fill_op (render,
|
||||
GSK_VULKAN_SHADER_CLIP_NONE,
|
||||
&GRAPHENE_POINT_INIT (- clipped.origin.x, - clipped.origin.y),
|
||||
&node->bounds,
|
||||
gsk_fill_node_get_path (node),
|
||||
gsk_fill_node_get_fill_rule (node),
|
||||
&(GdkRGBA) { 1.0, 1.0, 1.0, 1.0 });
|
||||
gsk_vulkan_render_pass_end_op (render,
|
||||
mask_image,
|
||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
|
||||
gsk_vulkan_mask_op (render,
|
||||
gsk_vulkan_clip_get_shader_clip (&state->clip, &state->offset, &node->bounds),
|
||||
&state->offset,
|
||||
child_image,
|
||||
&child->bounds,
|
||||
&child_tex_rect,
|
||||
mask_image,
|
||||
&node->bounds,
|
||||
&clipped,
|
||||
GSK_MASK_MODE_ALPHA);
|
||||
|
||||
g_object_unref (mask_image);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static inline gboolean
|
||||
gsk_vulkan_render_pass_add_stroke_node (GskVulkanRenderPass *self,
|
||||
GskVulkanRender *render,
|
||||
const GskVulkanParseState *state,
|
||||
GskRenderNode *node)
|
||||
{
|
||||
GskRenderNode *child;
|
||||
const GskStroke *stroke;
|
||||
GdkRGBA color;
|
||||
|
||||
child = gsk_stroke_node_get_child (node);
|
||||
stroke = gsk_stroke_node_get_stroke (node);
|
||||
|
||||
if (gsk_vulkan_render_pass_is_color_node (child, &color))
|
||||
{
|
||||
gsk_vulkan_stroke_op (render,
|
||||
gsk_vulkan_clip_get_shader_clip (&state->clip, &state->offset, &node->bounds),
|
||||
&state->offset,
|
||||
&node->bounds,
|
||||
gsk_stroke_node_get_path (node),
|
||||
stroke->line_width,
|
||||
stroke->line_cap,
|
||||
stroke->line_join,
|
||||
stroke->miter_limit,
|
||||
&color);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static inline gboolean
|
||||
gsk_vulkan_render_pass_add_debug_node (GskVulkanRenderPass *self,
|
||||
GskVulkanRender *render,
|
||||
@@ -1260,6 +1407,8 @@ static const GskVulkanRenderPassNodeFunc nodes_vtable[] = {
|
||||
[GSK_GL_SHADER_NODE] = NULL,
|
||||
[GSK_TEXTURE_SCALE_NODE] = gsk_vulkan_render_pass_add_texture_scale_node,
|
||||
[GSK_MASK_NODE] = gsk_vulkan_render_pass_add_mask_node,
|
||||
[GSK_FILL_NODE] = gsk_vulkan_render_pass_add_fill_node,
|
||||
[GSK_STROKE_NODE] = gsk_vulkan_render_pass_add_stroke_node,
|
||||
};
|
||||
|
||||
static void
|
||||
|
||||
@@ -276,16 +276,15 @@ gsk_vulkan_render_pass_op_offscreen (GskVulkanRender *render,
|
||||
GskRenderNode *node)
|
||||
{
|
||||
GskVulkanRenderPass *render_pass;
|
||||
GdkVulkanContext *context;
|
||||
GskVulkanImage *image;
|
||||
int width, height;
|
||||
|
||||
width = ceil (graphene_vec2_get_x (scale) * viewport->size.width);
|
||||
height = ceil (graphene_vec2_get_y (scale) * viewport->size.height);
|
||||
|
||||
context = gsk_vulkan_render_get_context (render);
|
||||
image = gsk_vulkan_image_new_for_offscreen (context,
|
||||
gdk_vulkan_context_get_offscreen_format (context,
|
||||
image = gsk_vulkan_image_new_for_offscreen (gsk_vulkan_render_get_device (render),
|
||||
gdk_vulkan_context_get_offscreen_format (
|
||||
gsk_vulkan_render_get_context (render),
|
||||
gsk_render_node_get_preferred_depth (node)),
|
||||
width, height);
|
||||
|
||||
|
||||
@@ -24,7 +24,8 @@ typedef void (* GskVulkanDownloadFunc) (gpointe
|
||||
gsize stride);
|
||||
|
||||
GskVulkanRender * gsk_vulkan_render_new (GskRenderer *renderer,
|
||||
GdkVulkanContext *context);
|
||||
GdkVulkanContext *context,
|
||||
GskVulkanDevice *device);
|
||||
void gsk_vulkan_render_free (GskVulkanRender *self);
|
||||
|
||||
gboolean gsk_vulkan_render_is_busy (GskVulkanRender *self);
|
||||
@@ -38,6 +39,7 @@ void gsk_vulkan_render_render (GskVulk
|
||||
|
||||
GskRenderer * gsk_vulkan_render_get_renderer (GskVulkanRender *self);
|
||||
GdkVulkanContext * gsk_vulkan_render_get_context (GskVulkanRender *self);
|
||||
GskVulkanDevice * gsk_vulkan_render_get_device (GskVulkanRender *self);
|
||||
|
||||
gpointer gsk_vulkan_render_alloc_op (GskVulkanRender *self,
|
||||
gsize size);
|
||||
|
||||
141
gsk/vulkan/gskvulkanstrokeop.c
Normal file
141
gsk/vulkan/gskvulkanstrokeop.c
Normal file
@@ -0,0 +1,141 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "gskvulkanstrokeopprivate.h"
|
||||
|
||||
#include "gskvulkanprivate.h"
|
||||
#include "gskvulkanshaderopprivate.h"
|
||||
|
||||
#include "gskpathprivate.h"
|
||||
|
||||
#include "vulkan/resources/stroke.vert.h"
|
||||
|
||||
typedef struct _GskVulkanStrokeOp GskVulkanStrokeOp;
|
||||
|
||||
struct _GskVulkanStrokeOp
|
||||
{
|
||||
GskVulkanShaderOp op;
|
||||
|
||||
graphene_point_t offset;
|
||||
graphene_rect_t rect;
|
||||
GskPath *path;
|
||||
float line_width;
|
||||
GskLineCap line_cap;
|
||||
GskLineJoin line_join;
|
||||
float miter_limit;
|
||||
GdkRGBA color;
|
||||
|
||||
gsize buffer_offset;
|
||||
};
|
||||
|
||||
static void
|
||||
gsk_vulkan_stroke_op_finish (GskVulkanOp *op)
|
||||
{
|
||||
GskVulkanStrokeOp *self = (GskVulkanStrokeOp *) op;
|
||||
|
||||
gsk_path_unref (self->path);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_stroke_op_print (GskVulkanOp *op,
|
||||
GString *string,
|
||||
guint indent)
|
||||
{
|
||||
GskVulkanStrokeOp *self = (GskVulkanStrokeOp *) op;
|
||||
|
||||
print_indent (string, indent);
|
||||
print_rect (string, &self->rect);
|
||||
g_string_append_printf (string, "stroke ");
|
||||
print_newline (string);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_stroke_op_collect_vertex_data (GskVulkanOp *op,
|
||||
guchar *data)
|
||||
{
|
||||
GskVulkanStrokeOp *self = (GskVulkanStrokeOp *) op;
|
||||
GskVulkanStrokeInstance *instance = (GskVulkanStrokeInstance *) (data + ((GskVulkanShaderOp *) op)->vertex_offset);
|
||||
|
||||
gsk_vulkan_rect_to_float (&self->rect, instance->rect);
|
||||
gsk_vulkan_rgba_to_float (&self->color, instance->color);
|
||||
gsk_vulkan_point_to_float (&self->offset, instance->offset);
|
||||
instance->points_id = self->buffer_offset;
|
||||
instance->line_width = self->line_width;
|
||||
instance->line_cap = self->line_cap;
|
||||
instance->line_join = self->line_join;
|
||||
instance->miter_limit = self->miter_limit;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_stroke_op_reserve_descriptor_sets (GskVulkanOp *op,
|
||||
GskVulkanRender *render)
|
||||
{
|
||||
GskVulkanStrokeOp *self = (GskVulkanStrokeOp *) op;
|
||||
const GskContour *contour;
|
||||
gsize size, i, n;
|
||||
guchar *mem;
|
||||
|
||||
size = sizeof (guint32);
|
||||
n = gsk_path_get_n_contours (self->path);
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
contour = gsk_path_get_contour (self->path, i);
|
||||
size += gsk_contour_get_shader_size (contour);
|
||||
}
|
||||
|
||||
mem = gsk_vulkan_render_get_buffer_memory (render,
|
||||
size,
|
||||
G_ALIGNOF (float),
|
||||
&self->buffer_offset);
|
||||
|
||||
*(guint32*) mem = n;
|
||||
mem += sizeof (guint32);
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
contour = gsk_path_get_contour (self->path, i);
|
||||
gsk_contour_to_shader (contour, mem);
|
||||
mem += gsk_contour_get_shader_size (contour);
|
||||
}
|
||||
}
|
||||
|
||||
static const GskVulkanShaderOpClass GSK_VULKAN_STROKE_OP_CLASS = {
|
||||
{
|
||||
GSK_VULKAN_OP_SIZE (GskVulkanStrokeOp),
|
||||
GSK_VULKAN_STAGE_SHADER,
|
||||
gsk_vulkan_stroke_op_finish,
|
||||
gsk_vulkan_stroke_op_print,
|
||||
gsk_vulkan_shader_op_count_vertex_data,
|
||||
gsk_vulkan_stroke_op_collect_vertex_data,
|
||||
gsk_vulkan_stroke_op_reserve_descriptor_sets,
|
||||
gsk_vulkan_shader_op_command
|
||||
},
|
||||
"stroke",
|
||||
0,
|
||||
&gsk_vulkan_stroke_info,
|
||||
};
|
||||
|
||||
void
|
||||
gsk_vulkan_stroke_op (GskVulkanRender *render,
|
||||
GskVulkanShaderClip clip,
|
||||
const graphene_point_t *offset,
|
||||
const graphene_rect_t *rect,
|
||||
GskPath *path,
|
||||
float line_width,
|
||||
GskLineCap line_cap,
|
||||
GskLineJoin line_join,
|
||||
float miter_limit,
|
||||
const GdkRGBA *color)
|
||||
{
|
||||
GskVulkanStrokeOp *self;
|
||||
|
||||
self = (GskVulkanStrokeOp *) gsk_vulkan_shader_op_alloc (render, &GSK_VULKAN_STROKE_OP_CLASS, clip, NULL);
|
||||
|
||||
self->offset = *offset;
|
||||
self->rect = *rect;
|
||||
self->path = gsk_path_ref (path);
|
||||
self->line_width = line_width;
|
||||
self->line_cap = line_cap;
|
||||
self->line_join = line_join;
|
||||
self->miter_limit = miter_limit;
|
||||
self->color = *color;
|
||||
}
|
||||
22
gsk/vulkan/gskvulkanstrokeopprivate.h
Normal file
22
gsk/vulkan/gskvulkanstrokeopprivate.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "gskvulkanopprivate.h"
|
||||
|
||||
#include "gskenums.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
void gsk_vulkan_stroke_op (GskVulkanRender *render,
|
||||
GskVulkanShaderClip clip,
|
||||
const graphene_point_t *offset,
|
||||
const graphene_rect_t *rect,
|
||||
GskPath *path,
|
||||
float line_width,
|
||||
GskLineCap line_cap,
|
||||
GskLineJoin line_join,
|
||||
float miter_limit,
|
||||
const GdkRGBA *color);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
@@ -38,15 +38,13 @@ gsk_vulkan_upload_op_command_with_area (GskVulkanOp *op,
|
||||
guchar *data;
|
||||
|
||||
stride = area->width * gdk_memory_format_bytes_per_pixel (gsk_vulkan_image_get_format (image));
|
||||
*buffer = gsk_vulkan_buffer_new_map (gsk_vulkan_render_get_context (render),
|
||||
*buffer = gsk_vulkan_buffer_new_map (gsk_vulkan_render_get_device (render),
|
||||
area->height * stride,
|
||||
GSK_VULKAN_WRITE);
|
||||
data = gsk_vulkan_buffer_map (*buffer);
|
||||
data = gsk_vulkan_buffer_get_data (*buffer);
|
||||
|
||||
draw_func (op, data, stride);
|
||||
|
||||
gsk_vulkan_buffer_unmap (*buffer);
|
||||
|
||||
vkCmdPipelineBarrier (command_buffer,
|
||||
VK_PIPELINE_STAGE_HOST_BIT,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
@@ -112,12 +110,11 @@ gsk_vulkan_upload_op_command (GskVulkanOp *op,
|
||||
gsize stride;
|
||||
guchar *data;
|
||||
|
||||
data = gsk_vulkan_image_try_map (image, &stride);
|
||||
data = gsk_vulkan_image_get_data (image, &stride);
|
||||
if (data)
|
||||
{
|
||||
draw_func (op, data, stride);
|
||||
|
||||
gsk_vulkan_image_unmap (image);
|
||||
*buffer = NULL;
|
||||
|
||||
return op->next;
|
||||
@@ -221,7 +218,7 @@ gsk_vulkan_upload_texture_op (GskVulkanRender *render,
|
||||
self = (GskVulkanUploadTextureOp *) gsk_vulkan_op_alloc (render, &GSK_VULKAN_UPLOAD_TEXTURE_OP_CLASS);
|
||||
|
||||
self->texture = g_object_ref (texture);
|
||||
self->image = gsk_vulkan_image_new_for_upload (gsk_vulkan_render_get_context (render),
|
||||
self->image = gsk_vulkan_image_new_for_upload (gsk_vulkan_render_get_device (render),
|
||||
gdk_texture_get_format (texture),
|
||||
gdk_texture_get_width (texture),
|
||||
gdk_texture_get_height (texture));
|
||||
@@ -338,7 +335,7 @@ gsk_vulkan_upload_cairo_op (GskVulkanRender *render,
|
||||
self = (GskVulkanUploadCairoOp *) gsk_vulkan_op_alloc (render, &GSK_VULKAN_UPLOAD_CAIRO_OP_CLASS);
|
||||
|
||||
self->node = gsk_render_node_ref (node);
|
||||
self->image = gsk_vulkan_image_new_for_upload (gsk_vulkan_render_get_context (render),
|
||||
self->image = gsk_vulkan_image_new_for_upload (gsk_vulkan_render_get_device (render),
|
||||
GDK_MEMORY_DEFAULT,
|
||||
ceil (graphene_vec2_get_x (scale) * viewport->size.width),
|
||||
ceil (graphene_vec2_get_y (scale) * viewport->size.height));
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#version 420 core
|
||||
#version 450
|
||||
#extension GL_EXT_debug_printf : enable
|
||||
|
||||
#include "clip.frag.glsl"
|
||||
#include "rect.frag.glsl"
|
||||
|
||||
@@ -6,4 +6,5 @@ layout(set = 1, binding = 0) readonly buffer FloatBuffers {
|
||||
#define get_sampler(id) textures[id]
|
||||
#define get_buffer(id) buffers[id]
|
||||
#define get_float(id) get_buffer(0).floats[id]
|
||||
#define get_int(id) floatBitsToInt (get_float (id))
|
||||
|
||||
|
||||
382
gsk/vulkan/resources/fill.frag
Normal file
382
gsk/vulkan/resources/fill.frag
Normal file
@@ -0,0 +1,382 @@
|
||||
#version 450
|
||||
|
||||
#include "common.frag.glsl"
|
||||
#include "clip.frag.glsl"
|
||||
#include "rect.frag.glsl"
|
||||
|
||||
#define GSK_FILL_RULE_WINDING 0
|
||||
#define GSK_FILL_RULE_EVEN_ODD 1
|
||||
|
||||
#define GSK_PATH_MOVE 0
|
||||
#define GSK_PATH_CLOSE 1
|
||||
#define GSK_PATH_LINE 2
|
||||
#define GSK_PATH_CURVE 3
|
||||
#define GSK_PATH_CONIC 4
|
||||
|
||||
layout(location = 0) in vec2 in_pos;
|
||||
layout(location = 1) in flat Rect in_rect;
|
||||
layout(location = 2) in flat vec4 in_color;
|
||||
layout(location = 3) in flat vec2 in_offset;
|
||||
layout(location = 4) in flat int in_points_id;
|
||||
layout(location = 5) in flat int in_fill_rule;
|
||||
|
||||
layout(location = 0) out vec4 color;
|
||||
|
||||
vec2
|
||||
get_point (inout int idx)
|
||||
{
|
||||
vec2 p = vec2 (get_float (idx), get_float (idx + 1));
|
||||
idx += 2;
|
||||
p *= push.scale;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
|
||||
float
|
||||
cross2d (vec2 v0, vec2 v1)
|
||||
{
|
||||
return v0.x * v1.y - v0.y * v1.x;
|
||||
}
|
||||
|
||||
float
|
||||
line_distance_squared (vec2 start,
|
||||
vec2 end,
|
||||
float d0)
|
||||
{
|
||||
vec2 e = end - start;
|
||||
vec2 v = - start;
|
||||
vec2 pq = v - e * clamp (dot (v, e) / dot(e, e), 0.0, 1.0);
|
||||
return dot (pq, pq);
|
||||
}
|
||||
|
||||
int
|
||||
line_winding (vec2 start,
|
||||
vec2 end)
|
||||
{
|
||||
// winding number from http://geomalgorithms.com/a03-_inclusion.html
|
||||
float val3 = cross2d (start, end - start); //isLeft
|
||||
bvec3 cond = bvec3 (start.y <= 0.0, end.y > 0.0, val3 > 0.0);
|
||||
if (all (cond))
|
||||
return 1;
|
||||
else if (all (not (cond)))
|
||||
return -1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Modified from http://tog.acm.org/resources/GraphicsGems/gems/Roots3And4.c
|
||||
// Credits to Doublefresh for hinting there
|
||||
int
|
||||
solve_quadratic (vec2 coeffs,
|
||||
inout vec2 roots)
|
||||
{
|
||||
// normal form: x^2 + px + q = 0
|
||||
float p = coeffs[1] / 2.;
|
||||
float q = coeffs[0];
|
||||
|
||||
float D = p * p - q;
|
||||
|
||||
if (D < 0.)
|
||||
return 0;
|
||||
|
||||
roots[0] = -sqrt (D) - p;
|
||||
roots[1] = sqrt (D) - p;
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
vec3
|
||||
sort3 (vec3 v)
|
||||
{
|
||||
vec2 t = vec2 ( min(v.x, v.y), max (v.x, v.y));
|
||||
return vec3 (min (t.x, v.z), min (max (v.z, t.x), t.y), max (t.y, v.z));
|
||||
}
|
||||
|
||||
//From Trisomie21
|
||||
//But instead of his cancellation fix i'm using a newton iteration
|
||||
int solve_cubic(vec3 coeffs, inout vec3 r){
|
||||
|
||||
float a = coeffs[2];
|
||||
float b = coeffs[1];
|
||||
float c = coeffs[0];
|
||||
|
||||
float p = b - a*a / 3.0;
|
||||
float q = a * (2.0*a*a - 9.0*b) / 27.0 + c;
|
||||
float p3 = p*p*p;
|
||||
float d = q*q + 4.0*p3 / 27.0;
|
||||
float offset = -a / 3.0;
|
||||
if(d >= 0.0)
|
||||
{
|
||||
// Single solution
|
||||
float z = sqrt(d);
|
||||
float u = (-q + z) / 2.0;
|
||||
float v = (-q - z) / 2.0;
|
||||
u = sign (u) * pow (abs (u), 1.0 / 3.0);
|
||||
v = sign (v) * pow (abs (v), 1.0 / 3.0);
|
||||
r[0] = offset + u + v;
|
||||
|
||||
//Single newton iteration to account for cancellation
|
||||
float f = ((r[0] + a) * r[0] + b) * r[0] + c;
|
||||
float f1 = (3. * r[0] + 2. * a) * r[0] + b;
|
||||
|
||||
r[0] -= f / f1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
float u = sqrt (-p / 3.0);
|
||||
float v = acos (-sqrt ( -27.0 / p3) * q / 2.0) / 3.0;
|
||||
float m = cos (v), n = sin (v)*1.732050808;
|
||||
|
||||
// Single newton iteration to account for cancellation
|
||||
// (once for every root)
|
||||
r[0] = offset + u * (m + m);
|
||||
r[1] = offset - u * (n + m);
|
||||
r[2] = offset + u * (n - m);
|
||||
|
||||
vec3 f = ((r + a) * r + b) * r + c;
|
||||
vec3 f1 = (3. * r + 2. * a) * r + b;
|
||||
|
||||
r -= f / f1;
|
||||
r = sort3 (r);
|
||||
|
||||
return 3;
|
||||
}
|
||||
|
||||
float
|
||||
cubic_bezier_normal_iteration (float t,
|
||||
vec2 a0,
|
||||
vec2 a1,
|
||||
vec2 a2,
|
||||
vec2 a3)
|
||||
{
|
||||
// Horner's method
|
||||
vec2 a_2 = a2 + t * a3;
|
||||
vec2 a_1 = a1 + t * a_2;
|
||||
vec2 b_2 = a_2 + t * a3;
|
||||
|
||||
vec2 uv_to_p = a0 + t * a_1;
|
||||
vec2 tang = a_1 + t*b_2;
|
||||
|
||||
float l_tang = dot (tang,tang);
|
||||
return t - dot (tang, uv_to_p) / l_tang;
|
||||
}
|
||||
|
||||
float
|
||||
cubic_distance_squared (vec2 p0,
|
||||
vec2 p1,
|
||||
vec2 p2,
|
||||
vec2 p3,
|
||||
float d0)
|
||||
{
|
||||
if (dot (p0, p0) >= d0 &&
|
||||
dot (p1, p1) >= d0 &&
|
||||
dot (p2, p2) >= d0 &&
|
||||
dot (p3, p3) >= d0)
|
||||
return d0;
|
||||
|
||||
vec2 a3 = (-p0 + 3. * p1 - 3. * p2 + p3);
|
||||
vec2 a2 = (3. * p0 - 6. * p1 + 3. * p2);
|
||||
vec2 a1 = (-3. * p0 + 3. * p1);
|
||||
vec2 a0 = p0;
|
||||
|
||||
float t;
|
||||
vec3 params = vec3 (0, .5, 1);
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
t = params[i];
|
||||
for (int j = 0; j < 3; j++)
|
||||
{
|
||||
t = cubic_bezier_normal_iteration (t, a0, a1, a2, a3);
|
||||
}
|
||||
t = clamp (t, 0., 1.);
|
||||
vec2 uv_to_p = ((a3 * t + a2) * t + a1) * t + a0;
|
||||
d0 = min (d0, dot (uv_to_p, uv_to_p));
|
||||
}
|
||||
|
||||
return d0;
|
||||
}
|
||||
|
||||
int
|
||||
cubic_winding (vec2 p0,
|
||||
vec2 p1,
|
||||
vec2 p2,
|
||||
vec2 p3)
|
||||
{
|
||||
vec3 roots = vec3 (1e38);
|
||||
int n_roots;
|
||||
int w = 0;
|
||||
|
||||
if ((p0.y > 0 && p1.y > 0 && p2.y > 0 && p3.y > 0) ||
|
||||
(p0.y <= 0 && p1.y <= 0 && p2.y <= 0 && p3.y <= 0))
|
||||
return 0;
|
||||
|
||||
if (0 <= min (min (p0.x, p1.x), min (p2.x, p3.x)))
|
||||
{
|
||||
if (p0.y <= 0 && p3.y > 0)
|
||||
return 1;
|
||||
else if (p0.y > 0 && p3.y <= 0)
|
||||
return -1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
vec2 a3 = (-p0 + 3. * p1 - 3. * p2 + p3);
|
||||
vec2 a2 = (3. * p0 - 6. * p1 + 3. * p2);
|
||||
vec2 a1 = (-3. * p0 + 3. * p1);
|
||||
vec2 a0 = p0;
|
||||
|
||||
if (abs (a3.y) < max (max (a0.y, a1.y), a2.y) * .001)
|
||||
n_roots = solve_quadratic (vec2 (a0.y / a2.y, a1.y / a2.y), roots.xy);
|
||||
else
|
||||
n_roots = solve_cubic (vec3 (a0.y / a3.y, a1.y / a3.y, a2.y / a3.y), roots);
|
||||
|
||||
bool greater = p0.y > 0.0;
|
||||
float last = 0;
|
||||
float last_x = p0.x;
|
||||
for (int i = 0; i < n_roots; i++)
|
||||
{
|
||||
if (roots[i] < 0. || roots[i] > 1.)
|
||||
continue;
|
||||
|
||||
float t = 0.5 * (roots[i] + last);
|
||||
bool new_greater = fma (fma (fma (a3.y, t, a2.y), t, a1.y), t, a0.y) > 0.0;
|
||||
if (new_greater != greater)
|
||||
{
|
||||
greater = new_greater;
|
||||
|
||||
if (last_x > 0.0)
|
||||
w += (greater ? 1 : -1);
|
||||
}
|
||||
|
||||
last_x = fma (fma (fma (a3.x, roots[i], a2.x), roots[i], a1.x), roots[i], a0.x);
|
||||
last = roots[i];
|
||||
}
|
||||
|
||||
bool new_greater = p3.y > 0;
|
||||
if (new_greater != greater)
|
||||
{
|
||||
if (last_x > 0.0)
|
||||
w += (new_greater ? 1 : -1);
|
||||
}
|
||||
|
||||
return w;
|
||||
}
|
||||
|
||||
void
|
||||
standard_contour_distance (vec2 p,
|
||||
inout float d,
|
||||
inout int path_idx,
|
||||
inout int w)
|
||||
{
|
||||
int n_points = get_int (path_idx);
|
||||
int ops_idx = path_idx + 1;
|
||||
int pts_idx = ops_idx + n_points;
|
||||
|
||||
vec2 start = get_point (pts_idx) - p;
|
||||
int op = GSK_PATH_MOVE;
|
||||
|
||||
for (int i = 1; i < n_points; i++)
|
||||
{
|
||||
op = get_int (ops_idx + i);
|
||||
switch (op)
|
||||
{
|
||||
case GSK_PATH_MOVE:
|
||||
discard;
|
||||
|
||||
case GSK_PATH_CLOSE:
|
||||
case GSK_PATH_LINE:
|
||||
{
|
||||
vec2 end = get_point (pts_idx) - p;
|
||||
d = line_distance_squared (start, end, d);
|
||||
w += line_winding (start, end);
|
||||
start = end;
|
||||
}
|
||||
break;
|
||||
|
||||
case GSK_PATH_CURVE:
|
||||
{
|
||||
vec2 p1 = get_point (pts_idx) - p;
|
||||
vec2 p2 = get_point (pts_idx) - p;
|
||||
vec2 p3 = get_point (pts_idx) - p;
|
||||
d = cubic_distance_squared (start, p1, p2, p3, d);
|
||||
w += cubic_winding (start, p1, p2, p3);
|
||||
start = p3;
|
||||
}
|
||||
break;
|
||||
|
||||
case GSK_PATH_CONIC:
|
||||
{
|
||||
pts_idx += 4;
|
||||
vec2 end = get_point (pts_idx) - p;
|
||||
d = line_distance_squared (start, end, d);
|
||||
w += line_winding (start, end);
|
||||
start = end;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (op != GSK_PATH_CLOSE)
|
||||
{
|
||||
int tmp_idx = ops_idx + n_points;
|
||||
vec2 end = get_point (tmp_idx) - p;
|
||||
d = line_distance_squared (start, end, d);
|
||||
w += line_winding (start, end);
|
||||
}
|
||||
|
||||
path_idx = pts_idx;
|
||||
}
|
||||
|
||||
float
|
||||
path_distance (vec2 p)
|
||||
{
|
||||
int path_idx = in_points_id;
|
||||
|
||||
int n = get_int (path_idx);
|
||||
path_idx++;
|
||||
int w = 0;
|
||||
float d = 1e38;
|
||||
|
||||
while (n-- > 0)
|
||||
{
|
||||
standard_contour_distance (p, d, path_idx, w);
|
||||
}
|
||||
|
||||
if (in_fill_rule == GSK_FILL_RULE_WINDING)
|
||||
{
|
||||
if (w == 0)
|
||||
return sqrt (d);
|
||||
else if (abs (w) == 1)
|
||||
return - sqrt (d);
|
||||
else
|
||||
return -1.0; /* not really, but good enough */
|
||||
}
|
||||
else if (in_fill_rule == GSK_FILL_RULE_EVEN_ODD)
|
||||
{
|
||||
return sqrt (d) * (w % 2 == 0 ? 1.0 : -1.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
discard;
|
||||
}
|
||||
}
|
||||
|
||||
float
|
||||
path_coverage (vec2 p)
|
||||
{
|
||||
float d = path_distance (p);
|
||||
|
||||
return clamp (0.5 - d, 0.0, 1.0);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
float cov = path_coverage (in_pos - in_offset * push.scale);
|
||||
float alpha = in_color.a * cov;
|
||||
color = clip_scaled (in_pos, vec4(in_color.rgb, 1) * alpha);
|
||||
}
|
||||
28
gsk/vulkan/resources/fill.vert
Normal file
28
gsk/vulkan/resources/fill.vert
Normal file
@@ -0,0 +1,28 @@
|
||||
#version 450
|
||||
|
||||
#include "common.vert.glsl"
|
||||
#include "rect.vert.glsl"
|
||||
|
||||
layout(location = 0) in vec4 in_rect;
|
||||
layout(location = 1) in vec4 in_color;
|
||||
layout(location = 2) in vec2 in_offset;
|
||||
layout(location = 3) in int in_points_id;
|
||||
layout(location = 4) in int in_fill_rule;
|
||||
|
||||
layout(location = 0) out vec2 out_pos;
|
||||
layout(location = 1) out flat Rect out_rect;
|
||||
layout(location = 2) out flat vec4 out_color;
|
||||
layout(location = 3) out flat vec2 out_offset;
|
||||
layout(location = 4) out flat int out_points_id;
|
||||
layout(location = 5) out flat int out_fill_rule;
|
||||
|
||||
void main() {
|
||||
Rect r = rect_from_gsk (in_rect + vec4(in_offset, 0.0, 0.0));
|
||||
vec2 pos = set_position_from_rect (r);
|
||||
out_pos = pos;
|
||||
out_rect = r;
|
||||
out_color = in_color;
|
||||
out_offset = in_offset;
|
||||
out_points_id = in_points_id;
|
||||
out_fill_rule = in_fill_rule;
|
||||
}
|
||||
@@ -19,11 +19,13 @@ gsk_private_vulkan_fragment_shaders = [
|
||||
'color-matrix.frag',
|
||||
'convert.frag',
|
||||
'cross-fade.frag',
|
||||
'fill.frag',
|
||||
'glyph.frag',
|
||||
'inset-shadow.frag',
|
||||
'linear.frag',
|
||||
'mask.frag',
|
||||
'outset-shadow.frag',
|
||||
'stroke.frag',
|
||||
'texture.frag',
|
||||
]
|
||||
|
||||
@@ -35,11 +37,13 @@ gsk_private_vulkan_vertex_shaders = [
|
||||
'color-matrix.vert',
|
||||
'convert.vert',
|
||||
'cross-fade.vert',
|
||||
'fill.vert',
|
||||
'glyph.vert',
|
||||
'inset-shadow.vert',
|
||||
'linear.vert',
|
||||
'mask.vert',
|
||||
'outset-shadow.vert',
|
||||
'stroke.vert',
|
||||
'texture.vert',
|
||||
]
|
||||
|
||||
|
||||
315
gsk/vulkan/resources/stroke.frag
Normal file
315
gsk/vulkan/resources/stroke.frag
Normal file
@@ -0,0 +1,315 @@
|
||||
#version 450
|
||||
|
||||
#include "common.frag.glsl"
|
||||
#include "clip.frag.glsl"
|
||||
#include "rect.frag.glsl"
|
||||
|
||||
#define GSK_LINE_CAP_BUTT 0
|
||||
#define GSK_LINE_CAP_ROUND 1
|
||||
#define GSK_LINE_CAP_SQUARE 2
|
||||
|
||||
#define GSK_LINE_JOIN_MITER 0
|
||||
#define GSK_LINE_JOIN_MITER_CLIP 1
|
||||
#define GSK_LINE_JOIN_ROUND 2
|
||||
#define GSK_LINE_JOIN_BEVEL 3
|
||||
|
||||
#define GSK_PATH_MOVE 0
|
||||
#define GSK_PATH_CLOSE 1
|
||||
#define GSK_PATH_LINE 2
|
||||
#define GSK_PATH_CURVE 3
|
||||
#define GSK_PATH_CONIC 4
|
||||
|
||||
layout(location = 0) in vec2 in_pos;
|
||||
layout(location = 1) in flat Rect in_rect;
|
||||
layout(location = 2) in flat vec4 in_color;
|
||||
layout(location = 3) in flat vec2 in_offset;
|
||||
layout(location = 4) in flat int in_points_id;
|
||||
layout(location = 5) in flat float in_half_line_width;
|
||||
layout(location = 6) in flat int in_line_cap;
|
||||
layout(location = 7) in flat int in_line_join;
|
||||
layout(location = 8) in flat float in_miter_limit;
|
||||
|
||||
layout(location = 0) out vec4 color;
|
||||
|
||||
float scale;
|
||||
float line_width;
|
||||
|
||||
vec2
|
||||
get_point (inout int idx)
|
||||
{
|
||||
vec2 p = vec2 (get_float (idx), get_float (idx + 1));
|
||||
idx += 2;
|
||||
|
||||
return p * scale;
|
||||
}
|
||||
|
||||
|
||||
|
||||
float
|
||||
cross2d (vec2 v0, vec2 v1)
|
||||
{
|
||||
return v0.x * v1.y - v0.y * v1.x;
|
||||
}
|
||||
|
||||
float
|
||||
line_distance (vec2 start,
|
||||
vec2 end,
|
||||
out vec2 tan)
|
||||
{
|
||||
tan = end - start;
|
||||
float offset = dot (-start, tan) / dot (tan, tan);
|
||||
if (offset < 0.0 || offset > 1.0)
|
||||
return 1e38;
|
||||
vec2 pq = start + tan * offset;
|
||||
return length (pq) - line_width;
|
||||
}
|
||||
|
||||
float
|
||||
cubic_bezier_normal_iteration (float t,
|
||||
vec2 a0,
|
||||
vec2 a1,
|
||||
vec2 a2,
|
||||
vec2 a3)
|
||||
{
|
||||
// Horner's method
|
||||
vec2 a_2 = a2 + t * a3;
|
||||
vec2 a_1 = a1 + t * a_2;
|
||||
vec2 b_2 = a_2 + t * a3;
|
||||
|
||||
vec2 uv_to_p = a0 + t * a_1;
|
||||
vec2 tang = a_1 + t * b_2;
|
||||
|
||||
float l_tang = dot (tang, tang);
|
||||
return t - dot (tang, uv_to_p) / l_tang;
|
||||
}
|
||||
|
||||
void
|
||||
cubic_distance (vec2 p0,
|
||||
vec2 p1,
|
||||
vec2 p2,
|
||||
vec2 p3,
|
||||
inout float d,
|
||||
inout vec2 p0_tan,
|
||||
inout vec2 p1_tan)
|
||||
{
|
||||
vec2 a3 = (-p0 + 3. * p1 - 3. * p2 + p3);
|
||||
vec2 a2 = (3. * p0 - 6. * p1 + 3. * p2);
|
||||
vec2 a1 = (-3. * p0 + 3. * p1);
|
||||
vec2 a0 = p0;
|
||||
|
||||
p0_tan = a1;
|
||||
p1_tan = 3 * a3 + 2 * a2 + a1;
|
||||
|
||||
vec2 tmp = min (min (p0, p1), min (p2, p3)) - line_width;
|
||||
if (tmp.x >= d || tmp.y >= d)
|
||||
return;
|
||||
tmp = max (max (p0, p1), max (p2, p3)) + line_width;
|
||||
if (tmp.x <= - d || tmp.y <= - d)
|
||||
return;
|
||||
|
||||
float t;
|
||||
vec3 params = vec3 (0, .5, 1);
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
t = params[i];
|
||||
for (int j = 0; j < 3; j++)
|
||||
{
|
||||
t = cubic_bezier_normal_iteration (t, a0, a1, a2, a3);
|
||||
}
|
||||
if (t < 0.0 || t > 1.0)
|
||||
continue;
|
||||
|
||||
vec2 uv = ((a3 * t + a2) * t + a1) * t + a0;
|
||||
d = min (d, length (uv) - line_width);
|
||||
}
|
||||
}
|
||||
|
||||
float
|
||||
cap_distance (vec2 p,
|
||||
vec2 p_tan)
|
||||
{
|
||||
switch (in_line_cap)
|
||||
{
|
||||
case GSK_LINE_CAP_BUTT:
|
||||
return 1e38;
|
||||
|
||||
case GSK_LINE_CAP_ROUND:
|
||||
return length (p) - line_width;
|
||||
|
||||
case GSK_LINE_CAP_SQUARE:
|
||||
vec2 unused;
|
||||
return line_distance (p, p - line_width * normalize (p_tan), unused);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
float
|
||||
ray_distance (vec2 p,
|
||||
vec2 dir,
|
||||
out float offset)
|
||||
{
|
||||
offset = dot (-p, dir) / dot (dir, dir);
|
||||
vec2 pq = p + dir * offset;
|
||||
return length (pq) - line_width;
|
||||
}
|
||||
|
||||
float
|
||||
join_distance (vec2 p,
|
||||
vec2 in_tan,
|
||||
vec2 out_tan)
|
||||
{
|
||||
out_tan = - out_tan;
|
||||
|
||||
float in_d, out_d;
|
||||
float d = ray_distance (p, in_tan, in_d);
|
||||
d = max (d, ray_distance (p, out_tan, out_d));
|
||||
if (in_d < 0 || out_d < 0)
|
||||
return 1e38;
|
||||
|
||||
if (in_line_join == GSK_LINE_JOIN_ROUND)
|
||||
return length (p) - line_width;
|
||||
|
||||
in_tan = normalize (in_tan);
|
||||
out_tan = normalize (out_tan);
|
||||
vec2 bisector = normalize (in_tan + out_tan);
|
||||
|
||||
float miter_d;
|
||||
ray_distance (p, bisector, miter_d);
|
||||
|
||||
float bevel_d;
|
||||
ray_distance (line_width * vec2(in_tan.y, -in_tan.x), bisector, bevel_d);
|
||||
bevel_d = abs (bevel_d);
|
||||
|
||||
if (in_line_join == GSK_LINE_JOIN_BEVEL)
|
||||
return max (d, miter_d - bevel_d);
|
||||
else if (in_line_join == GSK_LINE_JOIN_MITER_CLIP)
|
||||
return max (d, miter_d - max (bevel_d, in_miter_limit * line_width));
|
||||
|
||||
/* GSK_LINE_JOIN_MITER is the fallback, so no if() */
|
||||
if (miter_d > in_miter_limit * line_width)
|
||||
return max (d, miter_d - bevel_d);
|
||||
else
|
||||
return max (d, miter_d - in_miter_limit * line_width);
|
||||
}
|
||||
|
||||
void
|
||||
standard_contour_stroke_distance (vec2 p,
|
||||
inout int path_idx,
|
||||
inout float d)
|
||||
{
|
||||
int n_points = get_int (path_idx);
|
||||
int ops_idx = path_idx + 1;
|
||||
int pts_idx = ops_idx + n_points;
|
||||
|
||||
vec2 start = get_point (pts_idx) - p;
|
||||
vec2 start_tan = vec2(1, 0);
|
||||
vec2 p0 = start;
|
||||
vec2 p0_tan;
|
||||
int op = GSK_PATH_MOVE;
|
||||
|
||||
for (int i = 1; i < n_points && d > -1.0; i++)
|
||||
{
|
||||
op = get_int (ops_idx + i);
|
||||
switch (op)
|
||||
{
|
||||
case GSK_PATH_MOVE:
|
||||
discard;
|
||||
|
||||
case GSK_PATH_CLOSE:
|
||||
case GSK_PATH_LINE:
|
||||
{
|
||||
vec2 p1 = get_point (pts_idx) - p;
|
||||
vec2 line_tan;
|
||||
d = min (d, line_distance (p0, p1, line_tan));
|
||||
if (i > 1)
|
||||
d = min (d, join_distance (p0, p0_tan, line_tan));
|
||||
else
|
||||
start_tan = line_tan;
|
||||
p0_tan = line_tan;
|
||||
p0 = p1;
|
||||
}
|
||||
break;
|
||||
|
||||
case GSK_PATH_CURVE:
|
||||
{
|
||||
vec2 p1 = get_point (pts_idx) - p;
|
||||
vec2 p2 = get_point (pts_idx) - p;
|
||||
vec2 p3 = get_point (pts_idx) - p;
|
||||
vec2 start, end;
|
||||
cubic_distance (p0, p1, p2, p3, d, start, end);
|
||||
if (i > 1)
|
||||
d = min (d, join_distance (p0, p0_tan, start));
|
||||
else
|
||||
start_tan = start;
|
||||
p0 = p3;
|
||||
p0_tan = end;
|
||||
}
|
||||
break;
|
||||
|
||||
case GSK_PATH_CONIC:
|
||||
{
|
||||
/* FIXME */
|
||||
pts_idx += 4;
|
||||
vec2 p1 = get_point (pts_idx) - p;
|
||||
vec2 line_tan;
|
||||
d = min (d, line_distance (p0, p1, line_tan));
|
||||
if (i > 1)
|
||||
d = min (d, join_distance (p0, p0_tan, line_tan));
|
||||
else
|
||||
start_tan = line_tan;
|
||||
p0_tan = line_tan;
|
||||
p0 = p1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (op == GSK_PATH_CLOSE)
|
||||
{
|
||||
d = min (d, join_distance (p0, p0_tan, start_tan));
|
||||
}
|
||||
else
|
||||
{
|
||||
d = min (d, cap_distance (start, start_tan));
|
||||
d = min (d, cap_distance (p0, - p0_tan));
|
||||
}
|
||||
|
||||
path_idx = pts_idx;
|
||||
}
|
||||
|
||||
float
|
||||
stroke_distance (vec2 p)
|
||||
{
|
||||
int path_idx = in_points_id;
|
||||
|
||||
int n = get_int (path_idx);
|
||||
path_idx++;
|
||||
float d = 1.0;
|
||||
|
||||
while (n-- > 0 && d > -1.0)
|
||||
{
|
||||
standard_contour_stroke_distance (p, path_idx, d);
|
||||
}
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
float
|
||||
stroke_coverage (vec2 p)
|
||||
{
|
||||
float d = stroke_distance (p * scale);
|
||||
|
||||
return clamp (0.5 - d, 0, 1);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
scale = max (push.scale.x, push.scale.y);
|
||||
line_width = in_half_line_width * scale;
|
||||
|
||||
float alpha = in_color.a * rect_coverage (in_rect, in_pos);
|
||||
alpha *= stroke_coverage (in_pos / push.scale - in_offset);
|
||||
color = clip_scaled (in_pos, vec4(in_color.rgb, 1) * alpha);
|
||||
}
|
||||
37
gsk/vulkan/resources/stroke.vert
Normal file
37
gsk/vulkan/resources/stroke.vert
Normal file
@@ -0,0 +1,37 @@
|
||||
#version 450
|
||||
|
||||
#include "common.vert.glsl"
|
||||
#include "rect.vert.glsl"
|
||||
|
||||
layout(location = 0) in vec4 in_rect;
|
||||
layout(location = 1) in vec4 in_color;
|
||||
layout(location = 2) in vec2 in_offset;
|
||||
layout(location = 3) in int in_points_id;
|
||||
layout(location = 4) in float in_line_width;
|
||||
layout(location = 5) in int in_line_cap;
|
||||
layout(location = 6) in int in_line_join;
|
||||
layout(location = 7) in float in_miter_limit;
|
||||
|
||||
layout(location = 0) out vec2 out_pos;
|
||||
layout(location = 1) out flat Rect out_rect;
|
||||
layout(location = 2) out flat vec4 out_color;
|
||||
layout(location = 3) out flat vec2 out_offset;
|
||||
layout(location = 4) out flat int out_points_id;
|
||||
layout(location = 5) out flat float out_half_line_width;
|
||||
layout(location = 6) out flat int out_line_cap;
|
||||
layout(location = 7) out flat int out_line_join;
|
||||
layout(location = 8) out flat float out_miter_limit;
|
||||
|
||||
void main() {
|
||||
Rect r = rect_from_gsk (in_rect + vec4(in_offset, 0.0, 0.0));
|
||||
vec2 pos = set_position_from_rect (r);
|
||||
out_pos = pos;
|
||||
out_rect = r;
|
||||
out_color = in_color;
|
||||
out_offset = in_offset;
|
||||
out_points_id = in_points_id;
|
||||
out_half_line_width = 0.5 * in_line_width;
|
||||
out_line_cap = in_line_cap;
|
||||
out_line_join = in_line_join;
|
||||
out_miter_limit = in_miter_limit;
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
|
||||
#include "gsk/gskrendernodeprivate.h"
|
||||
#include "gsk/gskroundedrectprivate.h"
|
||||
#include "gsk/gskstrokeprivate.h"
|
||||
|
||||
#include "gtk/gskpangoprivate.h"
|
||||
|
||||
@@ -105,6 +106,14 @@ struct _GtkSnapshotState {
|
||||
struct {
|
||||
GskRoundedRect bounds;
|
||||
} rounded_clip;
|
||||
struct {
|
||||
GskPath *path;
|
||||
GskFillRule fill_rule;
|
||||
} fill;
|
||||
struct {
|
||||
GskPath *path;
|
||||
GskStroke stroke;
|
||||
} stroke;
|
||||
struct {
|
||||
gsize n_shadows;
|
||||
GskShadow *shadows;
|
||||
@@ -1096,6 +1105,135 @@ gtk_snapshot_push_rounded_clip (GtkSnapshot *snapshot,
|
||||
gsk_rounded_rect_scale_affine (&state->data.rounded_clip.bounds, bounds, scale_x, scale_y, dx, dy);
|
||||
}
|
||||
|
||||
static GskRenderNode *
|
||||
gtk_snapshot_collect_fill (GtkSnapshot *snapshot,
|
||||
GtkSnapshotState *state,
|
||||
GskRenderNode **nodes,
|
||||
guint n_nodes)
|
||||
{
|
||||
GskRenderNode *node, *fill_node;
|
||||
|
||||
node = gtk_snapshot_collect_default (snapshot, state, nodes, n_nodes);
|
||||
if (node == NULL)
|
||||
return NULL;
|
||||
|
||||
fill_node = gsk_fill_node_new (node,
|
||||
state->data.fill.path,
|
||||
state->data.fill.fill_rule);
|
||||
|
||||
if (fill_node->bounds.size.width == 0 ||
|
||||
fill_node->bounds.size.height == 0)
|
||||
{
|
||||
gsk_render_node_unref (node);
|
||||
gsk_render_node_unref (fill_node);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gsk_render_node_unref (node);
|
||||
|
||||
return fill_node;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_snapshot_clear_fill (GtkSnapshotState *state)
|
||||
{
|
||||
gsk_path_unref (state->data.fill.path);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_snapshot_push_fill:
|
||||
* @snapshot: a #GtkSnapshot
|
||||
* @path: The path describing the area to fill
|
||||
* @fill_rule: The fill rule to use
|
||||
*
|
||||
* Fills the area given by @path and @fill_rule with an image and discards everything
|
||||
* outside of it.
|
||||
*
|
||||
* The image is recorded until the next call to gtk_snapshot_pop().
|
||||
*/
|
||||
void
|
||||
gtk_snapshot_push_fill (GtkSnapshot *snapshot,
|
||||
GskPath *path,
|
||||
GskFillRule fill_rule)
|
||||
{
|
||||
GtkSnapshotState *state;
|
||||
|
||||
/* FIXME: Is it worth calling ensure_affine() and transforming the path here? */
|
||||
gtk_snapshot_ensure_identity (snapshot);
|
||||
|
||||
state = gtk_snapshot_push_state (snapshot,
|
||||
gtk_snapshot_get_current_state (snapshot)->transform,
|
||||
gtk_snapshot_collect_fill,
|
||||
gtk_snapshot_clear_fill);
|
||||
|
||||
state->data.fill.path = gsk_path_ref (path);
|
||||
state->data.fill.fill_rule = fill_rule;
|
||||
}
|
||||
|
||||
static GskRenderNode *
|
||||
gtk_snapshot_collect_stroke (GtkSnapshot *snapshot,
|
||||
GtkSnapshotState *state,
|
||||
GskRenderNode **nodes,
|
||||
guint n_nodes)
|
||||
{
|
||||
GskRenderNode *node, *stroke_node;
|
||||
|
||||
node = gtk_snapshot_collect_default (snapshot, state, nodes, n_nodes);
|
||||
if (node == NULL)
|
||||
return NULL;
|
||||
|
||||
stroke_node = gsk_stroke_node_new (node,
|
||||
state->data.stroke.path,
|
||||
&state->data.stroke.stroke);
|
||||
|
||||
if (stroke_node->bounds.size.width == 0 ||
|
||||
stroke_node->bounds.size.height == 0)
|
||||
{
|
||||
gsk_render_node_unref (node);
|
||||
gsk_render_node_unref (stroke_node);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gsk_render_node_unref (node);
|
||||
|
||||
return stroke_node;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_snapshot_clear_stroke (GtkSnapshotState *state)
|
||||
{
|
||||
gsk_path_unref (state->data.stroke.path);
|
||||
gsk_stroke_clear (&state->data.stroke.stroke);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_snapshot_push_stroke:
|
||||
* @snapshot: a #GtkSnapshot
|
||||
* @path: The path to stroke
|
||||
* @stroke: The stroke attributes
|
||||
*
|
||||
* Strokes the given @path with the attributes given by @stroke and the
|
||||
* image being recorded until the next call to gtk_snapshot_pop().
|
||||
*/
|
||||
void
|
||||
gtk_snapshot_push_stroke (GtkSnapshot *snapshot,
|
||||
GskPath *path,
|
||||
const GskStroke *stroke)
|
||||
{
|
||||
GtkSnapshotState *state;
|
||||
|
||||
/* FIXME: Is it worth calling ensure_affine() and transforming the path here? */
|
||||
gtk_snapshot_ensure_identity (snapshot);
|
||||
|
||||
state = gtk_snapshot_push_state (snapshot,
|
||||
gtk_snapshot_get_current_state (snapshot)->transform,
|
||||
gtk_snapshot_collect_stroke,
|
||||
gtk_snapshot_clear_stroke);
|
||||
|
||||
state->data.stroke.path = gsk_path_ref (path);
|
||||
gsk_stroke_init_copy (&state->data.stroke.stroke, stroke);
|
||||
}
|
||||
|
||||
static GskRenderNode *
|
||||
gtk_snapshot_collect_shadow (GtkSnapshot *snapshot,
|
||||
GtkSnapshotState *state,
|
||||
|
||||
@@ -88,6 +88,14 @@ GDK_AVAILABLE_IN_ALL
|
||||
void gtk_snapshot_push_rounded_clip (GtkSnapshot *snapshot,
|
||||
const GskRoundedRect *bounds);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_snapshot_push_fill (GtkSnapshot *snapshot,
|
||||
GskPath *path,
|
||||
GskFillRule fill_rule);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_snapshot_push_stroke (GtkSnapshot *snapshot,
|
||||
GskPath *path,
|
||||
const GskStroke *stroke);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_snapshot_push_shadow (GtkSnapshot *snapshot,
|
||||
const GskShadow *shadow,
|
||||
gsize n_shadows);
|
||||
|
||||
@@ -11952,6 +11952,18 @@ gtk_widget_render (GtkWidget *widget,
|
||||
|
||||
gdk_profiler_end_mark (before_render, "widget render", "");
|
||||
}
|
||||
|
||||
{
|
||||
static gint64 last_print = 0;
|
||||
gint64 t = g_get_monotonic_time ();
|
||||
|
||||
if (t - last_print > 5 * G_USEC_PER_SEC)
|
||||
{
|
||||
double fps = gdk_frame_clock_get_fps (gtk_widget_get_frame_clock (widget));
|
||||
g_print ("%.2f fps\n", fps);
|
||||
last_print = t;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
|
||||
@@ -298,6 +298,12 @@ create_list_model_for_render_node (GskRenderNode *node)
|
||||
case GSK_ROUNDED_CLIP_NODE:
|
||||
return create_render_node_list_model ((GskRenderNode *[1]) { gsk_rounded_clip_node_get_child (node) }, 1);
|
||||
|
||||
case GSK_FILL_NODE:
|
||||
return create_render_node_list_model ((GskRenderNode *[1]) { gsk_fill_node_get_child (node) }, 1);
|
||||
|
||||
case GSK_STROKE_NODE:
|
||||
return create_render_node_list_model ((GskRenderNode *[1]) { gsk_stroke_node_get_child (node) }, 1);
|
||||
|
||||
case GSK_SHADOW_NODE:
|
||||
return create_render_node_list_model ((GskRenderNode *[1]) { gsk_shadow_node_get_child (node) }, 1);
|
||||
|
||||
@@ -425,6 +431,10 @@ node_type_name (GskRenderNodeType type)
|
||||
return "Clip";
|
||||
case GSK_ROUNDED_CLIP_NODE:
|
||||
return "Rounded Clip";
|
||||
case GSK_FILL_NODE:
|
||||
return "Fill";
|
||||
case GSK_STROKE_NODE:
|
||||
return "Stroke";
|
||||
case GSK_SHADOW_NODE:
|
||||
return "Shadow";
|
||||
case GSK_BLEND_NODE:
|
||||
@@ -466,6 +476,8 @@ node_name (GskRenderNode *node)
|
||||
case GSK_REPEAT_NODE:
|
||||
case GSK_CLIP_NODE:
|
||||
case GSK_ROUNDED_CLIP_NODE:
|
||||
case GSK_FILL_NODE:
|
||||
case GSK_STROKE_NODE:
|
||||
case GSK_SHADOW_NODE:
|
||||
case GSK_BLEND_NODE:
|
||||
case GSK_MASK_NODE:
|
||||
@@ -894,6 +906,20 @@ add_float_row (GListStore *store,
|
||||
g_free (text);
|
||||
}
|
||||
|
||||
static const char *
|
||||
enum_to_nick (GType type,
|
||||
int value)
|
||||
{
|
||||
GEnumClass *class;
|
||||
GEnumValue *v;
|
||||
|
||||
class = g_type_class_ref (type);
|
||||
v = g_enum_get_value (class, value);
|
||||
g_type_class_unref (class);
|
||||
|
||||
return v->value_nick;
|
||||
}
|
||||
|
||||
static void
|
||||
populate_render_node_properties (GListStore *store,
|
||||
GskRenderNode *node)
|
||||
@@ -1133,9 +1159,7 @@ populate_render_node_properties (GListStore *store,
|
||||
case GSK_BLEND_NODE:
|
||||
{
|
||||
GskBlendMode mode = gsk_blend_node_get_blend_mode (node);
|
||||
tmp = g_enum_to_string (GSK_TYPE_BLEND_MODE, mode);
|
||||
add_text_row (store, "Blendmode", tmp);
|
||||
g_free (tmp);
|
||||
add_text_row (store, "Blendmode", enum_to_nick (GSK_TYPE_BLEND_MODE, mode));
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -1377,6 +1401,39 @@ populate_render_node_properties (GListStore *store,
|
||||
}
|
||||
break;
|
||||
|
||||
case GSK_FILL_NODE:
|
||||
{
|
||||
GskPath *path = gsk_fill_node_get_path (node);
|
||||
GskFillRule fill_rule = gsk_fill_node_get_fill_rule (node);
|
||||
|
||||
tmp = gsk_path_to_string (path);
|
||||
add_text_row (store, "Path", tmp);
|
||||
g_free (tmp);
|
||||
|
||||
add_text_row (store, "Fill rule", enum_to_nick (GSK_TYPE_FILL_RULE, fill_rule));
|
||||
}
|
||||
break;
|
||||
|
||||
case GSK_STROKE_NODE:
|
||||
{
|
||||
GskPath *path = gsk_stroke_node_get_path (node);
|
||||
const GskStroke *stroke = gsk_stroke_node_get_stroke (node);
|
||||
GskLineCap line_cap = gsk_stroke_get_line_cap (stroke);
|
||||
GskLineJoin line_join = gsk_stroke_get_line_join (stroke);
|
||||
|
||||
tmp = gsk_path_to_string (path);
|
||||
add_text_row (store, "Path", tmp);
|
||||
g_free (tmp);
|
||||
|
||||
tmp = g_strdup_printf ("%.2f", gsk_stroke_get_line_width (stroke));
|
||||
add_text_row (store, "Line width", tmp);
|
||||
g_free (tmp);
|
||||
|
||||
add_text_row (store, "Line cap", enum_to_nick (GSK_TYPE_LINE_CAP, line_cap));
|
||||
add_text_row (store, "Line join", enum_to_nick (GSK_TYPE_LINE_JOIN, line_join));
|
||||
}
|
||||
break;
|
||||
|
||||
case GSK_CONTAINER_NODE:
|
||||
tmp = g_strdup_printf ("%d", gsk_container_node_get_n_children (node));
|
||||
add_text_row (store, "Children", tmp);
|
||||
|
||||
@@ -23,7 +23,6 @@ $popover_radius: $button_radius + 4;
|
||||
|
||||
&:backdrop {
|
||||
text-shadow: none;
|
||||
-gtk-icon-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,14 +38,6 @@ dnd {
|
||||
-gtk-icon-size: 32px;
|
||||
}
|
||||
|
||||
image:disabled {
|
||||
-gtk-icon-filter: opacity(0.5);
|
||||
}
|
||||
|
||||
picture:disabled {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.view,
|
||||
%view {
|
||||
color: $text_color;
|
||||
@@ -95,13 +86,11 @@ iconview {
|
||||
|
||||
@if $contrast == 'high' {
|
||||
&:selected {
|
||||
box-shadow: inset 0 0 0 1px $borders_color;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
&:drop(active) {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
> dndtarget:drop(active) {
|
||||
@@ -148,7 +137,6 @@ gridview {
|
||||
@extend %selected_items;
|
||||
|
||||
@if $contrast == 'high' {
|
||||
box-shadow: inset 0 0 0 1px $borders_color;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -198,7 +186,6 @@ label {
|
||||
}
|
||||
|
||||
.dim-label {
|
||||
opacity: $dim_label_opacity;
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
@@ -228,7 +215,6 @@ window.aboutdialog image.large-icons {
|
||||
border: none;
|
||||
background-color: $osd_bg_color;
|
||||
background-clip: padding-box;
|
||||
-gtk-icon-shadow: 0 1px black;
|
||||
|
||||
}
|
||||
|
||||
@@ -248,8 +234,6 @@ spinner {
|
||||
&:checked {
|
||||
opacity: 1;
|
||||
animation: spin 1s linear infinite;
|
||||
|
||||
&:disabled { opacity: 0.5; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -385,7 +369,6 @@ entry {
|
||||
&:drop(active) {
|
||||
&:focus-within, & {
|
||||
border-color: $drop_target_color;
|
||||
box-shadow: inset 0 0 0 1px $drop_target_color;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -409,7 +392,6 @@ entry {
|
||||
border-width: 0 0 2px;
|
||||
border-color: $selected_bg_color;
|
||||
border-style: solid;
|
||||
box-shadow: none;
|
||||
|
||||
}
|
||||
|
||||
@@ -560,7 +542,6 @@ button {
|
||||
|
||||
&:disabled {
|
||||
@include button(insensitive);
|
||||
-gtk-icon-filter: opacity(0.5);
|
||||
|
||||
&:active,
|
||||
&:checked { @include button(insensitive-active); }
|
||||
@@ -623,7 +604,6 @@ button {
|
||||
&:drop(active) {
|
||||
color: $drop_target_color;
|
||||
border-color: $drop_target_color;
|
||||
box-shadow: inset 0 0 0 1px $drop_target_color;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -655,7 +635,6 @@ button {
|
||||
&:only-child {
|
||||
margin: 4px;
|
||||
border-radius: 50%; // completely round when it is a single button
|
||||
box-shadow: 0 1px 2px transparentize(black, 0.8); // drop shadow for the only child
|
||||
};
|
||||
}
|
||||
|
||||
@@ -665,13 +644,11 @@ button {
|
||||
@include button(osd);
|
||||
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
|
||||
&:hover {
|
||||
@include button(osd-hover);
|
||||
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
&:active,
|
||||
@@ -679,7 +656,6 @@ button {
|
||||
@include button(osd-active);
|
||||
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -698,16 +674,12 @@ button {
|
||||
&.flat {
|
||||
@include button(undecorated);
|
||||
|
||||
box-shadow: none;
|
||||
-gtk-icon-shadow: 0 1px black;
|
||||
|
||||
&:hover { @include button(osd-hover); }
|
||||
|
||||
&:disabled {
|
||||
@include button(osd-insensitive);
|
||||
background-image: none;
|
||||
border-color: transparent;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
&:active,
|
||||
@@ -873,10 +845,7 @@ button {
|
||||
background-color: transparent;
|
||||
background-image: none;
|
||||
border-color: transparent;
|
||||
box-shadow: inset 0 1px transparentize(white, 1),
|
||||
0 1px transparentize(white, 1);
|
||||
text-shadow: none;
|
||||
-gtk-icon-shadow: none;
|
||||
}
|
||||
|
||||
.linked:not(.vertical) > menubutton,
|
||||
@@ -928,7 +897,6 @@ modelbutton.flat arrow {
|
||||
background: none;
|
||||
min-width: 16px;
|
||||
min-height: 16px;
|
||||
opacity: 0.3; //dim icon
|
||||
|
||||
&:hover { background: none; }
|
||||
|
||||
@@ -963,19 +931,6 @@ button.color {
|
||||
> colorswatch:only-child {
|
||||
&, > overlay { border-radius: 0; }
|
||||
|
||||
@if $variant == 'light' {
|
||||
box-shadow: 0 1px $shadow_color;
|
||||
|
||||
.osd & { box-shadow: none; }
|
||||
}
|
||||
}
|
||||
|
||||
@if $variant == 'light' {
|
||||
.osd &, & {
|
||||
&:disabled,
|
||||
&:active,
|
||||
&:checked { colorswatch:only-child { box-shadow: none; }}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1076,7 +1031,6 @@ spinbutton {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
padding: 6px;
|
||||
|
||||
&:backdrop:disabled { background-color: transparent; }
|
||||
@@ -1098,7 +1052,6 @@ spinbutton {
|
||||
border-style: none none none solid;
|
||||
border-color: transparentize($borders_color, 0.7);
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
|
||||
&:dir(rtl) { border-style: none solid none none; }
|
||||
|
||||
@@ -1114,7 +1067,6 @@ spinbutton {
|
||||
|
||||
&:active {
|
||||
background-color: transparentize(black, 0.9);
|
||||
box-shadow: inset 0 2px 3px -1px transparentize(black, 0.8);
|
||||
}
|
||||
|
||||
&:dir(ltr):last-child { border-radius: 0 $button_radius $button_radius 0; }
|
||||
@@ -1137,8 +1089,6 @@ spinbutton {
|
||||
border-style: none none none solid;
|
||||
border-color: transparentize($osd_borders_color, 0.3);
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
-gtk-icon-shadow: 0 1px black;
|
||||
|
||||
&:dir(rtl) { border-style: none solid none none; }
|
||||
|
||||
@@ -1148,8 +1098,6 @@ spinbutton {
|
||||
color: $osd_fg_color;
|
||||
border-color: transparentize(opacify($osd_borders_color, 1), 0.5);
|
||||
background-color: darken($osd_bg_color,10%);
|
||||
-gtk-icon-shadow: 0 1px black;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
@@ -1157,8 +1105,6 @@ spinbutton {
|
||||
|
||||
color: $osd_insensitive_fg_color;
|
||||
border-color: transparentize(opacify($osd_borders_color, 1), 0.5);
|
||||
-gtk-icon-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
&:dir(ltr):last-child { border-radius: 0 $button_radius $button_radius 0; }
|
||||
@@ -1179,7 +1125,6 @@ spinbutton {
|
||||
|
||||
&:drop(active) {
|
||||
border-color: transparent;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
> text {
|
||||
@@ -1261,7 +1206,6 @@ dropdown > button > box {
|
||||
|
||||
> stack > row.activatable:hover {
|
||||
background: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1287,7 +1231,6 @@ combobox {
|
||||
}
|
||||
|
||||
&:drop(active) { // FIXME: untested
|
||||
box-shadow: none;
|
||||
|
||||
button.combo { @extend %button_basic_drop_active; }
|
||||
}
|
||||
@@ -1308,7 +1251,6 @@ combobox {
|
||||
outline-color: $alt_focus_border_color;
|
||||
color: $text-color;
|
||||
background-color: $menu_selected_color;
|
||||
box-shadow: none;
|
||||
border-radius: $button_radius;
|
||||
}
|
||||
}
|
||||
@@ -1403,7 +1345,6 @@ searchbar > revealer > box {
|
||||
&:backdrop {
|
||||
border-color: $backdrop_borders_color;
|
||||
background-color: $backdrop_dark_fill;
|
||||
box-shadow: none;
|
||||
transition: $backdrop_transition;
|
||||
}
|
||||
}
|
||||
@@ -1508,8 +1449,6 @@ headerbar {
|
||||
background-image: none;
|
||||
|
||||
transition: $backdrop_transition;
|
||||
|
||||
.title { filter: opacity(.5); }
|
||||
}
|
||||
|
||||
.title {
|
||||
@@ -1580,7 +1519,6 @@ headerbar {
|
||||
margin-right: -1px;
|
||||
margin-top: -1px;
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1740,7 +1678,6 @@ treeview.view {
|
||||
}
|
||||
|
||||
&:drop(active) {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
> dndtarget:drop(active) {
|
||||
@@ -1779,7 +1716,6 @@ treeview.view {
|
||||
|
||||
background-color: $selected_bg_color;
|
||||
background-image: image($selected_bg_color);
|
||||
box-shadow: none;
|
||||
|
||||
&:selected {
|
||||
&:focus, & {
|
||||
@@ -1789,8 +1725,6 @@ treeview.view {
|
||||
|
||||
}
|
||||
|
||||
@else { box-shadow: inset 0 1px transparentize(white, 0.95); }
|
||||
|
||||
background-image: image($base_color);
|
||||
|
||||
&:backdrop {
|
||||
@@ -1826,13 +1760,11 @@ treeview.view {
|
||||
background-color: $base_color;
|
||||
font-weight: bold;
|
||||
text-shadow: none;
|
||||
box-shadow: none;
|
||||
|
||||
&:hover {
|
||||
@extend %column_header_button;
|
||||
|
||||
color: mix($_column_header_color, $fg_color, 50%);
|
||||
box-shadow: none;
|
||||
transition: none; //I shouldn't need this
|
||||
}
|
||||
|
||||
@@ -1865,7 +1797,6 @@ treeview.view {
|
||||
background-color: $selected_bg_color;
|
||||
border-style: none;
|
||||
border-radius: 0;
|
||||
box-shadow: inset 0 0 0 1px $base_color;
|
||||
text-shadow: none;
|
||||
transition: none;
|
||||
}
|
||||
@@ -1905,7 +1836,6 @@ popover.background {
|
||||
background-color: $menu_color;
|
||||
background-clip: padding-box;
|
||||
border: 1px solid $_popover_border;
|
||||
box-shadow: 0 1px 2px transparentize(black, 0.7);
|
||||
}
|
||||
|
||||
&:backdrop {
|
||||
@@ -1941,7 +1871,6 @@ popover.background {
|
||||
@extend %osd;
|
||||
|
||||
border: 1px solid transparentize(white, 0.9);
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1982,7 +1911,6 @@ popover.menu {
|
||||
background: image($menu_selected_color);
|
||||
|
||||
@if $contrast == 'high' {
|
||||
box-shadow: inset 0 0 0 1px $borders_color;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2072,20 +2000,17 @@ popover.menu {
|
||||
// initial styling for popover menu and bar
|
||||
menubar {
|
||||
padding: 0px;
|
||||
box-shadow: inset 0 -1px transparentize(black, 0.9);
|
||||
|
||||
> item {
|
||||
min-height: 16px;
|
||||
padding: 4px 8px;
|
||||
|
||||
&:selected { //Seems like it :hover even with keyboard focus
|
||||
box-shadow: inset 0 -3px $selected_bg_color;
|
||||
color: $link_color;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
color: $insensitive_fg_color;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2122,10 +2047,6 @@ notebook {
|
||||
> tabs {
|
||||
margin-bottom: -2px;
|
||||
> tab {
|
||||
&:hover { box-shadow: inset 0 -4px $borders_color; }
|
||||
|
||||
|
||||
&:checked { box-shadow: inset 0 -4px $selected_bg_color; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2135,9 +2056,6 @@ notebook {
|
||||
> tabs {
|
||||
margin-top: -2px;
|
||||
> tab {
|
||||
&:hover { box-shadow: inset 0 4px $borders_color; }
|
||||
|
||||
&:checked { box-shadow: inset 0 4px $selected_bg_color; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2147,9 +2065,6 @@ notebook {
|
||||
> tabs {
|
||||
margin-right: -2px;
|
||||
> tab {
|
||||
&:hover { box-shadow: inset -4px 0 $borders_color; }
|
||||
|
||||
&:checked { box-shadow: inset -4px 0 $selected_bg_color; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2159,9 +2074,6 @@ notebook {
|
||||
> tabs {
|
||||
margin-left: -2px;
|
||||
> tab {
|
||||
&:hover { box-shadow: inset 4px 0 $borders_color; }
|
||||
|
||||
&:checked { box-shadow: inset 4px 0 $selected_bg_color; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2226,7 +2138,6 @@ notebook {
|
||||
background-image: none;
|
||||
background-color: transparentize(white, 0.7);
|
||||
border-color: transparent;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
&:disabled { @include button(undecorated); }
|
||||
@@ -2499,7 +2410,6 @@ switch {
|
||||
outline-color: $alt_focus_border_color;
|
||||
|
||||
@if $variant == 'light' {
|
||||
box-shadow: none;
|
||||
border-color: $switch_borders_color;
|
||||
|
||||
> slider { &:checked, & { border-color: $switch_borders_color; } }
|
||||
@@ -2536,10 +2446,8 @@ switch {
|
||||
border-radius: 5px;
|
||||
background-image: none;
|
||||
transition: 200ms;
|
||||
box-shadow: none;
|
||||
border-width: 0;
|
||||
-gtk-icon-source: #{$check_icon};
|
||||
-gtk-icon-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2786,7 +2694,6 @@ scale {
|
||||
border-style: solid;
|
||||
border-radius: 100%;
|
||||
transition: $button_transition;
|
||||
transition-property: background, border, box-shadow;
|
||||
|
||||
&:hover { @include button(hover); }
|
||||
|
||||
@@ -2934,7 +2841,6 @@ scale {
|
||||
|
||||
background-position: $_scale_slider_bg_pos;
|
||||
background-repeat: no-repeat;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2972,7 +2878,6 @@ scale {
|
||||
|
||||
> slider {
|
||||
margin: 0;
|
||||
opacity: .8;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3071,7 +2976,6 @@ progressbar {
|
||||
border-style: none;
|
||||
border-radius: 0;
|
||||
background-color: transparent;
|
||||
box-shadow: none;
|
||||
|
||||
> progress {
|
||||
border-style: none;
|
||||
@@ -3297,11 +3201,7 @@ row {
|
||||
|
||||
&:hover { background-color: if(variant == light, transparentize($fg_color, 0.9), transparentize($fg_color, 0.95)); }
|
||||
|
||||
&:active { box-shadow: inset 0 2px 2px -2px transparentize(black, 0.8); }
|
||||
|
||||
&:selected {
|
||||
&:active { box-shadow: inset 0 2px 3px -1px transparentize(black, 0.5); }
|
||||
|
||||
&.has-open-popup,
|
||||
&:hover { background-color: mix($fg_color, $selected_bg_color, 10%); }
|
||||
}
|
||||
@@ -3497,17 +3397,6 @@ expander-widget {
|
||||
}
|
||||
}
|
||||
|
||||
.navigation-sidebar,
|
||||
placessidebar,
|
||||
stackswitcher,
|
||||
expander-widget {
|
||||
&:not(decoration):not(window):drop(active):focus,
|
||||
&:not(decoration):not(window):drop(active) {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/************
|
||||
* Calendar *
|
||||
***********/
|
||||
@@ -3521,7 +3410,6 @@ calendar {
|
||||
|
||||
> button {
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
background: none;
|
||||
border-radius: 0;
|
||||
}
|
||||
@@ -3538,10 +3426,7 @@ calendar {
|
||||
}
|
||||
|
||||
> label.today {
|
||||
box-shadow: inset 0px -2px $borders_color;
|
||||
|
||||
&:selected {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3664,7 +3549,6 @@ filechooserbutton>button>box {
|
||||
border-spacing: 6px;
|
||||
}
|
||||
filechooserbutton:drop(active) {
|
||||
box-shadow: none;
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
@@ -3725,7 +3609,6 @@ stacksidebar {
|
||||
background-color: darken($menu_selected_color,5%);
|
||||
}
|
||||
&.activatable:active, &.activatable:selected:active {
|
||||
box-shadow: none; // #3413
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3761,7 +3644,6 @@ separator.sidebar {
|
||||
color: inherit;
|
||||
|
||||
@if $contrast == 'high' {
|
||||
box-shadow: inset 0 0 0 1px $borders_color;
|
||||
}
|
||||
|
||||
|
||||
@@ -3780,12 +3662,6 @@ separator.sidebar {
|
||||
* File chooser *
|
||||
****************/
|
||||
|
||||
$_placesidebar_icons_opacity: 0.7;
|
||||
|
||||
row image.sidebar-icon { opacity: $_placesidebar_icons_opacity; } // dim the sidebar icons
|
||||
// see bug #786613 for details
|
||||
// on this oddity
|
||||
|
||||
/* this should be more generic, only using .navigation-sidebar
|
||||
https://gitlab.gnome.org/GNOME/gtk/-/issues/2929
|
||||
*/
|
||||
@@ -3822,7 +3698,6 @@ placessidebar {
|
||||
}
|
||||
|
||||
// in the sidebar case it makes no sense to click the selected row
|
||||
&:selected:active { box-shadow: none; }
|
||||
|
||||
&.sidebar-placeholder-row {
|
||||
padding: 0 8px;
|
||||
@@ -3835,9 +3710,6 @@ placessidebar {
|
||||
|
||||
&:drop(active):not(:disabled) {
|
||||
color: $drop_target_color;
|
||||
box-shadow: inset 0 1px $drop_target_color,
|
||||
inset 0 -1px $drop_target_color;
|
||||
|
||||
&:selected {
|
||||
color: $selected_fg_color;
|
||||
background-color: $drop_target_color;
|
||||
@@ -3958,7 +3830,6 @@ tooltip {
|
||||
|
||||
padding: 6px 10px;
|
||||
border-radius: $window_radius;
|
||||
box-shadow: none; // otherwise it gets inherited by windowframe.csd
|
||||
|
||||
> box {
|
||||
border-spacing: 6px;
|
||||
@@ -4050,18 +3921,13 @@ colorswatch {
|
||||
}
|
||||
|
||||
&:drop(active) {
|
||||
box-shadow: none;
|
||||
|
||||
&.light > overlay {
|
||||
border-color: $drop_target_color;
|
||||
box-shadow: inset 0 0 0 2px if($variant == 'light', darken($drop_target_color, 7%), $borders_color),
|
||||
inset 0 0 0 1px $drop_target_color;
|
||||
}
|
||||
|
||||
&.dark > overlay {
|
||||
border-color: $drop_target_color;
|
||||
box-shadow: inset 0 0 0 2px if($variant == 'light', transparentize(black, 0.7), $borders_color),
|
||||
inset 0 0 0 1px $drop_target_color;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4070,8 +3936,6 @@ colorswatch {
|
||||
}
|
||||
|
||||
&.activatable:hover > overlay {
|
||||
box-shadow: inset 0 1px transparentize(white, 0.6),
|
||||
inset 0 -1px transparentize(black, 0.8);
|
||||
}
|
||||
|
||||
&#add-color-button {
|
||||
@@ -4089,16 +3953,12 @@ colorswatch {
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
opacity: 0.5;
|
||||
|
||||
> overlay {
|
||||
border-color: transparentize(black, 0.4);
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
row:selected & { box-shadow: 0 0 0 2px $selected_fg_color; }
|
||||
|
||||
&#editor-color-sample {
|
||||
border-radius: 4px;
|
||||
|
||||
@@ -4168,8 +4028,6 @@ window {
|
||||
$_wm_border_backdrop: if($variant=='light', transparentize(black, 0.82), transparentize(black, 0.25));
|
||||
|
||||
&.csd {
|
||||
box-shadow: 0 3px 9px 1px transparentize(black, 0.5),
|
||||
0 0 0 1px $_wm_border; //doing borders with box-shadow
|
||||
margin: 0px;
|
||||
border-radius: $window_radius $window_radius 0 0;
|
||||
&:backdrop {
|
||||
@@ -4177,22 +4035,15 @@ window {
|
||||
// change when we go to backdrop, to prevent jumping windows.
|
||||
// The biggest shadow should be in the same order then in the active state
|
||||
// or the jumping will happen during the transition.
|
||||
box-shadow: 0 3px 9px 1px transparent,
|
||||
0 2px 6px 2px transparentize(black, 0.8),
|
||||
0 0 0 1px $_wm_border_backdrop;
|
||||
transition: $backdrop_transition;
|
||||
}
|
||||
|
||||
&.popup {
|
||||
border-radius: $menu_radius;
|
||||
box-shadow: 0 1px 2px transparentize(black, 0.8),
|
||||
0 0 0 1px transparentize($_wm_border, 0.1);
|
||||
}
|
||||
|
||||
&.dialog.message {
|
||||
border-radius: $window_radius;
|
||||
box-shadow: 0 1px 2px transparentize(black, 0.8),
|
||||
0 0 0 1px transparentize($_wm_border, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4201,13 +4052,9 @@ window {
|
||||
padding: 4px;
|
||||
border: solid 1px $borders_color;
|
||||
border-radius: 0;
|
||||
box-shadow: inset 0 0 0 4px $borders_color, inset 0 0 0 3px $headerbar_bg_color, inset 0 1px $top_hilight;
|
||||
|
||||
&:backdrop { box-shadow: inset 0 0 0 4px $borders_color, inset 0 0 0 3px $backdrop_bg_color, inset 0 1px $top_hilight; }
|
||||
}
|
||||
|
||||
&.maximized,
|
||||
&.fullscreen { border-radius: 0; box-shadow: none; }
|
||||
|
||||
&.tiled,
|
||||
&.tiled-top,
|
||||
@@ -4215,24 +4062,15 @@ window {
|
||||
&.tiled-right,
|
||||
&.tiled-bottom {
|
||||
border-radius: 0;
|
||||
box-shadow: 0 0 0 1px $_wm_border,
|
||||
0 0 0 20px transparent; //transparent control workaround -- #3670
|
||||
|
||||
&:backdrop { box-shadow: 0 0 0 1px $_wm_border_backdrop,
|
||||
0 0 0 20px transparent; // #3670
|
||||
}
|
||||
}
|
||||
|
||||
&.popup { box-shadow: none; }
|
||||
|
||||
// server-side decorations as used by mutter
|
||||
&.ssd { box-shadow: 0 0 0 1px $_wm_border; } //just doing borders, wm draws actual shadows
|
||||
|
||||
}
|
||||
|
||||
tooltip.csd {
|
||||
border-radius: 5px;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
// catch all extend :)
|
||||
@@ -4259,7 +4097,6 @@ tooltip.csd {
|
||||
cursor-handle {
|
||||
background-color: transparent;
|
||||
background-image: none;
|
||||
box-shadow: none;
|
||||
border-style: none;
|
||||
min-width: 20px;
|
||||
min-height: 24px;
|
||||
@@ -4320,7 +4157,6 @@ shortcut > .keycap {
|
||||
border: 1px solid;
|
||||
border-color: if($variant == 'light', mix($borders_color, $bg_color, 50%), $borders_color);
|
||||
border-radius: 5px;
|
||||
box-shadow: if($variant == 'light', inset 0 -3px mix($base_color, $bg_color, 20%), inset 0 -3px mix($borders_color, $base_color, 60%));
|
||||
font-size: smaller;
|
||||
|
||||
}
|
||||
@@ -4328,7 +4164,6 @@ shortcut > .keycap {
|
||||
:not(decoration):not(window):drop(active):focus,
|
||||
:not(decoration):not(window):drop(active) { // FIXME needs to be done widget by widget, this wildcard should really die
|
||||
border-color: $drop_target_color;
|
||||
box-shadow: inset 0 0 0 1px $drop_target_color;
|
||||
caret-color: $drop_target_color;
|
||||
}
|
||||
|
||||
@@ -4345,25 +4180,6 @@ stackswitcher.circular {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
/*************
|
||||
* App Icons *
|
||||
*************/
|
||||
/* Outline for low res icons */
|
||||
.lowres-icon {
|
||||
-gtk-icon-shadow: 0 -1px rgba(0,0,0,0.05),
|
||||
1px 0 rgba(0,0,0,0.1),
|
||||
0 1px rgba(0,0,0,0.3),
|
||||
-1px 0 rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
/* Drapshadow for large icons */
|
||||
.icon-dropshadow {
|
||||
-gtk-icon-shadow: 0 1px 12px rgba(0,0,0,0.05),
|
||||
0 -1px rgba(0,0,0,0.05),
|
||||
1px 0 rgba(0,0,0,0.1),
|
||||
0 1px rgba(0,0,0,0.3),
|
||||
-1px 0 rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
/*********
|
||||
* Emoji *
|
||||
@@ -4397,7 +4213,6 @@ button.emoji-section {
|
||||
|
||||
/* reset props inherited from the button style */
|
||||
background: none;
|
||||
box-shadow: none;
|
||||
text-shadow: none;
|
||||
|
||||
&:hover { border-color: if($variant == 'light', $borders_color, transparentize($fg_color, .9)); }
|
||||
@@ -4414,7 +4229,6 @@ popover.emoji-picker emoji {
|
||||
background: $selected_bg_color;
|
||||
|
||||
@if $contrast == 'high' {
|
||||
box-shadow: inset 0 0 0 1px $borders_color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,19 +25,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
@mixin _shadows($list...) {
|
||||
//
|
||||
// Helper mixin to stack up to box-shadows;
|
||||
//
|
||||
$shadows: null;
|
||||
|
||||
@each $shadow in $list {
|
||||
@if $shadow!=none { $shadows: $shadows, $shadow; }
|
||||
}
|
||||
|
||||
box-shadow: $shadows;
|
||||
}
|
||||
|
||||
// entries
|
||||
|
||||
@mixin entry($t, $fc:$focus_border_color) {
|
||||
@@ -78,7 +65,6 @@
|
||||
background-color: transparentize(opacify($osd_borders_color, 1), 0.5);
|
||||
background-clip: padding-box;
|
||||
box-shadow: none;
|
||||
-gtk-icon-shadow: 0 1px black;
|
||||
}
|
||||
@if $t==osd-focus {
|
||||
color: $osd_text_color;
|
||||
@@ -136,7 +122,6 @@ $_default_button_c: lighten($bg_color,2%);
|
||||
border-color: if($c!=$_default_button_c, _border_color($c, true), $borders_color); //tint if not default button color
|
||||
background-image: if($variant == 'light', linear-gradient(to top, darken($c, 2%) 2px, $c),
|
||||
linear-gradient(to top, darken($c,1%) 2px, $c));
|
||||
@include _shadows($_button_shadow);
|
||||
}
|
||||
|
||||
@else if $t==hover {
|
||||
@@ -147,11 +132,9 @@ $_default_button_c: lighten($bg_color,2%);
|
||||
border-color: if($c != $_default_button_c, _border_color($c), $borders_color);
|
||||
@if $variant == 'light' {
|
||||
background-image: linear-gradient(to top, darken($c,16%), darken($c,8%) 1px);
|
||||
@include _shadows($_button_shadow);
|
||||
}
|
||||
@else {
|
||||
background-image: linear-gradient(to top, darken($c,4%) 20%, darken($c, 3%) 90%);
|
||||
@include _shadows($_button_shadow);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -460,7 +443,6 @@ $_default_button_c: lighten($bg_color,2%);
|
||||
background-clip: if($checked, border-box, padding-box);
|
||||
background-image: linear-gradient(to bottom, lighten($c, 5%) 20%, $c 90%);
|
||||
border-color: $_border_color;
|
||||
box-shadow: 0 1px transparentize(black, 0.95);
|
||||
color: $tc;
|
||||
}
|
||||
|
||||
@@ -469,7 +451,6 @@ $_default_button_c: lighten($bg_color,2%);
|
||||
}
|
||||
|
||||
@if $t==active {
|
||||
box-shadow: inset 0 1px if($variant == 'light', rgba(0, 0, 0, 0.2), black);
|
||||
background-image: if($c == white, image(darken($c, 15%)), image(darken($c, 5%)));
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
clip {
|
||||
clip: -50 -50 100 100;
|
||||
child: cross-fade {
|
||||
progress: 0.333333;
|
||||
start: color {
|
||||
bounds: 0 0 50000 50000;
|
||||
color: red;
|
||||
}
|
||||
end: color {
|
||||
bounds: -50000 -50000 50000 50000;
|
||||
color: blue;
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
testsuite/gsk/compare/cross-fade-clipped-with-huge-children.png
Normal file
BIN
testsuite/gsk/compare/cross-fade-clipped-with-huge-children.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 333 B |
@@ -0,0 +1,6 @@
|
||||
linear-gradient {
|
||||
bounds: 0 0 320 50;
|
||||
start: 0 0;
|
||||
end: 320 0;
|
||||
stops: 0 rgb(255,0,0), 0.015625 rgb(255,0,0), 0.015625 rgb(178,0,255), 0.03125 rgb(178,0,255), 0.03125 rgb(0,153,255), 0.046875 rgb(0,153,255), 0.046875 rgb(0,255,25), 0.0625 rgb(0,255,25), 0.0625 rgb(255,204,0), 0.078125 rgb(255,204,0), 0.078125 rgb(255,0,127), 0.09375 rgb(255,0,127), 0.09375 rgb(51,0,255), 0.109375 rgb(51,0,255), 0.109375 rgb(0,255,229), 0.125 rgb(0,255,229), 0.125 rgb(102,255,0), 0.140625 rgb(102,255,0), 0.140625 rgb(255,76,0), 0.15625 rgb(255,76,0), 0.15625 rgb(255,0,255), 0.171875 rgb(255,0,255), 0.171875 rgb(0,76,255), 0.1875 rgb(0,76,255), 0.1875 rgb(0,255,102), 0.203125 rgb(0,255,102), 0.203125 rgb(229,255,0), 0.21875 rgb(229,255,0), 0.21875 rgb(255,0,51), 0.234375 rgb(255,0,51), 0.234375 rgb(127,0,255), 0.25 rgb(127,0,255), 0.25 rgb(0,204,255), 0.265625 rgb(0,204,255), 0.265625 rgb(25,255,0), 0.28125 rgb(25,255,0), 0.28125 rgb(255,153,0), 0.296875 rgb(255,153,0), 0.296875 rgb(255,0,178), 0.3125 rgb(255,0,178), 0.3125 rgb(0,0,255), 0.328125 rgb(0,0,255), 0.328125 rgb(0,255,178), 0.34375 rgb(0,255,178), 0.34375 rgb(153,255,0), 0.359375 rgb(153,255,0), 0.359375 rgb(255,25,0), 0.375 rgb(255,25,0), 0.375 rgb(204,0,255), 0.390625 rgb(204,0,255), 0.390625 rgb(0,127,255), 0.40625 rgb(0,127,255), 0.40625 rgb(0,255,51), 0.421875 rgb(0,255,51), 0.421875 rgb(255,229,0), 0.4375 rgb(255,229,0), 0.4375 rgb(255,0,102), 0.453125 rgb(255,0,102), 0.453125 rgb(76,0,255), 0.46875 rgb(76,0,255), 0.46875 rgb(0,255,255), 0.484375 rgb(0,255,255), 0.484375 rgb(76,255,0), 0.5 rgb(76,255,0), 0.5 rgb(255,102,0), 0.515625 rgb(255,102,0), 0.515625 rgb(255,0,229), 0.53125 rgb(255,0,229), 0.53125 rgb(0,51,255), 0.546875 rgb(0,51,255), 0.546875 rgb(0,255,127), 0.5625 rgb(0,255,127), 0.5625 rgb(204,255,0), 0.578125 rgb(204,255,0), 0.578125 rgb(255,0,25), 0.59375 rgb(255,0,25), 0.59375 rgb(153,0,255), 0.609375 rgb(153,0,255), 0.609375 rgb(0,178,255), 0.625 rgb(0,178,255), 0.625 rgb(0,255,0), 0.640625 rgb(0,255,0), 0.640625 rgb(255,178,0), 0.65625 rgb(255,178,0), 0.65625 rgb(255,0,153), 0.671875 rgb(255,0,153), 0.671875 rgb(25,0,255), 0.6875 rgb(25,0,255), 0.6875 rgb(0,255,204), 0.703125 rgb(0,255,204), 0.703125 rgb(127,255,0), 0.71875 rgb(127,255,0), 0.71875 rgb(255,51,0), 0.734375 rgb(255,51,0), 0.734375 rgb(229,0,255), 0.75 rgb(229,0,255), 0.75 rgb(0,102,255), 0.765625 rgb(0,102,255), 0.765625 rgb(0,255,76), 0.78125 rgb(0,255,76), 0.78125 rgb(255,255,0), 0.796875 rgb(255,255,0), 0.796875 rgb(255,0,76), 0.8125 rgb(255,0,76), 0.8125 rgb(102,0,255), 0.828125 rgb(102,0,255), 0.828125 rgb(0,229,255), 0.84375 rgb(0,229,255), 0.84375 rgb(51,255,0), 0.859375 rgb(51,255,0), 0.859375 rgb(255,127,0), 0.875 rgb(255,127,0), 0.875 rgb(255,0,204), 0.890625 rgb(255,0,204), 0.890625 rgb(0,25,255), 0.90625 rgb(0,25,255), 0.90625 rgb(0,255,153), 0.921875 rgb(0,255,153), 0.921875 rgb(178,255,0), 0.9375 rgb(178,255,0), 0.9375 rgb(255,0,0), 0.953125 rgb(255,0,0), 0.953125 rgb(178,0,255), 0.96875 rgb(178,0,255), 0.96875 rgb(0,153,255), 0.984375 rgb(0,153,255), 0.984375 rgb(0,255,25), 1 rgb(0,255,25);
|
||||
}
|
||||
BIN
testsuite/gsk/compare/linear-gradient-with-64-colorstops.png
Normal file
BIN
testsuite/gsk/compare/linear-gradient-with-64-colorstops.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 484 B |
13
testsuite/gsk/compare/repeat-scaling.node
Normal file
13
testsuite/gsk/compare/repeat-scaling.node
Normal file
@@ -0,0 +1,13 @@
|
||||
transform {
|
||||
transform: matrix(0, 4,
|
||||
-8, 0,
|
||||
0, 0);
|
||||
child: repeat {
|
||||
bounds: 0 0 5 2.5;
|
||||
child-bounds: 0 0 1 0.5;
|
||||
child: color {
|
||||
bounds: 0.25 0.125 0.5 0.25;
|
||||
color: rgb(0,0,255);
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
testsuite/gsk/compare/repeat-scaling.png
Normal file
BIN
testsuite/gsk/compare/repeat-scaling.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 97 B |
17
testsuite/gsk/compare/transform-huge-child-3d.node
Normal file
17
testsuite/gsk/compare/transform-huge-child-3d.node
Normal file
@@ -0,0 +1,17 @@
|
||||
/* a normal clip */
|
||||
clip {
|
||||
clip: 0 0 50 50;
|
||||
/* a 3D transform */
|
||||
child: transform {
|
||||
transform: rotateX(1);
|
||||
/* a node that forces an offscreen */
|
||||
child: rounded-clip {
|
||||
clip: -30000 -30000 60000 60000 / 5;
|
||||
child: color {
|
||||
/* way too big, but it's clipped, so no problem */
|
||||
bounds: -30000 -30000 60000 60000;
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
testsuite/gsk/compare/transform-huge-child-3d.png
Normal file
BIN
testsuite/gsk/compare/transform-huge-child-3d.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 144 B |
13
testsuite/gsk/compare/transform-huge-child.node
Normal file
13
testsuite/gsk/compare/transform-huge-child.node
Normal file
@@ -0,0 +1,13 @@
|
||||
clip {
|
||||
clip: 0 0 50 50;
|
||||
child: transform {
|
||||
transform: rotate(1);
|
||||
child: rounded-clip {
|
||||
clip: -30000 -30000 60000 60000 / 5;
|
||||
child: color {
|
||||
bounds: -30000 -30000 60000 60000;
|
||||
color: rgb(255,0,0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
testsuite/gsk/compare/transform-huge-child.png
Normal file
BIN
testsuite/gsk/compare/transform-huge-child.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 144 B |
134
testsuite/gsk/curve-special-cases.c
Normal file
134
testsuite/gsk/curve-special-cases.c
Normal file
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright © 2020 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "gsk/gskcurveprivate.h"
|
||||
|
||||
static gboolean
|
||||
measure_segment (const graphene_point_t *from,
|
||||
const graphene_point_t *to,
|
||||
float from_t,
|
||||
float to_t,
|
||||
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)
|
||||
{
|
||||
GskCurve c;
|
||||
graphene_point_t p[4];
|
||||
graphene_vec2_t t;
|
||||
|
||||
graphene_point_init (&p[0], 0, 0);
|
||||
graphene_point_init (&p[1], 100, 0);
|
||||
gsk_curve_init (&c, gsk_pathop_encode (GSK_PATH_LINE, p));
|
||||
|
||||
gsk_curve_get_start_tangent (&c, &t);
|
||||
g_assert_true (graphene_vec2_near (&t, graphene_vec2_x_axis (), 0.0001));
|
||||
gsk_curve_get_end_tangent (&c, &t);
|
||||
g_assert_true (graphene_vec2_near (&t, graphene_vec2_x_axis (), 0.0001));
|
||||
|
||||
|
||||
graphene_point_init (&p[0], 0, 0);
|
||||
graphene_point_init (&p[1], 0, 100);
|
||||
gsk_curve_init (&c, gsk_pathop_encode (GSK_PATH_LINE, p));
|
||||
|
||||
gsk_curve_get_start_tangent (&c, &t);
|
||||
g_assert_true (graphene_vec2_near (&t, graphene_vec2_y_axis (), 0.0001));
|
||||
gsk_curve_get_end_tangent (&c, &t);
|
||||
g_assert_true (graphene_vec2_near (&t, graphene_vec2_y_axis (), 0.0001));
|
||||
|
||||
graphene_point_init (&p[0], 0, 0);
|
||||
graphene_point_init (&p[1], 100, 0);
|
||||
p[2] = GRAPHENE_POINT_INIT (g_test_rand_double_range (0, 20), 0);
|
||||
graphene_point_init (&p[3], 100, 100);
|
||||
gsk_curve_init (&c, gsk_pathop_encode (GSK_PATH_CONIC, p));
|
||||
|
||||
gsk_curve_get_start_tangent (&c, &t);
|
||||
g_assert_true (graphene_vec2_near (&t, graphene_vec2_x_axis (), 0.0001));
|
||||
gsk_curve_get_end_tangent (&c, &t);
|
||||
g_assert_true (graphene_vec2_near (&t, graphene_vec2_y_axis (), 0.0001));
|
||||
|
||||
graphene_point_init (&p[0], 0, 0);
|
||||
graphene_point_init (&p[1], 50, 0);
|
||||
graphene_point_init (&p[2], 100, 50);
|
||||
graphene_point_init (&p[3], 100, 100);
|
||||
gsk_curve_init (&c, gsk_pathop_encode (GSK_PATH_CURVE, p));
|
||||
|
||||
gsk_curve_get_start_tangent (&c, &t);
|
||||
g_assert_true (graphene_vec2_near (&t, graphene_vec2_x_axis (), 0.0001));
|
||||
gsk_curve_get_end_tangent (&c, &t);
|
||||
g_assert_true (graphene_vec2_near (&t, graphene_vec2_y_axis (), 0.0001));
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc,
|
||||
char *argv[])
|
||||
{
|
||||
gtk_test_init (&argc, &argv, NULL);
|
||||
|
||||
g_test_add_func ("/curve/special/conic-segment", test_conic_segment);
|
||||
g_test_add_func ("/curve/special/tangents", test_curve_tangents);
|
||||
|
||||
return g_test_run ();
|
||||
}
|
||||
205
testsuite/gsk/curve.c
Normal file
205
testsuite/gsk/curve.c
Normal file
@@ -0,0 +1,205 @@
|
||||
#include <gtk/gtk.h>
|
||||
#include "gsk/gskcurveprivate.h"
|
||||
|
||||
static void
|
||||
init_random_point (graphene_point_t *p)
|
||||
{
|
||||
p->x = g_test_rand_double_range (0, 1000);
|
||||
p->y = g_test_rand_double_range (0, 1000);
|
||||
}
|
||||
|
||||
static float
|
||||
random_weight (void)
|
||||
{
|
||||
if (g_test_rand_bit ())
|
||||
return g_test_rand_double_range (0, 100);
|
||||
else
|
||||
return 1.0 / g_test_rand_double_range (1, 100);
|
||||
}
|
||||
|
||||
static void
|
||||
init_random_curve (GskCurve *curve)
|
||||
{
|
||||
switch (g_test_rand_int_range (GSK_PATH_LINE, GSK_PATH_CONIC + 1))
|
||||
{
|
||||
case GSK_PATH_LINE:
|
||||
{
|
||||
graphene_point_t p[2];
|
||||
|
||||
init_random_point (&p[0]);
|
||||
init_random_point (&p[1]);
|
||||
gsk_curve_init (curve, gsk_pathop_encode (GSK_PATH_LINE, p));
|
||||
}
|
||||
break;
|
||||
|
||||
case GSK_PATH_CURVE:
|
||||
{
|
||||
graphene_point_t p[4];
|
||||
|
||||
init_random_point (&p[0]);
|
||||
init_random_point (&p[1]);
|
||||
init_random_point (&p[2]);
|
||||
init_random_point (&p[3]);
|
||||
gsk_curve_init (curve, gsk_pathop_encode (GSK_PATH_CURVE, p));
|
||||
}
|
||||
break;
|
||||
|
||||
case GSK_PATH_CONIC:
|
||||
{
|
||||
graphene_point_t p[4];
|
||||
|
||||
init_random_point (&p[0]);
|
||||
init_random_point (&p[1]);
|
||||
p[2] = GRAPHENE_POINT_INIT (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 ();
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_curve_tangents (void)
|
||||
{
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
GskCurve c;
|
||||
graphene_vec2_t vec, exact;
|
||||
|
||||
init_random_curve (&c);
|
||||
|
||||
gsk_curve_get_tangent (&c, 0, &vec);
|
||||
g_assert_cmpfloat_with_epsilon (graphene_vec2_length (&vec), 1.0f, 0.00001);
|
||||
gsk_curve_get_start_tangent (&c, &exact);
|
||||
g_assert_cmpfloat_with_epsilon (graphene_vec2_length (&exact), 1.0f, 0.00001);
|
||||
g_assert_true (graphene_vec2_near (&vec, &exact, 0.05));
|
||||
|
||||
gsk_curve_get_tangent (&c, 1, &vec);
|
||||
g_assert_cmpfloat_with_epsilon (graphene_vec2_length (&vec), 1.0f, 0.00001);
|
||||
gsk_curve_get_end_tangent (&c, &exact);
|
||||
g_assert_cmpfloat_with_epsilon (graphene_vec2_length (&exact), 1.0f, 0.00001);
|
||||
g_assert_true (graphene_vec2_near (&vec, &exact, 0.05));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_curve_points (void)
|
||||
{
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
GskCurve c;
|
||||
graphene_point_t p;
|
||||
|
||||
init_random_curve (&c);
|
||||
|
||||
/* We can assert equality here because evaluating the polynomials with 0
|
||||
* has no effect on accuracy.
|
||||
*/
|
||||
gsk_curve_get_point (&c, 0, &p);
|
||||
g_assert_true (graphene_point_equal (gsk_curve_get_start_point (&c), &p));
|
||||
/* But here we evaluate the polynomials with 1 which gives the highest possible
|
||||
* accuracy error. So we'll just be generous here.
|
||||
*/
|
||||
gsk_curve_get_point (&c, 1, &p);
|
||||
g_assert_true (graphene_point_near (gsk_curve_get_end_point (&c), &p, 0.05));
|
||||
}
|
||||
}
|
||||
|
||||
/* at this point the subdivision stops and the decomposer
|
||||
* violates tolerance rules
|
||||
*/
|
||||
#define MIN_PROGRESS (1/1024.f)
|
||||
|
||||
typedef struct
|
||||
{
|
||||
graphene_point_t p;
|
||||
float t;
|
||||
} PointOnLine;
|
||||
|
||||
static gboolean
|
||||
add_line_to_array (const graphene_point_t *from,
|
||||
const graphene_point_t *to,
|
||||
float from_progress,
|
||||
float to_progress,
|
||||
GskCurveLineReason reason,
|
||||
gpointer user_data)
|
||||
{
|
||||
GArray *array = user_data;
|
||||
PointOnLine *last = &g_array_index (array, PointOnLine, array->len - 1);
|
||||
|
||||
g_assert (array->len > 0);
|
||||
g_assert_cmpfloat (from_progress, >=, 0.0f);
|
||||
g_assert_cmpfloat (from_progress, <, to_progress);
|
||||
g_assert_cmpfloat (to_progress, <=, 1.0f);
|
||||
|
||||
g_assert_true (graphene_point_equal (&last->p, from));
|
||||
g_assert_cmpfloat (last->t, ==, from_progress);
|
||||
|
||||
g_array_append_vals (array, (PointOnLine[1]) { { *to, to_progress } }, 1);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
test_curve_decompose (void)
|
||||
{
|
||||
static const float tolerance = 0.5;
|
||||
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
GArray *array;
|
||||
GskCurve c;
|
||||
|
||||
init_random_curve (&c);
|
||||
|
||||
array = g_array_new (FALSE, FALSE, sizeof (PointOnLine));
|
||||
g_array_append_vals (array, (PointOnLine[1]) { { *gsk_curve_get_start_point (&c), 0.f } }, 1);
|
||||
|
||||
g_assert_true (gsk_curve_decompose (&c, tolerance, add_line_to_array, array));
|
||||
|
||||
g_assert_cmpint (array->len, >=, 2); /* We at least got a line to the end */
|
||||
g_assert_cmpfloat (g_array_index (array, PointOnLine, array->len - 1).t, ==, 1.0);
|
||||
|
||||
for (int j = 0; j < array->len; j++)
|
||||
{
|
||||
PointOnLine *pol = &g_array_index (array, PointOnLine, j);
|
||||
graphene_point_t p;
|
||||
|
||||
/* Check that the points we got are actually on the line */
|
||||
gsk_curve_get_point (&c, pol->t, &p);
|
||||
g_assert_true (graphene_point_near (&pol->p, &p, 0.05));
|
||||
|
||||
/* Check that the mid point is not further than the tolerance */
|
||||
if (j > 0)
|
||||
{
|
||||
PointOnLine *last = &g_array_index (array, PointOnLine, j - 1);
|
||||
graphene_point_t mid;
|
||||
|
||||
if (pol->t - last->t > MIN_PROGRESS)
|
||||
{
|
||||
graphene_point_interpolate (&last->p, &pol->p, 0.5, &mid);
|
||||
gsk_curve_get_point (&c, (pol->t + last->t) / 2, &p);
|
||||
/* The decomposer does this cheaper Manhattan distance test,
|
||||
* so graphene_point_near() does not work */
|
||||
g_assert_cmpfloat (fabs (mid.x - p.x), <=, tolerance);
|
||||
g_assert_cmpfloat (fabs (mid.y - p.y), <=, tolerance);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
g_test_init (&argc, &argv, NULL);
|
||||
|
||||
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);
|
||||
|
||||
return g_test_run ();
|
||||
}
|
||||
181
testsuite/gsk/dash.c
Normal file
181
testsuite/gsk/dash.c
Normal file
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
* Copyright © 2020 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "gsk/gskpathdashprivate.h"
|
||||
|
||||
static gboolean
|
||||
build_path (GskPathOperation op,
|
||||
const graphene_point_t *pts,
|
||||
gsize n_pts,
|
||||
float weight,
|
||||
gpointer user_data)
|
||||
{
|
||||
GskPathBuilder *builder = user_data;
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case GSK_PATH_MOVE:
|
||||
gsk_path_builder_move_to (builder, pts[0].x, pts[0].y);
|
||||
break;
|
||||
|
||||
case GSK_PATH_CLOSE:
|
||||
gsk_path_builder_close (builder);
|
||||
break;
|
||||
|
||||
case GSK_PATH_LINE:
|
||||
gsk_path_builder_line_to (builder, pts[1].x, pts[1].y);
|
||||
break;
|
||||
|
||||
case GSK_PATH_CURVE:
|
||||
gsk_path_builder_curve_to (builder, pts[1].x, pts[1].y, pts[2].x, pts[2].y, pts[3].x, pts[3].y);
|
||||
break;
|
||||
|
||||
case GSK_PATH_CONIC:
|
||||
gsk_path_builder_conic_to (builder, pts[1].x, pts[1].y, pts[2].x, pts[2].y, weight);
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
break;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
test_simple (void)
|
||||
{
|
||||
const struct {
|
||||
const char *test;
|
||||
float dash[4];
|
||||
gsize n_dash;
|
||||
float dash_offset;
|
||||
const char *result;
|
||||
} tests[] = {
|
||||
/* a line with a dash of a quarter its size, very simple test */
|
||||
{
|
||||
"M 0 0 L 20 0",
|
||||
{ 5, }, 1, 0.f,
|
||||
"M 0 0 L 5 0 M 10 0 L 15 0",
|
||||
},
|
||||
/* a square with a dash of half its size, another simple test */
|
||||
{
|
||||
"M 0 0 h 10 v 10 h -10 z",
|
||||
{ 5, }, 1, 0.f,
|
||||
"M 10 0 L 10 5 M 10 10 L 5 10 M 0 10 L 0 5 M 0 0 L 5 0"
|
||||
},
|
||||
/* a square smaller than the dash, make sure it closes */
|
||||
{
|
||||
"M 0 0 h 10 v 10 h -10 z",
|
||||
{ 50, }, 1, 0.f,
|
||||
"M 0 0 L 10 0 L 10 10 L 0 10 Z"
|
||||
},
|
||||
/* a square exactly the dash's size, make sure it still closes */
|
||||
{
|
||||
"M 0 0 h 10 v 10 h -10 z",
|
||||
{ 40, }, 1, 0.f,
|
||||
"M 0 0 L 10 0 L 10 10 L 0 10 Z"
|
||||
},
|
||||
/* a dash with offset */
|
||||
{
|
||||
"M 0 0 h 10 v 10 h -10 z",
|
||||
{ 5, }, 1, 2.5f,
|
||||
"M 7.5 0 L 10 0 L 10 2.5 M 10 7.5 L 10 10 L 7.5 10 M 2.5 10 L 0 10 L 0 7.5 M 0 2.5 L 0 0 L 2.5 0"
|
||||
},
|
||||
/* a dash with offset, but this time the rect isn't closed */
|
||||
{
|
||||
"M 0 0 L 10 0 L 10 10 L 0 10 L 0 0",
|
||||
{ 5, }, 1, 2.5f,
|
||||
"M 0 0 L 2.5 0 M 7.5 0 L 10 0 L 10 2.5 M 10 7.5 L 10 10 L 7.5 10 M 2.5 10 L 0 10 L 0 7.5 M 0 2.5 L 0 0"
|
||||
},
|
||||
/* a dash with offset into an empty dash */
|
||||
{
|
||||
"M 0 0 h 10 v 10 h -10 z",
|
||||
{ 5, }, 1, 7.5f,
|
||||
"M 2.5 0 L 7.5 0 M 10 2.5 L 10 7.5 M 7.5 10 L 2.5 10 M 0 7.5 L 0 2.5"
|
||||
},
|
||||
/* a dash with offset where the whole rectangle fits into one element - make sure it closes */
|
||||
{
|
||||
"M 0 0 h 10 v 10 h -10 z",
|
||||
{ 1, 1, 100 }, 3, 3.f,
|
||||
"M 0 0 L 10 0 L 10 10 L 0 10 Z"
|
||||
},
|
||||
/* a dash with 0-length elements, aka dotting */
|
||||
{
|
||||
"M 0 0 h 10 v 10 h -10 z",
|
||||
{ 0, 5 }, 2, 0.f,
|
||||
"M 5 0 M 10 0 M 10 5 M 10 10 M 5 10 M 0 10 M 0 5 M 0 0"
|
||||
},
|
||||
/* a dash of a circle */
|
||||
{
|
||||
"M 10 5 O 10 10, 5 10, 0.70710676908493042 O 0 10, 0 5, 0.70710676908493042 O 0 0, 5 0, 0.70710676908493042 O 10 0, 10 5, 0.70710676908493042 Z",
|
||||
{ 32, }, 1, 0.f,
|
||||
"M 10 5 O 10 10, 5 10, 0.70710676908493042 O 0 10, 0 5, 0.70710676908493042 O 0 0, 5 0, 0.70710676908493042 O 10 0, 10 5, 0.70710676908493042 Z",
|
||||
},
|
||||
/* a dash with offset and 2 contours */
|
||||
{
|
||||
"M 10 10 h 10 v 10 h -10 z M 20 20 h 10 v 10 h -10 z",
|
||||
{ 5, }, 1, 2.5f,
|
||||
"M 17.5 10 L 20 10 L 20 12.5 M 20 17.5 L 20 20 L 17.5 20 M 12.5 20 L 10 20 L 10 17.5 M 10 12.5 L 10 10 L 12.5 10 "
|
||||
"M 27.5 20 L 30 20 L 30 22.5 M 30 27.5 L 30 30 L 27.5 30 M 22.5 30 L 20 30 L 20 27.5 M 20 22.5 L 20 20 L 22.5 20"
|
||||
},
|
||||
};
|
||||
GskPath *path, *result;
|
||||
GskPathBuilder *builder;
|
||||
GskStroke *stroke;
|
||||
char *s;
|
||||
|
||||
for (gsize i = 0; i < G_N_ELEMENTS(tests); i++)
|
||||
{
|
||||
stroke = gsk_stroke_new (1);
|
||||
gsk_stroke_set_dash (stroke, tests[i].dash, tests[i].n_dash);
|
||||
gsk_stroke_set_dash_offset (stroke, tests[i].dash_offset);
|
||||
|
||||
path = gsk_path_parse (tests[i].test);
|
||||
g_assert_nonnull (path);
|
||||
s = gsk_path_to_string (path);
|
||||
g_assert_cmpstr (s, ==, tests[i].test);
|
||||
g_free (s);
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
gsk_path_dash (path, stroke, 0.5, build_path, builder);
|
||||
result = gsk_path_builder_free_to_path (builder);
|
||||
|
||||
s = gsk_path_to_string (result);
|
||||
g_assert_cmpstr (s, ==, tests[i].result);
|
||||
g_free (s);
|
||||
|
||||
gsk_path_unref (result);
|
||||
gsk_stroke_free (stroke);
|
||||
gsk_path_unref (path);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc,
|
||||
char *argv[])
|
||||
{
|
||||
gtk_test_init (&argc, &argv, NULL);
|
||||
|
||||
g_test_add_func ("/dash/simple", test_simple);
|
||||
|
||||
return g_test_run ();
|
||||
}
|
||||
@@ -36,6 +36,7 @@ compare_render_tests = [
|
||||
'color-matrix-identity',
|
||||
'color-matrix-parsing',
|
||||
'crossfade-clip-both-children',
|
||||
'cross-fade-clipped-with-huge-children',
|
||||
'cross-fade-in-opacity',
|
||||
'cross-fade-in-rotate',
|
||||
'css-background',
|
||||
@@ -64,6 +65,7 @@ compare_render_tests = [
|
||||
'inset-shadow-multiple',
|
||||
'invalid-transform',
|
||||
'issue-3615',
|
||||
'linear-gradient-with-64-colorstops',
|
||||
'mask',
|
||||
'mask-clipped-inverted-alpha',
|
||||
'mask-modes',
|
||||
@@ -81,6 +83,7 @@ compare_render_tests = [
|
||||
'repeat-no-repeat',
|
||||
'repeat-empty-child-bounds',
|
||||
'repeat-negative-coords',
|
||||
'repeat-scaling',
|
||||
'repeat-texture',
|
||||
'repeating-gradient-scaled',
|
||||
'scale-textures-negative-ngl',
|
||||
@@ -92,6 +95,8 @@ compare_render_tests = [
|
||||
'texture-scale-magnify-rotate',
|
||||
'texture-scale-stripes',
|
||||
'texture-url',
|
||||
'transform-huge-child',
|
||||
'transform-huge-child-3d',
|
||||
'transform-in-transform',
|
||||
'transform-in-transform-in-transform',
|
||||
'z-transform-clipping-bounds-3d',
|
||||
@@ -108,6 +113,7 @@ informative_render_tests = [
|
||||
|
||||
renderers = [
|
||||
{ 'name': 'gl' },
|
||||
{ 'name': 'vulkan' },
|
||||
{ 'name': 'broadway', 'exclude_term': '-3d' },
|
||||
{ 'name': 'cairo', 'exclude_term': '-3d' },
|
||||
]
|
||||
@@ -138,7 +144,7 @@ foreach renderer : renderers
|
||||
'gsk-compare-' + renderer_name,
|
||||
]
|
||||
|
||||
if compare_xfails.contains(testname)
|
||||
if compare_xfails.contains(testname) or renderer_name == 'vulkan'
|
||||
suites += 'failing'
|
||||
endif
|
||||
|
||||
@@ -147,7 +153,8 @@ foreach renderer : renderers
|
||||
endif
|
||||
|
||||
if ((exclude_term == '' or not testname.contains(exclude_term)) and
|
||||
(renderer_name != 'broadway' or broadway_enabled))
|
||||
(renderer_name != 'broadway' or broadway_enabled) and
|
||||
(renderer_name != 'vulkan' or have_vulkan))
|
||||
test(renderer_name + ' ' + testname, compare_render,
|
||||
args: [
|
||||
'--output', join_paths(meson.current_build_dir(), 'compare', renderer_name),
|
||||
@@ -357,6 +364,11 @@ foreach test : node_parser_tests
|
||||
endforeach
|
||||
|
||||
tests = [
|
||||
['curve', ['../../gsk/gskcurve.c'], ['-DGTK_COMPILATION']],
|
||||
['curve-special-cases', ['../../gsk/gskcurve.c'], ['-DGTK_COMPILATION']],
|
||||
['dash'],
|
||||
['path'],
|
||||
['path-special-cases'],
|
||||
['transform'],
|
||||
['shader'],
|
||||
]
|
||||
|
||||
319
testsuite/gsk/path-special-cases.c
Normal file
319
testsuite/gsk/path-special-cases.c
Normal file
@@ -0,0 +1,319 @@
|
||||
/*
|
||||
* Copyright © 2020 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
/* testcases from path_parser.rs in librsvg */
|
||||
static void
|
||||
test_rsvg_parse (void)
|
||||
{
|
||||
struct {
|
||||
const char *in;
|
||||
const char *out;
|
||||
} tests[] = {
|
||||
{ "", "" },
|
||||
// numbers
|
||||
{ "M 10 20", "M 10 20" },
|
||||
{ "M -10 -20", "M -10 -20" },
|
||||
{ "M .10 0.20", "M 0.1 0.2" },
|
||||
{ "M -.10 -0.20", "M -0.1 -0.2" },
|
||||
{ "M-.10-0.20", "M -0.1 -0.2" },
|
||||
{ "M10.5.50", "M 10.5 0.5" },
|
||||
{ "M.10.20", "M 0.1 0.2" },
|
||||
{ "M .10E1 .20e-4", "M 1 2e-05" },
|
||||
{ "M-.10E1-.20", "M -1 -0.2" },
|
||||
{ "M10.10E2 -0.20e3", "M 1010 -200" },
|
||||
{ "M-10.10E2-0.20e-3", "M -1010 -0.0002" },
|
||||
{ "M1e2.5", "M 100 0.5" },
|
||||
{ "M1e-2.5", "M 0.01 0.5" },
|
||||
{ "M1e+2.5", "M 100 0.5" },
|
||||
// bogus numbers
|
||||
{ "M+", NULL },
|
||||
{ "M-", NULL },
|
||||
{ "M+x", NULL },
|
||||
{ "M10e", NULL },
|
||||
{ "M10ex", NULL },
|
||||
{ "M10e-", NULL },
|
||||
{ "M10e+x", NULL },
|
||||
// numbers with comma
|
||||
{ "M 10, 20", "M 10 20" },
|
||||
{ "M -10,-20", "M -10 -20" },
|
||||
{ "M.10 , 0.20", "M 0.1 0.2" },
|
||||
{ "M -.10, -0.20 ", "M -0.1 -0.2" },
|
||||
{ "M-.10-0.20", "M -0.1 -0.2" },
|
||||
{ "M.10.20", "M 0.1 0.2" },
|
||||
{ "M .10E1,.20e-4", "M 1 2e-05" },
|
||||
{ "M-.10E-2,-.20", "M -0.001 -0.2" },
|
||||
{ "M10.10E2,-0.20e3", "M 1010 -200" },
|
||||
{ "M-10.10E2,-0.20e-3", "M -1010 -0.0002" },
|
||||
// single moveto
|
||||
{ "M 10 20 ", "M 10 20" },
|
||||
{ "M10,20 ", "M 10 20" },
|
||||
{ "M10 20 ", "M 10 20" },
|
||||
{ " M10,20 ", "M 10 20" },
|
||||
// relative moveto
|
||||
{ "m10 20", "M 10 20" },
|
||||
// absolute moveto with implicit lineto
|
||||
{ "M10 20 30 40", "M 10 20 L 30 40" },
|
||||
{ "M10,20,30,40", "M 10 20 L 30 40" },
|
||||
{ "M.1-2,3E2-4", "M 0.1 -2 L 300 -4" },
|
||||
// relative moveto with implicit lineto
|
||||
{ "m10 20 30 40", "M 10 20 L 40 60" },
|
||||
// relative moveto with relative lineto sequence
|
||||
{ "m 46,447 l 0,0.5 -1,0 -1,0 0,1 0,12",
|
||||
"M 46 447 L 46 447.5 L 45 447.5 L 44 447.5 L 44 448.5 L 44 460.5" },
|
||||
// absolute moveto with implicit linetos
|
||||
{ "M10,20 30,40,50 60", "M 10 20 L 30 40 L 50 60" },
|
||||
// relative moveto with implicit linetos
|
||||
{ "m10 20 30 40 50 60", "M 10 20 L 40 60 L 90 120" },
|
||||
// absolute moveto moveto
|
||||
{ "M10 20 M 30 40", "M 10 20 M 30 40" },
|
||||
// relative moveto moveto
|
||||
{ "m10 20 m 30 40", "M 10 20 M 40 60" },
|
||||
// relative moveto lineto moveto
|
||||
{ "m10 20 30 40 m 50 60", "M 10 20 L 40 60 M 90 120" },
|
||||
// absolute moveto lineto
|
||||
{ "M10 20 L30,40", "M 10 20 L 30 40" },
|
||||
// relative moveto lineto
|
||||
{ "m10 20 l30,40", "M 10 20 L 40 60" },
|
||||
// relative moveto lineto lineto abs lineto
|
||||
{ "m10 20 30 40l30,40,50 60L200,300",
|
||||
"M 10 20 L 40 60 L 70 100 L 120 160 L 200 300" },
|
||||
// horizontal lineto
|
||||
{ "M10 20 H30", "M 10 20 L 30 20" },
|
||||
{ "M 10 20 H 30 40", "M 10 20 L 30 20 L 40 20" },
|
||||
{ "M10 20 H30,40-50", "M 10 20 L 30 20 L 40 20 L -50 20" },
|
||||
{ "m10 20 h30,40-50", "M 10 20 L 40 20 L 80 20 L 30 20" },
|
||||
// vertical lineto
|
||||
{ "M10 20 V30", "M 10 20 L 10 30" },
|
||||
{ "M10 20 V30 40", "M 10 20 L 10 30 L 10 40" },
|
||||
{ "M10 20 V30,40-50", "M 10 20 L 10 30 L 10 40 L 10 -50" },
|
||||
{ "m10 20 v30,40-50", "M 10 20 L 10 50 L 10 90 L 10 40" },
|
||||
// curveto
|
||||
{ "M10 20 C 30,40 50 60-70,80", "M 10 20 C 30 40, 50 60, -70 80" },
|
||||
{ "M10 20 C 30,40 50 60-70,80,90 100,110 120,130,140",
|
||||
"M 10 20 C 30 40, 50 60, -70 80 C 90 100, 110 120, 130 140" },
|
||||
{ "m10 20 c 30,40 50 60-70,80,90 100,110 120,130,140",
|
||||
"M 10 20 C 40 60, 60 80, -60 100 C 30 200, 50 220, 70 240" },
|
||||
{ "m10 20 c 30,40 50 60-70,80 90 100,110 120,130,140",
|
||||
"M 10 20 C 40 60, 60 80, -60 100 C 30 200, 50 220, 70 240" },
|
||||
// smooth curveto
|
||||
{ "M10 20 S 30,40-50,60", "M 10 20 C 10 20, 30 40, -50 60" },
|
||||
{ "M10 20 S 30,40 50 60-70,80,90 100",
|
||||
"M 10 20 C 10 20, 30 40, 50 60 C 70 80, -70 80, 90 100" },
|
||||
// quadratic curveto
|
||||
{ "M10 20 Q30 40 50 60", "M 10 20 C 23.3333333 33.3333333, 36.6666667 46.6666667, 50 60" },
|
||||
{ "M10 20 Q30 40 50 60,70,80-90 100",
|
||||
"M 10 20 C 23.3333333 33.3333333, 36.6666667 46.6666667, 50 60 C 63.3333333 73.3333333, 16.6666667 86.6666667, -90 100" },
|
||||
{ "m10 20 q 30,40 50 60-70,80 90 100",
|
||||
"M 10 20 C 30 46.6666667, 46.6666667 66.6666667, 60 80 C 13.3333333 133.3333333, 43.3333333 166.6666667, 150 180" },
|
||||
// smooth quadratic curveto
|
||||
{ "M10 20 T30 40", "M 10 20 C 10 20, 16.6666667 26.6666667, 30 40" },
|
||||
{ "M10 20 Q30 40 50 60 T70 80",
|
||||
"M 10 20 C 23.3333333 33.3333333, 36.6666667 46.6666667, 50 60 C 63.3333333 73.3333333, 70 80, 70 80" },
|
||||
{ "m10 20 q 30,40 50 60t-70,80",
|
||||
"M 10 20 C 30 46.6666667, 46.6666667 66.6666667, 60 80 C 73.3333333 93.3333333, 50 120, -10 160" },
|
||||
// elliptical arc. Exact numbers depend on too much math, so just verify
|
||||
// that these parse successfully
|
||||
{ "M 1 3 A 1 2 3 00 6 7", "path" },
|
||||
{ "M 1 2 A 1 2 3 016 7", "path" },
|
||||
{ "M 1 2 A 1 2 3 10,6 7", "path" },
|
||||
{ "M 1 2 A 1 2 3 1,1 6 7", "path" },
|
||||
{ "M 1 2 A 1 2 3 1 1 6 7", "path" },
|
||||
{ "M 1 2 A 1 2 3 1 16 7", "path" },
|
||||
// close path
|
||||
{ "M10 20 Z", "M 10 20 Z" },
|
||||
{ "m10 20 30 40 m 50 60 70 80 90 100z", "M 10 20 L 40 60 M 90 120 L 160 200 L 250 300 Z" },
|
||||
// must start with moveto
|
||||
{ " L10 20", NULL },
|
||||
// moveto args
|
||||
{ "M", NULL },
|
||||
{ "M,", NULL },
|
||||
{ "M10", NULL },
|
||||
{ "M10,", NULL },
|
||||
{ "M10x", NULL },
|
||||
{ "M10,x", NULL },
|
||||
{ "M10-20,", NULL },
|
||||
{ "M10-20-30", NULL },
|
||||
{ "M10-20-30 x", NULL },
|
||||
// closepath args
|
||||
{ "M10-20z10", NULL },
|
||||
{ "M10-20z,", NULL },
|
||||
// lineto args
|
||||
{ "M10-20L10", NULL },
|
||||
{ "M 10,10 L 20,20,30", NULL },
|
||||
{ "M 10,10 L 20,20,", NULL },
|
||||
// horizontal lineto args
|
||||
{ "M10-20H", NULL },
|
||||
{ "M10-20H,", NULL },
|
||||
{ "M10-20H30,", NULL },
|
||||
// vertical lineto args
|
||||
{ "M10-20v", NULL },
|
||||
{ "M10-20v,", NULL },
|
||||
{ "M10-20v30,", NULL },
|
||||
// curveto args
|
||||
{ "M10-20C1", NULL },
|
||||
{ "M10-20C1,", NULL },
|
||||
{ "M10-20C1 2", NULL },
|
||||
{ "M10-20C1,2,", NULL },
|
||||
{ "M10-20C1 2 3", NULL },
|
||||
{ "M10-20C1,2,3", NULL },
|
||||
{ "M10-20C1,2,3,", NULL },
|
||||
{ "M10-20C1 2 3 4", NULL },
|
||||
{ "M10-20C1,2,3,4", NULL },
|
||||
{ "M10-20C1,2,3,4,", NULL },
|
||||
{ "M10-20C1 2 3 4 5", NULL },
|
||||
{ "M10-20C1,2,3,4,5", NULL },
|
||||
{ "M10-20C1,2,3,4,5,", NULL },
|
||||
{ "M10-20C1,2,3,4,5,6,", NULL },
|
||||
// smooth curveto args
|
||||
{ "M10-20S1", NULL },
|
||||
{ "M10-20S1,", NULL },
|
||||
{ "M10-20S1 2", NULL },
|
||||
{ "M10-20S1,2,", NULL },
|
||||
{ "M10-20S1 2 3", NULL },
|
||||
{ "M10-20S1,2,3,", NULL },
|
||||
{ "M10-20S1,2,3,4,", NULL },
|
||||
// quadratic curveto args
|
||||
{ "M10-20Q1", NULL },
|
||||
{ "M10-20Q1,", NULL },
|
||||
{ "M10-20Q1 2", NULL },
|
||||
{ "M10-20Q1,2,", NULL },
|
||||
{ "M10-20Q1 2 3", NULL },
|
||||
{ "M10-20Q1,2,3", NULL },
|
||||
{ "M10-20Q1,2,3,", NULL },
|
||||
{ "M10 20 Q30 40 50 60,", NULL },
|
||||
// smooth quadratic curveto args
|
||||
{ "M10-20T1", NULL },
|
||||
{ "M10-20T1,", NULL },
|
||||
{ "M10 20 T 30 40,", NULL },
|
||||
// elliptical arc args
|
||||
{ "M10-20A1", NULL },
|
||||
{ "M10-20A1,", NULL },
|
||||
{ "M10-20A1 2", NULL },
|
||||
{ "M10-20A1 2,", NULL },
|
||||
{ "M10-20A1 2 3", NULL },
|
||||
{ "M10-20A1 2 3,", NULL },
|
||||
{ "M10-20A1 2 3 4", NULL },
|
||||
{ "M10-20A1 2 3 1", NULL },
|
||||
{ "M10-20A1 2 3,1,", NULL },
|
||||
{ "M10-20A1 2 3 1 5", NULL },
|
||||
{ "M10-20A1 2 3 1 1", NULL },
|
||||
{ "M10-20A1 2 3,1,1,", NULL },
|
||||
{ "M10-20A1 2 3 1 1 6", NULL },
|
||||
{ "M10-20A1 2 3,1,1,6,", NULL },
|
||||
{ "M 1 2 A 1 2 3 1.0 0.0 6 7", NULL },
|
||||
{ "M10-20A1 2 3,1,1,6,7,", NULL },
|
||||
// misc
|
||||
{ "M.. 1,0 0,100000", NULL },
|
||||
{ "M 10 20,M 10 20", NULL },
|
||||
{ "M 10 20, M 10 20", NULL },
|
||||
{ "M 10 20, M 10 20", NULL },
|
||||
{ "M 10 20, ", NULL },
|
||||
};
|
||||
int i;
|
||||
|
||||
for (i = 0; i < G_N_ELEMENTS (tests); i++)
|
||||
{
|
||||
GskPath *path;
|
||||
char *string;
|
||||
char *string2;
|
||||
|
||||
if (g_test_verbose ())
|
||||
g_print ("%d: %s\n", i, tests[i].in);
|
||||
|
||||
path = gsk_path_parse (tests[i].in);
|
||||
if (tests[i].out)
|
||||
{
|
||||
g_assert_nonnull (path);
|
||||
string = gsk_path_to_string (path);
|
||||
gsk_path_unref (path);
|
||||
|
||||
if (strcmp (tests[i].out, "path") != 0)
|
||||
{
|
||||
/* Preferred, but doesn't work, because
|
||||
* gsk_path_print() prints numbers with
|
||||
* insane accuracy */
|
||||
/* g_assert_cmpstr (tests[i].out, ==, string); */
|
||||
path = gsk_path_parse (tests[i].out);
|
||||
g_assert_nonnull (path);
|
||||
|
||||
string2 = gsk_path_to_string (path);
|
||||
gsk_path_unref (path);
|
||||
|
||||
g_assert_cmpstr (string, ==, string2);
|
||||
}
|
||||
|
||||
path = gsk_path_parse (string);
|
||||
g_assert_nonnull (path);
|
||||
|
||||
string2 = gsk_path_to_string (path);
|
||||
gsk_path_unref (path);
|
||||
|
||||
g_assert_cmpstr (string, ==, string2);
|
||||
|
||||
g_free (string);
|
||||
g_free (string2);
|
||||
}
|
||||
else
|
||||
g_assert_null (path);
|
||||
}
|
||||
}
|
||||
|
||||
/* Test that circles and rectangles serialize as expected and can be
|
||||
* round-tripped through strings.
|
||||
*/
|
||||
static void
|
||||
test_serialize_custom_contours (void)
|
||||
{
|
||||
GskPathBuilder *builder;
|
||||
GskPath *path;
|
||||
GskPath *path1;
|
||||
char *string;
|
||||
char *string1;
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
gsk_path_builder_add_circle (builder, &GRAPHENE_POINT_INIT (100, 100), 50);
|
||||
gsk_path_builder_add_rect (builder, &GRAPHENE_RECT_INIT (111, 222, 333, 444));
|
||||
path = gsk_path_builder_free_to_path (builder);
|
||||
|
||||
string = gsk_path_to_string (path);
|
||||
g_assert_cmpstr ("M 150 100 A 50 50 0 0 0 50 100 A 50 50 0 0 0 150 100 z M 111 222 h 333 v 444 h -333 z", ==, string);
|
||||
|
||||
path1 = gsk_path_parse (string);
|
||||
string1 = gsk_path_to_string (path1);
|
||||
g_assert_cmpstr (string, ==, string1);
|
||||
|
||||
g_free (string);
|
||||
g_free (string1);
|
||||
gsk_path_unref (path);
|
||||
gsk_path_unref (path1);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc,
|
||||
char *argv[])
|
||||
{
|
||||
gtk_test_init (&argc, &argv, NULL);
|
||||
|
||||
g_test_add_func ("/path/rsvg-parse", test_rsvg_parse);
|
||||
g_test_add_func ("/path/serialize-custom-contours", test_serialize_custom_contours);
|
||||
|
||||
return g_test_run ();
|
||||
}
|
||||
1152
testsuite/gsk/path.c
Normal file
1152
testsuite/gsk/path.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -130,6 +130,14 @@ count_nodes (GskRenderNode *node,
|
||||
d = MAX (d, dd);
|
||||
break;
|
||||
|
||||
case GSK_FILL_NODE:
|
||||
count_nodes (gsk_fill_node_get_child (node), counts, &d);
|
||||
break;
|
||||
|
||||
case GSK_STROKE_NODE:
|
||||
count_nodes (gsk_stroke_node_get_child (node), counts, &d);
|
||||
break;
|
||||
|
||||
case GSK_NOT_A_RENDER_NODE:
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
|
||||
Reference in New Issue
Block a user