Compare commits
119 Commits
wayland-po
...
path-tests
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e16c836f60 | ||
|
|
55bfc167e3 | ||
|
|
5516ec2bf0 | ||
|
|
7e7d1987a3 | ||
|
|
304c3055ad | ||
|
|
226f556cf8 | ||
|
|
be7fead1fb | ||
|
|
b871c2a3cb | ||
|
|
7c67a9c76a | ||
|
|
427e185bcf | ||
|
|
30552e9988 | ||
|
|
08d8d6c029 | ||
|
|
61a84956d4 | ||
|
|
e44780923a | ||
|
|
dd1a2e0396 | ||
|
|
0100c33b71 | ||
|
|
8b8197a662 | ||
|
|
e293f10fa1 | ||
|
|
3a4c3c3d8c | ||
|
|
944735071e | ||
|
|
91356de96a | ||
|
|
3f29c50800 | ||
|
|
59b1f9e7b1 | ||
|
|
dd3cf83a35 | ||
|
|
7f8a9e5543 | ||
|
|
8523c6cb2d | ||
|
|
49b25424c5 | ||
|
|
db2046cb67 | ||
|
|
61264165ff | ||
|
|
7b3d283fc0 | ||
|
|
44ffed0283 | ||
|
|
e91ec1f26f | ||
|
|
eace55daa6 | ||
|
|
097e58558b | ||
|
|
f0acb0f073 | ||
|
|
b779ba19ca | ||
|
|
aa0f1ce956 | ||
|
|
e9d01c1a63 | ||
|
|
e235392894 | ||
|
|
73904f3034 | ||
|
|
c2ed71af7a | ||
|
|
aa2f6345c8 | ||
|
|
cef1bc9098 | ||
|
|
828ccc51d9 | ||
|
|
8397d10c20 | ||
|
|
78d06e58d0 | ||
|
|
a5e13cc96b | ||
|
|
776dc54c8a | ||
|
|
ee6879fba8 | ||
|
|
14f9395476 | ||
|
|
1349cf2454 | ||
|
|
d47ebd388c | ||
|
|
61214221b3 | ||
|
|
7eb4ed8f86 | ||
|
|
92b472fec1 | ||
|
|
7a6b008479 | ||
|
|
d8c26172a5 | ||
|
|
da835321a5 | ||
|
|
30635d1cac | ||
|
|
a60b72ba58 | ||
|
|
36347892d8 | ||
|
|
924f0bc2c5 | ||
|
|
23c5318de1 | ||
|
|
678405bd22 | ||
|
|
946b76881b | ||
|
|
061683344f | ||
|
|
af9a85616d | ||
|
|
0f332de3d3 | ||
|
|
15e8f2bacc | ||
|
|
25f9eadb0d | ||
|
|
ced2959cdd | ||
|
|
3c363f00d5 | ||
|
|
a9ad37ed89 | ||
|
|
acdf04287d | ||
|
|
34725c1207 | ||
|
|
cb10cf07e6 | ||
|
|
a7ba2bde86 | ||
|
|
87b3cb1df8 | ||
|
|
2478963cbb | ||
|
|
abbbaef7e8 | ||
|
|
69177416a0 | ||
|
|
ff47bfdbcf | ||
|
|
120e449bdd | ||
|
|
237c37f6c2 | ||
|
|
683729c388 | ||
|
|
9fe71e7dae | ||
|
|
c74970e6c1 | ||
|
|
5225d01504 | ||
|
|
916d950069 | ||
|
|
76d6fed248 | ||
|
|
7ff6235ae6 | ||
|
|
9b957c26cb | ||
|
|
445b2382e6 | ||
|
|
790a4201c3 | ||
|
|
00b9af65b5 | ||
|
|
f7c367338c | ||
|
|
5d69887618 | ||
|
|
b658c26a3e | ||
|
|
e4c147cf2f | ||
|
|
fc2c6f1566 | ||
|
|
b0d6130905 | ||
|
|
a0b9238a15 | ||
|
|
801c63494c | ||
|
|
d2cfe74140 | ||
|
|
a3ac003546 | ||
|
|
483a4773cd | ||
|
|
c82ad0214e | ||
|
|
207feeb7a9 | ||
|
|
9a2e4ac9a9 | ||
|
|
2bd3aa3cbe | ||
|
|
118e41f432 | ||
|
|
2d7897a769 | ||
|
|
c279e1aa1e | ||
|
|
3c91d0be91 | ||
|
|
e7bbed54b5 | ||
|
|
4eb7d68970 | ||
|
|
98337f7115 | ||
|
|
08ee6c7acf | ||
|
|
c815496fe3 |
@@ -319,6 +319,9 @@
|
|||||||
<file>paintable_svg.c</file>
|
<file>paintable_svg.c</file>
|
||||||
<file>panes.c</file>
|
<file>panes.c</file>
|
||||||
<file>password_entry.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>peg_solitaire.c</file>
|
||||||
<file>pickers.c</file>
|
<file>pickers.c</file>
|
||||||
<file>printing.c</file>
|
<file>printing.c</file>
|
||||||
@@ -403,6 +406,9 @@
|
|||||||
<gresource prefix="/fontrendering">
|
<gresource prefix="/fontrendering">
|
||||||
<file>fontrendering.ui</file>
|
<file>fontrendering.ui</file>
|
||||||
</gresource>
|
</gresource>
|
||||||
|
<gresource prefix="/path_text">
|
||||||
|
<file>path_text.ui</file>
|
||||||
|
</gresource>
|
||||||
<gresource prefix="/org/gtk/Demo4">
|
<gresource prefix="/org/gtk/Demo4">
|
||||||
<file>icons/16x16/actions/application-exit.png</file>
|
<file>icons/16x16/actions/application-exit.png</file>
|
||||||
<file>icons/16x16/actions/document-new.png</file>
|
<file>icons/16x16/actions/document-new.png</file>
|
||||||
|
|||||||
@@ -68,6 +68,9 @@ demos = files([
|
|||||||
'paintable_mediastream.c',
|
'paintable_mediastream.c',
|
||||||
'panes.c',
|
'panes.c',
|
||||||
'password_entry.c',
|
'password_entry.c',
|
||||||
|
'path_fill.c',
|
||||||
|
'path_maze.c',
|
||||||
|
'path_text.c',
|
||||||
'peg_solitaire.c',
|
'peg_solitaire.c',
|
||||||
'pickers.c',
|
'pickers.c',
|
||||||
'printing.c',
|
'printing.c',
|
||||||
|
|||||||
359
demos/gtk-demo/path_fill.c
Normal file
359
demos/gtk-demo/path_fill.c
Normal file
@@ -0,0 +1,359 @@
|
|||||||
|
/* 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 don't need to do anything special here, because we keep no
|
||||||
|
* data of our own.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
gtk_path_paintable_class_init (GtkPathPaintableClass *klass)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gtk_path_paintable_init (GtkPathPaintable *self)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/* And finally, we add a simple constructor.
|
||||||
|
* It is declared in the header so that the other examples
|
||||||
|
* can use it.
|
||||||
|
*/
|
||||||
|
GdkPaintable *
|
||||||
|
gtk_path_paintable_new (GskPath *path,
|
||||||
|
GdkPaintable *background,
|
||||||
|
int width,
|
||||||
|
int height)
|
||||||
|
{
|
||||||
|
GtkPathPaintable *self;
|
||||||
|
|
||||||
|
self = g_object_new (GTK_TYPE_PATH_PAINTABLE, NULL);
|
||||||
|
self->path = path;
|
||||||
|
self->background = background;
|
||||||
|
if (self->background)
|
||||||
|
{
|
||||||
|
g_signal_connect_swapped (self->background, "invalidate-contents", G_CALLBACK (gdk_paintable_invalidate_contents), self);
|
||||||
|
g_signal_connect_swapped (self->background, "invalidate-size", G_CALLBACK (gdk_paintable_invalidate_size), self);
|
||||||
|
}
|
||||||
|
self->width = width;
|
||||||
|
self->height = height;
|
||||||
|
|
||||||
|
return GDK_PAINTABLE (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
gtk_path_paintable_set_path (GtkPathPaintable *self,
|
||||||
|
GskPath *path)
|
||||||
|
{
|
||||||
|
g_clear_pointer (&self->path, gsk_path_unref);
|
||||||
|
self->path = gsk_path_ref (path);
|
||||||
|
|
||||||
|
gdk_paintable_invalidate_contents (GDK_PAINTABLE (self));
|
||||||
|
}
|
||||||
|
|
||||||
|
static GskPath *
|
||||||
|
create_hexagon (GtkWidget *widget)
|
||||||
|
{
|
||||||
|
GskPathBuilder *builder;
|
||||||
|
|
||||||
|
builder = gsk_path_builder_new ();
|
||||||
|
|
||||||
|
gsk_path_builder_move_to (builder, 120, 0);
|
||||||
|
gsk_path_builder_line_to (builder, 360, 0);
|
||||||
|
gsk_path_builder_line_to (builder, 480, 208);
|
||||||
|
gsk_path_builder_line_to (builder, 360, 416);
|
||||||
|
gsk_path_builder_line_to (builder, 120, 416);
|
||||||
|
gsk_path_builder_line_to (builder, 0, 208);
|
||||||
|
gsk_path_builder_close (builder);
|
||||||
|
|
||||||
|
return gsk_path_builder_free_to_path (builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GskPath *
|
||||||
|
create_path_from_text (GtkWidget *widget)
|
||||||
|
{
|
||||||
|
cairo_surface_t *surface;
|
||||||
|
cairo_t *cr;
|
||||||
|
cairo_path_t *path;
|
||||||
|
PangoLayout *layout;
|
||||||
|
PangoFontDescription *desc;
|
||||||
|
GskPath *result;
|
||||||
|
|
||||||
|
surface = cairo_recording_surface_create (CAIRO_CONTENT_COLOR_ALPHA, NULL);
|
||||||
|
cr = cairo_create (surface);
|
||||||
|
|
||||||
|
layout = gtk_widget_create_pango_layout (widget, "Pango power!\nPango power!\nPango power!");
|
||||||
|
desc = pango_font_description_from_string ("sans bold 36");
|
||||||
|
pango_layout_set_font_description (layout, desc);
|
||||||
|
pango_font_description_free (desc);
|
||||||
|
|
||||||
|
pango_cairo_layout_path (cr, layout);
|
||||||
|
path = cairo_copy_path_flat (cr);
|
||||||
|
result = gsk_path_new_from_cairo (path);
|
||||||
|
|
||||||
|
cairo_path_destroy (path);
|
||||||
|
g_object_unref (layout);
|
||||||
|
cairo_destroy (cr);
|
||||||
|
cairo_surface_destroy (surface);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
build_path (GskPathOperation op,
|
||||||
|
const graphene_point_t *pts,
|
||||||
|
gsize n_pts,
|
||||||
|
float weight,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
GskPathBuilder *builder = user_data;
|
||||||
|
|
||||||
|
switch (op)
|
||||||
|
{
|
||||||
|
case GSK_PATH_MOVE:
|
||||||
|
gsk_path_builder_move_to (builder, pts[0].x, pts[0].y);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GSK_PATH_CLOSE:
|
||||||
|
gsk_path_builder_close (builder);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GSK_PATH_LINE:
|
||||||
|
gsk_path_builder_line_to (builder, pts[1].x, pts[1].y);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GSK_PATH_CURVE:
|
||||||
|
gsk_path_builder_curve_to (builder, pts[1].x, pts[1].y, pts[2].x, pts[2].y, pts[3].x, pts[3].y);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GSK_PATH_CONIC:
|
||||||
|
gsk_path_builder_conic_to (builder, pts[1].x, pts[1].y, pts[2].x, pts[2].y, weight);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
g_assert_not_reached ();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
update_path (GtkWidget *widget,
|
||||||
|
GdkFrameClock *frame_clock,
|
||||||
|
gpointer measure)
|
||||||
|
{
|
||||||
|
float progress = gdk_frame_clock_get_frame_time (frame_clock) % (60 * G_USEC_PER_SEC) / (float) (30 * G_USEC_PER_SEC);
|
||||||
|
GskPathBuilder *builder;
|
||||||
|
GskPath *path;
|
||||||
|
graphene_point_t pos;
|
||||||
|
graphene_vec2_t tangent;
|
||||||
|
GskStroke *stroke;
|
||||||
|
|
||||||
|
builder = gsk_path_builder_new ();
|
||||||
|
gsk_path_builder_add_segment (builder,
|
||||||
|
measure,
|
||||||
|
#if 1
|
||||||
|
0.0, gsk_path_measure_get_length (measure));
|
||||||
|
#else
|
||||||
|
progress > 1 ? (progress - 1) * gsk_path_measure_get_length (measure) : 0.0,
|
||||||
|
(progress < 1 ? progress : 1.0) * gsk_path_measure_get_length (measure));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
path = gsk_path_builder_free_to_path (builder);
|
||||||
|
|
||||||
|
stroke = gsk_stroke_new (1);
|
||||||
|
gsk_stroke_set_dash (stroke, (float[2]) { 10, 5 }, 2);
|
||||||
|
gsk_stroke_set_dash_offset (stroke, - (gdk_frame_clock_get_frame_time (frame_clock) % G_USEC_PER_SEC) * 15. / G_USEC_PER_SEC);
|
||||||
|
builder = gsk_path_builder_new ();
|
||||||
|
gsk_path_dash (path, stroke, 0.2, build_path, builder);
|
||||||
|
gsk_path_unref (path);
|
||||||
|
|
||||||
|
gsk_path_measure_get_point (measure,
|
||||||
|
(progress > 1 ? (progress - 1) : progress) * gsk_path_measure_get_length (measure),
|
||||||
|
&pos,
|
||||||
|
&tangent);
|
||||||
|
gsk_path_builder_move_to (builder, pos.x + 5 * graphene_vec2_get_x (&tangent), pos.y + 5 * graphene_vec2_get_y (&tangent));
|
||||||
|
gsk_path_builder_line_to (builder, pos.x + 3 * graphene_vec2_get_y (&tangent), pos.y + 3 * graphene_vec2_get_x (&tangent));
|
||||||
|
gsk_path_builder_line_to (builder, pos.x - 3 * graphene_vec2_get_y (&tangent), pos.y - 3 * graphene_vec2_get_x (&tangent));
|
||||||
|
gsk_path_builder_close (builder);
|
||||||
|
|
||||||
|
path = gsk_path_builder_free_to_path (builder);
|
||||||
|
|
||||||
|
gtk_path_paintable_set_path (GTK_PATH_PAINTABLE (gtk_picture_get_paintable (GTK_PICTURE (widget))),
|
||||||
|
path);
|
||||||
|
gsk_path_unref (path);
|
||||||
|
|
||||||
|
return G_SOURCE_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
GtkWidget *
|
||||||
|
do_path_fill (GtkWidget *do_widget)
|
||||||
|
{
|
||||||
|
static GtkWidget *window = NULL;
|
||||||
|
|
||||||
|
if (!window)
|
||||||
|
{
|
||||||
|
GtkWidget *picture;
|
||||||
|
GdkPaintable *paintable;
|
||||||
|
GtkMediaStream *stream;
|
||||||
|
GskPath *path;
|
||||||
|
graphene_rect_t bounds;
|
||||||
|
GskPathMeasure *measure;
|
||||||
|
|
||||||
|
window = gtk_window_new ();
|
||||||
|
gtk_window_set_resizable (GTK_WINDOW (window), TRUE);
|
||||||
|
gtk_window_set_title (GTK_WINDOW (window), "Path Fill");
|
||||||
|
g_object_add_weak_pointer (G_OBJECT (window), (gpointer *)&window);
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
stream = gtk_media_file_new_for_resource ("/images/gtk-logo.webm");
|
||||||
|
#else
|
||||||
|
stream = gtk_nuclear_media_stream_new ();
|
||||||
|
#endif
|
||||||
|
gtk_media_stream_play (stream);
|
||||||
|
gtk_media_stream_set_loop (stream, TRUE);
|
||||||
|
|
||||||
|
path = create_hexagon (window);
|
||||||
|
path = create_path_from_text (window);
|
||||||
|
gsk_path_get_bounds (path, &bounds);
|
||||||
|
|
||||||
|
paintable = gtk_path_paintable_new (path,
|
||||||
|
GDK_PAINTABLE (stream),
|
||||||
|
bounds.origin.x + bounds.size.width,
|
||||||
|
bounds.origin.y + bounds.size.height);
|
||||||
|
picture = gtk_picture_new_for_paintable (paintable);
|
||||||
|
measure = gsk_path_measure_new (path);
|
||||||
|
gtk_widget_add_tick_callback (picture, update_path, measure, (GDestroyNotify) gsk_path_measure_unref);
|
||||||
|
gtk_picture_set_keep_aspect_ratio (GTK_PICTURE (picture), FALSE);
|
||||||
|
gtk_picture_set_can_shrink (GTK_PICTURE (picture), FALSE);
|
||||||
|
g_object_unref (paintable);
|
||||||
|
|
||||||
|
gtk_window_set_child (GTK_WINDOW (window), picture);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!gtk_widget_get_visible (window))
|
||||||
|
gtk_widget_show (window);
|
||||||
|
else
|
||||||
|
gtk_window_destroy (GTK_WINDOW (window));
|
||||||
|
|
||||||
|
return window;
|
||||||
|
}
|
||||||
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] = { };
|
||||||
|
guint dir;
|
||||||
|
|
||||||
|
gtk_bitset_add (maze, y * MAZE_WIDTH + x);
|
||||||
|
|
||||||
|
while (TRUE)
|
||||||
|
{
|
||||||
|
set[0] = set[0] || x == 0 || gtk_bitset_contains (maze, y * MAZE_WIDTH + x - 1);
|
||||||
|
set[1] = set[1] || y == 0 || gtk_bitset_contains (maze, (y - 1) * MAZE_WIDTH + x);
|
||||||
|
set[2] = set[2] || x + 1 == MAZE_WIDTH || gtk_bitset_contains (maze, y * MAZE_WIDTH + x + 1);
|
||||||
|
set[3] = set[3] || y + 1 == MAZE_HEIGHT || gtk_bitset_contains (maze, (y + 1) * MAZE_WIDTH + x);
|
||||||
|
|
||||||
|
if (set[0] && set[1] && set[2] && set[3])
|
||||||
|
return;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
dir = g_random_int_range (0, 4);
|
||||||
|
}
|
||||||
|
while (set[dir]);
|
||||||
|
|
||||||
|
switch (dir)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
gsk_path_builder_move_to (builder, (x + 0.5) * MAZE_GRID_SIZE, (y + 0.5) * MAZE_GRID_SIZE);
|
||||||
|
gsk_path_builder_line_to (builder, (x - 0.5) * MAZE_GRID_SIZE, (y + 0.5) * MAZE_GRID_SIZE);
|
||||||
|
add_point_to_maze (maze, builder, x - 1, y);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
gsk_path_builder_move_to (builder, (x + 0.5) * MAZE_GRID_SIZE, (y + 0.5) * MAZE_GRID_SIZE);
|
||||||
|
gsk_path_builder_line_to (builder, (x + 0.5) * MAZE_GRID_SIZE, (y - 0.5) * MAZE_GRID_SIZE);
|
||||||
|
add_point_to_maze (maze, builder, x, y - 1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
gsk_path_builder_move_to (builder, (x + 0.5) * MAZE_GRID_SIZE, (y + 0.5) * MAZE_GRID_SIZE);
|
||||||
|
gsk_path_builder_line_to (builder, (x + 1.5) * MAZE_GRID_SIZE, (y + 0.5) * MAZE_GRID_SIZE);
|
||||||
|
add_point_to_maze (maze, builder, x + 1, y);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
gsk_path_builder_move_to (builder, (x + 0.5) * MAZE_GRID_SIZE, (y + 0.5) * MAZE_GRID_SIZE);
|
||||||
|
gsk_path_builder_line_to (builder, (x + 0.5) * MAZE_GRID_SIZE, (y + 1.5) * MAZE_GRID_SIZE);
|
||||||
|
add_point_to_maze (maze, builder, x, y + 1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
g_assert_not_reached ();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static GskPath *
|
||||||
|
create_path_for_maze (GtkWidget *widget)
|
||||||
|
{
|
||||||
|
GskPathBuilder *builder;
|
||||||
|
GtkBitset *maze;
|
||||||
|
|
||||||
|
builder = gsk_path_builder_new ();
|
||||||
|
maze = gtk_bitset_new_empty ();
|
||||||
|
/* make sure the outer lines are unreachable:
|
||||||
|
* Set the full range, then remove the center again. */
|
||||||
|
gtk_bitset_add_range (maze, 0, MAZE_WIDTH * MAZE_HEIGHT);
|
||||||
|
gtk_bitset_remove_rectangle (maze, MAZE_WIDTH + 1, MAZE_WIDTH - 2, MAZE_HEIGHT - 2, MAZE_WIDTH);
|
||||||
|
|
||||||
|
/* Fill the maze */
|
||||||
|
add_point_to_maze (maze, builder, MAZE_WIDTH / 2, MAZE_HEIGHT / 2);
|
||||||
|
|
||||||
|
/* Add start and stop lines */
|
||||||
|
gsk_path_builder_move_to (builder, 1.5 * MAZE_GRID_SIZE, -0.5 * MAZE_GRID_SIZE);
|
||||||
|
gsk_path_builder_line_to (builder, 1.5 * MAZE_GRID_SIZE, 1.5 * MAZE_GRID_SIZE);
|
||||||
|
gsk_path_builder_move_to (builder, (MAZE_WIDTH - 1.5) * MAZE_GRID_SIZE, (MAZE_HEIGHT - 1.5) * MAZE_GRID_SIZE);
|
||||||
|
gsk_path_builder_line_to (builder, (MAZE_WIDTH - 1.5) * MAZE_GRID_SIZE, (MAZE_HEIGHT + 0.5) * MAZE_GRID_SIZE);
|
||||||
|
|
||||||
|
|
||||||
|
gtk_bitset_unref (maze);
|
||||||
|
|
||||||
|
return gsk_path_builder_free_to_path (builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
GtkWidget *
|
||||||
|
do_path_maze (GtkWidget *do_widget)
|
||||||
|
{
|
||||||
|
static GtkWidget *window = NULL;
|
||||||
|
|
||||||
|
if (!window)
|
||||||
|
{
|
||||||
|
GtkWidget *maze;
|
||||||
|
GtkMediaStream *stream;
|
||||||
|
GskPath *path;
|
||||||
|
|
||||||
|
window = gtk_window_new ();
|
||||||
|
gtk_window_set_resizable (GTK_WINDOW (window), TRUE);
|
||||||
|
gtk_window_set_title (GTK_WINDOW (window), "Follow the maze with the mouse");
|
||||||
|
g_object_add_weak_pointer (G_OBJECT (window), (gpointer *)&window);
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
stream = gtk_media_file_new_for_resource ("/images/gtk-logo.webm");
|
||||||
|
#else
|
||||||
|
stream = gtk_nuclear_media_stream_new ();
|
||||||
|
#endif
|
||||||
|
gtk_media_stream_play (stream);
|
||||||
|
gtk_media_stream_set_loop (stream, TRUE);
|
||||||
|
|
||||||
|
path = create_path_for_maze (window);
|
||||||
|
|
||||||
|
maze = gtk_maze_new (path,
|
||||||
|
GDK_PAINTABLE (stream),
|
||||||
|
MAZE_WIDTH * MAZE_GRID_SIZE,
|
||||||
|
MAZE_HEIGHT * MAZE_GRID_SIZE);
|
||||||
|
|
||||||
|
gtk_window_set_child (GTK_WINDOW (window), maze);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!gtk_widget_get_visible (window))
|
||||||
|
gtk_widget_show (window);
|
||||||
|
else
|
||||||
|
gtk_window_destroy (GTK_WINDOW (window));
|
||||||
|
|
||||||
|
return window;
|
||||||
|
}
|
||||||
590
demos/gtk-demo/path_text.c
Normal file
590
demos/gtk-demo/path_text.c
Normal file
@@ -0,0 +1,590 @@
|
|||||||
|
/* Path/Text
|
||||||
|
*
|
||||||
|
* This demo shows how to use GskPath to animate a path along another path.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <glib/gi18n.h>
|
||||||
|
#include <gtk/gtk.h>
|
||||||
|
|
||||||
|
#define GTK_TYPE_PATH_WIDGET (gtk_path_widget_get_type ())
|
||||||
|
G_DECLARE_FINAL_TYPE (GtkPathWidget, gtk_path_widget, GTK, PATH_WIDGET, GtkWidget)
|
||||||
|
|
||||||
|
#define POINT_SIZE 8
|
||||||
|
|
||||||
|
enum {
|
||||||
|
PROP_0,
|
||||||
|
PROP_TEXT,
|
||||||
|
PROP_EDITABLE,
|
||||||
|
N_PROPS
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _GtkPathWidget
|
||||||
|
{
|
||||||
|
GtkWidget parent_instance;
|
||||||
|
|
||||||
|
char *text;
|
||||||
|
gboolean editable;
|
||||||
|
|
||||||
|
graphene_point_t points[4];
|
||||||
|
|
||||||
|
guint active_point;
|
||||||
|
float line_closest;
|
||||||
|
|
||||||
|
GskPath *line_path;
|
||||||
|
GskPathMeasure *line_measure;
|
||||||
|
GskPath *text_path;
|
||||||
|
|
||||||
|
GdkPaintable *background;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _GtkPathWidgetClass
|
||||||
|
{
|
||||||
|
GtkWidgetClass parent_class;
|
||||||
|
};
|
||||||
|
|
||||||
|
static GParamSpec *properties[N_PROPS] = { NULL, };
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (GtkPathWidget, gtk_path_widget, GTK_TYPE_WIDGET)
|
||||||
|
|
||||||
|
static GskPath *
|
||||||
|
create_path_from_text (GtkWidget *widget,
|
||||||
|
const char *text)
|
||||||
|
{
|
||||||
|
cairo_surface_t *surface;
|
||||||
|
cairo_t *cr;
|
||||||
|
cairo_path_t *path;
|
||||||
|
PangoLayout *layout;
|
||||||
|
PangoFontDescription *desc;
|
||||||
|
GskPath *result;
|
||||||
|
|
||||||
|
surface = cairo_recording_surface_create (CAIRO_CONTENT_COLOR_ALPHA, NULL);
|
||||||
|
cr = cairo_create (surface);
|
||||||
|
|
||||||
|
layout = gtk_widget_create_pango_layout (widget, text);
|
||||||
|
desc = pango_font_description_from_string ("sans bold 36");
|
||||||
|
pango_layout_set_font_description (layout, desc);
|
||||||
|
pango_font_description_free (desc);
|
||||||
|
|
||||||
|
cairo_move_to (cr, 0, - pango_layout_get_baseline (layout) / (double) PANGO_SCALE);
|
||||||
|
pango_cairo_layout_path (cr, layout);
|
||||||
|
path = cairo_copy_path_flat (cr);
|
||||||
|
result = gsk_path_new_from_cairo (path);
|
||||||
|
|
||||||
|
cairo_path_destroy (path);
|
||||||
|
g_object_unref (layout);
|
||||||
|
cairo_destroy (cr);
|
||||||
|
cairo_surface_destroy (surface);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
GskPathMeasure *measure;
|
||||||
|
GskPathBuilder *builder;
|
||||||
|
double scale;
|
||||||
|
} GtkPathTransform;
|
||||||
|
|
||||||
|
static void
|
||||||
|
gtk_path_transform_point (GskPathMeasure *measure,
|
||||||
|
const graphene_point_t *pt,
|
||||||
|
float scale,
|
||||||
|
graphene_point_t *res)
|
||||||
|
{
|
||||||
|
graphene_vec2_t tangent;
|
||||||
|
|
||||||
|
gsk_path_measure_get_point (measure, pt->x * scale, res, &tangent);
|
||||||
|
|
||||||
|
res->x -= pt->y * scale * graphene_vec2_get_y (&tangent);
|
||||||
|
res->y += pt->y * scale * graphene_vec2_get_x (&tangent);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gtk_path_transform_op (GskPathOperation op,
|
||||||
|
const graphene_point_t *pts,
|
||||||
|
gsize n_pts,
|
||||||
|
float weight,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
GtkPathTransform *transform = data;
|
||||||
|
|
||||||
|
switch (op)
|
||||||
|
{
|
||||||
|
case GSK_PATH_MOVE:
|
||||||
|
{
|
||||||
|
graphene_point_t res;
|
||||||
|
gtk_path_transform_point (transform->measure, &pts[0], transform->scale, &res);
|
||||||
|
gsk_path_builder_move_to (transform->builder, res.x, res.y);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GSK_PATH_LINE:
|
||||||
|
{
|
||||||
|
graphene_point_t res;
|
||||||
|
gtk_path_transform_point (transform->measure, &pts[1], transform->scale, &res);
|
||||||
|
gsk_path_builder_line_to (transform->builder, res.x, res.y);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GSK_PATH_CURVE:
|
||||||
|
{
|
||||||
|
graphene_point_t res[3];
|
||||||
|
gtk_path_transform_point (transform->measure, &pts[1], transform->scale, &res[0]);
|
||||||
|
gtk_path_transform_point (transform->measure, &pts[2], transform->scale, &res[1]);
|
||||||
|
gtk_path_transform_point (transform->measure, &pts[3], transform->scale, &res[2]);
|
||||||
|
gsk_path_builder_curve_to (transform->builder, res[0].x, res[0].y, res[1].x, res[1].y, res[2].x, res[2].y);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GSK_PATH_CONIC:
|
||||||
|
{
|
||||||
|
graphene_point_t res[2];
|
||||||
|
gtk_path_transform_point (transform->measure, &pts[1], transform->scale, &res[0]);
|
||||||
|
gtk_path_transform_point (transform->measure, &pts[2], transform->scale, &res[1]);
|
||||||
|
gsk_path_builder_conic_to (transform->builder, res[0].x, res[0].y, res[1].x, res[1].y, weight);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GSK_PATH_CLOSE:
|
||||||
|
gsk_path_builder_close (transform->builder);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
g_assert_not_reached();
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GskPath *
|
||||||
|
gtk_path_transform (GskPathMeasure *measure,
|
||||||
|
GskPath *path)
|
||||||
|
{
|
||||||
|
GtkPathTransform transform = { measure, gsk_path_builder_new () };
|
||||||
|
graphene_rect_t bounds;
|
||||||
|
|
||||||
|
gsk_path_get_bounds (path, &bounds);
|
||||||
|
if (bounds.origin.x + bounds.size.width > 0)
|
||||||
|
transform.scale = gsk_path_measure_get_length (measure) / (bounds.origin.x + bounds.size.width);
|
||||||
|
else
|
||||||
|
transform.scale = 1.0f;
|
||||||
|
|
||||||
|
gsk_path_foreach (path, GSK_PATH_FOREACH_ALLOW_CURVE, gtk_path_transform_op, &transform);
|
||||||
|
|
||||||
|
return gsk_path_builder_free_to_path (transform.builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gtk_path_widget_clear_text_path (GtkPathWidget *self)
|
||||||
|
{
|
||||||
|
g_clear_pointer (&self->text_path, gsk_path_unref);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gtk_path_widget_clear_paths (GtkPathWidget *self)
|
||||||
|
{
|
||||||
|
gtk_path_widget_clear_text_path (self);
|
||||||
|
|
||||||
|
g_clear_pointer (&self->line_path, gsk_path_unref);
|
||||||
|
g_clear_pointer (&self->line_measure, gsk_path_measure_unref);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gtk_path_widget_create_text_path (GtkPathWidget *self)
|
||||||
|
{
|
||||||
|
GskPath *path;
|
||||||
|
|
||||||
|
gtk_path_widget_clear_text_path (self);
|
||||||
|
|
||||||
|
if (self->line_measure == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
path = create_path_from_text (GTK_WIDGET (self), self->text);
|
||||||
|
self->text_path = gtk_path_transform (self->line_measure, path);
|
||||||
|
|
||||||
|
gsk_path_unref (path);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gtk_path_widget_create_paths (GtkPathWidget *self)
|
||||||
|
{
|
||||||
|
double width = gtk_widget_get_width (GTK_WIDGET (self));
|
||||||
|
double height = gtk_widget_get_height (GTK_WIDGET (self));
|
||||||
|
GskPathBuilder *builder;
|
||||||
|
|
||||||
|
gtk_path_widget_clear_paths (self);
|
||||||
|
|
||||||
|
if (width <= 0 || height <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
builder = gsk_path_builder_new ();
|
||||||
|
gsk_path_builder_move_to (builder,
|
||||||
|
self->points[0].x * width, self->points[0].y * height);
|
||||||
|
gsk_path_builder_curve_to (builder,
|
||||||
|
self->points[1].x * width, self->points[1].y * height,
|
||||||
|
self->points[2].x * width, self->points[2].y * height,
|
||||||
|
self->points[3].x * width, self->points[3].y * height);
|
||||||
|
self->line_path = gsk_path_builder_free_to_path (builder);
|
||||||
|
|
||||||
|
self->line_measure = gsk_path_measure_new (self->line_path);
|
||||||
|
|
||||||
|
gtk_path_widget_create_text_path (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gtk_path_widget_allocate (GtkWidget *widget,
|
||||||
|
int width,
|
||||||
|
int height,
|
||||||
|
int baseline)
|
||||||
|
{
|
||||||
|
GtkPathWidget *self = GTK_PATH_WIDGET (widget);
|
||||||
|
|
||||||
|
GTK_WIDGET_CLASS (gtk_path_widget_parent_class)->size_allocate (widget, width, height, baseline);
|
||||||
|
|
||||||
|
gtk_path_widget_create_paths (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gtk_path_widget_snapshot (GtkWidget *widget,
|
||||||
|
GtkSnapshot *snapshot)
|
||||||
|
{
|
||||||
|
GtkPathWidget *self = GTK_PATH_WIDGET (widget);
|
||||||
|
double width = gtk_widget_get_width (widget);
|
||||||
|
double height = gtk_widget_get_height (widget);
|
||||||
|
GskPath *path;
|
||||||
|
GskStroke *stroke;
|
||||||
|
gsize i;
|
||||||
|
|
||||||
|
/* frosted glass the background */
|
||||||
|
gtk_snapshot_push_blur (snapshot, 100);
|
||||||
|
gdk_paintable_snapshot (self->background, snapshot, width, height);
|
||||||
|
gtk_snapshot_append_color (snapshot, &(GdkRGBA) { 1, 1, 1, 0.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_widget_show (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>
|
||||||
@@ -23,6 +23,14 @@
|
|||||||
<xi:include href="xml/GskGLShader.xml" />
|
<xi:include href="xml/GskGLShader.xml" />
|
||||||
</reference>
|
</reference>
|
||||||
|
|
||||||
|
<part id="paths">
|
||||||
|
<title>Paths</title>
|
||||||
|
<xi:include href="xml/GskPath.xml" />
|
||||||
|
<xi:include href="xml/GskPathBuilder.xml" />
|
||||||
|
<xi:include href="xml/GskPathMeasure.xml" />
|
||||||
|
<xi:include href="xml/GskStroke.xml" />
|
||||||
|
</part>
|
||||||
|
|
||||||
<index id="api-index-full">
|
<index id="api-index-full">
|
||||||
<title>Index of all symbols</title>
|
<title>Index of all symbols</title>
|
||||||
<xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include>
|
<xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include>
|
||||||
|
|||||||
@@ -182,6 +182,12 @@ gsk_rounded_clip_node_new
|
|||||||
gsk_rounded_clip_node_get_child
|
gsk_rounded_clip_node_get_child
|
||||||
gsk_rounded_clip_node_get_clip
|
gsk_rounded_clip_node_get_clip
|
||||||
<SUBSECTION>
|
<SUBSECTION>
|
||||||
|
GskFillRule
|
||||||
|
gsk_fill_node_new
|
||||||
|
gsk_fill_node_get_child
|
||||||
|
gsk_fill_node_get_path
|
||||||
|
gsk_fill_node_get_fill_rule
|
||||||
|
<SUBSECTION>
|
||||||
GskShadow
|
GskShadow
|
||||||
gsk_shadow_node_new
|
gsk_shadow_node_new
|
||||||
gsk_shadow_node_get_shadow
|
gsk_shadow_node_get_shadow
|
||||||
@@ -235,6 +241,7 @@ GSK_TYPE_CONTAINER_NODE
|
|||||||
GSK_TYPE_CONIC_GRADIENT_NODE
|
GSK_TYPE_CONIC_GRADIENT_NODE
|
||||||
GSK_TYPE_CROSS_FADE_NODE
|
GSK_TYPE_CROSS_FADE_NODE
|
||||||
GSK_TYPE_DEBUG_NODE
|
GSK_TYPE_DEBUG_NODE
|
||||||
|
GSK_TYPE_FILL_NODE
|
||||||
GSK_TYPE_GL_SHADER_NODE
|
GSK_TYPE_GL_SHADER_NODE
|
||||||
GSK_TYPE_INSET_SHADOW_NODE
|
GSK_TYPE_INSET_SHADOW_NODE
|
||||||
GSK_TYPE_LINEAR_GRADIENT_NODE
|
GSK_TYPE_LINEAR_GRADIENT_NODE
|
||||||
@@ -265,6 +272,7 @@ gsk_conic_gradient_node_get_type
|
|||||||
gsk_container_node_get_type
|
gsk_container_node_get_type
|
||||||
gsk_cross_fade_node_get_type
|
gsk_cross_fade_node_get_type
|
||||||
gsk_debug_node_get_type
|
gsk_debug_node_get_type
|
||||||
|
gsk_fill_node_get_type
|
||||||
gsk_gl_shader_node_get_type
|
gsk_gl_shader_node_get_type
|
||||||
gsk_inset_shadow_node_get_type
|
gsk_inset_shadow_node_get_type
|
||||||
gsk_linear_gradient_node_get_type
|
gsk_linear_gradient_node_get_type
|
||||||
@@ -306,6 +314,114 @@ gsk_rounded_rect_intersects_rect
|
|||||||
GSK_TYPE_CORNER
|
GSK_TYPE_CORNER
|
||||||
</SECTION>
|
</SECTION>
|
||||||
|
|
||||||
|
<SECTION>
|
||||||
|
<FILE>GskPath</FILE>
|
||||||
|
<SUBSECTION>
|
||||||
|
GskPath
|
||||||
|
gsk_path_ref
|
||||||
|
gsk_path_unref
|
||||||
|
gsk_path_new_rect
|
||||||
|
gsk_path_new_from_cairo
|
||||||
|
gsk_path_parse
|
||||||
|
<SUBSECTION>
|
||||||
|
gsk_path_print
|
||||||
|
gsk_path_to_string
|
||||||
|
gsk_path_to_cairo
|
||||||
|
<SUBSECTION>
|
||||||
|
gsk_path_is_empty
|
||||||
|
gsk_path_get_bounds
|
||||||
|
gsk_path_get_stroke_bounds
|
||||||
|
<SUBSECTION>
|
||||||
|
GskPathOperation
|
||||||
|
GskPathForeachFlags
|
||||||
|
GskPathForeachFunc
|
||||||
|
gsk_path_foreach
|
||||||
|
<SUBSECTION Private>
|
||||||
|
GSK_TYPE_PATH
|
||||||
|
gsk_path_get_type
|
||||||
|
</SECTION>
|
||||||
|
|
||||||
|
<SECTION>
|
||||||
|
<FILE>GskPathBuilder</FILE>
|
||||||
|
GskPathBuilder
|
||||||
|
gsk_path_builder_new
|
||||||
|
gsk_path_builder_ref
|
||||||
|
gsk_path_builder_unref
|
||||||
|
gsk_path_builder_to_path
|
||||||
|
gsk_path_builder_free_to_path
|
||||||
|
<SUBSECTION>
|
||||||
|
gsk_path_builder_get_current_point
|
||||||
|
<SUBSECTION>
|
||||||
|
gsk_path_builder_add_rect
|
||||||
|
gsk_path_builder_add_rounded_rect
|
||||||
|
gsk_path_builder_add_circle
|
||||||
|
gsk_path_builder_add_ellipse
|
||||||
|
gsk_path_builder_add_path
|
||||||
|
gtk_path_builder_add_segment
|
||||||
|
<SUBSECTION>
|
||||||
|
gsk_path_builder_move_to
|
||||||
|
gsk_path_builder_rel_move_to
|
||||||
|
gsk_path_builder_line_to
|
||||||
|
gsk_path_builder_rel_line_to
|
||||||
|
gsk_path_builder_curve_to
|
||||||
|
gsk_path_builder_rel_curve_to
|
||||||
|
gsk_path_builder_conic_to
|
||||||
|
gsk_path_builder_rel_conic_to
|
||||||
|
gsk_path_builder_close
|
||||||
|
<SUBSECTION Private>
|
||||||
|
GSK_TYPE_PATH_BUILDER
|
||||||
|
gsk_path_builder_get_type
|
||||||
|
</SECTION>
|
||||||
|
|
||||||
|
<SECTION>
|
||||||
|
<FILE>GskPathMeasure</FILE>
|
||||||
|
GskPathMeasure
|
||||||
|
gsk_path_measure_new
|
||||||
|
gsk_path_measure_new_with_tolerance
|
||||||
|
gsk_path_measure_ref
|
||||||
|
gsk_path_measure_unref
|
||||||
|
<SUBSECTION>
|
||||||
|
gsk_path_measure_get_path
|
||||||
|
gsk_path_measure_get_tolerance
|
||||||
|
gsk_path_measure_get_n_contours
|
||||||
|
gsk_path_measure_restrict_to_contour
|
||||||
|
<SUBSECTION>
|
||||||
|
gsk_path_measure_get_length
|
||||||
|
gsk_path_measure_is_closed
|
||||||
|
gsk_path_measure_get_point
|
||||||
|
gsk_path_measure_get_closest_point
|
||||||
|
gsk_path_measure_get_closest_point_full
|
||||||
|
gsk_path_measure_in_fill
|
||||||
|
<SUBSECTION Private>
|
||||||
|
GSK_TYPE_PATH_MEASURE
|
||||||
|
gsk_path_measure_get_type
|
||||||
|
</SECTION>
|
||||||
|
|
||||||
|
<SECTION>
|
||||||
|
<FILE>GskStroke</FILE>
|
||||||
|
GskLineCap
|
||||||
|
GskLineJoin
|
||||||
|
gsk_stroke_new
|
||||||
|
gsk_stroke_copy
|
||||||
|
gsk_stroke_free
|
||||||
|
gsk_stroke_equal
|
||||||
|
gsk_stroke_set_line_width
|
||||||
|
gsk_stroke_get_line_width
|
||||||
|
gsk_stroke_set_line_join
|
||||||
|
gsk_stroke_get_line_join
|
||||||
|
gsk_stroke_set_line_cap
|
||||||
|
gsk_stroke_get_line_cap
|
||||||
|
gsk_stroke_set_miter_limit
|
||||||
|
gsk_stroke_get_miter_limit
|
||||||
|
gsk_stroke_set_dash
|
||||||
|
gsk_stroke_get_dash
|
||||||
|
gsk_stroke_set_dash_offset
|
||||||
|
gsk_stroke_get_dash_offset
|
||||||
|
<SUBSECTION Private>
|
||||||
|
GSK_TYPE_STROKE
|
||||||
|
gsk_stroke_get_type
|
||||||
|
</SECTION>
|
||||||
|
|
||||||
<SECTION>
|
<SECTION>
|
||||||
<FILE>GskTransform</FILE>
|
<FILE>GskTransform</FILE>
|
||||||
GskTransform
|
GskTransform
|
||||||
|
|||||||
@@ -4295,6 +4295,8 @@ gtk_snapshot_push_color_matrix
|
|||||||
gtk_snapshot_push_repeat
|
gtk_snapshot_push_repeat
|
||||||
gtk_snapshot_push_clip
|
gtk_snapshot_push_clip
|
||||||
gtk_snapshot_push_rounded_clip
|
gtk_snapshot_push_rounded_clip
|
||||||
|
gtk_snapshot_push_fill
|
||||||
|
gtk_snapshot_push_stroke
|
||||||
gtk_snapshot_push_cross_fade
|
gtk_snapshot_push_cross_fade
|
||||||
gtk_snapshot_push_blend
|
gtk_snapshot_push_blend
|
||||||
gtk_snapshot_push_blur
|
gtk_snapshot_push_blur
|
||||||
|
|||||||
@@ -269,6 +269,8 @@ collect_reused_child_nodes (GskRenderer *renderer,
|
|||||||
case GSK_BLEND_NODE:
|
case GSK_BLEND_NODE:
|
||||||
case GSK_CROSS_FADE_NODE:
|
case GSK_CROSS_FADE_NODE:
|
||||||
case GSK_BLUR_NODE:
|
case GSK_BLUR_NODE:
|
||||||
|
case GSK_FILL_NODE:
|
||||||
|
case GSK_STROKE_NODE:
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
||||||
@@ -855,6 +857,8 @@ gsk_broadway_renderer_add_node (GskRenderer *renderer,
|
|||||||
case GSK_CROSS_FADE_NODE:
|
case GSK_CROSS_FADE_NODE:
|
||||||
case GSK_BLUR_NODE:
|
case GSK_BLUR_NODE:
|
||||||
case GSK_GL_SHADER_NODE:
|
case GSK_GL_SHADER_NODE:
|
||||||
|
case GSK_FILL_NODE:
|
||||||
|
case GSK_STROKE_NODE:
|
||||||
default:
|
default:
|
||||||
break; /* Fallback */
|
break; /* Fallback */
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3813,6 +3813,8 @@ gsk_gl_renderer_add_render_ops (GskGLRenderer *self,
|
|||||||
|
|
||||||
case GSK_REPEATING_LINEAR_GRADIENT_NODE:
|
case GSK_REPEATING_LINEAR_GRADIENT_NODE:
|
||||||
case GSK_REPEATING_RADIAL_GRADIENT_NODE:
|
case GSK_REPEATING_RADIAL_GRADIENT_NODE:
|
||||||
|
case GSK_FILL_NODE:
|
||||||
|
case GSK_STROKE_NODE:
|
||||||
case GSK_CAIRO_NODE:
|
case GSK_CAIRO_NODE:
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -22,6 +22,8 @@
|
|||||||
|
|
||||||
#ifndef __GI_SCANNER__
|
#ifndef __GI_SCANNER__
|
||||||
|
|
||||||
|
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GskPath, gsk_path_unref)
|
||||||
|
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GskPathMeasure, gsk_path_measure_unref)
|
||||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GskRenderer, g_object_unref)
|
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GskRenderer, g_object_unref)
|
||||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GskRenderNode, gsk_render_node_unref)
|
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GskRenderNode, gsk_render_node_unref)
|
||||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GskTransform, gsk_transform_unref)
|
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GskTransform, gsk_transform_unref)
|
||||||
|
|||||||
@@ -21,11 +21,15 @@
|
|||||||
#define __GSK_H_INSIDE__
|
#define __GSK_H_INSIDE__
|
||||||
|
|
||||||
#include <gsk/gskenums.h>
|
#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/gskrenderer.h>
|
||||||
#include <gsk/gskrendernode.h>
|
#include <gsk/gskrendernode.h>
|
||||||
#include <gsk/gskroundedrect.h>
|
#include <gsk/gskroundedrect.h>
|
||||||
|
#include <gsk/gskstroke.h>
|
||||||
#include <gsk/gsktransform.h>
|
#include <gsk/gsktransform.h>
|
||||||
#include <gsk/gskglshader.h>
|
|
||||||
|
|
||||||
#include <gsk/gskcairorenderer.h>
|
#include <gsk/gskcairorenderer.h>
|
||||||
|
|
||||||
|
|||||||
1962
gsk/gskcontour.c
Normal file
1962
gsk/gskcontour.c
Normal file
File diff suppressed because it is too large
Load Diff
124
gsk/gskcontourprivate.h
Normal file
124
gsk/gskcontourprivate.h
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
float gsk_contour_get_curvature (const GskContour *self,
|
||||||
|
gpointer measure_data,
|
||||||
|
float distance,
|
||||||
|
graphene_point_t *center);
|
||||||
|
gboolean gsk_contour_get_closest_point (const GskContour *self,
|
||||||
|
gpointer measure_data,
|
||||||
|
float tolerance,
|
||||||
|
const graphene_point_t *point,
|
||||||
|
float threshold,
|
||||||
|
float *out_distance,
|
||||||
|
graphene_point_t *out_pos,
|
||||||
|
float *out_offset,
|
||||||
|
graphene_vec2_t *out_tangent);
|
||||||
|
int gsk_contour_get_winding (const GskContour *self,
|
||||||
|
gpointer measure_data,
|
||||||
|
const graphene_point_t *point,
|
||||||
|
gboolean *on_edge);
|
||||||
|
void gsk_contour_add_segment (const GskContour *self,
|
||||||
|
GskPathBuilder *builder,
|
||||||
|
gpointer measure_data,
|
||||||
|
gboolean emit_move_to,
|
||||||
|
float start,
|
||||||
|
float end);
|
||||||
|
gboolean gsk_contour_get_stroke_bounds (const GskContour *self,
|
||||||
|
const GskStroke *stroke,
|
||||||
|
graphene_rect_t *bounds);
|
||||||
|
void gsk_contour_add_stroke (const GskContour *contour,
|
||||||
|
GskPathBuilder *builder,
|
||||||
|
GskStroke *stroke);
|
||||||
|
void gsk_contour_default_add_stroke (const GskContour *contour,
|
||||||
|
GskPathBuilder *builder,
|
||||||
|
GskStroke *stroke);
|
||||||
|
|
||||||
|
void gsk_contour_offset (const GskContour *contour,
|
||||||
|
GskPathBuilder *builder,
|
||||||
|
float distance,
|
||||||
|
GskLineJoin line_join,
|
||||||
|
float miter_limit);
|
||||||
|
void gsk_contour_default_offset (const GskContour *contour,
|
||||||
|
GskPathBuilder *builder,
|
||||||
|
float distance,
|
||||||
|
GskLineJoin line_join,
|
||||||
|
float miter_limit);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* __GSK_CONTOUR_PRIVATE_H__ */
|
||||||
|
|
||||||
1805
gsk/gskcurve.c
Normal file
1805
gsk/gskcurve.c
Normal file
File diff suppressed because it is too large
Load Diff
459
gsk/gskcurveintersect.c
Normal file
459
gsk/gskcurveintersect.c
Normal file
@@ -0,0 +1,459 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2020 Red Hat, Inc
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* Authors: Matthias Clasen <mclasen@redhat.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "gskcurveprivate.h"
|
||||||
|
|
||||||
|
static inline gboolean
|
||||||
|
acceptable (float t)
|
||||||
|
{
|
||||||
|
return 0 <= t && t <= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
line_intersect (const GskCurve *curve1,
|
||||||
|
const GskCurve *curve2,
|
||||||
|
float *t1,
|
||||||
|
float *t2,
|
||||||
|
graphene_point_t *p)
|
||||||
|
{
|
||||||
|
const graphene_point_t *pts1 = curve1->line.points;
|
||||||
|
const graphene_point_t *pts2 = curve2->line.points;
|
||||||
|
float a1 = pts1[0].x - pts1[1].x;
|
||||||
|
float b1 = pts1[0].y - pts1[1].y;
|
||||||
|
float a2 = pts2[0].x - pts2[1].x;
|
||||||
|
float b2 = pts2[0].y - pts2[1].y;
|
||||||
|
float det = a1 * b2 - b1 * a2;
|
||||||
|
|
||||||
|
if (det != 0)
|
||||||
|
{
|
||||||
|
float tt = ((pts1[0].x - pts2[0].x) * b2 - (pts1[0].y - pts2[0].y) * a2) / det;
|
||||||
|
float ss = - ((pts1[0].y - pts2[0].y) * a1 - (pts1[0].x - pts2[0].x) * b1) / det;
|
||||||
|
|
||||||
|
if (acceptable (tt) && acceptable (ss))
|
||||||
|
{
|
||||||
|
p->x = pts1[0].x + tt * (pts1[1].x - pts1[0].x);
|
||||||
|
p->y = pts1[0].y + tt * (pts1[1].y - pts1[0].y);
|
||||||
|
|
||||||
|
*t1 = tt;
|
||||||
|
*t2 = ss;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
get_tangent (const graphene_point_t *p0,
|
||||||
|
const graphene_point_t *p1,
|
||||||
|
graphene_vec2_t *t)
|
||||||
|
{
|
||||||
|
graphene_vec2_init (t, p1->x - p0->x, p1->y - p0->y);
|
||||||
|
graphene_vec2_normalize (t, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
align_points (const graphene_point_t *p,
|
||||||
|
const graphene_point_t *a,
|
||||||
|
const graphene_point_t *b,
|
||||||
|
graphene_point_t *q,
|
||||||
|
int n)
|
||||||
|
{
|
||||||
|
graphene_vec2_t n1;
|
||||||
|
float angle;
|
||||||
|
float s, c;
|
||||||
|
|
||||||
|
get_tangent (a, b, &n1);
|
||||||
|
angle = - atan2 (graphene_vec2_get_y (&n1), graphene_vec2_get_x (&n1));
|
||||||
|
sincosf (angle, &s, &c);
|
||||||
|
|
||||||
|
for (int i = 0; i < n; i++)
|
||||||
|
{
|
||||||
|
q[i].x = (p[i].x - a->x) * c - (p[i].y - a->y) * s;
|
||||||
|
q[i].y = (p[i].x - a->x) * s + (p[i].y - a->y) * c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
find_point_on_line (const graphene_point_t *p1,
|
||||||
|
const graphene_point_t *p2,
|
||||||
|
const graphene_point_t *q,
|
||||||
|
float *t)
|
||||||
|
{
|
||||||
|
float tx = p2->x - p1->x;
|
||||||
|
float ty = p2->y - p1->y;
|
||||||
|
float sx = q->x - p1->x;
|
||||||
|
float sy = q->y - p1->y;
|
||||||
|
|
||||||
|
*t = (tx*sx + ty*sy) / (tx*tx + ty*ty);
|
||||||
|
}
|
||||||
|
|
||||||
|
static float
|
||||||
|
cuberoot (float v)
|
||||||
|
{
|
||||||
|
if (v < 0)
|
||||||
|
return -pow (-v, 1.f / 3);
|
||||||
|
return pow (v, 1.f / 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Solve P = 0 where P is
|
||||||
|
* P = (1-t)^3*pa + 3*t*(1-t)^2*pb + 3*t^2*(1-t)*pc + t^3*pd
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
get_cubic_roots (float pa, float pb, float pc, float pd, float roots[3])
|
||||||
|
{
|
||||||
|
float a, b, c, d;
|
||||||
|
float q, q2;
|
||||||
|
float p, p3;
|
||||||
|
float discriminant;
|
||||||
|
float u1, v1, sd;
|
||||||
|
int n_roots = 0;
|
||||||
|
|
||||||
|
d = -pa + 3*pb - 3*pc + pd;
|
||||||
|
a = 3*pa - 6*pb + 3*pc;
|
||||||
|
b = -3*pa + 3*pb;
|
||||||
|
c = pa;
|
||||||
|
|
||||||
|
if (fabs (d) < 0.0001)
|
||||||
|
{
|
||||||
|
if (fabs (a) < 0.0001)
|
||||||
|
{
|
||||||
|
if (fabs (b) < 0.0001)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (acceptable (-c / b))
|
||||||
|
{
|
||||||
|
roots[0] = -c / b;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
q = sqrt (b*b - 4*a*c);
|
||||||
|
roots[n_roots] = (-b + q) / (2 * a);
|
||||||
|
if (acceptable (roots[n_roots]))
|
||||||
|
n_roots++;
|
||||||
|
|
||||||
|
roots[n_roots] = (-b - q) / (2 * a);
|
||||||
|
if (acceptable (roots[n_roots]))
|
||||||
|
n_roots++;
|
||||||
|
|
||||||
|
return n_roots;
|
||||||
|
}
|
||||||
|
|
||||||
|
a /= d;
|
||||||
|
b /= d;
|
||||||
|
c /= d;
|
||||||
|
|
||||||
|
p = (3*b - a*a)/3;
|
||||||
|
p3 = p/3;
|
||||||
|
q = (2*a*a*a - 9*a*b + 27*c)/27;
|
||||||
|
q2 = q/2;
|
||||||
|
discriminant = q2*q2 + p3*p3*p3;
|
||||||
|
|
||||||
|
if (discriminant < 0)
|
||||||
|
{
|
||||||
|
float mp3 = -p/3;
|
||||||
|
float mp33 = mp3*mp3*mp3;
|
||||||
|
float r = sqrt (mp33);
|
||||||
|
float t = -q / (2*r);
|
||||||
|
float cosphi = t < -1 ? -1 : (t > 1 ? 1 : t);
|
||||||
|
float phi = acos (cosphi);
|
||||||
|
float crtr = cuberoot (r);
|
||||||
|
float t1 = 2*crtr;
|
||||||
|
|
||||||
|
roots[n_roots] = t1 * cos (phi/3) - a/3;
|
||||||
|
if (acceptable (roots[n_roots]))
|
||||||
|
n_roots++;
|
||||||
|
roots[n_roots] = t1 * cos ((phi + 2*M_PI) / 3) - a/3;
|
||||||
|
if (acceptable (roots[n_roots]))
|
||||||
|
n_roots++;
|
||||||
|
roots[n_roots] = t1 * cos ((phi + 4*M_PI) / 3) - a/3;
|
||||||
|
if (acceptable (roots[n_roots]))
|
||||||
|
n_roots++;
|
||||||
|
|
||||||
|
return n_roots;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (discriminant == 0)
|
||||||
|
{
|
||||||
|
u1 = q2 < 0 ? cuberoot (-q2) : -cuberoot (q2);
|
||||||
|
roots[n_roots] = 2*u1 - a/3;
|
||||||
|
if (acceptable (roots[n_roots]))
|
||||||
|
n_roots++;
|
||||||
|
roots[n_roots] = -u1 - a/3;
|
||||||
|
if (acceptable (roots[n_roots]))
|
||||||
|
n_roots++;
|
||||||
|
|
||||||
|
return n_roots;
|
||||||
|
}
|
||||||
|
|
||||||
|
sd = sqrt (discriminant);
|
||||||
|
u1 = cuberoot (sd - q2);
|
||||||
|
v1 = cuberoot (sd + q2);
|
||||||
|
roots[n_roots] = u1 - v1 - a/3;
|
||||||
|
if (acceptable (roots[n_roots]))
|
||||||
|
n_roots++;
|
||||||
|
|
||||||
|
return n_roots;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
line_curve_intersect (const GskCurve *curve1,
|
||||||
|
const GskCurve *curve2,
|
||||||
|
float *t1,
|
||||||
|
float *t2,
|
||||||
|
graphene_point_t *p,
|
||||||
|
int n)
|
||||||
|
{
|
||||||
|
const graphene_point_t *a = &curve1->line.points[0];
|
||||||
|
const graphene_point_t *b = &curve1->line.points[1];
|
||||||
|
graphene_point_t pts[4];
|
||||||
|
float t[3];
|
||||||
|
int m, i;
|
||||||
|
|
||||||
|
/* Rotate things to place curve1 on the x axis,
|
||||||
|
* then solve curve2 for y == 0.
|
||||||
|
*/
|
||||||
|
align_points (curve2->curve.points, a, b, pts, 4);
|
||||||
|
|
||||||
|
m = get_cubic_roots (pts[0].y, pts[1].y, pts[2].y, pts[3].y, t);
|
||||||
|
|
||||||
|
m = MIN (m, n);
|
||||||
|
for (i = 0; i < m; i++)
|
||||||
|
{
|
||||||
|
t2[i] = t[i];
|
||||||
|
gsk_curve_get_point (curve2, t[i], &p[i]);
|
||||||
|
find_point_on_line (a, b, &p[i], &t1[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
curve_intersect_recurse (const GskCurve *curve1,
|
||||||
|
const GskCurve *curve2,
|
||||||
|
float t1l,
|
||||||
|
float t1r,
|
||||||
|
float t2l,
|
||||||
|
float t2r,
|
||||||
|
float *t1,
|
||||||
|
float *t2,
|
||||||
|
graphene_point_t *p,
|
||||||
|
int n,
|
||||||
|
int *pos)
|
||||||
|
{
|
||||||
|
GskCurve p11, p12, p21, p22;
|
||||||
|
graphene_rect_t b1, b2;
|
||||||
|
float d1, d2;
|
||||||
|
|
||||||
|
if (*pos == n)
|
||||||
|
return;
|
||||||
|
|
||||||
|
gsk_curve_get_tight_bounds (curve1, &b1);
|
||||||
|
gsk_curve_get_tight_bounds (curve2, &b2);
|
||||||
|
|
||||||
|
if (!graphene_rect_intersection (&b1, &b2, NULL))
|
||||||
|
return;
|
||||||
|
|
||||||
|
d1 = (t1r - t1l) / 2;
|
||||||
|
d2 = (t2r - t2l) / 2;
|
||||||
|
|
||||||
|
if (b1.size.width < 0.1 && b1.size.height < 0.1 &&
|
||||||
|
b2.size.width < 0.1 && b2.size.height < 0.1)
|
||||||
|
{
|
||||||
|
graphene_point_t c;
|
||||||
|
t1[*pos] = t1l + d1;
|
||||||
|
t2[*pos] = t2l + d2;
|
||||||
|
gsk_curve_get_point (curve1, 0.5, &c);
|
||||||
|
|
||||||
|
for (int i = 0; i < *pos; i++)
|
||||||
|
{
|
||||||
|
if (graphene_point_near (&c, &p[i], 0.1))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
p[*pos] = c;
|
||||||
|
(*pos)++;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gsk_curve_split (curve1, 0.5, &p11, &p12);
|
||||||
|
gsk_curve_split (curve2, 0.5, &p21, &p22);
|
||||||
|
|
||||||
|
curve_intersect_recurse (&p11, &p21, t1l, t1l + d1, t2l, t2l + d2, t1, t2, p, n, pos);
|
||||||
|
curve_intersect_recurse (&p11, &p22, t1l, t1l + d1, t2l + d2, t2r, t1, t2, p, n, pos);
|
||||||
|
curve_intersect_recurse (&p12, &p21, t1l + d1, t1r, t2l, t2l + d2, t1, t2, p, n, pos);
|
||||||
|
curve_intersect_recurse (&p12, &p22, t1l + d1, t1r, t2l + d2, t2r, t1, t2, p, n, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
curve_intersect (const GskCurve *curve1,
|
||||||
|
const GskCurve *curve2,
|
||||||
|
float *t1,
|
||||||
|
float *t2,
|
||||||
|
graphene_point_t *p,
|
||||||
|
int n)
|
||||||
|
{
|
||||||
|
int pos = 0;
|
||||||
|
|
||||||
|
curve_intersect_recurse (curve1, curve2, 0, 1, 0, 1, t1, t2, p, n, &pos);
|
||||||
|
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
get_bounds (const GskCurve *curve,
|
||||||
|
float tl,
|
||||||
|
float tr,
|
||||||
|
graphene_rect_t *bounds)
|
||||||
|
{
|
||||||
|
GskCurve c;
|
||||||
|
|
||||||
|
gsk_curve_segment (curve, tl, tr, &c);
|
||||||
|
gsk_curve_get_tight_bounds (&c, bounds);
|
||||||
|
|
||||||
|
/* FIXME this is working around inadequacies of
|
||||||
|
* graphene_rect_t as bounding box
|
||||||
|
*/
|
||||||
|
bounds->size.width += 0.0001;
|
||||||
|
bounds->size.height += 0.0001;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
general_intersect_recurse (const GskCurve *curve1,
|
||||||
|
const GskCurve *curve2,
|
||||||
|
float t1l,
|
||||||
|
float t1r,
|
||||||
|
float t2l,
|
||||||
|
float t2r,
|
||||||
|
float *t1,
|
||||||
|
float *t2,
|
||||||
|
graphene_point_t *p,
|
||||||
|
int n,
|
||||||
|
int *pos)
|
||||||
|
{
|
||||||
|
graphene_rect_t b1, b2;
|
||||||
|
float d1, d2;
|
||||||
|
|
||||||
|
if (*pos == n)
|
||||||
|
return;
|
||||||
|
|
||||||
|
get_bounds (curve1, t1l, t1r, &b1);
|
||||||
|
get_bounds (curve2, t2l, t2r, &b2);
|
||||||
|
|
||||||
|
if (!graphene_rect_intersection (&b1, &b2, NULL))
|
||||||
|
return;
|
||||||
|
|
||||||
|
d1 = (t1r - t1l) / 2;
|
||||||
|
d2 = (t2r - t2l) / 2;
|
||||||
|
|
||||||
|
if (b1.size.width < 0.1 && b1.size.height < 0.1 &&
|
||||||
|
b2.size.width < 0.1 && b2.size.height < 0.1)
|
||||||
|
{
|
||||||
|
graphene_point_t c;
|
||||||
|
t1[*pos] = t1l + d1;
|
||||||
|
t2[*pos] = t2l + d2;
|
||||||
|
gsk_curve_get_point (curve1, t1[*pos], &c);
|
||||||
|
|
||||||
|
for (int i = 0; i < *pos; i++)
|
||||||
|
{
|
||||||
|
if (graphene_point_near (&c, &p[i], 0.1))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
p[*pos] = c;
|
||||||
|
(*pos)++;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Note that in the conic case, we cannot just split the curves and
|
||||||
|
* pass the two halves down, since splitting changes the parametrization,
|
||||||
|
* and we need the t's to be valid parameters wrt to the original curve.
|
||||||
|
*
|
||||||
|
* So, instead, we determine the bounding boxes above by always starting
|
||||||
|
* from the original curve. That is a bit less efficient, but also works
|
||||||
|
* for conics.
|
||||||
|
*/
|
||||||
|
general_intersect_recurse (curve1, curve2, t1l, t1l + d1, t2l, t2l + d2, t1, t2, p, n, pos);
|
||||||
|
general_intersect_recurse (curve1, curve2, t1l, t1l + d1, t2l + d2, t2r, t1, t2, p, n, pos);
|
||||||
|
general_intersect_recurse (curve1, curve2, t1l + d1, t1r, t2l, t2l + d2, t1, t2, p, n, pos);
|
||||||
|
general_intersect_recurse (curve1, curve2, t1l + d1, t1r, t2l + d2, t2r, t1, t2, p, n, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
general_intersect (const GskCurve *curve1,
|
||||||
|
const GskCurve *curve2,
|
||||||
|
float *t1,
|
||||||
|
float *t2,
|
||||||
|
graphene_point_t *p,
|
||||||
|
int n)
|
||||||
|
{
|
||||||
|
int pos = 0;
|
||||||
|
|
||||||
|
general_intersect_recurse (curve1, curve2, 0, 1, 0, 1, t1, t2, p, n, &pos);
|
||||||
|
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Place intersections between the curves in p, and their Bezier positions
|
||||||
|
* in t1 and t2, up to n. Return the number of intersections found.
|
||||||
|
*
|
||||||
|
* Note that two cubic Beziers can have up to 9 intersections.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
gsk_curve_intersect (const GskCurve *curve1,
|
||||||
|
const GskCurve *curve2,
|
||||||
|
float *t1,
|
||||||
|
float *t2,
|
||||||
|
graphene_point_t *p,
|
||||||
|
int n)
|
||||||
|
{
|
||||||
|
GskPathOperation op1 = curve1->op;
|
||||||
|
GskPathOperation op2 = curve2->op;
|
||||||
|
|
||||||
|
if (op1 == GSK_PATH_CLOSE)
|
||||||
|
op1 = GSK_PATH_LINE;
|
||||||
|
|
||||||
|
if (op2 == GSK_PATH_CLOSE)
|
||||||
|
op2 = GSK_PATH_LINE;
|
||||||
|
|
||||||
|
/* We special-case line-line and line-curve intersections,
|
||||||
|
* since we can solve them directly.
|
||||||
|
* Everything else is done via bisection.
|
||||||
|
*/
|
||||||
|
if (op1 == GSK_PATH_LINE && op2 == GSK_PATH_LINE)
|
||||||
|
return line_intersect (curve1, curve2, t1, t2, p);
|
||||||
|
else if (op1 == GSK_PATH_LINE && op2 == GSK_PATH_CURVE)
|
||||||
|
return line_curve_intersect (curve1, curve2, t1, t2, p, n);
|
||||||
|
else if (op1 == GSK_PATH_CURVE && op2 == GSK_PATH_LINE)
|
||||||
|
return line_curve_intersect (curve2, curve1, t2, t1, p, n);
|
||||||
|
else if (op1 == GSK_PATH_CURVE && op2 == GSK_PATH_CURVE)
|
||||||
|
return curve_intersect (curve1, curve2, t1, t2, p, n);
|
||||||
|
else
|
||||||
|
return general_intersect (curve1, curve2, t1, t2, p, n);
|
||||||
|
|
||||||
|
}
|
||||||
151
gsk/gskcurveprivate.h
Normal file
151
gsk/gskcurveprivate.h
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
/*
|
||||||
|
* 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 gboolean (* GskCurveAddLineFunc) (const graphene_point_t *from,
|
||||||
|
const graphene_point_t *to,
|
||||||
|
float from_progress,
|
||||||
|
float to_progress,
|
||||||
|
gpointer user_data);
|
||||||
|
|
||||||
|
typedef gboolean (* GskCurveAddCurveFunc) (const graphene_point_t points[4],
|
||||||
|
gpointer user_data);
|
||||||
|
|
||||||
|
void gsk_curve_init (GskCurve *curve,
|
||||||
|
gskpathop op);
|
||||||
|
void gsk_curve_init_foreach (GskCurve *curve,
|
||||||
|
GskPathOperation op,
|
||||||
|
const graphene_point_t *pts,
|
||||||
|
gsize n_pts,
|
||||||
|
float weight);
|
||||||
|
|
||||||
|
void gsk_curve_get_point (const GskCurve *curve,
|
||||||
|
float progress,
|
||||||
|
graphene_point_t *pos);
|
||||||
|
void gsk_curve_get_tangent (const GskCurve *curve,
|
||||||
|
float progress,
|
||||||
|
graphene_vec2_t *tangent);
|
||||||
|
void gsk_curve_get_normal (const GskCurve *curve,
|
||||||
|
float progress,
|
||||||
|
graphene_vec2_t *normal);
|
||||||
|
void gsk_curve_split (const GskCurve *curve,
|
||||||
|
float progress,
|
||||||
|
GskCurve *start,
|
||||||
|
GskCurve *end);
|
||||||
|
void gsk_curve_segment (const GskCurve *curve,
|
||||||
|
float start,
|
||||||
|
float end,
|
||||||
|
GskCurve *segment);
|
||||||
|
gboolean gsk_curve_decompose (const GskCurve *curve,
|
||||||
|
float tolerance,
|
||||||
|
GskCurveAddLineFunc add_line_func,
|
||||||
|
gpointer user_data);
|
||||||
|
gboolean gsk_curve_decompose_curve (const GskCurve *curve,
|
||||||
|
float tolerance,
|
||||||
|
GskCurveAddCurveFunc add_curve_func,
|
||||||
|
gpointer user_data);
|
||||||
|
gskpathop gsk_curve_pathop (const GskCurve *curve);
|
||||||
|
#define gsk_curve_builder_to(curve, builder) gsk_path_builder_pathop_to ((builder), gsk_curve_pathop (curve))
|
||||||
|
const graphene_point_t *gsk_curve_get_start_point (const GskCurve *curve);
|
||||||
|
const graphene_point_t *gsk_curve_get_end_point (const GskCurve *curve);
|
||||||
|
void gsk_curve_get_start_tangent (const GskCurve *curve,
|
||||||
|
graphene_vec2_t *tangent);
|
||||||
|
void gsk_curve_get_end_tangent (const GskCurve *curve,
|
||||||
|
graphene_vec2_t *tangent);
|
||||||
|
void gsk_curve_get_bounds (const GskCurve *curve,
|
||||||
|
graphene_rect_t *bounds);
|
||||||
|
void gsk_curve_get_tight_bounds (const GskCurve *curve,
|
||||||
|
graphene_rect_t *bounds);
|
||||||
|
|
||||||
|
int gsk_curve_intersect (const GskCurve *curve1,
|
||||||
|
const GskCurve *curve2,
|
||||||
|
float *t1,
|
||||||
|
float *t2,
|
||||||
|
graphene_point_t *p,
|
||||||
|
int n);
|
||||||
|
|
||||||
|
void gsk_curve_offset (const GskCurve *curve,
|
||||||
|
float distance,
|
||||||
|
GskCurve *offset_curve);
|
||||||
|
void gsk_curve_reverse (const GskCurve *curve,
|
||||||
|
GskCurve *reverse);
|
||||||
|
|
||||||
|
float gsk_curve_get_curvature (const GskCurve *curve,
|
||||||
|
float t,
|
||||||
|
graphene_point_t *center);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* __GSK_CURVE_PRIVATE_H__ */
|
||||||
|
|
||||||
103
gsk/gskenums.h
103
gsk/gskenums.h
@@ -73,6 +73,8 @@ typedef enum {
|
|||||||
GSK_REPEAT_NODE,
|
GSK_REPEAT_NODE,
|
||||||
GSK_CLIP_NODE,
|
GSK_CLIP_NODE,
|
||||||
GSK_ROUNDED_CLIP_NODE,
|
GSK_ROUNDED_CLIP_NODE,
|
||||||
|
GSK_FILL_NODE,
|
||||||
|
GSK_STROKE_NODE,
|
||||||
GSK_SHADOW_NODE,
|
GSK_SHADOW_NODE,
|
||||||
GSK_BLEND_NODE,
|
GSK_BLEND_NODE,
|
||||||
GSK_CROSS_FADE_NODE,
|
GSK_CROSS_FADE_NODE,
|
||||||
@@ -167,6 +169,106 @@ typedef enum {
|
|||||||
GSK_CORNER_BOTTOM_LEFT
|
GSK_CORNER_BOTTOM_LEFT
|
||||||
} GskCorner;
|
} 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @GSK_LINE_CAP_BUTT: Start and stop the line exactly at the start
|
||||||
|
* and end point
|
||||||
|
* @GSK_LINE_CAP_ROUND: Use a round ending, the center of the circle
|
||||||
|
* is the start or end point.
|
||||||
|
* @GSK_LINE_CAP_SQUARE: use squared ending, the center of the square
|
||||||
|
* is the start or end point.
|
||||||
|
*
|
||||||
|
* Specifies how to render the start and end points of contours or
|
||||||
|
* dashes when stroking.
|
||||||
|
*
|
||||||
|
* The default line cap style is %GSK_LINE_CAP_BUTT.
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
GSK_LINE_CAP_BUTT,
|
||||||
|
GSK_LINE_CAP_ROUND,
|
||||||
|
GSK_LINE_CAP_SQUARE
|
||||||
|
} GskLineCap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GskLineJoin:
|
||||||
|
* @GSK_LINE_JOIN_MITER: Use a sharp, angled corner
|
||||||
|
* @GSK_LINE_JOIN_MITER_CLIP: Use a sharp, angled corner, at a distance
|
||||||
|
* @GSK_LINE_JOIN_ROUND: Use a round join, the center of the circle is
|
||||||
|
* the joint point
|
||||||
|
* @GSK_LINE_JOIN_BEVEL: Use a cut-off join, the join is cut off at half
|
||||||
|
* the line width from the joint point
|
||||||
|
* @GSK_LINE_JOIN_ARCS: Use a sharp angled corner made from circles
|
||||||
|
*
|
||||||
|
* Specifies how to render the junction of two lines when stroking.
|
||||||
|
*
|
||||||
|
* See gsk_stroke_set_miter_limit() for details on the difference between
|
||||||
|
* @GSK_LINE_JOIN_MITER and @GSK_LINE_JOIN_MITER_CLIP.
|
||||||
|
*
|
||||||
|
* The default line join style is %GSK_LINE_JOIN_MITER.
|
||||||
|
**/
|
||||||
|
typedef enum {
|
||||||
|
GSK_LINE_JOIN_MITER,
|
||||||
|
GSK_LINE_JOIN_MITER_CLIP,
|
||||||
|
GSK_LINE_JOIN_ROUND,
|
||||||
|
GSK_LINE_JOIN_BEVEL,
|
||||||
|
GSK_LINE_JOIN_ARCS
|
||||||
|
} GskLineJoin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GskPathOperation:
|
||||||
|
* @GSK_PATH_MOVE: A move-to operation, with 1 point describing the
|
||||||
|
* target point.
|
||||||
|
* @GSK_PATH_LINE: A line-to operation, with 2 points describing the
|
||||||
|
* start and end point of a straight line.
|
||||||
|
* @GSK_PATH_CLOSE: A close operation ending the current contour with
|
||||||
|
* a line back to the starting point. Two points describe the start
|
||||||
|
* and end of the line.
|
||||||
|
* @GSK_PATH_CURVE: A curve-to operation describing a cubic Bézier curve
|
||||||
|
* with 4 points describing the start point, the two control points
|
||||||
|
* and the end point of the curve.
|
||||||
|
* @GSK_PATH_CONIC: A weighted quadratic 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:
|
* GskSerializationError:
|
||||||
* @GSK_SERIALIZATION_UNSUPPORTED_FORMAT: The format can not be
|
* @GSK_SERIALIZATION_UNSUPPORTED_FORMAT: The format can not be
|
||||||
@@ -249,5 +351,4 @@ typedef enum
|
|||||||
GSK_GL_UNIFORM_TYPE_VEC4,
|
GSK_GL_UNIFORM_TYPE_VEC4,
|
||||||
} GskGLUniformType;
|
} GskGLUniformType;
|
||||||
|
|
||||||
|
|
||||||
#endif /* __GSK_TYPES_H__ */
|
#endif /* __GSK_TYPES_H__ */
|
||||||
|
|||||||
1299
gsk/gskpath.c
Normal file
1299
gsk/gskpath.c
Normal file
File diff suppressed because it is too large
Load Diff
126
gsk/gskpath.h
Normal file
126
gsk/gskpath.h
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
/*
|
||||||
|
* 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_new_from_cairo (const cairo_path_t *path);
|
||||||
|
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
GskPath * gsk_path_ref (GskPath *self);
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
void gsk_path_unref (GskPath *self);
|
||||||
|
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
void gsk_path_print (GskPath *self,
|
||||||
|
GString *string);
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
char * gsk_path_to_string (GskPath *self);
|
||||||
|
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
GskPath * gsk_path_parse (const char *string);
|
||||||
|
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
void gsk_path_to_cairo (GskPath *self,
|
||||||
|
cairo_t *cr);
|
||||||
|
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
gboolean gsk_path_is_empty (GskPath *path);
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
gboolean gsk_path_get_bounds (GskPath *path,
|
||||||
|
graphene_rect_t *bounds);
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
gboolean gsk_path_get_stroke_bounds (GskPath *path,
|
||||||
|
const GskStroke *stroke,
|
||||||
|
graphene_rect_t *bounds);
|
||||||
|
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
gboolean gsk_path_foreach (GskPath *path,
|
||||||
|
GskPathForeachFlags flags,
|
||||||
|
GskPathForeachFunc func,
|
||||||
|
gpointer user_data);
|
||||||
|
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
GskPath * gsk_path_stroke (GskPath *path,
|
||||||
|
GskStroke *stroke);
|
||||||
|
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
GskPath * gsk_path_offset (GskPath *path,
|
||||||
|
float distance,
|
||||||
|
GskLineJoin line_join,
|
||||||
|
float miter_limit);
|
||||||
|
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* __GSK_PATH_H__ */
|
||||||
943
gsk/gskpathbuilder.c
Normal file
943
gsk/gskpathbuilder.c
Normal file
@@ -0,0 +1,943 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2020 Benjamin Otte
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* Authors: Benjamin Otte <otte@gnome.org>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "gskpathbuilder.h"
|
||||||
|
|
||||||
|
#include "gskpathprivate.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SECTION:gskpathbuilder
|
||||||
|
* @Title: Building paths
|
||||||
|
* @Short_description: Building paths of lines and curves
|
||||||
|
* @See_also: #GskPath, #GskPathMeasure
|
||||||
|
*
|
||||||
|
* This section describes how to construct #GskPath structures.
|
||||||
|
*
|
||||||
|
* A path is constructed like this:
|
||||||
|
*
|
||||||
|
* |[<!-- language="C" -->
|
||||||
|
* GskPath *
|
||||||
|
* construct_path (void)
|
||||||
|
* {
|
||||||
|
* GskPathBuilder *builder;
|
||||||
|
*
|
||||||
|
* builder = gsk_path_builder_new ();
|
||||||
|
*
|
||||||
|
* // add contours to the path here
|
||||||
|
*
|
||||||
|
* return gsk_path_builder_free_to_path (builder);
|
||||||
|
* ]|
|
||||||
|
*
|
||||||
|
* Adding contours to the path can be done in two ways.
|
||||||
|
* The easiest option is to use the `gsk_path_builder_add_*` group
|
||||||
|
* of functions that add predefined contours to the current path,
|
||||||
|
* either common shapes like gsk_path_builder_add_circle()
|
||||||
|
* or by adding from other paths like gsk_path_builder_add_path().
|
||||||
|
*
|
||||||
|
* The other option is to define each line and curve manually with
|
||||||
|
* the `gsk_path_builder_*_to` group of functions. You start with
|
||||||
|
* a call to gsk_path_builder_move_to() to set the starting point
|
||||||
|
* and then use multiple calls to any of the drawing functions to
|
||||||
|
* move the pen along the plane. Once you are done, you can call
|
||||||
|
* gsk_path_builder_close() to close the path by connecting it
|
||||||
|
* back with a line to the starting point.
|
||||||
|
* This is similar for how paths are drawn in Cairo.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GskPathBuilder:
|
||||||
|
*
|
||||||
|
* A #GskPathBuilder struct is an opaque struct. It is meant to
|
||||||
|
* not be kept around and only be used to create new #GskPath
|
||||||
|
* objects.
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct _GskPathBuilder
|
||||||
|
{
|
||||||
|
int ref_count;
|
||||||
|
|
||||||
|
GSList *contours; /* (reverse) list of already recorded contours */
|
||||||
|
|
||||||
|
GskPathFlags flags; /* flags for the current path */
|
||||||
|
graphene_point_t current_point; /* the point all drawing ops start from */
|
||||||
|
GArray *ops; /* operations for current contour - size == 0 means no current contour */
|
||||||
|
GArray *points; /* points for the operations */
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_BOXED_TYPE (GskPathBuilder,
|
||||||
|
gsk_path_builder,
|
||||||
|
gsk_path_builder_ref,
|
||||||
|
gsk_path_builder_unref)
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gsk_path_builder_new:
|
||||||
|
*
|
||||||
|
* Create a new #GskPathBuilder object. The resulting builder
|
||||||
|
* would create an empty #GskPath. Use addition functions to add
|
||||||
|
* types to it.
|
||||||
|
*
|
||||||
|
* Returns: a new #GskPathBuilder
|
||||||
|
**/
|
||||||
|
GskPathBuilder *
|
||||||
|
gsk_path_builder_new (void)
|
||||||
|
{
|
||||||
|
GskPathBuilder *builder;
|
||||||
|
|
||||||
|
builder = g_slice_new0 (GskPathBuilder);
|
||||||
|
builder->ref_count = 1;
|
||||||
|
|
||||||
|
builder->ops = g_array_new (FALSE, FALSE, sizeof (gskpathop));
|
||||||
|
builder->points = g_array_new (FALSE, FALSE, sizeof (graphene_point_t));
|
||||||
|
|
||||||
|
/* Be explicit here */
|
||||||
|
builder->current_point = GRAPHENE_POINT_INIT (0, 0);
|
||||||
|
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gsk_path_builder_ref:
|
||||||
|
* @builder: a #GskPathBuilder
|
||||||
|
*
|
||||||
|
* Acquires a reference on the given @builder.
|
||||||
|
*
|
||||||
|
* This function is intended primarily for bindings. #GskPathBuilder objects
|
||||||
|
* should not be kept around.
|
||||||
|
*
|
||||||
|
* Returns: (transfer none): the given #GskPathBuilder with
|
||||||
|
* its reference count increased
|
||||||
|
*/
|
||||||
|
GskPathBuilder *
|
||||||
|
gsk_path_builder_ref (GskPathBuilder *builder)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (builder != NULL, NULL);
|
||||||
|
g_return_val_if_fail (builder->ref_count > 0, NULL);
|
||||||
|
|
||||||
|
builder->ref_count += 1;
|
||||||
|
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We're cheating here. Out pathops are relative to the NULL pointer,
|
||||||
|
* so that we can not care about the points GArray reallocating itself
|
||||||
|
* until we create the contour.
|
||||||
|
* This does however mean that we need to not use gsk_pathop_get_points()
|
||||||
|
* without offsetting the returned pointer.
|
||||||
|
*/
|
||||||
|
static inline gskpathop
|
||||||
|
gsk_pathop_encode_index (GskPathOperation op,
|
||||||
|
gsize index)
|
||||||
|
{
|
||||||
|
return gsk_pathop_encode (op, ((graphene_point_t *) NULL) + index);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gsk_path_builder_ensure_current (GskPathBuilder *builder)
|
||||||
|
{
|
||||||
|
if (builder->ops->len != 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
builder->flags = GSK_PATH_FLAT;
|
||||||
|
g_array_append_vals (builder->ops, (gskpathop[1]) { gsk_pathop_encode_index (GSK_PATH_MOVE, 0) }, 1);
|
||||||
|
g_array_append_val (builder->points, builder->current_point);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gsk_path_builder_append_current (GskPathBuilder *builder,
|
||||||
|
GskPathOperation op,
|
||||||
|
gsize n_points,
|
||||||
|
const graphene_point_t *points)
|
||||||
|
{
|
||||||
|
gsk_path_builder_ensure_current (builder);
|
||||||
|
|
||||||
|
g_array_append_vals (builder->ops, (gskpathop[1]) { gsk_pathop_encode_index (op, builder->points->len - 1) }, 1);
|
||||||
|
g_array_append_vals (builder->points, points, n_points);
|
||||||
|
|
||||||
|
builder->current_point = points[n_points - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gsk_path_builder_end_current (GskPathBuilder *builder)
|
||||||
|
{
|
||||||
|
GskContour *contour;
|
||||||
|
|
||||||
|
if (builder->ops->len == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
contour = gsk_standard_contour_new (builder->flags,
|
||||||
|
(graphene_point_t *) builder->points->data,
|
||||||
|
builder->points->len,
|
||||||
|
(gskpathop *) builder->ops->data,
|
||||||
|
builder->ops->len,
|
||||||
|
(graphene_point_t *) builder->points->data - (graphene_point_t *) NULL);
|
||||||
|
|
||||||
|
g_array_set_size (builder->ops, 0);
|
||||||
|
g_array_set_size (builder->points, 0);
|
||||||
|
|
||||||
|
/* do this at the end to avoid inflooping when add_contour calls back here */
|
||||||
|
gsk_path_builder_add_contour (builder, contour);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gsk_path_builder_clear (GskPathBuilder *builder)
|
||||||
|
{
|
||||||
|
gsk_path_builder_end_current (builder);
|
||||||
|
|
||||||
|
g_slist_free_full (builder->contours, g_free);
|
||||||
|
builder->contours = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gsk_path_builder_unref:
|
||||||
|
* @builder: a #GskPathBuilder
|
||||||
|
*
|
||||||
|
* Releases a reference on the given @builder.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
gsk_path_builder_unref (GskPathBuilder *builder)
|
||||||
|
{
|
||||||
|
g_return_if_fail (builder != NULL);
|
||||||
|
g_return_if_fail (builder->ref_count > 0);
|
||||||
|
|
||||||
|
builder->ref_count -= 1;
|
||||||
|
|
||||||
|
if (builder->ref_count > 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
gsk_path_builder_clear (builder);
|
||||||
|
g_array_unref (builder->ops);
|
||||||
|
g_array_unref (builder->points);
|
||||||
|
g_slice_free (GskPathBuilder, builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gsk_path_builder_free_to_path: (skip)
|
||||||
|
* @builder: a #GskPathBuilder
|
||||||
|
*
|
||||||
|
* Creates a new #GskPath from the current state of the
|
||||||
|
* given @builder, and frees the @builder instance.
|
||||||
|
*
|
||||||
|
* Returns: (transfer full): the newly created #GskPath
|
||||||
|
* with all the contours added to @builder
|
||||||
|
*/
|
||||||
|
GskPath *
|
||||||
|
gsk_path_builder_free_to_path (GskPathBuilder *builder)
|
||||||
|
{
|
||||||
|
GskPath *res;
|
||||||
|
|
||||||
|
g_return_val_if_fail (builder != NULL, NULL);
|
||||||
|
|
||||||
|
res = gsk_path_builder_to_path (builder);
|
||||||
|
|
||||||
|
gsk_path_builder_unref (builder);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gsk_path_builder_to_path:
|
||||||
|
* @builder: a #GskPathBuilder
|
||||||
|
*
|
||||||
|
* Creates a new #GskPath from the given @builder.
|
||||||
|
*
|
||||||
|
* The given #GskPathBuilder is reset once this function returns;
|
||||||
|
* you cannot call this function multiple times on the same @builder instance.
|
||||||
|
*
|
||||||
|
* This function is intended primarily for bindings. C code should use
|
||||||
|
* gsk_path_builder_free_to_path().
|
||||||
|
*
|
||||||
|
* Returns: (transfer full): the newly created #GskPath
|
||||||
|
* with all the contours added to @builder
|
||||||
|
*/
|
||||||
|
GskPath *
|
||||||
|
gsk_path_builder_to_path (GskPathBuilder *builder)
|
||||||
|
{
|
||||||
|
GskPath *path;
|
||||||
|
|
||||||
|
g_return_val_if_fail (builder != NULL, NULL);
|
||||||
|
|
||||||
|
gsk_path_builder_end_current (builder);
|
||||||
|
|
||||||
|
builder->contours = g_slist_reverse (builder->contours);
|
||||||
|
|
||||||
|
path = gsk_path_new_from_contours (builder->contours);
|
||||||
|
|
||||||
|
gsk_path_builder_clear (builder);
|
||||||
|
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
gsk_path_builder_add_contour (GskPathBuilder *builder,
|
||||||
|
GskContour *contour)
|
||||||
|
{
|
||||||
|
gsk_path_builder_end_current (builder);
|
||||||
|
|
||||||
|
builder->contours = g_slist_prepend (builder->contours, contour);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gsk_path_builder_get_current_point:
|
||||||
|
* @builder: a #GskPathBuilder
|
||||||
|
*
|
||||||
|
* Gets the current point. The current point is used for relative
|
||||||
|
* drawing commands and updated after every operation.
|
||||||
|
*
|
||||||
|
* When @builder is created, the default current point is set to (0, 0).
|
||||||
|
*
|
||||||
|
* Returns: (transfer none) The current point
|
||||||
|
**/
|
||||||
|
const graphene_point_t *
|
||||||
|
gsk_path_builder_get_current_point (GskPathBuilder *builder)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (builder != NULL, NULL);
|
||||||
|
|
||||||
|
return &builder->current_point;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gsk_path_builder_add_path:
|
||||||
|
* @builder: a #GskPathBuilder
|
||||||
|
* @path: (transfer none): the path to append
|
||||||
|
*
|
||||||
|
* Appends all of @path to @builder.
|
||||||
|
**/
|
||||||
|
void
|
||||||
|
gsk_path_builder_add_path (GskPathBuilder *builder,
|
||||||
|
GskPath *path)
|
||||||
|
{
|
||||||
|
gsize i;
|
||||||
|
|
||||||
|
g_return_if_fail (builder != NULL);
|
||||||
|
g_return_if_fail (path != NULL);
|
||||||
|
|
||||||
|
for (i = 0; i < gsk_path_get_n_contours (path); i++)
|
||||||
|
{
|
||||||
|
const GskContour *contour = gsk_path_get_contour (path, i);
|
||||||
|
|
||||||
|
gsk_path_builder_add_contour (builder, gsk_contour_dup (contour));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gsk_path_builder_add_rect:
|
||||||
|
* @builder: A #GskPathBuilder
|
||||||
|
* @rect: The rectangle to create a path for
|
||||||
|
*
|
||||||
|
* Creates a path representing the given rectangle.
|
||||||
|
*
|
||||||
|
* If the width or height of the rectangle is negative, the start
|
||||||
|
* point will be on the right or bottom, respectively.
|
||||||
|
*
|
||||||
|
* If the the width or height are 0, the path will be a closed
|
||||||
|
* horizontal or vertical line. If both are 0, it'll be a closed dot.
|
||||||
|
*
|
||||||
|
* Returns: a new #GskPath representing a rectangle
|
||||||
|
**/
|
||||||
|
void
|
||||||
|
gsk_path_builder_add_rect (GskPathBuilder *builder,
|
||||||
|
const graphene_rect_t *rect)
|
||||||
|
{
|
||||||
|
GskContour *contour;
|
||||||
|
|
||||||
|
g_return_if_fail (builder != NULL);
|
||||||
|
|
||||||
|
contour = gsk_rect_contour_new (rect);
|
||||||
|
gsk_path_builder_add_contour (builder, contour);
|
||||||
|
|
||||||
|
gsk_contour_get_start_end (contour, NULL, &builder->current_point);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gsk_path_builder_add_rounded_rect:
|
||||||
|
* @self: a #GskPathBuilder
|
||||||
|
* @rect: the rounded rect
|
||||||
|
*
|
||||||
|
* Adds @rect as a new contour to the path built in @self.
|
||||||
|
**/
|
||||||
|
void
|
||||||
|
gsk_path_builder_add_rounded_rect (GskPathBuilder *self,
|
||||||
|
const GskRoundedRect *rect)
|
||||||
|
{
|
||||||
|
const float weight = sqrt(0.5f);
|
||||||
|
|
||||||
|
g_return_if_fail (self != NULL);
|
||||||
|
g_return_if_fail (rect != NULL);
|
||||||
|
|
||||||
|
gsk_path_builder_move_to (self,
|
||||||
|
rect->bounds.origin.x + rect->corner[GSK_CORNER_TOP_LEFT].width,
|
||||||
|
rect->bounds.origin.y);
|
||||||
|
/* top */
|
||||||
|
gsk_path_builder_line_to (self,
|
||||||
|
rect->bounds.origin.x + rect->bounds.size.width - rect->corner[GSK_CORNER_TOP_RIGHT].width,
|
||||||
|
rect->bounds.origin.y);
|
||||||
|
/* topright corner */
|
||||||
|
gsk_path_builder_conic_to (self,
|
||||||
|
rect->bounds.origin.x + rect->bounds.size.width,
|
||||||
|
rect->bounds.origin.y,
|
||||||
|
rect->bounds.origin.x + rect->bounds.size.width,
|
||||||
|
rect->bounds.origin.y + rect->corner[GSK_CORNER_TOP_RIGHT].height,
|
||||||
|
weight);
|
||||||
|
/* right */
|
||||||
|
gsk_path_builder_line_to (self,
|
||||||
|
rect->bounds.origin.x + rect->bounds.size.width,
|
||||||
|
rect->bounds.origin.y + rect->bounds.size.height - rect->corner[GSK_CORNER_BOTTOM_RIGHT].height);
|
||||||
|
/* bottomright corner */
|
||||||
|
gsk_path_builder_conic_to (self,
|
||||||
|
rect->bounds.origin.x + rect->bounds.size.width,
|
||||||
|
rect->bounds.origin.y + rect->bounds.size.height,
|
||||||
|
rect->bounds.origin.x + rect->bounds.size.width - rect->corner[GSK_CORNER_BOTTOM_RIGHT].width,
|
||||||
|
rect->bounds.origin.y + rect->bounds.size.height,
|
||||||
|
weight);
|
||||||
|
/* bottom */
|
||||||
|
gsk_path_builder_line_to (self,
|
||||||
|
rect->bounds.origin.x + rect->corner[GSK_CORNER_BOTTOM_LEFT].width,
|
||||||
|
rect->bounds.origin.y + rect->bounds.size.height);
|
||||||
|
/* bottomleft corner */
|
||||||
|
gsk_path_builder_conic_to (self,
|
||||||
|
rect->bounds.origin.x,
|
||||||
|
rect->bounds.origin.y + rect->bounds.size.height,
|
||||||
|
rect->bounds.origin.x,
|
||||||
|
rect->bounds.origin.y + rect->bounds.size.height - rect->corner[GSK_CORNER_BOTTOM_LEFT].height,
|
||||||
|
weight);
|
||||||
|
/* left */
|
||||||
|
gsk_path_builder_line_to (self,
|
||||||
|
rect->bounds.origin.x,
|
||||||
|
rect->bounds.origin.y + rect->corner[GSK_CORNER_TOP_LEFT].height);
|
||||||
|
/* topleft corner */
|
||||||
|
gsk_path_builder_conic_to (self,
|
||||||
|
rect->bounds.origin.x,
|
||||||
|
rect->bounds.origin.y,
|
||||||
|
rect->bounds.origin.x + rect->corner[GSK_CORNER_TOP_LEFT].width,
|
||||||
|
rect->bounds.origin.y,
|
||||||
|
weight);
|
||||||
|
/* done */
|
||||||
|
gsk_path_builder_close (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gsk_path_builder_add_circle:
|
||||||
|
* @builder: a #GskPathBuilder
|
||||||
|
* @center: the center of the circle
|
||||||
|
* @radius: the radius of the circle
|
||||||
|
*
|
||||||
|
* Adds a circle with the @center and @radius.
|
||||||
|
**/
|
||||||
|
void
|
||||||
|
gsk_path_builder_add_circle (GskPathBuilder *builder,
|
||||||
|
const graphene_point_t *center,
|
||||||
|
float radius)
|
||||||
|
{
|
||||||
|
GskContour *contour;
|
||||||
|
|
||||||
|
g_return_if_fail (builder != NULL);
|
||||||
|
g_return_if_fail (center != NULL);
|
||||||
|
g_return_if_fail (radius > 0);
|
||||||
|
|
||||||
|
contour = gsk_circle_contour_new (center, radius, 0, 360);
|
||||||
|
gsk_path_builder_add_contour (builder, contour);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gsk_path_builder_add_ellipse:
|
||||||
|
* @builder: a #GskPathBuilder
|
||||||
|
* @center: the center point of the ellipse
|
||||||
|
* @radius: the radius of the ellipse in x/y direction
|
||||||
|
*
|
||||||
|
* Adds an ellipse with the given @center and the @radius in
|
||||||
|
* x/y direction.
|
||||||
|
**/
|
||||||
|
void
|
||||||
|
gsk_path_builder_add_ellipse (GskPathBuilder *self,
|
||||||
|
const graphene_point_t *center,
|
||||||
|
const graphene_size_t *radius)
|
||||||
|
{
|
||||||
|
const float weight = sqrt(0.5f);
|
||||||
|
graphene_point_t pts[8];
|
||||||
|
|
||||||
|
g_return_if_fail (self != NULL);
|
||||||
|
g_return_if_fail (center != NULL);
|
||||||
|
g_return_if_fail (radius != NULL);
|
||||||
|
|
||||||
|
pts[0] = GRAPHENE_POINT_INIT (center->x + radius->width / 2,
|
||||||
|
center->y);
|
||||||
|
pts[1] = GRAPHENE_POINT_INIT (center->x + radius->width / 2,
|
||||||
|
center->y + radius->height / 2);
|
||||||
|
pts[2] = GRAPHENE_POINT_INIT (center->x,
|
||||||
|
center->y + radius->height / 2);
|
||||||
|
pts[3] = GRAPHENE_POINT_INIT (center->x - radius->width / 2,
|
||||||
|
center->y + radius->height / 2);
|
||||||
|
pts[4] = GRAPHENE_POINT_INIT (center->x - radius->width / 2,
|
||||||
|
center->y);
|
||||||
|
pts[5] = GRAPHENE_POINT_INIT (center->x - radius->width / 2,
|
||||||
|
center->y - radius->height / 2);
|
||||||
|
pts[6] = GRAPHENE_POINT_INIT (center->x,
|
||||||
|
center->y - radius->height / 2);
|
||||||
|
pts[7] = GRAPHENE_POINT_INIT (center->x + radius->width / 2,
|
||||||
|
center->y - radius->height / 2);
|
||||||
|
|
||||||
|
gsk_path_builder_move_to (self, pts[0].x, pts[0].y);
|
||||||
|
gsk_path_builder_conic_to (self, pts[1].x, pts[1].y, pts[2].x, pts[2].y, weight);
|
||||||
|
gsk_path_builder_conic_to (self, pts[3].x, pts[3].y, pts[4].x, pts[4].y, weight);
|
||||||
|
gsk_path_builder_conic_to (self, pts[5].x, pts[5].y, pts[6].x, pts[6].y, weight);
|
||||||
|
gsk_path_builder_conic_to (self, pts[7].x, pts[7].y, pts[0].x, pts[0].y, weight);
|
||||||
|
gsk_path_builder_close (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gsk_path_builder_move_to:
|
||||||
|
* @builder: a #GskPathBuilder
|
||||||
|
* @x: x coordinate
|
||||||
|
* @y: y coordinate
|
||||||
|
*
|
||||||
|
* Starts a new contour by placing the pen at @x, @y.
|
||||||
|
*
|
||||||
|
* If gsk_path_builder_move_to() is called twice in succession, the first
|
||||||
|
* call will result in a contour made up of a single point. The second call
|
||||||
|
* will start a new contour.
|
||||||
|
**/
|
||||||
|
void
|
||||||
|
gsk_path_builder_move_to (GskPathBuilder *builder,
|
||||||
|
float x,
|
||||||
|
float y)
|
||||||
|
{
|
||||||
|
g_return_if_fail (builder != NULL);
|
||||||
|
|
||||||
|
gsk_path_builder_end_current (builder);
|
||||||
|
|
||||||
|
builder->current_point = GRAPHENE_POINT_INIT(x, y);
|
||||||
|
|
||||||
|
gsk_path_builder_ensure_current (builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gsk_path_builder_rel_move_to:
|
||||||
|
* @builder: a #GskPathBuilder
|
||||||
|
* @x: x offset
|
||||||
|
* @y: y offset
|
||||||
|
*
|
||||||
|
* Starts a new contour by placing the pen at @x, @y relative to the current
|
||||||
|
* point.
|
||||||
|
*
|
||||||
|
* This is the relative version of gsk_path_builder_move_to().
|
||||||
|
**/
|
||||||
|
void
|
||||||
|
gsk_path_builder_rel_move_to (GskPathBuilder *builder,
|
||||||
|
float x,
|
||||||
|
float y)
|
||||||
|
{
|
||||||
|
g_return_if_fail (builder != NULL);
|
||||||
|
|
||||||
|
gsk_path_builder_move_to (builder,
|
||||||
|
builder->current_point.x + x,
|
||||||
|
builder->current_point.y + y);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gsk_path_builder_line_to:
|
||||||
|
* @builder: a #GskPathBuilder
|
||||||
|
* @x: x coordinate
|
||||||
|
* @y: y coordinate
|
||||||
|
*
|
||||||
|
* Draws a line from the current point to @x, @y and makes it the new current
|
||||||
|
* point.
|
||||||
|
**/
|
||||||
|
void
|
||||||
|
gsk_path_builder_line_to (GskPathBuilder *builder,
|
||||||
|
float x,
|
||||||
|
float y)
|
||||||
|
{
|
||||||
|
g_return_if_fail (builder != NULL);
|
||||||
|
|
||||||
|
/* skip the line if it goes to the same point */
|
||||||
|
if (graphene_point_equal (&builder->current_point,
|
||||||
|
&GRAPHENE_POINT_INIT (x, y)))
|
||||||
|
return;
|
||||||
|
|
||||||
|
gsk_path_builder_append_current (builder,
|
||||||
|
GSK_PATH_LINE,
|
||||||
|
1, (graphene_point_t[1]) {
|
||||||
|
GRAPHENE_POINT_INIT (x, y)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gsk_path_builder_line_to:
|
||||||
|
* @builder: a #GskPathBuilder
|
||||||
|
* @x: x offset
|
||||||
|
* @y: y offset
|
||||||
|
*
|
||||||
|
* Draws a line from the current point to a point offset to it by @x, @y
|
||||||
|
* and makes it the new current point.
|
||||||
|
*
|
||||||
|
* This is the relative version of gsk_path_builder_line_to().
|
||||||
|
**/
|
||||||
|
void
|
||||||
|
gsk_path_builder_rel_line_to (GskPathBuilder *builder,
|
||||||
|
float x,
|
||||||
|
float y)
|
||||||
|
{
|
||||||
|
g_return_if_fail (builder != NULL);
|
||||||
|
|
||||||
|
gsk_path_builder_line_to (builder,
|
||||||
|
builder->current_point.x + x,
|
||||||
|
builder->current_point.y + y);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gsk_path_builder_curve_to:
|
||||||
|
* @builder: a #GskPathBuilder
|
||||||
|
* @x1: x coordinate of first control point
|
||||||
|
* @y1: y coordinate of first control point
|
||||||
|
* @x2: x coordinate of second control point
|
||||||
|
* @y2: y coordinate of second control point
|
||||||
|
* @x3: x coordinate of the end of the curve
|
||||||
|
* @y3: y coordinate of the end of the curve
|
||||||
|
*
|
||||||
|
* Adds a [cubic Bézier curve](https://en.wikipedia.org/wiki/B%C3%A9zier_curve)
|
||||||
|
* from the current point to @x3, @y3 with @x1, @y1 and @x2, @y2 as the control
|
||||||
|
* points.
|
||||||
|
*
|
||||||
|
* After this, @x3, @y3 will be the new current point.
|
||||||
|
**/
|
||||||
|
void
|
||||||
|
gsk_path_builder_curve_to (GskPathBuilder *builder,
|
||||||
|
float x1,
|
||||||
|
float y1,
|
||||||
|
float x2,
|
||||||
|
float y2,
|
||||||
|
float x3,
|
||||||
|
float y3)
|
||||||
|
{
|
||||||
|
g_return_if_fail (builder != NULL);
|
||||||
|
|
||||||
|
builder->flags &= ~GSK_PATH_FLAT;
|
||||||
|
gsk_path_builder_append_current (builder,
|
||||||
|
GSK_PATH_CURVE,
|
||||||
|
3, (graphene_point_t[3]) {
|
||||||
|
GRAPHENE_POINT_INIT (x1, y1),
|
||||||
|
GRAPHENE_POINT_INIT (x2, y2),
|
||||||
|
GRAPHENE_POINT_INIT (x3, y3)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gsk_path_builder_rel_curve_to:
|
||||||
|
* @builder: a #GskPathBuilder
|
||||||
|
* @x1: x offset of first control point
|
||||||
|
* @y1: y offset of first control point
|
||||||
|
* @x2: x offset of second control point
|
||||||
|
* @y2: y offset of second control point
|
||||||
|
* @x3: x offset of the end of the curve
|
||||||
|
* @y3: y offset of the end of the curve
|
||||||
|
*
|
||||||
|
* Adds a [cubic Bézier curve](https://en.wikipedia.org/wiki/B%C3%A9zier_curve)
|
||||||
|
* from the current point to @x3, @y3 with @x1, @y1 and @x2, @y2 as the control
|
||||||
|
* points. All coordinates are given relative to the current point.
|
||||||
|
*
|
||||||
|
* This is the relative version of gsk_path_builder_curve_to().
|
||||||
|
**/
|
||||||
|
void
|
||||||
|
gsk_path_builder_rel_curve_to (GskPathBuilder *builder,
|
||||||
|
float x1,
|
||||||
|
float y1,
|
||||||
|
float x2,
|
||||||
|
float y2,
|
||||||
|
float x3,
|
||||||
|
float y3)
|
||||||
|
{
|
||||||
|
g_return_if_fail (builder != NULL);
|
||||||
|
|
||||||
|
gsk_path_builder_curve_to (builder,
|
||||||
|
builder->current_point.x + x1,
|
||||||
|
builder->current_point.y + y1,
|
||||||
|
builder->current_point.x + x2,
|
||||||
|
builder->current_point.y + y2,
|
||||||
|
builder->current_point.x + x3,
|
||||||
|
builder->current_point.y + y3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gsk_path_builder_conic_to:
|
||||||
|
* @builder: a #GskPathBuilder
|
||||||
|
* @x1: x coordinate of control point
|
||||||
|
* @y1: y coordinate of control point
|
||||||
|
* @x2: x coordinate of the end of the curve
|
||||||
|
* @y2: y coordinate of the end of the curve
|
||||||
|
* @weight: weight of the curve
|
||||||
|
*
|
||||||
|
* Adds a [conic curve](https://en.wikipedia.org/wiki/Non-uniform_rational_B-spline)
|
||||||
|
* from the current point to @x2, @y2 with the given
|
||||||
|
* @weight and @x1, @y1 as the single control point.
|
||||||
|
*
|
||||||
|
* Conic curves can be used to draw ellipses and circles.
|
||||||
|
*
|
||||||
|
* After this, @x2, @y2 will be the new current point.
|
||||||
|
**/
|
||||||
|
void
|
||||||
|
gsk_path_builder_conic_to (GskPathBuilder *builder,
|
||||||
|
float x1,
|
||||||
|
float y1,
|
||||||
|
float x2,
|
||||||
|
float y2,
|
||||||
|
float weight)
|
||||||
|
{
|
||||||
|
g_return_if_fail (builder != NULL);
|
||||||
|
g_return_if_fail (weight >= 0);
|
||||||
|
|
||||||
|
builder->flags &= ~GSK_PATH_FLAT;
|
||||||
|
gsk_path_builder_append_current (builder,
|
||||||
|
GSK_PATH_CONIC,
|
||||||
|
3, (graphene_point_t[3]) {
|
||||||
|
GRAPHENE_POINT_INIT (x1, y1),
|
||||||
|
GRAPHENE_POINT_INIT (weight, 0),
|
||||||
|
GRAPHENE_POINT_INIT (x2, y2)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gsk_path_builder_rel_conic_to:
|
||||||
|
* @builder: a #GskPathBuilder
|
||||||
|
* @x1: x offset of control point
|
||||||
|
* @y1: y offset of control point
|
||||||
|
* @x2: x offset of the end of the curve
|
||||||
|
* @y2: y offset of the end of the curve
|
||||||
|
* @weight: weight of the curve
|
||||||
|
*
|
||||||
|
* Adds a [conic curve](https://en.wikipedia.org/wiki/Non-uniform_rational_B-spline)
|
||||||
|
* from the current point to @x2, @y2 with the given
|
||||||
|
* @weight and @x1, @y1 as the single control point.
|
||||||
|
*
|
||||||
|
* This is the relative version of gsk_path_builder_conic_to().
|
||||||
|
**/
|
||||||
|
void
|
||||||
|
gsk_path_builder_rel_conic_to (GskPathBuilder *builder,
|
||||||
|
float x1,
|
||||||
|
float y1,
|
||||||
|
float x2,
|
||||||
|
float y2,
|
||||||
|
float weight)
|
||||||
|
{
|
||||||
|
g_return_if_fail (builder != NULL);
|
||||||
|
g_return_if_fail (weight >= 0);
|
||||||
|
|
||||||
|
gsk_path_builder_conic_to (builder,
|
||||||
|
builder->current_point.x + x1,
|
||||||
|
builder->current_point.y + y1,
|
||||||
|
builder->current_point.x + x2,
|
||||||
|
builder->current_point.y + y2,
|
||||||
|
weight);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gsk_path_builder_close:
|
||||||
|
* @builder: a #GskPathBuilder
|
||||||
|
*
|
||||||
|
* Ends the current contour with a line back to the start point.
|
||||||
|
*
|
||||||
|
* Note that this is different from calling gsk_path_builder_line_to()
|
||||||
|
* with the start point in that the contour will be closed. A closed
|
||||||
|
* contour behaves different from an open one when stroking its start
|
||||||
|
* and end point are considered connected, so they will be joined
|
||||||
|
* via the line join, and not ended with line caps.
|
||||||
|
**/
|
||||||
|
void
|
||||||
|
gsk_path_builder_close (GskPathBuilder *builder)
|
||||||
|
{
|
||||||
|
g_return_if_fail (builder != NULL);
|
||||||
|
|
||||||
|
if (builder->ops->len == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
builder->flags |= GSK_PATH_CLOSED;
|
||||||
|
gsk_path_builder_append_current (builder,
|
||||||
|
GSK_PATH_CLOSE,
|
||||||
|
1, (graphene_point_t[1]) {
|
||||||
|
g_array_index (builder->points, graphene_point_t, 0)
|
||||||
|
});
|
||||||
|
|
||||||
|
gsk_path_builder_end_current (builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
arc_segment (GskPathBuilder *builder,
|
||||||
|
double cx,
|
||||||
|
double cy,
|
||||||
|
double rx,
|
||||||
|
double ry,
|
||||||
|
double sin_phi,
|
||||||
|
double cos_phi,
|
||||||
|
double sin_th0,
|
||||||
|
double cos_th0,
|
||||||
|
double sin_th1,
|
||||||
|
double cos_th1,
|
||||||
|
double t)
|
||||||
|
{
|
||||||
|
double x1, y1, x2, y2, x3, y3;
|
||||||
|
|
||||||
|
x1 = rx * (cos_th0 - t * sin_th0);
|
||||||
|
y1 = ry * (sin_th0 + t * cos_th0);
|
||||||
|
x3 = rx * cos_th1;
|
||||||
|
y3 = ry * sin_th1;
|
||||||
|
x2 = x3 + rx * (t * sin_th1);
|
||||||
|
y2 = y3 + ry * (-t * cos_th1);
|
||||||
|
|
||||||
|
gsk_path_builder_curve_to (builder,
|
||||||
|
cx + cos_phi * x1 - sin_phi * y1,
|
||||||
|
cy + sin_phi * x1 + cos_phi * y1,
|
||||||
|
cx + cos_phi * x2 - sin_phi * y2,
|
||||||
|
cy + sin_phi * x2 + cos_phi * y2,
|
||||||
|
cx + cos_phi * x3 - sin_phi * y3,
|
||||||
|
cy + sin_phi * x3 + cos_phi * y3);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
gsk_path_builder_svg_arc_to (GskPathBuilder *builder,
|
||||||
|
float rx,
|
||||||
|
float ry,
|
||||||
|
float x_axis_rotation,
|
||||||
|
gboolean large_arc,
|
||||||
|
gboolean positive_sweep,
|
||||||
|
float x,
|
||||||
|
float y)
|
||||||
|
{
|
||||||
|
graphene_point_t *current;
|
||||||
|
double x1, y1, x2, y2;
|
||||||
|
double phi, sin_phi, cos_phi;
|
||||||
|
double mid_x, mid_y;
|
||||||
|
double lambda;
|
||||||
|
double d;
|
||||||
|
double k;
|
||||||
|
double x1_, y1_;
|
||||||
|
double cx_, cy_;
|
||||||
|
double cx, cy;
|
||||||
|
double ux, uy, u_len;
|
||||||
|
double cos_theta1, theta1;
|
||||||
|
double vx, vy, v_len;
|
||||||
|
double dp_uv;
|
||||||
|
double cos_delta_theta, delta_theta;
|
||||||
|
int i, n_segs;
|
||||||
|
double d_theta, theta;
|
||||||
|
double sin_th0, cos_th0;
|
||||||
|
double sin_th1, cos_th1;
|
||||||
|
double th_half;
|
||||||
|
double t;
|
||||||
|
|
||||||
|
if (builder->points->len > 0)
|
||||||
|
{
|
||||||
|
current = &g_array_index (builder->points, graphene_point_t, builder->points->len - 1);
|
||||||
|
x1 = current->x;
|
||||||
|
y1 = current->y;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
x1 = 0;
|
||||||
|
y1 = 0;
|
||||||
|
}
|
||||||
|
x2 = x;
|
||||||
|
y2 = y;
|
||||||
|
|
||||||
|
phi = x_axis_rotation * M_PI / 180.0;
|
||||||
|
sincos (phi, &sin_phi, &cos_phi);
|
||||||
|
|
||||||
|
rx = fabs (rx);
|
||||||
|
ry = fabs (ry);
|
||||||
|
|
||||||
|
mid_x = (x1 - x2) / 2;
|
||||||
|
mid_y = (y1 - y2) / 2;
|
||||||
|
|
||||||
|
x1_ = cos_phi * mid_x + sin_phi * mid_y;
|
||||||
|
y1_ = - sin_phi * mid_x + cos_phi * mid_y;
|
||||||
|
|
||||||
|
lambda = (x1_ / rx) * (x1_ / rx) + (y1_ / ry) * (y1_ / ry);
|
||||||
|
if (lambda > 1)
|
||||||
|
{
|
||||||
|
lambda = sqrt (lambda);
|
||||||
|
rx *= lambda;
|
||||||
|
ry *= lambda;
|
||||||
|
}
|
||||||
|
|
||||||
|
d = (rx * y1_) * (rx * y1_) + (ry * x1_) * (ry * x1_);
|
||||||
|
if (d == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
k = sqrt (fabs ((rx * ry) * (rx * ry) / d - 1.0));
|
||||||
|
if (positive_sweep == large_arc)
|
||||||
|
k = -k;
|
||||||
|
|
||||||
|
cx_ = k * rx * y1_ / ry;
|
||||||
|
cy_ = -k * ry * x1_ / rx;
|
||||||
|
|
||||||
|
cx = cos_phi * cx_ - sin_phi * cy_ + (x1 + x2) / 2;
|
||||||
|
cy = sin_phi * cx_ + cos_phi * cy_ + (y1 + y2) / 2;
|
||||||
|
|
||||||
|
ux = (x1_ - cx_) / rx;
|
||||||
|
uy = (y1_ - cy_) / ry;
|
||||||
|
u_len = sqrt (ux * ux + uy * uy);
|
||||||
|
if (u_len == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
cos_theta1 = CLAMP (ux / u_len, -1, 1);
|
||||||
|
theta1 = acos (cos_theta1);
|
||||||
|
if (uy < 0)
|
||||||
|
theta1 = - theta1;
|
||||||
|
|
||||||
|
vx = (- x1_ - cx_) / rx;
|
||||||
|
vy = (- y1_ - cy_) / ry;
|
||||||
|
v_len = sqrt (vx * vx + vy * vy);
|
||||||
|
if (v_len == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
dp_uv = ux * vx + uy * vy;
|
||||||
|
cos_delta_theta = CLAMP (dp_uv / (u_len * v_len), -1, 1);
|
||||||
|
delta_theta = acos (cos_delta_theta);
|
||||||
|
if (ux * vy - uy * vx < 0)
|
||||||
|
delta_theta = - delta_theta;
|
||||||
|
if (positive_sweep && delta_theta < 0)
|
||||||
|
delta_theta += 2 * M_PI;
|
||||||
|
else if (!positive_sweep && delta_theta > 0)
|
||||||
|
delta_theta -= 2 * M_PI;
|
||||||
|
|
||||||
|
n_segs = ceil (fabs (delta_theta / (M_PI_2 + 0.001)));
|
||||||
|
d_theta = delta_theta / n_segs;
|
||||||
|
theta = theta1;
|
||||||
|
sincos (theta1, &sin_th1, &cos_th1);
|
||||||
|
|
||||||
|
th_half = d_theta / 2;
|
||||||
|
t = (8.0 / 3.0) * sin (th_half / 2) * sin (th_half / 2) / sin (th_half);
|
||||||
|
|
||||||
|
for (i = 0; i < n_segs; i++)
|
||||||
|
{
|
||||||
|
theta = theta1;
|
||||||
|
theta1 = theta + d_theta;
|
||||||
|
sin_th0 = sin_th1;
|
||||||
|
cos_th0 = cos_th1;
|
||||||
|
sincos (theta1, &sin_th1, &cos_th1);
|
||||||
|
arc_segment (builder,
|
||||||
|
cx, cy, rx, ry,
|
||||||
|
sin_phi, cos_phi,
|
||||||
|
sin_th0, cos_th0,
|
||||||
|
sin_th1, cos_th1,
|
||||||
|
t);
|
||||||
|
}
|
||||||
|
}
|
||||||
128
gsk/gskpathbuilder.h
Normal file
128
gsk/gskpathbuilder.h
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2020 Benjamin Otte
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* Authors: Benjamin Otte <otte@gnome.org>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __GSK_PATH_BUILDER_H__
|
||||||
|
#define __GSK_PATH_BUILDER_H__
|
||||||
|
|
||||||
|
#if !defined (__GSK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||||
|
#error "Only <gsk/gsk.h> can be included directly."
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#include <gsk/gskroundedrect.h>
|
||||||
|
#include <gsk/gsktypes.h>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
#define GSK_TYPE_PATH_BUILDER (gsk_path_builder_get_type ())
|
||||||
|
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
GType gsk_path_builder_get_type (void) G_GNUC_CONST;
|
||||||
|
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
GskPathBuilder * gsk_path_builder_new (void);
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
GskPathBuilder * gsk_path_builder_ref (GskPathBuilder *builder);
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
void gsk_path_builder_unref (GskPathBuilder *builder);
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
GskPath * gsk_path_builder_free_to_path (GskPathBuilder *builder) G_GNUC_WARN_UNUSED_RESULT;
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
GskPath * gsk_path_builder_to_path (GskPathBuilder *builder) G_GNUC_WARN_UNUSED_RESULT;
|
||||||
|
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
const graphene_point_t *gsk_path_builder_get_current_point (GskPathBuilder *builder);
|
||||||
|
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
void gsk_path_builder_add_path (GskPathBuilder *builder,
|
||||||
|
GskPath *path);
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
void gsk_path_builder_add_rect (GskPathBuilder *builder,
|
||||||
|
const graphene_rect_t *rect);
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
void gsk_path_builder_add_rounded_rect (GskPathBuilder *builder,
|
||||||
|
const GskRoundedRect *rect);
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
void gsk_path_builder_add_circle (GskPathBuilder *builder,
|
||||||
|
const graphene_point_t *center,
|
||||||
|
float radius);
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
void gsk_path_builder_add_ellipse (GskPathBuilder *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 *builder,
|
||||||
|
GskPathMeasure *self,
|
||||||
|
float start,
|
||||||
|
float end);
|
||||||
|
|
||||||
|
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
void gsk_path_builder_move_to (GskPathBuilder *builder,
|
||||||
|
float x,
|
||||||
|
float y);
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
void gsk_path_builder_rel_move_to (GskPathBuilder *builder,
|
||||||
|
float x,
|
||||||
|
float y);
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
void gsk_path_builder_line_to (GskPathBuilder *builder,
|
||||||
|
float x,
|
||||||
|
float y);
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
void gsk_path_builder_rel_line_to (GskPathBuilder *builder,
|
||||||
|
float x,
|
||||||
|
float y);
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
void gsk_path_builder_curve_to (GskPathBuilder *builder,
|
||||||
|
float x1,
|
||||||
|
float y1,
|
||||||
|
float x2,
|
||||||
|
float y2,
|
||||||
|
float x3,
|
||||||
|
float y3);
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
void gsk_path_builder_rel_curve_to (GskPathBuilder *builder,
|
||||||
|
float x1,
|
||||||
|
float y1,
|
||||||
|
float x2,
|
||||||
|
float y2,
|
||||||
|
float x3,
|
||||||
|
float y3);
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
void gsk_path_builder_conic_to (GskPathBuilder *builder,
|
||||||
|
float x1,
|
||||||
|
float y1,
|
||||||
|
float x2,
|
||||||
|
float y2,
|
||||||
|
float weight);
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
void gsk_path_builder_rel_conic_to (GskPathBuilder *builder,
|
||||||
|
float x1,
|
||||||
|
float y1,
|
||||||
|
float x2,
|
||||||
|
float y2,
|
||||||
|
float weight);
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
void gsk_path_builder_close (GskPathBuilder *builder);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* __GSK_PATH_BUILDER_H__ */
|
||||||
291
gsk/gskpathdash.c
Normal file
291
gsk/gskpathdash.c
Normal file
@@ -0,0 +1,291 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2020 Benjamin Otte
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* Authors: Benjamin Otte <otte@gnome.org>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "gskpathdashprivate.h"
|
||||||
|
|
||||||
|
#include "gskcontourprivate.h"
|
||||||
|
#include "gskcurveprivate.h"
|
||||||
|
#include "gskpathprivate.h"
|
||||||
|
#include "gskstrokeprivate.h"
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
float offset; /* how much of the current dash we've spent */
|
||||||
|
gsize dash_index; /* goes from 0 to n_dash * 2, so we don't have to care about on/off
|
||||||
|
for uneven dashes */
|
||||||
|
gboolean on; /* If we're currently dashing or not */
|
||||||
|
gboolean may_close; /* TRUE if we haven't turned the dash off in this contour */
|
||||||
|
gboolean needs_move_to; /* If we have emitted the initial move_to() yet */
|
||||||
|
enum {
|
||||||
|
NORMAL, /* no special behavior required */
|
||||||
|
SKIP, /* skip the next dash */
|
||||||
|
ONLY, /* only do the first dash */
|
||||||
|
DONE /* done with the first dash */
|
||||||
|
} first_dash_behavior; /* How to handle the first dash in the loop. We loop closed contours
|
||||||
|
twice to make sure the first dash and the last dash can get joined */
|
||||||
|
|
||||||
|
GskCurve curve; /* Curve we are currently processing */
|
||||||
|
|
||||||
|
float collect_start; /* We're collecting multiple line segments when decomposing. */
|
||||||
|
float collect_length; /* No need to emit a curve for every line segment when the dash is long enough. */
|
||||||
|
|
||||||
|
/* from the stroke */
|
||||||
|
float *dash;
|
||||||
|
gsize n_dash;
|
||||||
|
float dash_length;
|
||||||
|
float dash_offset;
|
||||||
|
|
||||||
|
float tolerance;
|
||||||
|
GskPathForeachFunc func;
|
||||||
|
gpointer user_data;
|
||||||
|
} GskPathDash;
|
||||||
|
|
||||||
|
static void
|
||||||
|
gsk_path_dash_setup (GskPathDash *self)
|
||||||
|
{
|
||||||
|
self->offset = fmodf (self->dash_offset, 2 * self->dash_length);
|
||||||
|
|
||||||
|
self->dash_index = 0;
|
||||||
|
self->on = TRUE;
|
||||||
|
self->may_close = TRUE;
|
||||||
|
while (self->offset > self->dash[self->dash_index % self->n_dash])
|
||||||
|
{
|
||||||
|
self->offset -= self->dash[self->dash_index % self->n_dash];
|
||||||
|
self->dash_index++;
|
||||||
|
self->on = !self->on;
|
||||||
|
}
|
||||||
|
if (self->first_dash_behavior != ONLY)
|
||||||
|
self->needs_move_to = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gsk_path_dash_ensure_move_to (GskPathDash *self,
|
||||||
|
const graphene_point_t *pt)
|
||||||
|
{
|
||||||
|
if (!self->needs_move_to)
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
if (!self->func (GSK_PATH_MOVE, pt, 1, 0, self->user_data))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
self->needs_move_to = FALSE;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gsk_path_dash_add_line_segment (const graphene_point_t *start,
|
||||||
|
const graphene_point_t *end,
|
||||||
|
float t_start,
|
||||||
|
float t_end,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
GskPathDash *self = user_data;
|
||||||
|
float remaining, length, t_step;
|
||||||
|
|
||||||
|
length = graphene_point_distance (start, end, NULL, NULL);
|
||||||
|
if (self->collect_length)
|
||||||
|
{
|
||||||
|
t_start = self->collect_start;
|
||||||
|
length += self->collect_length;
|
||||||
|
self->collect_length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
t_step = t_end - t_start;
|
||||||
|
remaining = length;
|
||||||
|
|
||||||
|
while (remaining)
|
||||||
|
{
|
||||||
|
float piece;
|
||||||
|
|
||||||
|
if (self->offset + remaining <= self->dash[self->dash_index % self->n_dash])
|
||||||
|
{
|
||||||
|
/* try collecting multiple line segments */
|
||||||
|
if (t_end < 1.0)
|
||||||
|
{
|
||||||
|
self->collect_start = t_start + t_step * (length - remaining) / length;
|
||||||
|
self->collect_length = remaining;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
piece = remaining;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
piece = self->dash[self->dash_index % self->n_dash] - self->offset;
|
||||||
|
|
||||||
|
if (self->on)
|
||||||
|
{
|
||||||
|
if (self->first_dash_behavior != SKIP)
|
||||||
|
{
|
||||||
|
GskCurve segment;
|
||||||
|
|
||||||
|
if (piece)
|
||||||
|
{
|
||||||
|
gsk_curve_segment (&self->curve,
|
||||||
|
t_start + t_step * (length - remaining) / length,
|
||||||
|
t_start + t_step * (length - (remaining - piece)) / length,
|
||||||
|
&segment);
|
||||||
|
if (!gsk_path_dash_ensure_move_to (self, gsk_curve_get_start_point (&segment)))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (!gsk_pathop_foreach (gsk_curve_pathop (&segment), self->func, self->user_data))
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
graphene_point_t p;
|
||||||
|
|
||||||
|
gsk_curve_get_point (&self->curve, t_start + t_step * (length - remaining) / length, &p);
|
||||||
|
if (!gsk_path_dash_ensure_move_to (self, &p))
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
self->may_close = FALSE;
|
||||||
|
if (self->first_dash_behavior == ONLY)
|
||||||
|
{
|
||||||
|
self->first_dash_behavior = DONE;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
self->first_dash_behavior = NORMAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self->offset + remaining <= self->dash[self->dash_index % self->n_dash])
|
||||||
|
{
|
||||||
|
self->offset += remaining;
|
||||||
|
remaining = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
remaining -= piece;
|
||||||
|
self->offset = 0;
|
||||||
|
self->dash_index++;
|
||||||
|
self->dash_index %= 2 * self->n_dash;
|
||||||
|
self->on = !self->on;
|
||||||
|
self->needs_move_to = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gsk_path_dash_foreach (GskPathOperation op,
|
||||||
|
const graphene_point_t *pts,
|
||||||
|
gsize n_pts,
|
||||||
|
float weight,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
GskPathDash *self = user_data;
|
||||||
|
|
||||||
|
switch (op)
|
||||||
|
{
|
||||||
|
case GSK_PATH_MOVE:
|
||||||
|
gsk_path_dash_setup (self);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GSK_PATH_CLOSE:
|
||||||
|
if (self->may_close)
|
||||||
|
{
|
||||||
|
if (graphene_point_equal (&pts[0], &pts[1]))
|
||||||
|
return self->func (GSK_PATH_CLOSE, pts, 2, 0, self->user_data);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
op = GSK_PATH_LINE;
|
||||||
|
G_GNUC_FALLTHROUGH;
|
||||||
|
|
||||||
|
case GSK_PATH_LINE:
|
||||||
|
case GSK_PATH_CURVE:
|
||||||
|
case GSK_PATH_CONIC:
|
||||||
|
gsk_curve_init_foreach (&self->curve, op, pts, n_pts, weight);
|
||||||
|
if (!gsk_curve_decompose (&self->curve, self->tolerance, gsk_path_dash_add_line_segment, self))
|
||||||
|
return FALSE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
g_assert_not_reached ();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
gsk_contour_dash (const GskContour *contour,
|
||||||
|
GskStroke *stroke,
|
||||||
|
float tolerance,
|
||||||
|
GskPathForeachFunc func,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
GskPathDash self = {
|
||||||
|
.offset = 0,
|
||||||
|
.dash = stroke->dash,
|
||||||
|
.n_dash = stroke->n_dash,
|
||||||
|
.dash_length = stroke->dash_length,
|
||||||
|
.dash_offset = stroke->dash_offset,
|
||||||
|
.tolerance = tolerance,
|
||||||
|
.func = func,
|
||||||
|
.user_data = user_data
|
||||||
|
};
|
||||||
|
gboolean is_closed = gsk_contour_get_flags (contour) & GSK_PATH_CLOSED ? TRUE : FALSE;
|
||||||
|
|
||||||
|
self.first_dash_behavior = is_closed ? SKIP : NORMAL;
|
||||||
|
if (!gsk_contour_foreach (contour, tolerance, gsk_path_dash_foreach, &self))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (is_closed)
|
||||||
|
{
|
||||||
|
if (self.first_dash_behavior == NORMAL)
|
||||||
|
self.first_dash_behavior = ONLY;
|
||||||
|
else
|
||||||
|
self.first_dash_behavior = NORMAL;
|
||||||
|
self.needs_move_to = !self.on;
|
||||||
|
if (!gsk_contour_foreach (contour, tolerance, gsk_path_dash_foreach, &self) &&
|
||||||
|
self.first_dash_behavior != DONE)
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
gsk_path_dash (GskPath *path,
|
||||||
|
GskStroke *stroke,
|
||||||
|
float tolerance,
|
||||||
|
GskPathForeachFunc func,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
gsize i;
|
||||||
|
|
||||||
|
/* Dashing disabled, no need to do any work */
|
||||||
|
if (stroke->dash_length <= 0)
|
||||||
|
return gsk_path_foreach (path, -1, func, user_data);
|
||||||
|
|
||||||
|
for (i = 0; i < gsk_path_get_n_contours (path); i++)
|
||||||
|
{
|
||||||
|
if (!gsk_contour_dash (gsk_path_get_contour (path, i), stroke, tolerance, func, user_data))
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
50
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__ */
|
||||||
|
|
||||||
681
gsk/gskpathmeasure.c
Normal file
681
gsk/gskpathmeasure.c
Normal file
@@ -0,0 +1,681 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2020 Benjamin Otte
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* Authors: Benjamin Otte <otte@gnome.org>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "gskpathmeasure.h"
|
||||||
|
#include "gskpathbuilder.h"
|
||||||
|
|
||||||
|
#include "gskpathbuilder.h"
|
||||||
|
#include "gskpathprivate.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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) (optional) (caller-allocates): The coordinates
|
||||||
|
* of the position at @distance
|
||||||
|
* @tangent: (out) (optional) (caller-allocates): The tangent
|
||||||
|
* to the position at @distance
|
||||||
|
*
|
||||||
|
* Calculates the coordinates and tangent of the point @distance
|
||||||
|
* units into the path. The value will be clamped to the length
|
||||||
|
* of the path.
|
||||||
|
*
|
||||||
|
* If the point is a discontinuous edge in the path, the returned
|
||||||
|
* point and tangent will describe the line starting at that point
|
||||||
|
* going forward.
|
||||||
|
*
|
||||||
|
* If @self describes an empty path, the returned point will be
|
||||||
|
* set to `(0, 0)` and the tangent will be the x axis or `(1, 0)`.
|
||||||
|
**/
|
||||||
|
void
|
||||||
|
gsk_path_measure_get_point (GskPathMeasure *self,
|
||||||
|
float distance,
|
||||||
|
graphene_point_t *pos,
|
||||||
|
graphene_vec2_t *tangent)
|
||||||
|
{
|
||||||
|
gsize i;
|
||||||
|
|
||||||
|
g_return_if_fail (self != NULL);
|
||||||
|
|
||||||
|
if (pos == NULL && tangent == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
distance = gsk_path_measure_clamp_distance (self, distance);
|
||||||
|
|
||||||
|
for (i = self->first; i < self->last; i++)
|
||||||
|
{
|
||||||
|
if (distance < self->measures[i].length)
|
||||||
|
break;
|
||||||
|
|
||||||
|
distance -= self->measures[i].length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* weird corner cases */
|
||||||
|
if (i == self->last)
|
||||||
|
{
|
||||||
|
/* the empty path goes here */
|
||||||
|
if (self->first == self->last)
|
||||||
|
{
|
||||||
|
if (pos)
|
||||||
|
graphene_point_init (pos, 0.f, 0.f);
|
||||||
|
if (tangent)
|
||||||
|
graphene_vec2_init (tangent, 1.f, 0.f);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* rounding errors can make this happen */
|
||||||
|
i = self->last - 1;
|
||||||
|
distance = self->measures[i].length;
|
||||||
|
}
|
||||||
|
|
||||||
|
gsk_contour_get_point (gsk_path_get_contour (self->path, i),
|
||||||
|
self->measures[i].contour_data,
|
||||||
|
distance,
|
||||||
|
pos,
|
||||||
|
tangent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @self: a #GskPathMeasure
|
||||||
|
* @distance: distance into the path
|
||||||
|
* @center: (out) (optional) (caller-allocates): The center
|
||||||
|
* of the osculating circle at the point
|
||||||
|
*
|
||||||
|
* Calculates the curvature at the point @distance units into
|
||||||
|
* the path. Optionally, returns the center of the osculating
|
||||||
|
* circle as well.
|
||||||
|
*
|
||||||
|
* If the curvature is infinite (at line segments), or does
|
||||||
|
* not exist (at sharp turns), zero is returned, and @center
|
||||||
|
* is not modified.
|
||||||
|
*
|
||||||
|
* Returns: The curvature of the path at the given point
|
||||||
|
*/
|
||||||
|
float
|
||||||
|
gsk_path_measure_get_curvature (GskPathMeasure *self,
|
||||||
|
float distance,
|
||||||
|
graphene_point_t *center)
|
||||||
|
{
|
||||||
|
gsize i;
|
||||||
|
|
||||||
|
g_return_val_if_fail (self != NULL, 0);
|
||||||
|
|
||||||
|
distance = gsk_path_measure_clamp_distance (self, distance);
|
||||||
|
|
||||||
|
for (i = self->first; i < self->last; i++)
|
||||||
|
{
|
||||||
|
if (distance < self->measures[i].length)
|
||||||
|
break;
|
||||||
|
|
||||||
|
distance -= self->measures[i].length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* weird corner cases */
|
||||||
|
if (i == self->last)
|
||||||
|
{
|
||||||
|
/* the empty path goes here */
|
||||||
|
if (self->first == self->last)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* rounding errors can make this happen */
|
||||||
|
i = self->last - 1;
|
||||||
|
distance = self->measures[i].length;
|
||||||
|
}
|
||||||
|
|
||||||
|
return gsk_contour_get_curvature (gsk_path_get_contour (self->path, i),
|
||||||
|
self->measures[i].contour_data,
|
||||||
|
distance,
|
||||||
|
center);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gsk_path_measure_get_closest_point:
|
||||||
|
* @self: a #GskPathMeasure
|
||||||
|
* @point: the point to fond the closest point to
|
||||||
|
* @out_pos: (out) (optional) (caller-allocates): return location
|
||||||
|
* for the closest point
|
||||||
|
*
|
||||||
|
* Gets the point on the path that is closest to @point.
|
||||||
|
*
|
||||||
|
* If the path being measured is empty, return 0 and set
|
||||||
|
* @out_pos to (0, 0).
|
||||||
|
*
|
||||||
|
* This is a simpler and slower version of
|
||||||
|
* 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) (optional) (caller-allocates): The
|
||||||
|
* distance between the found closest point on the path and the given
|
||||||
|
* @point.
|
||||||
|
* @out_pos: (out) (optional) (caller-allocates): return location
|
||||||
|
* for the closest point
|
||||||
|
* @out_offset: (out) (optional) (caller-allocates): The offset into
|
||||||
|
* the path of the found point
|
||||||
|
* @out_tangent: (out) (optional) (caller-allocates): return location for
|
||||||
|
* the tangent at the closest point
|
||||||
|
*
|
||||||
|
* Gets the point on the path that is closest to @point. If no point on
|
||||||
|
* path is closer to @point than @threshold, return %FALSE.
|
||||||
|
*
|
||||||
|
* Returns: %TRUE if a point was found, %FALSE otherwise.
|
||||||
|
**/
|
||||||
|
gboolean
|
||||||
|
gsk_path_measure_get_closest_point_full (GskPathMeasure *self,
|
||||||
|
const graphene_point_t *point,
|
||||||
|
float threshold,
|
||||||
|
float *out_distance,
|
||||||
|
graphene_point_t *out_pos,
|
||||||
|
float *out_offset,
|
||||||
|
graphene_vec2_t *out_tangent)
|
||||||
|
{
|
||||||
|
gboolean result;
|
||||||
|
gsize i;
|
||||||
|
float distance, length;
|
||||||
|
|
||||||
|
g_return_val_if_fail (self != NULL, FALSE);
|
||||||
|
g_return_val_if_fail (point != NULL, FALSE);
|
||||||
|
|
||||||
|
result = FALSE;
|
||||||
|
length = 0;
|
||||||
|
|
||||||
|
for (i = self->first; i < self->last; i++)
|
||||||
|
{
|
||||||
|
if (gsk_contour_get_closest_point (gsk_path_get_contour (self->path, i),
|
||||||
|
self->measures[i].contour_data,
|
||||||
|
self->tolerance,
|
||||||
|
point,
|
||||||
|
threshold,
|
||||||
|
&distance,
|
||||||
|
out_pos,
|
||||||
|
out_offset,
|
||||||
|
out_tangent))
|
||||||
|
{
|
||||||
|
result = TRUE;
|
||||||
|
if (out_offset)
|
||||||
|
*out_offset += length;
|
||||||
|
|
||||||
|
if (distance < self->tolerance)
|
||||||
|
break;
|
||||||
|
threshold = distance - self->tolerance;
|
||||||
|
}
|
||||||
|
|
||||||
|
length += self->measures[i].length;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result && out_distance)
|
||||||
|
*out_distance = distance;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gsk_path_measure_in_fill:
|
||||||
|
* @self: a #GskPathMeasure
|
||||||
|
* @point: the point to test
|
||||||
|
* @fill_rule: the fill rule to follow
|
||||||
|
*
|
||||||
|
* Returns whether the given point is inside the area that would be
|
||||||
|
* affected if the path of @self was filled according to @fill_rule.
|
||||||
|
*
|
||||||
|
* Returns: %TRUE if @point is inside
|
||||||
|
*/
|
||||||
|
gboolean
|
||||||
|
gsk_path_measure_in_fill (GskPathMeasure *self,
|
||||||
|
graphene_point_t *point,
|
||||||
|
GskFillRule fill_rule)
|
||||||
|
{
|
||||||
|
int winding = 0;
|
||||||
|
gboolean on_edge = FALSE;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = self->first; i < self->last; i++)
|
||||||
|
{
|
||||||
|
winding += gsk_contour_get_winding (gsk_path_get_contour (self->path, i),
|
||||||
|
self->measures[i].contour_data,
|
||||||
|
point,
|
||||||
|
&on_edge);
|
||||||
|
if (on_edge)
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (fill_rule)
|
||||||
|
{
|
||||||
|
case GSK_FILL_RULE_EVEN_ODD:
|
||||||
|
return winding & 1;
|
||||||
|
case GSK_FILL_RULE_WINDING:
|
||||||
|
return winding != 0;
|
||||||
|
default:
|
||||||
|
g_assert_not_reached ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gsk_path_builder_add_segment_chunk (GskPathBuilder *self,
|
||||||
|
GskPathMeasure *measure,
|
||||||
|
gboolean emit_move_to,
|
||||||
|
float start,
|
||||||
|
float end)
|
||||||
|
{
|
||||||
|
g_assert (start < end);
|
||||||
|
|
||||||
|
for (gsize i = measure->first; i < measure->last; i++)
|
||||||
|
{
|
||||||
|
if (measure->measures[i].length < start)
|
||||||
|
{
|
||||||
|
start -= measure->measures[i].length;
|
||||||
|
end -= measure->measures[i].length;
|
||||||
|
}
|
||||||
|
else if (start > 0 || end < measure->measures[i].length)
|
||||||
|
{
|
||||||
|
float len = MIN (end, measure->measures[i].length);
|
||||||
|
gsk_contour_add_segment (gsk_path_get_contour (measure->path, i),
|
||||||
|
self,
|
||||||
|
measure->measures[i].contour_data,
|
||||||
|
emit_move_to,
|
||||||
|
start,
|
||||||
|
len);
|
||||||
|
end -= len;
|
||||||
|
start = 0;
|
||||||
|
if (end <= 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
end -= measure->measures[i].length;
|
||||||
|
gsk_path_builder_add_contour (self, gsk_contour_dup (gsk_path_get_contour (measure->path, i)));
|
||||||
|
}
|
||||||
|
emit_move_to = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gsk_path_builder_add_segment:
|
||||||
|
* @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);
|
||||||
|
}
|
||||||
|
}
|
||||||
92
gsk/gskpathmeasure.h
Normal file
92
gsk/gskpathmeasure.h
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2020 Benjamin Otte
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* Authors: Benjamin Otte <otte@gnome.org>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __GSK_PATH_MEASURE_H__
|
||||||
|
#define __GSK_PATH_MEASURE_H__
|
||||||
|
|
||||||
|
#if !defined (__GSK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||||
|
#error "Only <gsk/gsk.h> can be included directly."
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#include <gsk/gskpath.h>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
#define GSK_TYPE_PATH_MEASURE (gsk_path_measure_get_type ())
|
||||||
|
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
GType gsk_path_measure_get_type (void) G_GNUC_CONST;
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
GskPathMeasure * gsk_path_measure_new (GskPath *path);
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
GskPathMeasure * gsk_path_measure_new_with_tolerance (GskPath *path,
|
||||||
|
float tolerance);
|
||||||
|
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
GskPathMeasure * gsk_path_measure_ref (GskPathMeasure *self);
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
void gsk_path_measure_unref (GskPathMeasure *self);
|
||||||
|
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
GskPath * gsk_path_measure_get_path (GskPathMeasure *self) G_GNUC_PURE;
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
float gsk_path_measure_get_tolerance (GskPathMeasure *self) G_GNUC_PURE;
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
gsize gsk_path_measure_get_n_contours (GskPathMeasure *self) G_GNUC_PURE;
|
||||||
|
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
void gsk_path_measure_restrict_to_contour (GskPathMeasure *self,
|
||||||
|
gsize contour);
|
||||||
|
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
float gsk_path_measure_get_length (GskPathMeasure *self);
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
gboolean gsk_path_measure_is_closed (GskPathMeasure *self);
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
void gsk_path_measure_get_point (GskPathMeasure *self,
|
||||||
|
float distance,
|
||||||
|
graphene_point_t *pos,
|
||||||
|
graphene_vec2_t *tangent);
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
float gsk_path_measure_get_curvature (GskPathMeasure *self,
|
||||||
|
float distance,
|
||||||
|
graphene_point_t *center);
|
||||||
|
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
float gsk_path_measure_get_closest_point (GskPathMeasure *self,
|
||||||
|
const graphene_point_t *point,
|
||||||
|
graphene_point_t *out_pos);
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
gboolean gsk_path_measure_get_closest_point_full (GskPathMeasure *self,
|
||||||
|
const graphene_point_t *point,
|
||||||
|
float threshold,
|
||||||
|
float *out_distance,
|
||||||
|
graphene_point_t *out_pos,
|
||||||
|
float *out_offset,
|
||||||
|
graphene_vec2_t *out_tangent);
|
||||||
|
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
gboolean gsk_path_measure_in_fill (GskPathMeasure *self,
|
||||||
|
graphene_point_t *point,
|
||||||
|
GskFillRule fill_rule);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* __GSK_PATH_MEASURE_H__ */
|
||||||
179
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__ */
|
||||||
|
|
||||||
2208
gsk/gskpathstroke.c
Normal file
2208
gsk/gskpathstroke.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -158,6 +158,8 @@ GskRenderNode * gsk_render_node_deserialize (GBytes
|
|||||||
#define GSK_TYPE_REPEAT_NODE (gsk_repeat_node_get_type())
|
#define GSK_TYPE_REPEAT_NODE (gsk_repeat_node_get_type())
|
||||||
#define GSK_TYPE_CLIP_NODE (gsk_clip_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_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_SHADOW_NODE (gsk_shadow_node_get_type())
|
||||||
#define GSK_TYPE_BLEND_NODE (gsk_blend_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())
|
#define GSK_TYPE_CROSS_FADE_NODE (gsk_cross_fade_node_get_type())
|
||||||
@@ -184,6 +186,8 @@ typedef struct _GskColorMatrixNode GskColorMatrixNode;
|
|||||||
typedef struct _GskRepeatNode GskRepeatNode;
|
typedef struct _GskRepeatNode GskRepeatNode;
|
||||||
typedef struct _GskClipNode GskClipNode;
|
typedef struct _GskClipNode GskClipNode;
|
||||||
typedef struct _GskRoundedClipNode GskRoundedClipNode;
|
typedef struct _GskRoundedClipNode GskRoundedClipNode;
|
||||||
|
typedef struct _GskFillNode GskFillNode;
|
||||||
|
typedef struct _GskStrokeNode GskStrokeNode;
|
||||||
typedef struct _GskShadowNode GskShadowNode;
|
typedef struct _GskShadowNode GskShadowNode;
|
||||||
typedef struct _GskBlendNode GskBlendNode;
|
typedef struct _GskBlendNode GskBlendNode;
|
||||||
typedef struct _GskCrossFadeNode GskCrossFadeNode;
|
typedef struct _GskCrossFadeNode GskCrossFadeNode;
|
||||||
@@ -443,6 +447,32 @@ GskRenderNode * gsk_rounded_clip_node_get_child (GskRenderNode
|
|||||||
GDK_AVAILABLE_IN_ALL
|
GDK_AVAILABLE_IN_ALL
|
||||||
const GskRoundedRect * gsk_rounded_clip_node_get_clip (GskRenderNode *node);
|
const GskRoundedRect * gsk_rounded_clip_node_get_clip (GskRenderNode *node);
|
||||||
|
|
||||||
|
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
|
GDK_AVAILABLE_IN_ALL
|
||||||
GType gsk_shadow_node_get_type (void) G_GNUC_CONST;
|
GType gsk_shadow_node_get_type (void) G_GNUC_CONST;
|
||||||
GDK_AVAILABLE_IN_ALL
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
|||||||
@@ -23,8 +23,10 @@
|
|||||||
#include "gskcairoblurprivate.h"
|
#include "gskcairoblurprivate.h"
|
||||||
#include "gskdebugprivate.h"
|
#include "gskdebugprivate.h"
|
||||||
#include "gskdiffprivate.h"
|
#include "gskdiffprivate.h"
|
||||||
|
#include "gskpath.h"
|
||||||
#include "gskrendererprivate.h"
|
#include "gskrendererprivate.h"
|
||||||
#include "gskroundedrectprivate.h"
|
#include "gskroundedrectprivate.h"
|
||||||
|
#include "gskstrokeprivate.h"
|
||||||
#include "gsktransformprivate.h"
|
#include "gsktransformprivate.h"
|
||||||
|
|
||||||
#include "gdk/gdktextureprivate.h"
|
#include "gdk/gdktextureprivate.h"
|
||||||
@@ -3682,6 +3684,340 @@ gsk_rounded_clip_node_get_clip (GskRenderNode *node)
|
|||||||
return &self->clip;
|
return &self->clip;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*** GSK_FILL_NODE ***/
|
||||||
|
|
||||||
|
struct _GskFillNode
|
||||||
|
{
|
||||||
|
GskRenderNode render_node;
|
||||||
|
|
||||||
|
GskRenderNode *child;
|
||||||
|
GskPath *path;
|
||||||
|
GskFillRule fill_rule;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
gsk_fill_node_finalize (GskRenderNode *node)
|
||||||
|
{
|
||||||
|
GskFillNode *self = (GskFillNode *) node;
|
||||||
|
GskRenderNodeClass *parent_class = g_type_class_peek (g_type_parent (GSK_TYPE_FILL_NODE));
|
||||||
|
|
||||||
|
gsk_render_node_unref (self->child);
|
||||||
|
gsk_path_unref (self->path);
|
||||||
|
|
||||||
|
parent_class->finalize (node);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gsk_fill_node_draw (GskRenderNode *node,
|
||||||
|
cairo_t *cr)
|
||||||
|
{
|
||||||
|
GskFillNode *self = (GskFillNode *) node;
|
||||||
|
|
||||||
|
cairo_save (cr);
|
||||||
|
|
||||||
|
switch (self->fill_rule)
|
||||||
|
{
|
||||||
|
case GSK_FILL_RULE_WINDING:
|
||||||
|
cairo_set_fill_rule (cr, CAIRO_FILL_RULE_WINDING);
|
||||||
|
break;
|
||||||
|
case GSK_FILL_RULE_EVEN_ODD:
|
||||||
|
cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
g_assert_not_reached ();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
gsk_path_to_cairo (self->path, cr);
|
||||||
|
cairo_clip (cr);
|
||||||
|
|
||||||
|
gsk_render_node_draw (self->child, cr);
|
||||||
|
|
||||||
|
cairo_restore (cr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gsk_fill_node_diff (GskRenderNode *node1,
|
||||||
|
GskRenderNode *node2,
|
||||||
|
cairo_region_t *region)
|
||||||
|
{
|
||||||
|
GskFillNode *self1 = (GskFillNode *) node1;
|
||||||
|
GskFillNode *self2 = (GskFillNode *) node2;
|
||||||
|
|
||||||
|
if (self1->path == self2->path)
|
||||||
|
{
|
||||||
|
cairo_region_t *sub;
|
||||||
|
cairo_rectangle_int_t clip_rect;
|
||||||
|
graphene_rect_t rect;
|
||||||
|
|
||||||
|
sub = cairo_region_create();
|
||||||
|
gsk_render_node_diff (self1->child, self2->child, sub);
|
||||||
|
graphene_rect_union (&node1->bounds, &node2->bounds, &rect);
|
||||||
|
rectangle_init_from_graphene (&clip_rect, &rect);
|
||||||
|
cairo_region_intersect_rectangle (sub, &clip_rect);
|
||||||
|
cairo_region_union (region, sub);
|
||||||
|
cairo_region_destroy (sub);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gsk_render_node_diff_impossible (node1, node2, region);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gsk_fill_node_new:
|
||||||
|
* @child: The node to fill the area with
|
||||||
|
* @path: The path describing the area to fill
|
||||||
|
* @fill_rule: The fill rule to use
|
||||||
|
*
|
||||||
|
* Creates a #GskRenderNode that will fill the @child in the area
|
||||||
|
* given by @path and @fill_rule.
|
||||||
|
*
|
||||||
|
* Returns: (transfer none) (type GskFillNode): A new #GskRenderNode
|
||||||
|
*/
|
||||||
|
GskRenderNode *
|
||||||
|
gsk_fill_node_new (GskRenderNode *child,
|
||||||
|
GskPath *path,
|
||||||
|
GskFillRule fill_rule)
|
||||||
|
{
|
||||||
|
GskFillNode *self;
|
||||||
|
GskRenderNode *node;
|
||||||
|
graphene_rect_t path_bounds;
|
||||||
|
|
||||||
|
g_return_val_if_fail (GSK_IS_RENDER_NODE (child), NULL);
|
||||||
|
g_return_val_if_fail (path != NULL, NULL);
|
||||||
|
|
||||||
|
self = gsk_render_node_alloc (GSK_FILL_NODE);
|
||||||
|
node = (GskRenderNode *) self;
|
||||||
|
|
||||||
|
self->child = gsk_render_node_ref (child);
|
||||||
|
self->path = gsk_path_ref (path);
|
||||||
|
self->fill_rule = fill_rule;
|
||||||
|
|
||||||
|
if (gsk_path_get_bounds (path, &path_bounds))
|
||||||
|
graphene_rect_intersection (&path_bounds, &child->bounds, &node->bounds);
|
||||||
|
else
|
||||||
|
graphene_rect_init_from_rect (&node->bounds, graphene_rect_zero ());
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gsk_fill_node_get_child:
|
||||||
|
* @node: (type GskFillNode): a fill #GskRenderNode
|
||||||
|
*
|
||||||
|
* Gets the child node that is getting drawn by the given @node.
|
||||||
|
*
|
||||||
|
* Returns: (transfer none): The child that is getting drawn
|
||||||
|
**/
|
||||||
|
GskRenderNode *
|
||||||
|
gsk_fill_node_get_child (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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gsk_stroke_node_new:
|
||||||
|
* @child: The node to stroke the area with
|
||||||
|
* @path: (transfer none): The path describing the area to stroke
|
||||||
|
* @stroke: (transfer none): The stroke attributes to use
|
||||||
|
*
|
||||||
|
* Creates a #GskRenderNode that will stroke the @child along the given
|
||||||
|
* @path using the attributes defined in @stroke.
|
||||||
|
*
|
||||||
|
* Returns: (transfer none) (type GskStrokeNode): A new #GskRenderNode
|
||||||
|
*/
|
||||||
|
GskRenderNode *
|
||||||
|
gsk_stroke_node_new (GskRenderNode *child,
|
||||||
|
GskPath *path,
|
||||||
|
const GskStroke *stroke)
|
||||||
|
{
|
||||||
|
GskStrokeNode *self;
|
||||||
|
GskRenderNode *node;
|
||||||
|
|
||||||
|
g_return_val_if_fail (GSK_IS_RENDER_NODE (child), NULL);
|
||||||
|
g_return_val_if_fail (path != NULL, NULL);
|
||||||
|
g_return_val_if_fail (stroke != NULL, NULL);
|
||||||
|
|
||||||
|
self = gsk_render_node_alloc (GSK_STROKE_NODE);
|
||||||
|
node = (GskRenderNode *) self;
|
||||||
|
|
||||||
|
self->child = gsk_render_node_ref (child);
|
||||||
|
self->path = gsk_path_ref (path);
|
||||||
|
gsk_stroke_init_copy (&self->stroke, stroke);
|
||||||
|
|
||||||
|
/* XXX: Figure out a way to compute bounds from the path */
|
||||||
|
graphene_rect_init_from_rect (&node->bounds, &child->bounds);
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gsk_stroke_node_get_child:
|
||||||
|
* @node: (type GskStrokeNode): a stroke #GskRenderNode
|
||||||
|
*
|
||||||
|
* Gets the child node that is getting drawn by the given @node.
|
||||||
|
*
|
||||||
|
* Returns: (transfer none): The child that is getting drawn
|
||||||
|
**/
|
||||||
|
GskRenderNode *
|
||||||
|
gsk_stroke_node_get_child (GskRenderNode *node)
|
||||||
|
{
|
||||||
|
GskStrokeNode *self = (GskStrokeNode *) node;
|
||||||
|
|
||||||
|
g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_STROKE_NODE), NULL);
|
||||||
|
|
||||||
|
return self->child;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gsk_stroke_node_get_path:
|
||||||
|
* @node: (type GskStrokeNode): a stroke #GskRenderNode
|
||||||
|
*
|
||||||
|
* Retrievs the path that will be stroked with the contents of
|
||||||
|
* the @node.
|
||||||
|
*
|
||||||
|
* Returns: (transfer none): a #GskPath
|
||||||
|
*/
|
||||||
|
GskPath *
|
||||||
|
gsk_stroke_node_get_path (GskRenderNode *node)
|
||||||
|
{
|
||||||
|
GskStrokeNode *self = (GskStrokeNode *) node;
|
||||||
|
|
||||||
|
g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_STROKE_NODE), NULL);
|
||||||
|
|
||||||
|
return self->path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gsk_stroke_node_get_stroke:
|
||||||
|
* @node: (type GskStrokeNode): a stroke #GskRenderNode
|
||||||
|
*
|
||||||
|
* Retrievs the stroke attributes used in this @node.
|
||||||
|
*
|
||||||
|
* Returns: a #GskStroke
|
||||||
|
*/
|
||||||
|
const GskStroke *
|
||||||
|
gsk_stroke_node_get_stroke (GskRenderNode *node)
|
||||||
|
{
|
||||||
|
GskStrokeNode *self = (GskStrokeNode *) node;
|
||||||
|
|
||||||
|
g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_STROKE_NODE), NULL);
|
||||||
|
|
||||||
|
return &self->stroke;
|
||||||
|
}
|
||||||
|
|
||||||
/*** GSK_SHADOW_NODE ***/
|
/*** GSK_SHADOW_NODE ***/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -5230,6 +5566,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_repeat_node, GSK_REPEAT_NODE)
|
||||||
GSK_DEFINE_RENDER_NODE_TYPE (gsk_clip_node, GSK_CLIP_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_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_shadow_node, GSK_SHADOW_NODE)
|
||||||
GSK_DEFINE_RENDER_NODE_TYPE (gsk_blend_node, GSK_BLEND_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)
|
GSK_DEFINE_RENDER_NODE_TYPE (gsk_cross_fade_node, GSK_CROSS_FADE_NODE)
|
||||||
@@ -5529,6 +5867,38 @@ gsk_render_node_init_types_once (void)
|
|||||||
gsk_render_node_types[GSK_ROUNDED_CLIP_NODE] = node_type;
|
gsk_render_node_types[GSK_ROUNDED_CLIP_NODE] = node_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const GskRenderNodeTypeInfo node_info =
|
||||||
|
{
|
||||||
|
GSK_FILL_NODE,
|
||||||
|
sizeof (GskFillNode),
|
||||||
|
NULL,
|
||||||
|
gsk_fill_node_finalize,
|
||||||
|
gsk_fill_node_draw,
|
||||||
|
NULL,
|
||||||
|
gsk_fill_node_diff,
|
||||||
|
};
|
||||||
|
|
||||||
|
GType node_type = gsk_render_node_type_register_static (I_("GskFillNode"), &node_info);
|
||||||
|
gsk_render_node_types[GSK_FILL_NODE] = node_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const GskRenderNodeTypeInfo node_info =
|
||||||
|
{
|
||||||
|
GSK_STROKE_NODE,
|
||||||
|
sizeof (GskStrokeNode),
|
||||||
|
NULL,
|
||||||
|
gsk_stroke_node_finalize,
|
||||||
|
gsk_stroke_node_draw,
|
||||||
|
NULL,
|
||||||
|
gsk_stroke_node_diff,
|
||||||
|
};
|
||||||
|
|
||||||
|
GType node_type = gsk_render_node_type_register_static (I_("GskStrokeNode"), &node_info);
|
||||||
|
gsk_render_node_types[GSK_STROKE_NODE] = node_type;
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const GskRenderNodeTypeInfo node_info =
|
const GskRenderNodeTypeInfo node_info =
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -23,9 +23,13 @@
|
|||||||
|
|
||||||
#include "gskrendernodeparserprivate.h"
|
#include "gskrendernodeparserprivate.h"
|
||||||
|
|
||||||
|
#include "gskpath.h"
|
||||||
|
#include "gskpathbuilder.h"
|
||||||
#include "gskroundedrectprivate.h"
|
#include "gskroundedrectprivate.h"
|
||||||
#include "gskrendernodeprivate.h"
|
#include "gskrendernodeprivate.h"
|
||||||
|
#include "gskstroke.h"
|
||||||
#include "gsktransformprivate.h"
|
#include "gsktransformprivate.h"
|
||||||
|
#include "gskenumtypes.h"
|
||||||
|
|
||||||
#include "gdk/gdkrgbaprivate.h"
|
#include "gdk/gdkrgbaprivate.h"
|
||||||
#include "gdk/gdktextureprivate.h"
|
#include "gdk/gdktextureprivate.h"
|
||||||
@@ -406,7 +410,10 @@ parse_string (GtkCssParser *parser,
|
|||||||
|
|
||||||
token = gtk_css_parser_get_token (parser);
|
token = gtk_css_parser_get_token (parser);
|
||||||
if (!gtk_css_token_is (token, GTK_CSS_TOKEN_STRING))
|
if (!gtk_css_token_is (token, GTK_CSS_TOKEN_STRING))
|
||||||
|
{
|
||||||
|
gtk_css_parser_error_syntax (parser, "Expected a string");
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
s = g_strdup (token->string.string);
|
s = g_strdup (token->string.string);
|
||||||
gtk_css_parser_consume_token (parser);
|
gtk_css_parser_consume_token (parser);
|
||||||
@@ -955,6 +962,26 @@ create_default_render_node (void)
|
|||||||
return gsk_color_node_new (&GDK_RGBA("FF00CC"), &GRAPHENE_RECT_INIT (0, 0, 50, 50));
|
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 *
|
static GskRenderNode *
|
||||||
parse_color_node (GtkCssParser *parser)
|
parse_color_node (GtkCssParser *parser)
|
||||||
{
|
{
|
||||||
@@ -1765,6 +1792,208 @@ parse_rounded_clip_node (GtkCssParser *parser)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
parse_path (GtkCssParser *parser,
|
||||||
|
gpointer out_path)
|
||||||
|
{
|
||||||
|
GskPath *path;
|
||||||
|
char *str = NULL;
|
||||||
|
|
||||||
|
if (!parse_string (parser, &str))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
path = gsk_path_parse (str);
|
||||||
|
g_free (str);
|
||||||
|
|
||||||
|
if (path == NULL)
|
||||||
|
{
|
||||||
|
gtk_css_parser_error_value (parser, "Invalid path");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
*((GskPath **) out_path) = path;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
clear_path (gpointer inout_path)
|
||||||
|
{
|
||||||
|
g_clear_pointer ((GskPath **) inout_path, gsk_path_unref);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
parse_dash (GtkCssParser *parser,
|
||||||
|
gpointer out_dash)
|
||||||
|
{
|
||||||
|
GArray *dash;
|
||||||
|
double d;
|
||||||
|
|
||||||
|
/* because CSS does this, too */
|
||||||
|
if (gtk_css_parser_try_ident (parser, "none"))
|
||||||
|
{
|
||||||
|
*((GArray **) out_dash) = NULL;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
dash = g_array_new (FALSE, FALSE, sizeof (float));
|
||||||
|
do {
|
||||||
|
if (!gtk_css_parser_consume_number (parser, &d))
|
||||||
|
{
|
||||||
|
g_array_free (dash, TRUE);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_array_append_vals (dash, (float[1]) { d }, 1);
|
||||||
|
} while (gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_SIGNLESS_NUMBER) ||
|
||||||
|
gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_SIGNLESS_INTEGER));
|
||||||
|
|
||||||
|
*((GArray **) out_dash) = dash;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
clear_dash (gpointer inout_array)
|
||||||
|
{
|
||||||
|
g_clear_pointer ((GArray **) inout_array, g_array_unref);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
parse_enum (GtkCssParser *parser,
|
||||||
|
GType type,
|
||||||
|
gpointer out_value)
|
||||||
|
{
|
||||||
|
GEnumClass *class;
|
||||||
|
GEnumValue *v;
|
||||||
|
const GtkCssToken *token;
|
||||||
|
|
||||||
|
token = gtk_css_parser_get_token (parser);
|
||||||
|
if (!gtk_css_token_is (token, GTK_CSS_TOKEN_IDENT))
|
||||||
|
{
|
||||||
|
gtk_css_parser_error_syntax (parser, "Expected a valid identifier");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
class = g_type_class_ref (type);
|
||||||
|
|
||||||
|
v = g_enum_get_value_by_nick (class, token->string.string);
|
||||||
|
if (v == NULL)
|
||||||
|
{
|
||||||
|
gtk_css_parser_error_value (parser, "\"%s\" is not a valid identifier here", token->string.string);
|
||||||
|
g_type_class_unref (class);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
*(int*)out_value = v->value;
|
||||||
|
|
||||||
|
g_type_class_unref (class);
|
||||||
|
|
||||||
|
gtk_css_parser_consume_token (parser);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
parse_fill_rule (GtkCssParser *parser,
|
||||||
|
gpointer out_rule)
|
||||||
|
{
|
||||||
|
return parse_enum (parser, GSK_TYPE_FILL_RULE, out_rule);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GskRenderNode *
|
||||||
|
parse_fill_node (GtkCssParser *parser)
|
||||||
|
{
|
||||||
|
GskRenderNode *child = NULL;
|
||||||
|
GskPath *path = NULL;
|
||||||
|
int rule = GSK_FILL_RULE_WINDING;
|
||||||
|
const Declaration declarations[] = {
|
||||||
|
{ "child", parse_node, clear_node, &child },
|
||||||
|
{ "path", parse_path, clear_path, &path },
|
||||||
|
{ "fill-rule", parse_fill_rule, NULL, &rule },
|
||||||
|
};
|
||||||
|
GskRenderNode *result;
|
||||||
|
|
||||||
|
parse_declarations (parser, declarations, G_N_ELEMENTS (declarations));
|
||||||
|
if (child == NULL)
|
||||||
|
child = create_default_render_node ();
|
||||||
|
if (path == NULL)
|
||||||
|
path = create_default_path ();
|
||||||
|
|
||||||
|
result = gsk_fill_node_new (child, path, rule);
|
||||||
|
|
||||||
|
gsk_path_unref (path);
|
||||||
|
|
||||||
|
gsk_render_node_unref (child);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
parse_line_cap (GtkCssParser *parser,
|
||||||
|
gpointer out)
|
||||||
|
{
|
||||||
|
return parse_enum (parser, GSK_TYPE_LINE_CAP, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
parse_line_join (GtkCssParser *parser,
|
||||||
|
gpointer out)
|
||||||
|
{
|
||||||
|
return parse_enum (parser, GSK_TYPE_LINE_JOIN, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GskRenderNode *
|
||||||
|
parse_stroke_node (GtkCssParser *parser)
|
||||||
|
{
|
||||||
|
GskRenderNode *child = NULL;
|
||||||
|
GskPath *path = NULL;
|
||||||
|
double line_width = 1.0;
|
||||||
|
int line_cap = GSK_LINE_CAP_BUTT;
|
||||||
|
int line_join = GSK_LINE_JOIN_MITER;
|
||||||
|
double miter_limit = 4.0;
|
||||||
|
GArray *dash = NULL;
|
||||||
|
double dash_offset = 0.0;
|
||||||
|
GskStroke *stroke;
|
||||||
|
|
||||||
|
const Declaration declarations[] = {
|
||||||
|
{ "child", parse_node, clear_node, &child },
|
||||||
|
{ "path", parse_path, clear_path, &path },
|
||||||
|
{ "line-width", parse_double, NULL, &line_width },
|
||||||
|
{ "line-cap", parse_line_cap, NULL, &line_cap },
|
||||||
|
{ "line-join", parse_line_join, NULL, &line_join },
|
||||||
|
{ "miter-limit", parse_double, NULL, &miter_limit },
|
||||||
|
{ "dash", parse_dash, clear_dash, &dash },
|
||||||
|
{ "dash-offset", parse_double, NULL, &dash_offset},
|
||||||
|
};
|
||||||
|
GskRenderNode *result;
|
||||||
|
|
||||||
|
parse_declarations (parser, declarations, G_N_ELEMENTS (declarations));
|
||||||
|
if (child == NULL)
|
||||||
|
child = create_default_render_node ();
|
||||||
|
if (path == NULL)
|
||||||
|
path = create_default_path ();
|
||||||
|
|
||||||
|
stroke = gsk_stroke_new (line_width);
|
||||||
|
gsk_stroke_set_line_cap (stroke, line_cap);
|
||||||
|
gsk_stroke_set_line_join (stroke, line_join);
|
||||||
|
gsk_stroke_set_miter_limit (stroke, miter_limit);
|
||||||
|
if (dash)
|
||||||
|
{
|
||||||
|
gsk_stroke_set_dash (stroke, (float *) dash->data, dash->len);
|
||||||
|
g_array_free (dash, TRUE);
|
||||||
|
}
|
||||||
|
gsk_stroke_set_dash_offset (stroke, dash_offset);
|
||||||
|
|
||||||
|
result = gsk_stroke_node_new (child, path, stroke);
|
||||||
|
|
||||||
|
gsk_path_unref (path);
|
||||||
|
gsk_stroke_free (stroke);
|
||||||
|
gsk_render_node_unref (child);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
static GskRenderNode *
|
static GskRenderNode *
|
||||||
parse_shadow_node (GtkCssParser *parser)
|
parse_shadow_node (GtkCssParser *parser)
|
||||||
{
|
{
|
||||||
@@ -1844,6 +2073,8 @@ parse_node (GtkCssParser *parser,
|
|||||||
{ "repeating-linear-gradient", parse_repeating_linear_gradient_node },
|
{ "repeating-linear-gradient", parse_repeating_linear_gradient_node },
|
||||||
{ "repeating-radial-gradient", parse_repeating_radial_gradient_node },
|
{ "repeating-radial-gradient", parse_repeating_radial_gradient_node },
|
||||||
{ "rounded-clip", parse_rounded_clip_node },
|
{ "rounded-clip", parse_rounded_clip_node },
|
||||||
|
{ "fill", parse_fill_node },
|
||||||
|
{ "stroke", parse_stroke_node },
|
||||||
{ "shadow", parse_shadow_node },
|
{ "shadow", parse_shadow_node },
|
||||||
{ "text", parse_text_node },
|
{ "text", parse_text_node },
|
||||||
{ "texture", parse_texture_node },
|
{ "texture", parse_texture_node },
|
||||||
@@ -2100,7 +2331,7 @@ append_float_param (Printer *p,
|
|||||||
float value,
|
float value,
|
||||||
float default_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)
|
if (value == default_value)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -2275,8 +2506,11 @@ append_escaping_newlines (GString *str,
|
|||||||
len = strcspn (string, "\n");
|
len = strcspn (string, "\n");
|
||||||
g_string_append_len (str, string, len);
|
g_string_append_len (str, string, len);
|
||||||
string += len;
|
string += len;
|
||||||
|
if (*string)
|
||||||
|
{
|
||||||
g_string_append (str, "\\\n");
|
g_string_append (str, "\\\n");
|
||||||
string++;
|
string++;
|
||||||
|
}
|
||||||
} while (*string);
|
} while (*string);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2388,6 +2622,83 @@ gsk_text_node_serialize_glyphs (GskRenderNode *node,
|
|||||||
pango_glyph_string_free (ascii);
|
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
|
static void
|
||||||
render_node_print (Printer *p,
|
render_node_print (Printer *p,
|
||||||
GskRenderNode *node)
|
GskRenderNode *node)
|
||||||
@@ -2536,6 +2847,42 @@ render_node_print (Printer *p,
|
|||||||
append_node_param (p, "child", gsk_rounded_clip_node_get_child (node));
|
append_node_param (p, "child", gsk_rounded_clip_node_get_child (node));
|
||||||
append_rounded_rect_param (p, "clip", gsk_rounded_clip_node_get_clip (node));
|
append_rounded_rect_param (p, "clip", gsk_rounded_clip_node_get_clip (node));
|
||||||
|
|
||||||
|
end_node (p);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GSK_FILL_NODE:
|
||||||
|
{
|
||||||
|
start_node (p, "fill");
|
||||||
|
|
||||||
|
append_node_param (p, "child", gsk_fill_node_get_child (node));
|
||||||
|
append_path_param (p, "path", gsk_fill_node_get_path (node));
|
||||||
|
append_enum_param (p, "fill-rule", GSK_TYPE_FILL_RULE, gsk_fill_node_get_fill_rule (node));
|
||||||
|
|
||||||
|
end_node (p);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GSK_STROKE_NODE:
|
||||||
|
{
|
||||||
|
const GskStroke *stroke;
|
||||||
|
const float *dash;
|
||||||
|
gsize n_dash;
|
||||||
|
|
||||||
|
start_node (p, "stroke");
|
||||||
|
|
||||||
|
append_node_param (p, "child", gsk_stroke_node_get_child (node));
|
||||||
|
append_path_param (p, "path", gsk_stroke_node_get_path (node));
|
||||||
|
|
||||||
|
stroke = gsk_stroke_node_get_stroke (node);
|
||||||
|
append_float_param (p, "line-width", gsk_stroke_get_line_width (stroke), 0.0f);
|
||||||
|
append_enum_param (p, "line-cap", GSK_TYPE_LINE_CAP, gsk_stroke_get_line_cap (stroke));
|
||||||
|
append_enum_param (p, "line-join", GSK_TYPE_LINE_JOIN, gsk_stroke_get_line_join (stroke));
|
||||||
|
append_float_param (p, "miter-limit", gsk_stroke_get_miter_limit (stroke), 4.0f);
|
||||||
|
dash = gsk_stroke_get_dash (stroke, &n_dash);
|
||||||
|
if (dash)
|
||||||
|
append_dash_param (p, "dash", dash, n_dash);
|
||||||
|
append_float_param (p, "dash-offset", gsk_stroke_get_dash_offset (stroke), 0.0f);
|
||||||
|
|
||||||
end_node (p);
|
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__ */
|
||||||
|
|
||||||
450
gsk/gskstroke.c
Normal file
450
gsk/gskstroke.c
Normal file
@@ -0,0 +1,450 @@
|
|||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gsk_stroke_to_cairo:
|
||||||
|
* @self: a #GskStroke
|
||||||
|
* @cr: the cairo context to configure
|
||||||
|
*
|
||||||
|
* A helper function that sets the stroke parameters
|
||||||
|
* of @cr from the values found in @self.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
gsk_stroke_to_cairo (const GskStroke *self,
|
||||||
|
cairo_t *cr)
|
||||||
|
{
|
||||||
|
cairo_set_line_width (cr, self->line_width);
|
||||||
|
|
||||||
|
/* gcc can optimize that to a direct case. This catches later additions to the enum */
|
||||||
|
switch (self->line_cap)
|
||||||
|
{
|
||||||
|
case GSK_LINE_CAP_BUTT:
|
||||||
|
cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
|
||||||
|
break;
|
||||||
|
case GSK_LINE_CAP_ROUND:
|
||||||
|
cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
|
||||||
|
break;
|
||||||
|
case GSK_LINE_CAP_SQUARE:
|
||||||
|
cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
g_assert_not_reached ();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* gcc can optimize that to a direct case. This catches later additions to the enum */
|
||||||
|
switch (self->line_join)
|
||||||
|
{
|
||||||
|
case GSK_LINE_JOIN_MITER:
|
||||||
|
case GSK_LINE_JOIN_MITER_CLIP:
|
||||||
|
case GSK_LINE_JOIN_ARCS:
|
||||||
|
cairo_set_line_join (cr, CAIRO_LINE_JOIN_MITER);
|
||||||
|
break;
|
||||||
|
case GSK_LINE_JOIN_ROUND:
|
||||||
|
cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
|
||||||
|
break;
|
||||||
|
case GSK_LINE_JOIN_BEVEL:
|
||||||
|
cairo_set_line_join (cr, CAIRO_LINE_JOIN_BEVEL);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
g_assert_not_reached ();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
cairo_set_miter_limit (cr, self->miter_limit);
|
||||||
|
|
||||||
|
if (self->dash_length)
|
||||||
|
{
|
||||||
|
gsize i;
|
||||||
|
double *dash = g_newa (double, self->n_dash);
|
||||||
|
|
||||||
|
for (i = 0; i < self->n_dash; i++)
|
||||||
|
{
|
||||||
|
dash[i] = self->dash[i];
|
||||||
|
}
|
||||||
|
cairo_set_dash (cr, dash, self->n_dash, self->dash_offset);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
cairo_set_dash (cr, NULL, 0, 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gsk_stroke_equal:
|
||||||
|
* @stroke1: the first #GskStroke
|
||||||
|
* @stroke2: the second #GskStroke
|
||||||
|
*
|
||||||
|
* Checks if 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) (allow none):
|
||||||
|
* the array of dashes
|
||||||
|
* @n_dash: number of elements in @dash
|
||||||
|
*
|
||||||
|
* Sets the dash pattern to use by this stroke. A dash pattern is specified by
|
||||||
|
* an array of alternating non-negative values. Each value provides the length
|
||||||
|
* of alternate "on" and "off" portions of the stroke.
|
||||||
|
*
|
||||||
|
* Each "on" segment will have caps applied as if the segment were a separate
|
||||||
|
* contour. In particular, it is valid to use an "on" length of 0 with
|
||||||
|
* @GSK_LINE_CAP_ROUND or @GSK_LINE_CAP_SQUARE to draw dots or squares along
|
||||||
|
* a path.
|
||||||
|
*
|
||||||
|
* If @n_dash is 0, if all elements in @dash are 0, or if there are negative
|
||||||
|
* values in @dash, then dashing is disabled.
|
||||||
|
*
|
||||||
|
* If @n_dash is 1, an alternating "on" and "off" pattern with the single
|
||||||
|
* dash length provided is assumed.
|
||||||
|
*
|
||||||
|
* If @n_dash is uneven, the dash array will be used with the first element
|
||||||
|
* in @dash defining an "on" or "off" in alternating passes through the array.
|
||||||
|
*
|
||||||
|
* You can specify a starting offset into the dash with
|
||||||
|
* @gsk_stroke_set_dash_offset().
|
||||||
|
**/
|
||||||
|
void
|
||||||
|
gsk_stroke_set_dash (GskStroke *self,
|
||||||
|
const float *dash,
|
||||||
|
gsize n_dash)
|
||||||
|
{
|
||||||
|
float dash_length;
|
||||||
|
gsize i;
|
||||||
|
|
||||||
|
g_return_if_fail (self != NULL);
|
||||||
|
g_return_if_fail (dash != NULL || n_dash == 0);
|
||||||
|
|
||||||
|
dash_length = 0;
|
||||||
|
for (i = 0; i < n_dash; i++)
|
||||||
|
{
|
||||||
|
if (!(dash[i] >= 0)) /* should catch NaN */
|
||||||
|
{
|
||||||
|
g_critical ("invalid value in dash array at position %zu", i);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dash_length += dash[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
self->dash_length = dash_length;
|
||||||
|
g_free (self->dash);
|
||||||
|
self->dash = g_memdup (dash, sizeof (gfloat) * n_dash);
|
||||||
|
self->n_dash = n_dash;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gsk_stroke_get_dash:
|
||||||
|
* @self: a #GskStroke
|
||||||
|
* @n_dash: (out) (caller allocates): number of elements
|
||||||
|
* in the array returned
|
||||||
|
*
|
||||||
|
* Gets the dash array in use or %NULL if dashing is disabled.
|
||||||
|
*
|
||||||
|
* Returns: (array length=n_dash) (transfer none) (allow none):
|
||||||
|
* The dash array or %NULL if the dash array is empty.
|
||||||
|
**/
|
||||||
|
const float *
|
||||||
|
gsk_stroke_get_dash (const GskStroke *self,
|
||||||
|
gsize *n_dash)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (self != NULL, NULL);
|
||||||
|
g_return_val_if_fail (n_dash != NULL, NULL);
|
||||||
|
|
||||||
|
*n_dash = self->n_dash;
|
||||||
|
|
||||||
|
return self->dash;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gsk_stroke_set_dash_offset:
|
||||||
|
* @self: a #GskStroke
|
||||||
|
* @offset: offset into the dash pattern
|
||||||
|
*
|
||||||
|
* Sets the offset into the dash pattern set via gsk_stroke_set_dash() where
|
||||||
|
* dashing should begin.
|
||||||
|
*
|
||||||
|
* This is an offset into the length of the path, not an index into the array values of
|
||||||
|
* the dash array.
|
||||||
|
**/
|
||||||
|
void
|
||||||
|
gsk_stroke_set_dash_offset (GskStroke *self,
|
||||||
|
float offset)
|
||||||
|
{
|
||||||
|
g_return_if_fail (self != NULL);
|
||||||
|
|
||||||
|
self->dash_offset = offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gsk_stroke_get_dash_offset:
|
||||||
|
* @self: a #GskStroke
|
||||||
|
*
|
||||||
|
* Returns the dash_offset of a #GskStroke.
|
||||||
|
*/
|
||||||
|
float
|
||||||
|
gsk_stroke_get_dash_offset (const GskStroke *self)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (self != NULL, 4.f);
|
||||||
|
|
||||||
|
return self->dash_offset;
|
||||||
|
}
|
||||||
91
gsk/gskstroke.h
Normal file
91
gsk/gskstroke.h
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2020 Benjamin Otte
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* Authors: Benjamin Otte <otte@gnome.org>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __GSK_STROKE_H__
|
||||||
|
#define __GSK_STROKE_H__
|
||||||
|
|
||||||
|
#if !defined (__GSK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||||
|
#error "Only <gsk/gsk.h> can be included directly."
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#include <gsk/gsktypes.h>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
#define GSK_TYPE_STROKE (gsk_stroke_get_type ())
|
||||||
|
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
GType gsk_stroke_get_type (void) G_GNUC_CONST;
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
GskStroke * gsk_stroke_new (float line_width);
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
GskStroke * gsk_stroke_copy (const GskStroke *other);
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
void gsk_stroke_free (GskStroke *self);
|
||||||
|
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
gboolean gsk_stroke_equal (gconstpointer stroke1,
|
||||||
|
gconstpointer stroke2);
|
||||||
|
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
void gsk_stroke_set_line_width (GskStroke *self,
|
||||||
|
float line_width);
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
float gsk_stroke_get_line_width (const GskStroke *self);
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
void gsk_stroke_set_line_cap (GskStroke *self,
|
||||||
|
GskLineCap line_cap);
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
GskLineCap gsk_stroke_get_line_cap (const GskStroke *self);
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
void gsk_stroke_set_line_join (GskStroke *self,
|
||||||
|
GskLineJoin line_join);
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
GskLineJoin gsk_stroke_get_line_join (const GskStroke *self);
|
||||||
|
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
void gsk_stroke_set_miter_limit (GskStroke *self,
|
||||||
|
float limit);
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
float gsk_stroke_get_miter_limit (const GskStroke *self);
|
||||||
|
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
void gsk_stroke_set_dash (GskStroke *self,
|
||||||
|
const float *dash,
|
||||||
|
gsize n_dash);
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
const float * gsk_stroke_get_dash (const GskStroke *self,
|
||||||
|
gsize *n_dash);
|
||||||
|
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
void gsk_stroke_set_dash_offset (GskStroke *self,
|
||||||
|
float offset);
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
float gsk_stroke_get_dash_offset (const GskStroke *self);
|
||||||
|
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
void gsk_stroke_to_cairo (const GskStroke *self,
|
||||||
|
cairo_t *cr);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* __GSK_STROKE_H__ */
|
||||||
60
gsk/gskstrokeprivate.h
Normal file
60
gsk/gskstrokeprivate.h
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2020 Benjamin Otte
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* Authors: Benjamin Otte <otte@gnome.org>
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef __GSK_STROKE_PRIVATE_H__
|
||||||
|
#define __GSK_STROKE_PRIVATE_H__
|
||||||
|
|
||||||
|
#include "gskstroke.h"
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
struct _GskStroke
|
||||||
|
{
|
||||||
|
float line_width;
|
||||||
|
GskLineCap line_cap;
|
||||||
|
GskLineJoin line_join;
|
||||||
|
float miter_limit;
|
||||||
|
|
||||||
|
float *dash;
|
||||||
|
gsize n_dash;
|
||||||
|
float dash_length; /* sum of all dashes in the array */
|
||||||
|
float dash_offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
gsk_stroke_init_copy (GskStroke *stroke,
|
||||||
|
const GskStroke *other)
|
||||||
|
{
|
||||||
|
*stroke = *other;
|
||||||
|
|
||||||
|
stroke->dash = g_memdup (other->dash, stroke->n_dash * sizeof (float));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
gsk_stroke_clear (GskStroke *stroke)
|
||||||
|
{
|
||||||
|
g_clear_pointer (&stroke->dash, g_free);
|
||||||
|
stroke->n_dash = 0; /* better safe than sorry */
|
||||||
|
}
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* __GSK_STROKE_PRIVATE_H__ */
|
||||||
|
|
||||||
@@ -26,7 +26,11 @@
|
|||||||
#include <gdk/gdk.h>
|
#include <gdk/gdk.h>
|
||||||
#include <gsk/gskenums.h>
|
#include <gsk/gskenums.h>
|
||||||
|
|
||||||
|
typedef struct _GskPath GskPath;
|
||||||
|
typedef struct _GskPathBuilder GskPathBuilder;
|
||||||
|
typedef struct _GskPathMeasure GskPathMeasure;
|
||||||
typedef struct _GskRenderer GskRenderer;
|
typedef struct _GskRenderer GskRenderer;
|
||||||
|
typedef struct _GskStroke GskStroke;
|
||||||
typedef struct _GskTransform GskTransform;
|
typedef struct _GskTransform GskTransform;
|
||||||
|
|
||||||
#endif /* __GSK_TYPES_H__ */
|
#endif /* __GSK_TYPES_H__ */
|
||||||
|
|||||||
@@ -21,23 +21,33 @@ gsk_private_gl_shaders = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
gsk_public_sources = files([
|
gsk_public_sources = files([
|
||||||
'gskdiff.c',
|
|
||||||
'gskcairorenderer.c',
|
'gskcairorenderer.c',
|
||||||
|
'gskdiff.c',
|
||||||
'gskglshader.c',
|
'gskglshader.c',
|
||||||
|
'gskpath.c',
|
||||||
|
'gskpathbuilder.c',
|
||||||
|
'gskpathdash.c',
|
||||||
|
'gskpathmeasure.c',
|
||||||
|
'gskpathstroke.c',
|
||||||
'gskrenderer.c',
|
'gskrenderer.c',
|
||||||
'gskrendernode.c',
|
'gskrendernode.c',
|
||||||
'gskrendernodeimpl.c',
|
'gskrendernodeimpl.c',
|
||||||
'gskrendernodeparser.c',
|
'gskrendernodeparser.c',
|
||||||
'gskroundedrect.c',
|
'gskroundedrect.c',
|
||||||
|
'gskstroke.c',
|
||||||
'gsktransform.c',
|
'gsktransform.c',
|
||||||
'gl/gskglrenderer.c',
|
'gl/gskglrenderer.c',
|
||||||
])
|
])
|
||||||
|
|
||||||
gsk_private_sources = files([
|
gsk_private_sources = files([
|
||||||
'gskcairoblur.c',
|
'gskcairoblur.c',
|
||||||
|
'gskcontour.c',
|
||||||
|
'gskcurve.c',
|
||||||
|
'gskcurveintersect.c',
|
||||||
'gskdebug.c',
|
'gskdebug.c',
|
||||||
'gskprivate.c',
|
'gskprivate.c',
|
||||||
'gskprofiler.c',
|
'gskprofiler.c',
|
||||||
|
'gskspline.c',
|
||||||
'gl/gskglshaderbuilder.c',
|
'gl/gskglshaderbuilder.c',
|
||||||
'gl/gskglprofiler.c',
|
'gl/gskglprofiler.c',
|
||||||
'gl/gskglglyphcache.c',
|
'gl/gskglglyphcache.c',
|
||||||
@@ -54,9 +64,13 @@ gsk_public_headers = files([
|
|||||||
'gskcairorenderer.h',
|
'gskcairorenderer.h',
|
||||||
'gskenums.h',
|
'gskenums.h',
|
||||||
'gskglshader.h',
|
'gskglshader.h',
|
||||||
|
'gskpath.h',
|
||||||
|
'gskpathbuilder.h',
|
||||||
|
'gskpathmeasure.h',
|
||||||
'gskrenderer.h',
|
'gskrenderer.h',
|
||||||
'gskrendernode.h',
|
'gskrendernode.h',
|
||||||
'gskroundedrect.h',
|
'gskroundedrect.h',
|
||||||
|
'gskstroke.h',
|
||||||
'gsktransform.h',
|
'gsktransform.h',
|
||||||
'gsktypes.h',
|
'gsktypes.h',
|
||||||
'gsk-autocleanup.h',
|
'gsk-autocleanup.h',
|
||||||
|
|||||||
@@ -260,6 +260,8 @@ gsk_vulkan_render_pass_add_node (GskVulkanRenderPass *self,
|
|||||||
case GSK_RADIAL_GRADIENT_NODE:
|
case GSK_RADIAL_GRADIENT_NODE:
|
||||||
case GSK_REPEATING_RADIAL_GRADIENT_NODE:
|
case GSK_REPEATING_RADIAL_GRADIENT_NODE:
|
||||||
case GSK_CONIC_GRADIENT_NODE:
|
case GSK_CONIC_GRADIENT_NODE:
|
||||||
|
case GSK_FILL_NODE:
|
||||||
|
case GSK_STROKE_NODE:
|
||||||
default:
|
default:
|
||||||
FALLBACK ("Unsupported node '%s'", g_type_name_from_instance ((GTypeInstance *) node));
|
FALLBACK ("Unsupported node '%s'", g_type_name_from_instance ((GTypeInstance *) node));
|
||||||
|
|
||||||
|
|||||||
@@ -639,4 +639,8 @@ gtk_media_file_extension_init (void)
|
|||||||
g_type_name (g_io_extension_get_type (ext)));
|
g_type_name (g_io_extension_get_type (ext)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* IF the env var is given, check at startup that things actually work */
|
||||||
|
if (g_getenv ("GTK_MEDIA"))
|
||||||
|
gtk_media_file_get_extension ();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1204,6 +1204,28 @@ get_border (GtkCssNode *node,
|
|||||||
border->left = _gtk_css_number_value_get (style->border->border_left_width, 100);
|
border->left = _gtk_css_number_value_get (style->border->border_left_width, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static GskPath *
|
||||||
|
gtk_popover_get_tail_path (GtkPopover *popover)
|
||||||
|
{
|
||||||
|
GskPathBuilder *builder;
|
||||||
|
int initial_x, initial_y;
|
||||||
|
int tip_x, tip_y;
|
||||||
|
int final_x, final_y;
|
||||||
|
|
||||||
|
builder = gsk_path_builder_new ();
|
||||||
|
|
||||||
|
gtk_popover_get_gap_coords (popover,
|
||||||
|
&initial_x, &initial_y,
|
||||||
|
&tip_x, &tip_y,
|
||||||
|
&final_x, &final_y);
|
||||||
|
|
||||||
|
gsk_path_builder_move_to (builder, initial_x, initial_y);
|
||||||
|
gsk_path_builder_line_to (builder, tip_x, tip_y);
|
||||||
|
gsk_path_builder_line_to (builder, final_x, final_y);
|
||||||
|
|
||||||
|
return gsk_path_builder_free_to_path (builder);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gtk_popover_apply_tail_path (GtkPopover *popover,
|
gtk_popover_apply_tail_path (GtkPopover *popover,
|
||||||
cairo_t *cr)
|
cairo_t *cr)
|
||||||
@@ -1397,22 +1419,14 @@ create_arrow_render_node (GtkPopover *popover)
|
|||||||
GtkWidget *widget = GTK_WIDGET (popover);
|
GtkWidget *widget = GTK_WIDGET (popover);
|
||||||
GtkStyleContext *context;
|
GtkStyleContext *context;
|
||||||
GtkBorder border;
|
GtkBorder border;
|
||||||
cairo_t *cr;
|
|
||||||
GtkSnapshot *snapshot;
|
GtkSnapshot *snapshot;
|
||||||
|
GskPath *path;
|
||||||
|
|
||||||
snapshot = gtk_snapshot_new ();
|
snapshot = gtk_snapshot_new ();
|
||||||
|
|
||||||
cr = gtk_snapshot_append_cairo (snapshot,
|
|
||||||
&GRAPHENE_RECT_INIT (
|
|
||||||
0, 0,
|
|
||||||
gtk_widget_get_width (widget),
|
|
||||||
gtk_widget_get_height (widget)
|
|
||||||
));
|
|
||||||
|
|
||||||
/* Clip to the arrow shape */
|
/* Clip to the arrow shape */
|
||||||
cairo_save (cr);
|
path = gtk_popover_get_tail_path (popover);
|
||||||
gtk_popover_apply_tail_path (popover, cr);
|
gtk_snapshot_push_fill (snapshot, path, GSK_FILL_RULE_WINDING);
|
||||||
cairo_clip (cr);
|
|
||||||
|
|
||||||
get_border (priv->arrow_node, &border);
|
get_border (priv->arrow_node, &border);
|
||||||
|
|
||||||
@@ -1420,7 +1434,7 @@ create_arrow_render_node (GtkPopover *popover)
|
|||||||
gtk_style_context_save_to_node (context, priv->arrow_node);
|
gtk_style_context_save_to_node (context, priv->arrow_node);
|
||||||
|
|
||||||
/* Render the arrow background */
|
/* Render the arrow background */
|
||||||
gtk_render_background (context, cr,
|
gtk_snapshot_render_background (snapshot, context,
|
||||||
0, 0,
|
0, 0,
|
||||||
gtk_widget_get_width (widget),
|
gtk_widget_get_width (widget),
|
||||||
gtk_widget_get_height (widget));
|
gtk_widget_get_height (widget));
|
||||||
@@ -1430,19 +1444,24 @@ create_arrow_render_node (GtkPopover *popover)
|
|||||||
{
|
{
|
||||||
GtkCssStyle *style;
|
GtkCssStyle *style;
|
||||||
const GdkRGBA *border_color;
|
const GdkRGBA *border_color;
|
||||||
|
GskStroke *stroke;
|
||||||
|
graphene_rect_t bounds;
|
||||||
|
|
||||||
style = gtk_css_node_get_style (priv->arrow_node);
|
style = gtk_css_node_get_style (priv->arrow_node);
|
||||||
border_color = gtk_css_color_value_get_rgba (style->border->border_left_color ? style->border->border_left_color : style->core->color);
|
border_color = gtk_css_color_value_get_rgba (style->border->border_left_color ? style->border->border_left_color : style->core->color);
|
||||||
|
|
||||||
gtk_popover_apply_tail_path (popover, cr);
|
stroke = gsk_stroke_new (border.bottom + 1);
|
||||||
gdk_cairo_set_source_rgba (cr, border_color);
|
gtk_snapshot_push_stroke (snapshot, path, stroke);
|
||||||
|
gsk_stroke_free (stroke);
|
||||||
|
|
||||||
cairo_set_line_width (cr, border.bottom + 1);
|
gsk_path_get_bounds (path, &bounds);
|
||||||
cairo_stroke (cr);
|
gtk_snapshot_append_color (snapshot, border_color, &bounds);
|
||||||
|
|
||||||
|
gtk_snapshot_pop (snapshot);
|
||||||
}
|
}
|
||||||
|
|
||||||
cairo_restore (cr);
|
gtk_snapshot_pop (snapshot);
|
||||||
cairo_destroy (cr);
|
gsk_path_unref (path);
|
||||||
|
|
||||||
gtk_style_context_restore (context);
|
gtk_style_context_restore (context);
|
||||||
|
|
||||||
|
|||||||
@@ -357,16 +357,16 @@ snapshot_frame_fill (GtkSnapshot *snapshot,
|
|||||||
gtk_snapshot_append_border (snapshot, outline, border_width, colors);
|
gtk_snapshot_append_border (snapshot, outline, border_width, colors);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static GskStroke *
|
||||||
set_stroke_style (cairo_t *cr,
|
create_stroke_style (double line_width,
|
||||||
double line_width,
|
|
||||||
GtkBorderStyle style,
|
GtkBorderStyle style,
|
||||||
double length)
|
double length)
|
||||||
{
|
{
|
||||||
double segments[2];
|
GskStroke *stroke;
|
||||||
|
float segments[2];
|
||||||
double n;
|
double n;
|
||||||
|
|
||||||
cairo_set_line_width (cr, line_width);
|
stroke = gsk_stroke_new (line_width);
|
||||||
|
|
||||||
if (style == GTK_BORDER_STYLE_DOTTED)
|
if (style == GTK_BORDER_STYLE_DOTTED)
|
||||||
{
|
{
|
||||||
@@ -374,12 +374,12 @@ set_stroke_style (cairo_t *cr,
|
|||||||
|
|
||||||
segments[0] = 0;
|
segments[0] = 0;
|
||||||
segments[1] = n ? length / n : 2;
|
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);
|
gsk_stroke_set_line_cap (stroke, GSK_LINE_CAP_ROUND);
|
||||||
cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
|
gsk_stroke_set_line_join (stroke, GSK_LINE_JOIN_ROUND);
|
||||||
}
|
}
|
||||||
else
|
else if (style == GTK_BORDER_STYLE_DASHED)
|
||||||
{
|
{
|
||||||
n = length / line_width;
|
n = length / line_width;
|
||||||
/* Optimize the common case of an integer-sized rectangle
|
/* 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[0] = n ? (1. / 3) * length / n : 1;
|
||||||
segments[1] = 2 * segments[0];
|
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);
|
gsk_stroke_set_line_cap (stroke, GSK_LINE_CAP_SQUARE);
|
||||||
cairo_set_line_join (cr, CAIRO_LINE_JOIN_MITER);
|
gsk_stroke_set_line_join (stroke, GSK_LINE_JOIN_MITER);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_assert_not_reached ();
|
||||||
|
}
|
||||||
|
|
||||||
|
return stroke;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
render_frame_stroke (cairo_t *cr,
|
snapshot_frame_stroke (GtkSnapshot *snapshot,
|
||||||
const GskRoundedRect *border_box,
|
const GskRoundedRect *border_box,
|
||||||
const double border_width[4],
|
const float border_width[4],
|
||||||
GdkRGBA colors[4],
|
GdkRGBA colors[4],
|
||||||
guint hidden_side,
|
guint hidden_side,
|
||||||
GtkBorderStyle stroke_style)
|
GtkBorderStyle stroke_style)
|
||||||
{
|
{
|
||||||
gboolean different_colors, different_borders;
|
|
||||||
GskRoundedRect stroke_box;
|
GskRoundedRect stroke_box;
|
||||||
|
GskPathBuilder *builder;
|
||||||
|
GskPath *path;
|
||||||
|
GskStroke *stroke;
|
||||||
guint i;
|
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;
|
stroke_box = *border_box;
|
||||||
gsk_rounded_rect_shrink (&stroke_box,
|
gsk_rounded_rect_shrink (&stroke_box,
|
||||||
border_width[GTK_CSS_TOP] / 2.0,
|
border_width[GTK_CSS_TOP] / 2.0,
|
||||||
@@ -430,7 +431,10 @@ render_frame_stroke (cairo_t *cr,
|
|||||||
border_width[GTK_CSS_BOTTOM] / 2.0,
|
border_width[GTK_CSS_BOTTOM] / 2.0,
|
||||||
border_width[GTK_CSS_LEFT] / 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;
|
double length = 0;
|
||||||
|
|
||||||
@@ -441,21 +445,22 @@ render_frame_stroke (cairo_t *cr,
|
|||||||
length += _gtk_rounded_box_guess_length (&stroke_box, i);
|
length += _gtk_rounded_box_guess_length (&stroke_box, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
gsk_rounded_rect_path (&stroke_box, cr);
|
builder = gsk_path_builder_new ();
|
||||||
gdk_cairo_set_source_rgba (cr, &colors[0]);
|
gsk_path_builder_add_rounded_rect (builder, &stroke_box);
|
||||||
set_stroke_style (cr, border_width[0], stroke_style, length);
|
path = gsk_path_builder_free_to_path (builder);
|
||||||
cairo_stroke (cr);
|
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
|
else
|
||||||
{
|
{
|
||||||
GskRoundedRect padding_box;
|
const float weight = sqrtf(2)/2.0;
|
||||||
|
|
||||||
padding_box = *border_box;
|
|
||||||
gsk_rounded_rect_shrink (&padding_box,
|
|
||||||
border_width[GTK_CSS_TOP],
|
|
||||||
border_width[GTK_CSS_RIGHT],
|
|
||||||
border_width[GTK_CSS_BOTTOM],
|
|
||||||
border_width[GTK_CSS_LEFT]);
|
|
||||||
|
|
||||||
for (i = 0; i < 4; i++)
|
for (i = 0; i < 4; i++)
|
||||||
{
|
{
|
||||||
@@ -465,49 +470,111 @@ render_frame_stroke (cairo_t *cr,
|
|||||||
if (border_width[i] == 0)
|
if (border_width[i] == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
cairo_save (cr);
|
builder = gsk_path_builder_new ();
|
||||||
|
|
||||||
if (i == 0)
|
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)
|
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)
|
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)
|
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],
|
||||||
gdk_cairo_set_source_rgba (cr, &colors[i]);
|
|
||||||
set_stroke_style (cr,
|
|
||||||
border_width[i],
|
|
||||||
stroke_style,
|
stroke_style,
|
||||||
_gtk_rounded_box_guess_length (&stroke_box, i));
|
_gtk_rounded_box_guess_length (&stroke_box, i));
|
||||||
cairo_stroke (cr);
|
gtk_snapshot_push_stroke (snapshot, path, stroke);
|
||||||
|
gsk_stroke_free (stroke);
|
||||||
|
gsk_path_unref (path);
|
||||||
|
|
||||||
cairo_restore (cr);
|
gtk_snapshot_append_border (snapshot, border_box, border_width, colors);
|
||||||
|
|
||||||
|
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
|
static void
|
||||||
color_shade (const GdkRGBA *color,
|
color_shade (const GdkRGBA *color,
|
||||||
double factor,
|
double factor,
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
#include "gsktransformprivate.h"
|
#include "gsktransformprivate.h"
|
||||||
|
|
||||||
#include "gsk/gskrendernodeprivate.h"
|
#include "gsk/gskrendernodeprivate.h"
|
||||||
|
#include "gsk/gskstrokeprivate.h"
|
||||||
|
|
||||||
#include "gtk/gskpango.h"
|
#include "gtk/gskpango.h"
|
||||||
|
|
||||||
@@ -108,6 +109,14 @@ struct _GtkSnapshotState {
|
|||||||
struct {
|
struct {
|
||||||
GskRoundedRect bounds;
|
GskRoundedRect bounds;
|
||||||
} rounded_clip;
|
} rounded_clip;
|
||||||
|
struct {
|
||||||
|
GskPath *path;
|
||||||
|
GskFillRule fill_rule;
|
||||||
|
} fill;
|
||||||
|
struct {
|
||||||
|
GskPath *path;
|
||||||
|
GskStroke stroke;
|
||||||
|
} stroke;
|
||||||
struct {
|
struct {
|
||||||
gsize n_shadows;
|
gsize n_shadows;
|
||||||
GskShadow *shadows;
|
GskShadow *shadows;
|
||||||
@@ -1102,6 +1111,135 @@ gtk_snapshot_push_rounded_clip (GtkSnapshot *snapshot,
|
|||||||
gtk_rounded_rect_scale_affine (&state->data.rounded_clip.bounds, bounds, scale_x, scale_y, dx, dy);
|
gtk_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 *
|
static GskRenderNode *
|
||||||
gtk_snapshot_collect_shadow (GtkSnapshot *snapshot,
|
gtk_snapshot_collect_shadow (GtkSnapshot *snapshot,
|
||||||
GtkSnapshotState *state,
|
GtkSnapshotState *state,
|
||||||
|
|||||||
@@ -89,6 +89,14 @@ GDK_AVAILABLE_IN_ALL
|
|||||||
void gtk_snapshot_push_rounded_clip (GtkSnapshot *snapshot,
|
void gtk_snapshot_push_rounded_clip (GtkSnapshot *snapshot,
|
||||||
const GskRoundedRect *bounds);
|
const GskRoundedRect *bounds);
|
||||||
GDK_AVAILABLE_IN_ALL
|
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,
|
void gtk_snapshot_push_shadow (GtkSnapshot *snapshot,
|
||||||
const GskShadow *shadow,
|
const GskShadow *shadow,
|
||||||
gsize n_shadows);
|
gsize n_shadows);
|
||||||
|
|||||||
@@ -161,6 +161,12 @@ create_list_model_for_render_node (GskRenderNode *node)
|
|||||||
case GSK_ROUNDED_CLIP_NODE:
|
case GSK_ROUNDED_CLIP_NODE:
|
||||||
return create_render_node_list_model ((GskRenderNode *[1]) { gsk_rounded_clip_node_get_child (node) }, 1);
|
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:
|
case GSK_SHADOW_NODE:
|
||||||
return create_render_node_list_model ((GskRenderNode *[1]) { gsk_shadow_node_get_child (node) }, 1);
|
return create_render_node_list_model ((GskRenderNode *[1]) { gsk_shadow_node_get_child (node) }, 1);
|
||||||
|
|
||||||
@@ -282,6 +288,10 @@ node_type_name (GskRenderNodeType type)
|
|||||||
return "Clip";
|
return "Clip";
|
||||||
case GSK_ROUNDED_CLIP_NODE:
|
case GSK_ROUNDED_CLIP_NODE:
|
||||||
return "Rounded Clip";
|
return "Rounded Clip";
|
||||||
|
case GSK_FILL_NODE:
|
||||||
|
return "Fill";
|
||||||
|
case GSK_STROKE_NODE:
|
||||||
|
return "Stroke";
|
||||||
case GSK_SHADOW_NODE:
|
case GSK_SHADOW_NODE:
|
||||||
return "Shadow";
|
return "Shadow";
|
||||||
case GSK_BLEND_NODE:
|
case GSK_BLEND_NODE:
|
||||||
@@ -321,6 +331,8 @@ node_name (GskRenderNode *node)
|
|||||||
case GSK_REPEAT_NODE:
|
case GSK_REPEAT_NODE:
|
||||||
case GSK_CLIP_NODE:
|
case GSK_CLIP_NODE:
|
||||||
case GSK_ROUNDED_CLIP_NODE:
|
case GSK_ROUNDED_CLIP_NODE:
|
||||||
|
case GSK_FILL_NODE:
|
||||||
|
case GSK_STROKE_NODE:
|
||||||
case GSK_SHADOW_NODE:
|
case GSK_SHADOW_NODE:
|
||||||
case GSK_BLEND_NODE:
|
case GSK_BLEND_NODE:
|
||||||
case GSK_CROSS_FADE_NODE:
|
case GSK_CROSS_FADE_NODE:
|
||||||
@@ -575,6 +587,20 @@ add_float_row (GtkListStore *store,
|
|||||||
g_free (text);
|
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
|
static void
|
||||||
populate_render_node_properties (GtkListStore *store,
|
populate_render_node_properties (GtkListStore *store,
|
||||||
GskRenderNode *node)
|
GskRenderNode *node)
|
||||||
@@ -837,9 +863,7 @@ populate_render_node_properties (GtkListStore *store,
|
|||||||
case GSK_BLEND_NODE:
|
case GSK_BLEND_NODE:
|
||||||
{
|
{
|
||||||
GskBlendMode mode = gsk_blend_node_get_blend_mode (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", enum_to_nick (GSK_TYPE_BLEND_MODE, mode));
|
||||||
add_text_row (store, "Blendmode", tmp);
|
|
||||||
g_free (tmp);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -1072,6 +1096,39 @@ populate_render_node_properties (GtkListStore *store,
|
|||||||
}
|
}
|
||||||
break;
|
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:
|
case GSK_CONTAINER_NODE:
|
||||||
tmp = g_strdup_printf ("%d", gsk_container_node_get_n_children (node));
|
tmp = g_strdup_printf ("%d", gsk_container_node_get_n_children (node));
|
||||||
add_text_row (store, "Children", tmp);
|
add_text_row (store, "Children", tmp);
|
||||||
|
|||||||
@@ -1102,14 +1102,24 @@ darwin_versions = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
# Library
|
# Library
|
||||||
|
libgtk_static = static_library('gtk',
|
||||||
|
sources: [typefuncs, gtk_sources, gtkmarshal_h, gtkprivatetypebuiltins_h],
|
||||||
|
dependencies: gtk_deps,
|
||||||
|
include_directories: [confinc, gdkinc, gskinc, gtkinc],
|
||||||
|
c_args: gtk_cargs + common_cflags,
|
||||||
|
link_with: [libgtk_css, libgdk, libgsk, libottie],
|
||||||
|
)
|
||||||
|
|
||||||
|
libgtk_static_dep = declare_dependency(include_directories: [ confinc, ],
|
||||||
|
dependencies: gtk_deps)
|
||||||
|
|
||||||
libgtk = library('gtk-4',
|
libgtk = library('gtk-4',
|
||||||
soversion: gtk_soversion,
|
soversion: gtk_soversion,
|
||||||
version: gtk_library_version,
|
version: gtk_library_version,
|
||||||
sources: [typefuncs, gtk_sources, gtkmarshal_h, gtkprivatetypebuiltins_h],
|
|
||||||
c_args: gtk_cargs + common_cflags,
|
c_args: gtk_cargs + common_cflags,
|
||||||
include_directories: [confinc, gdkinc, gskinc, gtkinc],
|
include_directories: [confinc, gdkinc, gskinc, gtkinc],
|
||||||
dependencies: gtk_deps + [libgtk_css_dep, libgdk_dep, libgsk_dep],
|
dependencies: [libgtk_static_dep, libgtk_css_dep, libgdk_dep, libgsk_dep, libottie_dep],
|
||||||
link_whole: [libgtk_css, libgdk, libgsk, ],
|
link_whole: [libgtk_static, libgtk_css, libgdk, libgsk, libottie],
|
||||||
link_args: common_ldflags,
|
link_args: common_ldflags,
|
||||||
darwin_versions: darwin_versions,
|
darwin_versions: darwin_versions,
|
||||||
install: true,
|
install: true,
|
||||||
|
|||||||
@@ -679,7 +679,10 @@ endif
|
|||||||
subdir('gtk/css')
|
subdir('gtk/css')
|
||||||
subdir('gdk')
|
subdir('gdk')
|
||||||
subdir('gsk')
|
subdir('gsk')
|
||||||
|
subdir('ottie')
|
||||||
subdir('gtk')
|
subdir('gtk')
|
||||||
|
subdir('ottie/tools')
|
||||||
|
subdir('ottie/editor')
|
||||||
subdir('modules')
|
subdir('modules')
|
||||||
if get_option('demos')
|
if get_option('demos')
|
||||||
subdir('demos')
|
subdir('demos')
|
||||||
|
|||||||
28
ottie/editor/main.c
Normal file
28
ottie/editor/main.c
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* 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 <ottie-editor-application.h>
|
||||||
|
|
||||||
|
int
|
||||||
|
main (int argc, char *argv[])
|
||||||
|
{
|
||||||
|
return g_application_run (G_APPLICATION (ottie_editor_application_new ()), argc, argv);
|
||||||
|
}
|
||||||
31
ottie/editor/meson.build
Normal file
31
ottie/editor/meson.build
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
ottie_editor_sources = [
|
||||||
|
'main.c',
|
||||||
|
'ottie-editor-application.c',
|
||||||
|
'ottie-editor-window.c',
|
||||||
|
]
|
||||||
|
|
||||||
|
ottie_editor_resources = gnome.compile_resources('ottie_editor_resources',
|
||||||
|
'ottie-editor.gresource.xml',
|
||||||
|
source_dir: '.',
|
||||||
|
)
|
||||||
|
|
||||||
|
executable('ottie-editor',
|
||||||
|
sources: [ottie_editor_sources, ottie_editor_resources],
|
||||||
|
dependencies: [libgtk_css_dep, libgdk_dep, libgsk_dep, libgtk_static_dep, libottie_dep],
|
||||||
|
include_directories: confinc,
|
||||||
|
c_args: [
|
||||||
|
'-DGTK_COMPILATION',
|
||||||
|
'-DG_LOG_DOMAIN="OttieEditor"',
|
||||||
|
] + common_cflags,
|
||||||
|
gui_app: true,
|
||||||
|
link_with: [libgtk_static, libgtk_css, libgdk, libgsk, libottie],
|
||||||
|
link_args: common_ldflags,
|
||||||
|
install: false,
|
||||||
|
)
|
||||||
|
|
||||||
|
# icons, don't install them until we decide to install ottie-editor
|
||||||
|
#icontheme_dir = join_paths(gtk_datadir, 'icons/hicolor')
|
||||||
|
|
||||||
|
#foreach size: ['scalable', 'symbolic']
|
||||||
|
# install_subdir('data/' + size, install_dir: icontheme_dir)
|
||||||
|
#endforeach
|
||||||
215
ottie/editor/ottie-editor-application.c
Normal file
215
ottie/editor/ottie-editor-application.c
Normal file
@@ -0,0 +1,215 @@
|
|||||||
|
/*
|
||||||
|
* 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 "ottie-editor-application.h"
|
||||||
|
|
||||||
|
#include "ottie-editor-window.h"
|
||||||
|
|
||||||
|
struct _OttieEditorApplication
|
||||||
|
{
|
||||||
|
GtkApplication parent;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _OttieEditorApplicationClass
|
||||||
|
{
|
||||||
|
GtkApplicationClass parent_class;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE(OttieEditorApplication, ottie_editor_application, GTK_TYPE_APPLICATION);
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_editor_application_init (OttieEditorApplication *app)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
activate_about (GSimpleAction *action,
|
||||||
|
GVariant *parameter,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
GtkApplication *app = user_data;
|
||||||
|
char *version;
|
||||||
|
GString *s;
|
||||||
|
char *os_name;
|
||||||
|
char *os_version;
|
||||||
|
GtkWidget *dialog;
|
||||||
|
|
||||||
|
os_name = g_get_os_info (G_OS_INFO_KEY_NAME);
|
||||||
|
os_version = g_get_os_info (G_OS_INFO_KEY_VERSION_ID);
|
||||||
|
s = g_string_new ("");
|
||||||
|
if (os_name && os_version)
|
||||||
|
g_string_append_printf (s, "OS\t%s %s\n\n", os_name, os_version);
|
||||||
|
|
||||||
|
g_string_append (s, "System libraries\n");
|
||||||
|
g_string_append_printf (s, "\tGLib\t%d.%d.%d\n",
|
||||||
|
glib_major_version,
|
||||||
|
glib_minor_version,
|
||||||
|
glib_micro_version);
|
||||||
|
g_string_append_printf (s, "\tPango\t%s\n",
|
||||||
|
pango_version_string ());
|
||||||
|
g_string_append_printf (s, "\tGTK\t%d.%d.%d\n",
|
||||||
|
gtk_get_major_version (),
|
||||||
|
gtk_get_minor_version (),
|
||||||
|
gtk_get_micro_version ());
|
||||||
|
|
||||||
|
version = g_strdup_printf ("%s\nRunning against GTK %d.%d.%d",
|
||||||
|
PACKAGE_VERSION,
|
||||||
|
gtk_get_major_version (),
|
||||||
|
gtk_get_minor_version (),
|
||||||
|
gtk_get_micro_version ());
|
||||||
|
|
||||||
|
dialog = g_object_new (GTK_TYPE_ABOUT_DIALOG,
|
||||||
|
"transient-for", gtk_application_get_active_window (app),
|
||||||
|
"program-name", "GTK Node Editor",
|
||||||
|
"version", version,
|
||||||
|
"copyright", "© 2019—2020 The GTK Team",
|
||||||
|
"license-type", GTK_LICENSE_LGPL_2_1,
|
||||||
|
"website", "http://www.gtk.org",
|
||||||
|
"comments", "Program to test GTK rendering",
|
||||||
|
"authors", (const char *[]){ "Benjamin Otte", "Timm Bäder", NULL},
|
||||||
|
"logo-icon-name", "org.gtk.gtk4.OttieEditor.Devel",
|
||||||
|
"title", "About GTK Node Editor",
|
||||||
|
"system-information", s->str,
|
||||||
|
NULL);
|
||||||
|
gtk_about_dialog_add_credit_section (GTK_ABOUT_DIALOG (dialog),
|
||||||
|
"Artwork by", (const char *[]) { "Jakub Steiner", NULL });
|
||||||
|
|
||||||
|
gtk_window_present (GTK_WINDOW (dialog));
|
||||||
|
|
||||||
|
g_string_free (s, TRUE);
|
||||||
|
g_free (version);
|
||||||
|
g_free (os_name);
|
||||||
|
g_free (os_version);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
activate_quit (GSimpleAction *action,
|
||||||
|
GVariant *parameter,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
g_application_quit (G_APPLICATION (data));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
activate_inspector (GSimpleAction *action,
|
||||||
|
GVariant *parameter,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
gtk_window_set_interactive_debugging (TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
activate_help (GSimpleAction *action,
|
||||||
|
GVariant *parameter,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
GtkBuilder *builder;
|
||||||
|
GtkWidget *window;
|
||||||
|
GtkTextBuffer *buffer;
|
||||||
|
GBytes *bytes;
|
||||||
|
const char *text;
|
||||||
|
gsize len;
|
||||||
|
|
||||||
|
builder = gtk_builder_new ();
|
||||||
|
gtk_builder_add_from_resource (builder, "/org/gtk/gtk4/node-editor/help-window.ui", NULL);
|
||||||
|
window = GTK_WIDGET (gtk_builder_get_object (builder, "window"));
|
||||||
|
buffer = GTK_TEXT_BUFFER (gtk_builder_get_object (builder, "buffer"));
|
||||||
|
|
||||||
|
bytes = g_resources_lookup_data ("/org/gtk/gtk4/node-editor/node-format.md",
|
||||||
|
G_RESOURCE_LOOKUP_FLAGS_NONE,
|
||||||
|
NULL);
|
||||||
|
text = g_bytes_get_data (bytes, &len);
|
||||||
|
gtk_text_buffer_set_text (buffer, text, len);
|
||||||
|
g_bytes_unref (bytes);
|
||||||
|
|
||||||
|
gtk_window_present (GTK_WINDOW (window));
|
||||||
|
g_object_unref (builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GActionEntry app_entries[] =
|
||||||
|
{
|
||||||
|
{ "about", activate_about, NULL, NULL, NULL },
|
||||||
|
{ "quit", activate_quit, NULL, NULL, NULL },
|
||||||
|
{ "inspector", activate_inspector, NULL, NULL, NULL },
|
||||||
|
{ "help", activate_help, NULL, NULL, NULL },
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_editor_application_startup (GApplication *app)
|
||||||
|
{
|
||||||
|
const char *help_accels[2] = { "F1", NULL };
|
||||||
|
const char *quit_accels[2] = { "<Ctrl>Q", NULL };
|
||||||
|
const char *open_accels[2] = { "<Ctrl>O", NULL };
|
||||||
|
|
||||||
|
G_APPLICATION_CLASS (ottie_editor_application_parent_class)->startup (app);
|
||||||
|
|
||||||
|
g_action_map_add_action_entries (G_ACTION_MAP (app),
|
||||||
|
app_entries, G_N_ELEMENTS (app_entries),
|
||||||
|
app);
|
||||||
|
gtk_application_set_accels_for_action (GTK_APPLICATION (app), "app.help", help_accels);
|
||||||
|
gtk_application_set_accels_for_action (GTK_APPLICATION (app), "app.quit", quit_accels);
|
||||||
|
gtk_application_set_accels_for_action (GTK_APPLICATION (app), "win.open", open_accels);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_editor_application_activate (GApplication *app)
|
||||||
|
{
|
||||||
|
OttieEditorWindow *win;
|
||||||
|
|
||||||
|
win = ottie_editor_window_new (OTTIE_EDITOR_APPLICATION (app));
|
||||||
|
gtk_window_present (GTK_WINDOW (win));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_editor_application_open (GApplication *app,
|
||||||
|
GFile **files,
|
||||||
|
int n_files,
|
||||||
|
const char *hint)
|
||||||
|
{
|
||||||
|
OttieEditorWindow *win;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < n_files; i++)
|
||||||
|
{
|
||||||
|
win = ottie_editor_window_new (OTTIE_EDITOR_APPLICATION (app));
|
||||||
|
ottie_editor_window_load (win, files[i]);
|
||||||
|
gtk_window_present (GTK_WINDOW (win));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_editor_application_class_init (OttieEditorApplicationClass *class)
|
||||||
|
{
|
||||||
|
GApplicationClass *application_class = G_APPLICATION_CLASS (class);
|
||||||
|
|
||||||
|
application_class->startup = ottie_editor_application_startup;
|
||||||
|
application_class->activate = ottie_editor_application_activate;
|
||||||
|
application_class->open = ottie_editor_application_open;
|
||||||
|
}
|
||||||
|
|
||||||
|
OttieEditorApplication *
|
||||||
|
ottie_editor_application_new (void)
|
||||||
|
{
|
||||||
|
return g_object_new (OTTIE_EDITOR_APPLICATION_TYPE,
|
||||||
|
"application-id", "org.gtk.gtk4.OttieEditor",
|
||||||
|
"flags", G_APPLICATION_HANDLES_OPEN,
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
38
ottie/editor/ottie-editor-application.h
Normal file
38
ottie/editor/ottie-editor-application.h
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* 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 __OTTIE_EDITOR_APPLICATION_H__
|
||||||
|
#define __OTTIE_EDITOR_APPLICATION_H__
|
||||||
|
|
||||||
|
#include <gtk/gtk.h>
|
||||||
|
|
||||||
|
|
||||||
|
#define OTTIE_EDITOR_APPLICATION_TYPE (ottie_editor_application_get_type ())
|
||||||
|
#define OTTIE_EDITOR_APPLICATION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), OTTIE_EDITOR_APPLICATION_TYPE, OttieEditorApplication))
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct _OttieEditorApplication OttieEditorApplication;
|
||||||
|
typedef struct _OttieEditorApplicationClass OttieEditorApplicationClass;
|
||||||
|
|
||||||
|
|
||||||
|
GType ottie_editor_application_get_type (void);
|
||||||
|
OttieEditorApplication *ottie_editor_application_new (void);
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* __OTTIE_EDITOR_APPLICATION_H__ */
|
||||||
431
ottie/editor/ottie-editor-window.c
Normal file
431
ottie/editor/ottie-editor-window.c
Normal file
@@ -0,0 +1,431 @@
|
|||||||
|
/*
|
||||||
|
* 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 "ottie-editor-window.h"
|
||||||
|
|
||||||
|
#include "ottie/ottiecreationprivate.h"
|
||||||
|
#include "ottie/ottiecompositionlayerprivate.h"
|
||||||
|
#include "ottie/ottiegroupshapeprivate.h"
|
||||||
|
#include "ottie/ottieshapelayerprivate.h"
|
||||||
|
#include "ottie/ottiepaintable.h"
|
||||||
|
|
||||||
|
struct _OttieEditorWindow
|
||||||
|
{
|
||||||
|
GtkApplicationWindow parent;
|
||||||
|
|
||||||
|
GFileMonitor *file_monitor;
|
||||||
|
|
||||||
|
OttieCreation *creation;
|
||||||
|
OttiePaintable *paintable;
|
||||||
|
|
||||||
|
GtkWidget *picture;
|
||||||
|
GtkWidget *listview;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _OttieEditorWindowClass
|
||||||
|
{
|
||||||
|
GtkApplicationWindowClass parent_class;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE(OttieEditorWindow, ottie_editor_window, GTK_TYPE_APPLICATION_WINDOW);
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
load_file_contents (OttieEditorWindow *self,
|
||||||
|
GFile *file)
|
||||||
|
{
|
||||||
|
GBytes *bytes;
|
||||||
|
|
||||||
|
bytes = g_file_load_bytes (file, NULL, NULL, NULL);
|
||||||
|
if (bytes == NULL)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (!g_utf8_validate (g_bytes_get_data (bytes, NULL), g_bytes_get_size (bytes), NULL))
|
||||||
|
{
|
||||||
|
g_bytes_unref (bytes);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
ottie_creation_load_bytes (self->creation, bytes);
|
||||||
|
#if 0
|
||||||
|
gtk_text_buffer_set_text (self->text_buffer,
|
||||||
|
g_bytes_get_data (bytes, NULL),
|
||||||
|
g_bytes_get_size (bytes));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
g_bytes_unref (bytes);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
file_changed_cb (GFileMonitor *monitor,
|
||||||
|
GFile *file,
|
||||||
|
GFile *other_file,
|
||||||
|
GFileMonitorEvent event_type,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
OttieEditorWindow *self = user_data;
|
||||||
|
|
||||||
|
if (event_type == G_FILE_MONITOR_EVENT_CHANGED)
|
||||||
|
load_file_contents (self, file);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ottie_editor_window_load (OttieEditorWindow *self,
|
||||||
|
GFile *file)
|
||||||
|
{
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
|
if (!load_file_contents (self, file))
|
||||||
|
return;
|
||||||
|
|
||||||
|
g_clear_object (&self->file_monitor);
|
||||||
|
self->file_monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, NULL, &error);
|
||||||
|
|
||||||
|
if (error)
|
||||||
|
{
|
||||||
|
g_warning ("couldn't monitor file: %s", error->message);
|
||||||
|
g_error_free (error);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_signal_connect (self->file_monitor, "changed", G_CALLBACK (file_changed_cb), self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
open_response_cb (GtkWidget *dialog,
|
||||||
|
int response,
|
||||||
|
OttieEditorWindow *self)
|
||||||
|
{
|
||||||
|
gtk_widget_hide (dialog);
|
||||||
|
|
||||||
|
if (response == GTK_RESPONSE_ACCEPT)
|
||||||
|
{
|
||||||
|
GFile *file;
|
||||||
|
|
||||||
|
file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
|
||||||
|
ottie_editor_window_load (self, file);
|
||||||
|
g_object_unref (file);
|
||||||
|
}
|
||||||
|
|
||||||
|
gtk_window_destroy (GTK_WINDOW (dialog));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
show_open_filechooser (OttieEditorWindow *self)
|
||||||
|
{
|
||||||
|
GtkWidget *dialog;
|
||||||
|
|
||||||
|
dialog = gtk_file_chooser_dialog_new ("Open lottie file",
|
||||||
|
GTK_WINDOW (self),
|
||||||
|
GTK_FILE_CHOOSER_ACTION_OPEN,
|
||||||
|
"_Cancel", GTK_RESPONSE_CANCEL,
|
||||||
|
"_Load", GTK_RESPONSE_ACCEPT,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
|
||||||
|
gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
|
||||||
|
|
||||||
|
GFile *cwd = g_file_new_for_path (".");
|
||||||
|
gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), cwd, NULL);
|
||||||
|
g_object_unref (cwd);
|
||||||
|
|
||||||
|
g_signal_connect (dialog, "response", G_CALLBACK (open_response_cb), self);
|
||||||
|
gtk_widget_show (dialog);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
open_cb (GtkWidget *button,
|
||||||
|
OttieEditorWindow *self)
|
||||||
|
{
|
||||||
|
show_open_filechooser (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
save_response_cb (GtkWidget *dialog,
|
||||||
|
int response,
|
||||||
|
OttieEditorWindow *self)
|
||||||
|
{
|
||||||
|
gtk_widget_hide (dialog);
|
||||||
|
|
||||||
|
if (response == GTK_RESPONSE_ACCEPT)
|
||||||
|
{
|
||||||
|
#if 0
|
||||||
|
GFile *file;
|
||||||
|
char *text;
|
||||||
|
#endif
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
text = get_current_text (self->text_buffer);
|
||||||
|
|
||||||
|
file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
|
||||||
|
g_file_replace_contents (file, text, strlen (text),
|
||||||
|
NULL, FALSE,
|
||||||
|
G_FILE_CREATE_NONE,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
&error);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (error != NULL)
|
||||||
|
{
|
||||||
|
GtkWidget *message_dialog;
|
||||||
|
|
||||||
|
message_dialog = gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_root (GTK_WIDGET (self))),
|
||||||
|
GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT,
|
||||||
|
GTK_MESSAGE_INFO,
|
||||||
|
GTK_BUTTONS_OK,
|
||||||
|
"Saving failed");
|
||||||
|
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (message_dialog),
|
||||||
|
"%s", error->message);
|
||||||
|
g_signal_connect (message_dialog, "response", G_CALLBACK (gtk_window_destroy), NULL);
|
||||||
|
gtk_widget_show (message_dialog);
|
||||||
|
g_error_free (error);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
g_free (text);
|
||||||
|
g_object_unref (file);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
gtk_window_destroy (GTK_WINDOW (dialog));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
save_cb (GtkWidget *button,
|
||||||
|
OttieEditorWindow *self)
|
||||||
|
{
|
||||||
|
GtkWidget *dialog;
|
||||||
|
|
||||||
|
dialog = gtk_file_chooser_dialog_new ("Save file",
|
||||||
|
GTK_WINDOW (gtk_widget_get_root (GTK_WIDGET (button))),
|
||||||
|
GTK_FILE_CHOOSER_ACTION_SAVE,
|
||||||
|
"_Cancel", GTK_RESPONSE_CANCEL,
|
||||||
|
"_Save", GTK_RESPONSE_ACCEPT,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
|
||||||
|
gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
|
||||||
|
|
||||||
|
GFile *cwd = g_file_new_for_path (".");
|
||||||
|
gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), cwd, NULL);
|
||||||
|
g_object_unref (cwd);
|
||||||
|
|
||||||
|
g_signal_connect (dialog, "response", G_CALLBACK (save_response_cb), self);
|
||||||
|
gtk_widget_show (dialog);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GdkTexture *
|
||||||
|
create_texture (OttieEditorWindow *self)
|
||||||
|
{
|
||||||
|
GtkSnapshot *snapshot;
|
||||||
|
GskRenderer *renderer;
|
||||||
|
GskRenderNode *node;
|
||||||
|
GdkTexture *texture;
|
||||||
|
int width, height;
|
||||||
|
|
||||||
|
width = gdk_paintable_get_intrinsic_width (GDK_PAINTABLE (self->paintable));
|
||||||
|
height = gdk_paintable_get_intrinsic_height (GDK_PAINTABLE (self->paintable));
|
||||||
|
|
||||||
|
if (width <= 0 || height <= 0)
|
||||||
|
return NULL;
|
||||||
|
snapshot = gtk_snapshot_new ();
|
||||||
|
gdk_paintable_snapshot (GDK_PAINTABLE (self->paintable), snapshot, width, height);
|
||||||
|
node = gtk_snapshot_free_to_node (snapshot);
|
||||||
|
if (node == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
renderer = gtk_native_get_renderer (gtk_widget_get_native (GTK_WIDGET (self)));
|
||||||
|
texture = gsk_renderer_render_texture (renderer, node, NULL);
|
||||||
|
gsk_render_node_unref (node);
|
||||||
|
|
||||||
|
return texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
export_image_response_cb (GtkWidget *dialog,
|
||||||
|
int response,
|
||||||
|
GdkTexture *texture)
|
||||||
|
{
|
||||||
|
gtk_widget_hide (dialog);
|
||||||
|
|
||||||
|
if (response == GTK_RESPONSE_ACCEPT)
|
||||||
|
{
|
||||||
|
GFile *file;
|
||||||
|
|
||||||
|
file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
|
||||||
|
if (!gdk_texture_save_to_png (texture, g_file_peek_path (file)))
|
||||||
|
{
|
||||||
|
GtkWidget *message_dialog;
|
||||||
|
|
||||||
|
message_dialog = gtk_message_dialog_new (GTK_WINDOW (gtk_window_get_transient_for (GTK_WINDOW (dialog))),
|
||||||
|
GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT,
|
||||||
|
GTK_MESSAGE_INFO,
|
||||||
|
GTK_BUTTONS_OK,
|
||||||
|
"Exporting to image failed");
|
||||||
|
g_signal_connect (message_dialog, "response", G_CALLBACK (gtk_window_destroy), NULL);
|
||||||
|
gtk_widget_show (message_dialog);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_object_unref (file);
|
||||||
|
}
|
||||||
|
|
||||||
|
gtk_window_destroy (GTK_WINDOW (dialog));
|
||||||
|
g_object_unref (texture);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
export_image_cb (GtkWidget *button,
|
||||||
|
OttieEditorWindow *self)
|
||||||
|
{
|
||||||
|
GdkTexture *texture;
|
||||||
|
GtkWidget *dialog;
|
||||||
|
|
||||||
|
texture = create_texture (self);
|
||||||
|
if (texture == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
dialog = gtk_file_chooser_dialog_new ("",
|
||||||
|
GTK_WINDOW (gtk_widget_get_root (GTK_WIDGET (button))),
|
||||||
|
GTK_FILE_CHOOSER_ACTION_SAVE,
|
||||||
|
"_Cancel", GTK_RESPONSE_CANCEL,
|
||||||
|
"_Save", GTK_RESPONSE_ACCEPT,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
|
||||||
|
gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
|
||||||
|
g_signal_connect (dialog, "response", G_CALLBACK (export_image_response_cb), texture);
|
||||||
|
gtk_widget_show (dialog);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GListModel *
|
||||||
|
create_object_children (gpointer item,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
if (OTTIE_IS_COMPOSITION_LAYER (item))
|
||||||
|
{
|
||||||
|
return G_LIST_MODEL (g_object_ref (ottie_composition_layer_get_composition (item)));
|
||||||
|
}
|
||||||
|
else if (OTTIE_IS_SHAPE_LAYER (item))
|
||||||
|
{
|
||||||
|
return G_LIST_MODEL (g_object_ref (ottie_shape_layer_get_shape (item)));
|
||||||
|
}
|
||||||
|
else if (OTTIE_IS_GROUP_SHAPE (item))
|
||||||
|
{
|
||||||
|
return g_object_ref (item);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
notify_prepared_cb (OttieCreation *creation,
|
||||||
|
GParamSpec *pspec,
|
||||||
|
OttieEditorWindow *self)
|
||||||
|
{
|
||||||
|
GtkTreeListModel *treemodel;
|
||||||
|
GtkSingleSelection *selection;
|
||||||
|
|
||||||
|
if (ottie_creation_is_prepared (creation))
|
||||||
|
{
|
||||||
|
treemodel = gtk_tree_list_model_new (G_LIST_MODEL (ottie_creation_get_composition (self->creation)),
|
||||||
|
FALSE,
|
||||||
|
TRUE,
|
||||||
|
create_object_children,
|
||||||
|
NULL,
|
||||||
|
NULL);
|
||||||
|
selection = gtk_single_selection_new (G_LIST_MODEL (treemodel));
|
||||||
|
gtk_list_view_set_model (GTK_LIST_VIEW (self->listview), GTK_SELECTION_MODEL (selection));
|
||||||
|
g_object_unref (selection);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gtk_list_view_set_model (GTK_LIST_VIEW (self->listview), NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_editor_window_finalize (GObject *object)
|
||||||
|
{
|
||||||
|
//OttieEditorWindow *self = OTTIE_EDITOR_WINDOW (object);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (ottie_editor_window_parent_class)->finalize (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_editor_window_class_init (OttieEditorWindowClass *class)
|
||||||
|
{
|
||||||
|
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
|
||||||
|
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
||||||
|
|
||||||
|
object_class->finalize = ottie_editor_window_finalize;
|
||||||
|
|
||||||
|
g_type_ensure (OTTIE_TYPE_CREATION);
|
||||||
|
g_type_ensure (OTTIE_TYPE_PAINTABLE);
|
||||||
|
|
||||||
|
gtk_widget_class_set_template_from_resource (widget_class,
|
||||||
|
"/org/gtk/gtk4/ottie-editor/ottie-editor-window.ui");
|
||||||
|
|
||||||
|
gtk_widget_class_bind_template_child (widget_class, OttieEditorWindow, creation);
|
||||||
|
gtk_widget_class_bind_template_child (widget_class, OttieEditorWindow, paintable);
|
||||||
|
gtk_widget_class_bind_template_child (widget_class, OttieEditorWindow, picture);
|
||||||
|
gtk_widget_class_bind_template_child (widget_class, OttieEditorWindow, listview);
|
||||||
|
|
||||||
|
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, notify_prepared_cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
window_open (GSimpleAction *action,
|
||||||
|
GVariant *parameter,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
OttieEditorWindow *self = user_data;
|
||||||
|
|
||||||
|
show_open_filechooser (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GActionEntry win_entries[] = {
|
||||||
|
{ "open", window_open, NULL, NULL, NULL },
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_editor_window_init (OttieEditorWindow *self)
|
||||||
|
{
|
||||||
|
gtk_widget_init_template (GTK_WIDGET (self));
|
||||||
|
|
||||||
|
g_action_map_add_action_entries (G_ACTION_MAP (self), win_entries, G_N_ELEMENTS (win_entries), self);
|
||||||
|
}
|
||||||
|
|
||||||
|
OttieEditorWindow *
|
||||||
|
ottie_editor_window_new (OttieEditorApplication *application)
|
||||||
|
{
|
||||||
|
return g_object_new (OTTIE_EDITOR_WINDOW_TYPE,
|
||||||
|
"application", application,
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
42
ottie/editor/ottie-editor-window.h
Normal file
42
ottie/editor/ottie-editor-window.h
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* 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 __OTTIE_EDITOR_WINDOW_H__
|
||||||
|
#define __OTTIE_EDITOR_WINDOW_H__
|
||||||
|
|
||||||
|
#include <gtk/gtk.h>
|
||||||
|
|
||||||
|
#include "ottie-editor-application.h"
|
||||||
|
|
||||||
|
#define OTTIE_EDITOR_WINDOW_TYPE (ottie_editor_window_get_type ())
|
||||||
|
#define OTTIE_EDITOR_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), OTTIE_EDITOR_WINDOW_TYPE, OttieEditorWindow))
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct _OttieEditorWindow OttieEditorWindow;
|
||||||
|
typedef struct _OttieEditorWindowClass OttieEditorWindowClass;
|
||||||
|
|
||||||
|
|
||||||
|
GType ottie_editor_window_get_type (void);
|
||||||
|
|
||||||
|
OttieEditorWindow * ottie_editor_window_new (OttieEditorApplication *application);
|
||||||
|
|
||||||
|
void ottie_editor_window_load (OttieEditorWindow *self,
|
||||||
|
GFile *file);
|
||||||
|
|
||||||
|
#endif /* __OTTIE_EDITOR_WINDOW_H__ */
|
||||||
133
ottie/editor/ottie-editor-window.ui
Normal file
133
ottie/editor/ottie-editor-window.ui
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<interface>
|
||||||
|
<menu id="gear_menu">
|
||||||
|
<section>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">_Help</attribute>
|
||||||
|
<attribute name="action">app.help</attribute>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">_Inspector</attribute>
|
||||||
|
<attribute name="action">app.inspector</attribute>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">_About Ottie Editor</attribute>
|
||||||
|
<attribute name="action">app.about</attribute>
|
||||||
|
</item>
|
||||||
|
</section>
|
||||||
|
</menu>
|
||||||
|
|
||||||
|
<object class="OttieCreation" id="creation">
|
||||||
|
<signal name="notify::prepared" handler="notify_prepared_cb"/>
|
||||||
|
</object>
|
||||||
|
|
||||||
|
<object class="OttiePaintable" id="paintable">
|
||||||
|
<property name="creation">creation</property>
|
||||||
|
</object>
|
||||||
|
|
||||||
|
<template class="OttieEditorWindow" parent="GtkApplicationWindow">
|
||||||
|
<property name="title" translatable="yes">Ottie Editor</property>
|
||||||
|
<property name="default-width">1024</property>
|
||||||
|
<property name="default-height">768</property>
|
||||||
|
<child type="titlebar">
|
||||||
|
<object class="GtkHeaderBar" id="header">
|
||||||
|
<child type="start">
|
||||||
|
<object class="GtkButton">
|
||||||
|
<property name="icon-name">document-open-symbolic</property>
|
||||||
|
<property name="tooltip-text">Open file</property>
|
||||||
|
<signal name="clicked" handler="open_cb"/>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child type="start">
|
||||||
|
<object class="GtkButton">
|
||||||
|
<property name="icon-name">document-save-symbolic</property>
|
||||||
|
<property name="tooltip-text">Save</property>
|
||||||
|
<signal name="clicked" handler="save_cb"/>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child type="start">
|
||||||
|
<object class="GtkButton">
|
||||||
|
<property name="icon-name">insert-image-symbolic</property>
|
||||||
|
<property name="tooltip-text">Export to image</property>
|
||||||
|
<signal name="clicked" handler="export_image_cb"/>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child type="end">
|
||||||
|
<object class="GtkMenuButton" id="gear_menu_button">
|
||||||
|
<property name="valign">center</property>
|
||||||
|
<property name="menu-model">gear_menu</property>
|
||||||
|
<property name="icon-name">open-menu-symbolic</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkPaned">
|
||||||
|
<property name="shrink-end-child">false</property>
|
||||||
|
<property name="position">400</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkScrolledWindow">
|
||||||
|
<property name="hexpand">1</property>
|
||||||
|
<property name="vexpand">1</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkListView" id="listview">
|
||||||
|
<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="GtkTreeExpander" id="expander">
|
||||||
|
<binding name="list-row">
|
||||||
|
<lookup name="item">GtkListItem</lookup>
|
||||||
|
</binding>
|
||||||
|
<property name="child">
|
||||||
|
<object class="GtkLabel">
|
||||||
|
<property name="xalign">0</property>
|
||||||
|
<binding name="label">
|
||||||
|
<lookup name="name" type="OttieObject">
|
||||||
|
<lookup name="item">expander</lookup>
|
||||||
|
</lookup>
|
||||||
|
</binding>
|
||||||
|
</object>
|
||||||
|
</property>
|
||||||
|
</object>
|
||||||
|
</property>
|
||||||
|
</template>
|
||||||
|
</interface>
|
||||||
|
]]></property>
|
||||||
|
</object>
|
||||||
|
</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<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>
|
||||||
|
<child>
|
||||||
|
<object class="GtkViewport">
|
||||||
|
<child>
|
||||||
|
<object class="GtkPicture" id="picture">
|
||||||
|
<property name="can-shrink">0</property>
|
||||||
|
<property name="paintable">paintable</property>
|
||||||
|
<property name="halign">center</property>
|
||||||
|
<property name="valign">center</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</template>
|
||||||
|
</interface>
|
||||||
6
ottie/editor/ottie-editor.gresource.xml
Normal file
6
ottie/editor/ottie-editor.gresource.xml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<gresources>
|
||||||
|
<gresource prefix="/org/gtk/gtk4/ottie-editor">
|
||||||
|
<file preprocess="xml-stripblanks">ottie-editor-window.ui</file>
|
||||||
|
</gresource>
|
||||||
|
</gresources>
|
||||||
71
ottie/meson.build
Normal file
71
ottie/meson.build
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
ottie_public_sources = files([
|
||||||
|
'ottiecreation.c',
|
||||||
|
'ottiepaintable.c',
|
||||||
|
'ottieplayer.c',
|
||||||
|
])
|
||||||
|
|
||||||
|
ottie_private_sources = files([
|
||||||
|
'ottiecolorvalue.c',
|
||||||
|
'ottiecomposition.c',
|
||||||
|
'ottiecompositionlayer.c',
|
||||||
|
'ottiedoublevalue.c',
|
||||||
|
'ottieellipseshape.c',
|
||||||
|
'ottiefillshape.c',
|
||||||
|
'ottiegroupshape.c',
|
||||||
|
'ottielayer.c',
|
||||||
|
'ottienulllayer.c',
|
||||||
|
'ottieobject.c',
|
||||||
|
'ottieparser.c',
|
||||||
|
'ottiepathshape.c',
|
||||||
|
'ottiepathvalue.c',
|
||||||
|
'ottiepointvalue.c',
|
||||||
|
'ottiepoint3dvalue.c',
|
||||||
|
'ottierectshape.c',
|
||||||
|
'ottierender.c',
|
||||||
|
'ottieshape.c',
|
||||||
|
'ottieshapelayer.c',
|
||||||
|
'ottiestrokeshape.c',
|
||||||
|
'ottietransform.c',
|
||||||
|
'ottietrimshape.c',
|
||||||
|
])
|
||||||
|
|
||||||
|
ottie_public_headers = files([
|
||||||
|
'ottie.h',
|
||||||
|
'ottiecreation.h',
|
||||||
|
'ottiepaintable.h',
|
||||||
|
'ottieplayer.h',
|
||||||
|
])
|
||||||
|
|
||||||
|
install_headers(ottie_public_headers, 'ottie.h', subdir: 'gtk-4.0/ottie')
|
||||||
|
|
||||||
|
json_glib_dep = dependency('json-glib-1.0', required: true)
|
||||||
|
ottie_deps = [
|
||||||
|
libm,
|
||||||
|
glib_dep,
|
||||||
|
gobject_dep,
|
||||||
|
platform_gio_dep,
|
||||||
|
libgdk_dep,
|
||||||
|
libgsk_dep,
|
||||||
|
json_glib_dep
|
||||||
|
]
|
||||||
|
|
||||||
|
libottie = static_library('ottie',
|
||||||
|
sources: [
|
||||||
|
ottie_public_sources,
|
||||||
|
ottie_private_sources,
|
||||||
|
],
|
||||||
|
dependencies: ottie_deps,
|
||||||
|
include_directories: [ confinc, ],
|
||||||
|
c_args: [
|
||||||
|
'-DGTK_COMPILATION',
|
||||||
|
'-DG_LOG_DOMAIN="Ottie"',
|
||||||
|
] + common_cflags,
|
||||||
|
link_with: [libgdk, libgsk ],
|
||||||
|
link_args: common_ldflags)
|
||||||
|
|
||||||
|
# We don't have link_with: to internal static libs here on purpose, just
|
||||||
|
# list the dependencies and generated headers and such, for use in the
|
||||||
|
# "public" libgtk_dep used by internal executables.
|
||||||
|
libottie_dep = declare_dependency(include_directories: [ confinc, ],
|
||||||
|
dependencies: ottie_deps)
|
||||||
|
|
||||||
31
ottie/ottie.h
Normal file
31
ottie/ottie.h
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* 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 __OTTIE_H__
|
||||||
|
#define __OTTIE_H__
|
||||||
|
|
||||||
|
#define __OTTIE_H_INSIDE__
|
||||||
|
|
||||||
|
#include <ottie/ottiecreation.h>
|
||||||
|
#include <ottie/ottiepaintable.h>
|
||||||
|
#include <ottie/ottieplayer.h>
|
||||||
|
|
||||||
|
#undef __OTTIE_H_INSIDE__
|
||||||
|
|
||||||
|
#endif /* __OTTIE_H__ */
|
||||||
149
ottie/ottiecolorvalue.c
Normal file
149
ottie/ottiecolorvalue.c
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
/*
|
||||||
|
* 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 "ottiecolorvalueprivate.h"
|
||||||
|
|
||||||
|
#include "ottieparserprivate.h"
|
||||||
|
|
||||||
|
#include <glib/gi18n-lib.h>
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
ottie_color_value_parse_one (JsonReader *reader,
|
||||||
|
gsize offset,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
GdkRGBA *rgba = (GdkRGBA *) ((guint8 *) data + offset);
|
||||||
|
double d[3];
|
||||||
|
|
||||||
|
if (!ottie_parser_parse_array (reader, "color value",
|
||||||
|
3, 3, NULL,
|
||||||
|
0, sizeof (double),
|
||||||
|
ottie_parser_option_double,
|
||||||
|
d))
|
||||||
|
{
|
||||||
|
d[0] = d[1] = d[2] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
rgba->red = d[0];
|
||||||
|
rgba->green = d[1];
|
||||||
|
rgba->blue = d[2];
|
||||||
|
rgba->alpha = 1;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_color_value_interpolate (const GdkRGBA *start,
|
||||||
|
const GdkRGBA *end,
|
||||||
|
double progress,
|
||||||
|
GdkRGBA *result)
|
||||||
|
{
|
||||||
|
result->red = start->red + progress * (end->red - start->red);
|
||||||
|
result->green = start->green + progress * (end->green - start->green);
|
||||||
|
result->blue = start->blue + progress * (end->blue - start->blue);
|
||||||
|
result->alpha = start->alpha + progress * (end->alpha - start->alpha);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define OTTIE_KEYFRAMES_NAME ottie_color_keyframes
|
||||||
|
#define OTTIE_KEYFRAMES_TYPE_NAME OttieColorKeyframes
|
||||||
|
#define OTTIE_KEYFRAMES_ELEMENT_TYPE GdkRGBA
|
||||||
|
#define OTTIE_KEYFRAMES_BY_VALUE 1
|
||||||
|
#define OTTIE_KEYFRAMES_DIMENSIONS 4
|
||||||
|
#define OTTIE_KEYFRAMES_PARSE_FUNC ottie_color_value_parse_one
|
||||||
|
#define OTTIE_KEYFRAMES_INTERPOLATE_FUNC ottie_color_value_interpolate
|
||||||
|
#include "ottiekeyframesimpl.c"
|
||||||
|
|
||||||
|
void
|
||||||
|
ottie_color_value_init (OttieColorValue *self,
|
||||||
|
const GdkRGBA *value)
|
||||||
|
{
|
||||||
|
self->is_static = TRUE;
|
||||||
|
self->static_value = *value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ottie_color_value_clear (OttieColorValue *self)
|
||||||
|
{
|
||||||
|
if (!self->is_static)
|
||||||
|
g_clear_pointer (&self->keyframes, ottie_color_keyframes_free);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ottie_color_value_get (OttieColorValue *self,
|
||||||
|
double timestamp,
|
||||||
|
GdkRGBA *rgba)
|
||||||
|
{
|
||||||
|
if (self->is_static)
|
||||||
|
{
|
||||||
|
*rgba = self->static_value;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ottie_color_keyframes_get (self->keyframes, timestamp, rgba);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
ottie_color_value_parse (JsonReader *reader,
|
||||||
|
gsize offset,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
OttieColorValue *self = (OttieColorValue *) ((guint8 *) data + offset);
|
||||||
|
|
||||||
|
if (json_reader_read_member (reader, "k"))
|
||||||
|
{
|
||||||
|
gboolean is_static;
|
||||||
|
|
||||||
|
if (!json_reader_is_array (reader))
|
||||||
|
is_static = TRUE;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (json_reader_read_element (reader, 0))
|
||||||
|
is_static = !json_reader_is_object (reader);
|
||||||
|
else
|
||||||
|
is_static = TRUE;
|
||||||
|
json_reader_end_element (reader);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_static)
|
||||||
|
{
|
||||||
|
self->is_static = TRUE;
|
||||||
|
ottie_color_value_parse_one (reader, 0, &self->static_value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
self->is_static = FALSE;
|
||||||
|
self->keyframes = ottie_color_keyframes_parse (reader);
|
||||||
|
if (self->keyframes == NULL)
|
||||||
|
{
|
||||||
|
json_reader_end_member (reader);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ottie_parser_error_syntax (reader, "Property is not a color value");
|
||||||
|
}
|
||||||
|
json_reader_end_member (reader);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
54
ottie/ottiecolorvalueprivate.h
Normal file
54
ottie/ottiecolorvalueprivate.h
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* 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 __OTTIE_COLOR_VALUE_PRIVATE_H__
|
||||||
|
#define __OTTIE_COLOR_VALUE_PRIVATE_H__
|
||||||
|
|
||||||
|
#include <json-glib/json-glib.h>
|
||||||
|
|
||||||
|
#include <gdk/gdk.h>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
typedef struct _OttieColorValue OttieColorValue;
|
||||||
|
|
||||||
|
struct _OttieColorValue
|
||||||
|
{
|
||||||
|
gboolean is_static;
|
||||||
|
union {
|
||||||
|
GdkRGBA static_value;
|
||||||
|
gpointer keyframes;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
void ottie_color_value_init (OttieColorValue *self,
|
||||||
|
const GdkRGBA *rgba);
|
||||||
|
void ottie_color_value_clear (OttieColorValue *self);
|
||||||
|
|
||||||
|
void ottie_color_value_get (OttieColorValue *self,
|
||||||
|
double timestamp,
|
||||||
|
GdkRGBA *rgba);
|
||||||
|
|
||||||
|
gboolean ottie_color_value_parse (JsonReader *reader,
|
||||||
|
gsize offset,
|
||||||
|
gpointer data);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* __OTTIE_COLOR_VALUE_PRIVATE_H__ */
|
||||||
268
ottie/ottiecomposition.c
Normal file
268
ottie/ottiecomposition.c
Normal file
@@ -0,0 +1,268 @@
|
|||||||
|
/*
|
||||||
|
* 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 "ottiecompositionprivate.h"
|
||||||
|
|
||||||
|
#include "ottieparserprivate.h"
|
||||||
|
#include "ottiecompositionlayerprivate.h"
|
||||||
|
#include "ottienulllayerprivate.h"
|
||||||
|
#include "ottieshapelayerprivate.h"
|
||||||
|
|
||||||
|
#include <glib/gi18n-lib.h>
|
||||||
|
#include <gsk/gsk.h>
|
||||||
|
|
||||||
|
#define GDK_ARRAY_ELEMENT_TYPE OttieLayer *
|
||||||
|
#define GDK_ARRAY_FREE_FUNC g_object_unref
|
||||||
|
#define GDK_ARRAY_TYPE_NAME OttieLayerList
|
||||||
|
#define GDK_ARRAY_NAME ottie_layer_list
|
||||||
|
#define GDK_ARRAY_PREALLOC 4
|
||||||
|
#include "gdk/gdkarrayimpl.c"
|
||||||
|
|
||||||
|
struct _OttieComposition
|
||||||
|
{
|
||||||
|
OttieLayer parent;
|
||||||
|
|
||||||
|
OttieLayerList layers;
|
||||||
|
GHashTable *layers_by_index;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _OttieCompositionClass
|
||||||
|
{
|
||||||
|
OttieLayerClass parent_class;
|
||||||
|
};
|
||||||
|
|
||||||
|
static GType
|
||||||
|
ottie_composition_get_item_type (GListModel *list)
|
||||||
|
{
|
||||||
|
return OTTIE_TYPE_LAYER;
|
||||||
|
}
|
||||||
|
|
||||||
|
static guint
|
||||||
|
ottie_composition_get_n_items (GListModel *list)
|
||||||
|
{
|
||||||
|
OttieComposition *self = OTTIE_COMPOSITION (list);
|
||||||
|
|
||||||
|
return ottie_layer_list_get_size (&self->layers);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gpointer
|
||||||
|
ottie_composition_get_item (GListModel *list,
|
||||||
|
guint position)
|
||||||
|
{
|
||||||
|
OttieComposition *self = OTTIE_COMPOSITION (list);
|
||||||
|
|
||||||
|
if (position >= ottie_layer_list_get_size (&self->layers))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return g_object_ref (ottie_layer_list_get (&self->layers, position));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_composition_list_model_init (GListModelInterface *iface)
|
||||||
|
{
|
||||||
|
iface->get_item_type = ottie_composition_get_item_type;
|
||||||
|
iface->get_n_items = ottie_composition_get_n_items;
|
||||||
|
iface->get_item = ottie_composition_get_item;
|
||||||
|
}
|
||||||
|
|
||||||
|
G_DEFINE_TYPE_WITH_CODE (OttieComposition, ottie_composition, OTTIE_TYPE_LAYER,
|
||||||
|
G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, ottie_composition_list_model_init))
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_composition_update (OttieLayer *layer,
|
||||||
|
GHashTable *compositions)
|
||||||
|
{
|
||||||
|
OttieComposition *self = OTTIE_COMPOSITION (layer);
|
||||||
|
|
||||||
|
for (gsize i = ottie_layer_list_get_size (&self->layers); i-- > 0; )
|
||||||
|
{
|
||||||
|
ottie_layer_update (ottie_layer_list_get (&self->layers, i), compositions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_composition_render (OttieLayer *layer,
|
||||||
|
OttieRender *render,
|
||||||
|
double timestamp)
|
||||||
|
{
|
||||||
|
OttieComposition *self = OTTIE_COMPOSITION (layer);
|
||||||
|
OttieRender child_render;
|
||||||
|
|
||||||
|
ottie_render_init (&child_render);
|
||||||
|
|
||||||
|
for (gsize i = 0; i < ottie_layer_list_get_size (&self->layers); i++)
|
||||||
|
{
|
||||||
|
OttieLayer *child = ottie_layer_list_get (&self->layers, i);
|
||||||
|
|
||||||
|
ottie_layer_render (child, &child_render, timestamp);
|
||||||
|
/* XXX: Should we clear paths here because they're not needed anymore? */
|
||||||
|
|
||||||
|
/* Use a counter here to avoid inflooping */
|
||||||
|
for (gsize j = 0; j < ottie_layer_list_get_size (&self->layers); j++)
|
||||||
|
{
|
||||||
|
if (child->transform)
|
||||||
|
ottie_shape_render (OTTIE_SHAPE (child->transform), &child_render, timestamp);
|
||||||
|
if (child->parent_index == OTTIE_INT_UNSET)
|
||||||
|
break;
|
||||||
|
child = g_hash_table_lookup (self->layers_by_index, GINT_TO_POINTER (child->parent_index));
|
||||||
|
if (child == NULL)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ottie_render_merge (render, &child_render);
|
||||||
|
}
|
||||||
|
|
||||||
|
ottie_render_clear (&child_render);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_composition_dispose (GObject *object)
|
||||||
|
{
|
||||||
|
OttieComposition *self = OTTIE_COMPOSITION (object);
|
||||||
|
|
||||||
|
ottie_layer_list_clear (&self->layers);
|
||||||
|
g_hash_table_remove_all (self->layers_by_index);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (ottie_composition_parent_class)->dispose (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_composition_finalize (GObject *object)
|
||||||
|
{
|
||||||
|
OttieComposition *self = OTTIE_COMPOSITION (object);
|
||||||
|
|
||||||
|
g_hash_table_unref (self->layers_by_index);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (ottie_composition_parent_class)->finalize (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_composition_class_init (OttieCompositionClass *klass)
|
||||||
|
{
|
||||||
|
OttieLayerClass *layer_class = OTTIE_LAYER_CLASS (klass);
|
||||||
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||||
|
|
||||||
|
layer_class->update = ottie_composition_update;
|
||||||
|
layer_class->render = ottie_composition_render;
|
||||||
|
|
||||||
|
gobject_class->dispose = ottie_composition_dispose;
|
||||||
|
gobject_class->finalize = ottie_composition_finalize;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_composition_init (OttieComposition *self)
|
||||||
|
{
|
||||||
|
ottie_layer_list_init (&self->layers);
|
||||||
|
|
||||||
|
self->layers_by_index = g_hash_table_new (g_direct_hash, g_direct_equal);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_composition_append (OttieComposition *self,
|
||||||
|
OttieLayer *layer)
|
||||||
|
{
|
||||||
|
ottie_layer_list_append (&self->layers, layer);
|
||||||
|
if (layer->index != OTTIE_INT_UNSET)
|
||||||
|
g_hash_table_insert (self->layers_by_index, GINT_TO_POINTER (layer->index), layer);
|
||||||
|
g_list_model_items_changed (G_LIST_MODEL (self), ottie_layer_list_get_size (&self->layers), 0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
ottie_composition_parse_layer (JsonReader *reader,
|
||||||
|
gsize offset,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
OttieComposition *self = data;
|
||||||
|
OttieLayer *layer;
|
||||||
|
int type;
|
||||||
|
|
||||||
|
if (!json_reader_is_object (reader))
|
||||||
|
{
|
||||||
|
ottie_parser_error_syntax (reader, "Layer %zu is not an object",
|
||||||
|
ottie_layer_list_get_size (&self->layers));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!json_reader_read_member (reader, "ty"))
|
||||||
|
{
|
||||||
|
ottie_parser_error_syntax (reader, "Layer %zu has no type",
|
||||||
|
ottie_layer_list_get_size (&self->layers));
|
||||||
|
json_reader_end_member (reader);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
type = json_reader_get_int_value (reader);
|
||||||
|
json_reader_end_member (reader);
|
||||||
|
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
layer = ottie_composition_layer_parse (reader);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
layer = ottie_null_layer_parse (reader);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 4:
|
||||||
|
layer = ottie_shape_layer_parse (reader);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
ottie_parser_error_value (reader, "Layer %zu has unknown type %d",
|
||||||
|
ottie_layer_list_get_size (&self->layers),
|
||||||
|
type);
|
||||||
|
layer = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (layer)
|
||||||
|
ottie_composition_append (self, layer);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
ottie_composition_parse_layers (JsonReader *reader,
|
||||||
|
gsize offset,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
OttieComposition **target = (OttieComposition **) ((guint8 *) data + offset);
|
||||||
|
OttieComposition *self;
|
||||||
|
|
||||||
|
self = g_object_new (OTTIE_TYPE_COMPOSITION, NULL);
|
||||||
|
|
||||||
|
if (!ottie_parser_parse_array (reader, "layers",
|
||||||
|
0, G_MAXUINT, NULL,
|
||||||
|
0, 0,
|
||||||
|
ottie_composition_parse_layer,
|
||||||
|
self))
|
||||||
|
{
|
||||||
|
g_object_unref (self);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_clear_object (target);
|
||||||
|
*target = self;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
153
ottie/ottiecompositionlayer.c
Normal file
153
ottie/ottiecompositionlayer.c
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
/*
|
||||||
|
* 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 "ottiecompositionlayerprivate.h"
|
||||||
|
|
||||||
|
#include "ottiedoublevalueprivate.h"
|
||||||
|
|
||||||
|
#include <glib/gi18n-lib.h>
|
||||||
|
#include <gsk/gsk.h>
|
||||||
|
|
||||||
|
struct _OttieCompositionLayer
|
||||||
|
{
|
||||||
|
OttieLayer parent;
|
||||||
|
|
||||||
|
OttieDoubleValue time_map;
|
||||||
|
double width;
|
||||||
|
double height;
|
||||||
|
char *ref_id;
|
||||||
|
OttieComposition *composition;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _OttieCompositionLayerClass
|
||||||
|
{
|
||||||
|
OttieLayerClass parent_class;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (OttieCompositionLayer, ottie_composition_layer, OTTIE_TYPE_LAYER)
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_composition_layer_update (OttieLayer *layer,
|
||||||
|
GHashTable *compositions)
|
||||||
|
{
|
||||||
|
OttieCompositionLayer *self = OTTIE_COMPOSITION_LAYER (layer);
|
||||||
|
|
||||||
|
g_clear_object (&self->composition);
|
||||||
|
|
||||||
|
if (self->ref_id)
|
||||||
|
self->composition = g_object_ref (g_hash_table_lookup (compositions, self->ref_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_composition_layer_render (OttieLayer *layer,
|
||||||
|
OttieRender *render,
|
||||||
|
double timestamp)
|
||||||
|
{
|
||||||
|
OttieCompositionLayer *self = OTTIE_COMPOSITION_LAYER (layer);
|
||||||
|
GskRenderNode *node;
|
||||||
|
double time_map;
|
||||||
|
|
||||||
|
if (self->composition == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (ottie_double_value_is_static (&self->time_map))
|
||||||
|
time_map = timestamp;
|
||||||
|
else
|
||||||
|
time_map = ottie_double_value_get (&self->time_map, timestamp);
|
||||||
|
|
||||||
|
ottie_layer_render (OTTIE_LAYER (self->composition),
|
||||||
|
render,
|
||||||
|
time_map);
|
||||||
|
|
||||||
|
node = ottie_render_get_node (render);
|
||||||
|
ottie_render_clear_nodes (render);
|
||||||
|
if (node)
|
||||||
|
{
|
||||||
|
ottie_render_add_node (render,
|
||||||
|
gsk_clip_node_new (node,
|
||||||
|
&GRAPHENE_RECT_INIT (
|
||||||
|
0, 0,
|
||||||
|
self->width, self->height
|
||||||
|
)));
|
||||||
|
gsk_render_node_unref (node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_composition_layer_dispose (GObject *object)
|
||||||
|
{
|
||||||
|
OttieCompositionLayer *self = OTTIE_COMPOSITION_LAYER (object);
|
||||||
|
|
||||||
|
g_clear_object (&self->composition);
|
||||||
|
g_clear_pointer (&self->ref_id, g_free);
|
||||||
|
ottie_double_value_clear (&self->time_map);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (ottie_composition_layer_parent_class)->dispose (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_composition_layer_class_init (OttieCompositionLayerClass *klass)
|
||||||
|
{
|
||||||
|
OttieLayerClass *layer_class = OTTIE_LAYER_CLASS (klass);
|
||||||
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||||
|
|
||||||
|
layer_class->update = ottie_composition_layer_update;
|
||||||
|
layer_class->render = ottie_composition_layer_render;
|
||||||
|
|
||||||
|
gobject_class->dispose = ottie_composition_layer_dispose;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_composition_layer_init (OttieCompositionLayer *self)
|
||||||
|
{
|
||||||
|
ottie_double_value_init (&self->time_map, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
OttieLayer *
|
||||||
|
ottie_composition_layer_parse (JsonReader *reader)
|
||||||
|
{
|
||||||
|
OttieParserOption options[] = {
|
||||||
|
OTTIE_PARSE_OPTIONS_LAYER,
|
||||||
|
{ "refId", ottie_parser_option_string, G_STRUCT_OFFSET (OttieCompositionLayer, ref_id) },
|
||||||
|
{ "tm", ottie_double_value_parse, 0 },
|
||||||
|
{ "w", ottie_parser_option_double, G_STRUCT_OFFSET (OttieCompositionLayer, width) },
|
||||||
|
{ "h", ottie_parser_option_double, G_STRUCT_OFFSET (OttieCompositionLayer, height) },
|
||||||
|
};
|
||||||
|
OttieCompositionLayer *self;
|
||||||
|
|
||||||
|
self = g_object_new (OTTIE_TYPE_COMPOSITION_LAYER, NULL);
|
||||||
|
|
||||||
|
if (!ottie_parser_parse_object (reader, "composition layer", options, G_N_ELEMENTS (options), self))
|
||||||
|
{
|
||||||
|
g_object_unref (self);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return OTTIE_LAYER (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
OttieComposition *
|
||||||
|
ottie_composition_layer_get_composition (OttieCompositionLayer *self)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (OTTIE_IS_COMPOSITION_LAYER (self), NULL);
|
||||||
|
|
||||||
|
return self->composition;
|
||||||
|
}
|
||||||
49
ottie/ottiecompositionlayerprivate.h
Normal file
49
ottie/ottiecompositionlayerprivate.h
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* 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 __OTTIE_COMPOSITION_LAYER_PRIVATE_H__
|
||||||
|
#define __OTTIE_COMPOSITION_LAYER_PRIVATE_H__
|
||||||
|
|
||||||
|
#include "ottielayerprivate.h"
|
||||||
|
|
||||||
|
#include "ottiecompositionprivate.h"
|
||||||
|
|
||||||
|
#include <json-glib/json-glib.h>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
#define OTTIE_TYPE_COMPOSITION_LAYER (ottie_composition_layer_get_type ())
|
||||||
|
#define OTTIE_COMPOSITION_LAYER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), OTTIE_TYPE_COMPOSITION_LAYER, OttieCompositionLayer))
|
||||||
|
#define OTTIE_COMPOSITION_LAYER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), OTTIE_TYPE_COMPOSITION_LAYER, OttieCompositionLayerClass))
|
||||||
|
#define OTTIE_IS_COMPOSITION_LAYER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), OTTIE_TYPE_COMPOSITION_LAYER))
|
||||||
|
#define OTTIE_IS_COMPOSITION_LAYER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), OTTIE_TYPE_COMPOSITION_LAYER))
|
||||||
|
#define OTTIE_COMPOSITION_LAYER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), OTTIE_TYPE_COMPOSITION_LAYER, OttieCompositionLayerClass))
|
||||||
|
|
||||||
|
typedef struct _OttieCompositionLayer OttieCompositionLayer;
|
||||||
|
typedef struct _OttieCompositionLayerClass OttieCompositionLayerClass;
|
||||||
|
|
||||||
|
GType ottie_composition_layer_get_type (void) G_GNUC_CONST;
|
||||||
|
|
||||||
|
OttieComposition * ottie_composition_layer_get_composition (OttieCompositionLayer *self);
|
||||||
|
|
||||||
|
OttieLayer * ottie_composition_layer_parse (JsonReader *reader);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* __OTTIE_COMPOSITION_LAYER_PRIVATE_H__ */
|
||||||
47
ottie/ottiecompositionprivate.h
Normal file
47
ottie/ottiecompositionprivate.h
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* 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 __OTTIE_COMPOSITION_PRIVATE_H__
|
||||||
|
#define __OTTIE_COMPOSITION_PRIVATE_H__
|
||||||
|
|
||||||
|
#include "ottielayerprivate.h"
|
||||||
|
|
||||||
|
#include <json-glib/json-glib.h>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
#define OTTIE_TYPE_COMPOSITION (ottie_composition_get_type ())
|
||||||
|
#define OTTIE_COMPOSITION(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), OTTIE_TYPE_COMPOSITION, OttieComposition))
|
||||||
|
#define OTTIE_COMPOSITION_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), OTTIE_TYPE_COMPOSITION, OttieCompositionClass))
|
||||||
|
#define OTTIE_IS_COMPOSITION(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), OTTIE_TYPE_COMPOSITION))
|
||||||
|
#define OTTIE_IS_COMPOSITION_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), OTTIE_TYPE_COMPOSITION))
|
||||||
|
#define OTTIE_COMPOSITION_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), OTTIE_TYPE_COMPOSITION, OttieCompositionClass))
|
||||||
|
|
||||||
|
typedef struct _OttieComposition OttieComposition;
|
||||||
|
typedef struct _OttieCompositionClass OttieCompositionClass;
|
||||||
|
|
||||||
|
GType ottie_composition_get_type (void) G_GNUC_CONST;
|
||||||
|
|
||||||
|
gboolean ottie_composition_parse_layers (JsonReader *reader,
|
||||||
|
gsize offset,
|
||||||
|
gpointer data);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* __OTTIE_COMPOSITION_PRIVATE_H__ */
|
||||||
746
ottie/ottiecreation.c
Normal file
746
ottie/ottiecreation.c
Normal file
@@ -0,0 +1,746 @@
|
|||||||
|
/*
|
||||||
|
* 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 "ottiecreationprivate.h"
|
||||||
|
|
||||||
|
#include "ottielayerprivate.h"
|
||||||
|
#include "ottieparserprivate.h"
|
||||||
|
#include "ottiecompositionprivate.h"
|
||||||
|
|
||||||
|
#include <glib/gi18n-lib.h>
|
||||||
|
#include <json-glib/json-glib.h>
|
||||||
|
|
||||||
|
struct _OttieCreation
|
||||||
|
{
|
||||||
|
GObject parent;
|
||||||
|
|
||||||
|
char *name;
|
||||||
|
double frame_rate;
|
||||||
|
double start_frame;
|
||||||
|
double end_frame;
|
||||||
|
double width;
|
||||||
|
double height;
|
||||||
|
|
||||||
|
OttieComposition *layers;
|
||||||
|
GHashTable *composition_assets;
|
||||||
|
|
||||||
|
GCancellable *cancellable;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _OttieCreationClass
|
||||||
|
{
|
||||||
|
GObjectClass parent_class;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
PROP_0,
|
||||||
|
PROP_END_FRAME,
|
||||||
|
PROP_FRAME_RATE,
|
||||||
|
PROP_HEIGHT,
|
||||||
|
PROP_LOADING,
|
||||||
|
PROP_NAME,
|
||||||
|
PROP_PREPARED,
|
||||||
|
PROP_START_FRAME,
|
||||||
|
PROP_WIDTH,
|
||||||
|
|
||||||
|
N_PROPS
|
||||||
|
};
|
||||||
|
|
||||||
|
static GParamSpec *properties[N_PROPS] = { NULL, };
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (OttieCreation, ottie_creation, G_TYPE_OBJECT)
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_creation_set_property (GObject *object,
|
||||||
|
guint prop_id,
|
||||||
|
const GValue *value,
|
||||||
|
GParamSpec *pspec)
|
||||||
|
|
||||||
|
{
|
||||||
|
//OttieCreation *self = OTTIE_CREATION (object);
|
||||||
|
|
||||||
|
switch (prop_id)
|
||||||
|
{
|
||||||
|
|
||||||
|
default:
|
||||||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_creation_get_property (GObject *object,
|
||||||
|
guint prop_id,
|
||||||
|
GValue *value,
|
||||||
|
GParamSpec *pspec)
|
||||||
|
{
|
||||||
|
OttieCreation *self = OTTIE_CREATION (object);
|
||||||
|
|
||||||
|
switch (prop_id)
|
||||||
|
{
|
||||||
|
case PROP_END_FRAME:
|
||||||
|
g_value_set_double (value, self->end_frame);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PROP_FRAME_RATE:
|
||||||
|
g_value_set_double (value, self->frame_rate);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PROP_HEIGHT:
|
||||||
|
g_value_set_double (value, self->height);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PROP_PREPARED:
|
||||||
|
g_value_set_boolean (value, self->cancellable != NULL);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PROP_NAME:
|
||||||
|
g_value_set_string (value, self->name);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PROP_START_FRAME:
|
||||||
|
g_value_set_double (value, self->start_frame);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PROP_WIDTH:
|
||||||
|
g_value_set_double (value, self->width);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_creation_stop_loading (OttieCreation *self,
|
||||||
|
gboolean emit)
|
||||||
|
{
|
||||||
|
if (self->cancellable == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
g_cancellable_cancel (self->cancellable);
|
||||||
|
g_clear_object (&self->cancellable);
|
||||||
|
|
||||||
|
if (emit)
|
||||||
|
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_LOADING]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_creation_reset (OttieCreation *self)
|
||||||
|
{
|
||||||
|
g_clear_object (&self->layers);
|
||||||
|
g_hash_table_remove_all (self->composition_assets);
|
||||||
|
|
||||||
|
g_clear_pointer (&self->name, g_free);
|
||||||
|
self->frame_rate = 0;
|
||||||
|
self->start_frame = 0;
|
||||||
|
self->end_frame = 0;
|
||||||
|
self->width = 0;
|
||||||
|
self->height = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_creation_dispose (GObject *object)
|
||||||
|
{
|
||||||
|
OttieCreation *self = OTTIE_CREATION (object);
|
||||||
|
|
||||||
|
ottie_creation_stop_loading (self, FALSE);
|
||||||
|
ottie_creation_reset (self);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (ottie_creation_parent_class)->dispose (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_creation_finalize (GObject *object)
|
||||||
|
{
|
||||||
|
OttieCreation *self = OTTIE_CREATION (object);
|
||||||
|
|
||||||
|
g_hash_table_unref (self->composition_assets);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (ottie_creation_parent_class)->finalize (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_creation_class_init (OttieCreationClass *class)
|
||||||
|
{
|
||||||
|
GObjectClass *gobject_class = G_OBJECT_CLASS (class);
|
||||||
|
|
||||||
|
gobject_class->set_property = ottie_creation_set_property;
|
||||||
|
gobject_class->get_property = ottie_creation_get_property;
|
||||||
|
gobject_class->finalize = ottie_creation_finalize;
|
||||||
|
gobject_class->dispose = ottie_creation_dispose;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OttieCreation:end-frame:
|
||||||
|
*
|
||||||
|
* End frame of the creation
|
||||||
|
*/
|
||||||
|
properties[PROP_END_FRAME] =
|
||||||
|
g_param_spec_double ("end-frame",
|
||||||
|
"End frame",
|
||||||
|
"End frame of the creation",
|
||||||
|
0.0, G_MAXDOUBLE, 0.0,
|
||||||
|
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OttieCreation:loading:
|
||||||
|
*
|
||||||
|
* Whether the creation is currently loading.
|
||||||
|
*/
|
||||||
|
properties[PROP_LOADING] =
|
||||||
|
g_param_spec_boolean ("loading",
|
||||||
|
"Loading",
|
||||||
|
"Whether the creation is currently loading",
|
||||||
|
FALSE,
|
||||||
|
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OttieCreation:frame-rate:
|
||||||
|
*
|
||||||
|
* Frame rate of this creation
|
||||||
|
*/
|
||||||
|
properties[PROP_FRAME_RATE] =
|
||||||
|
g_param_spec_double ("frame-rate",
|
||||||
|
"Frame rate",
|
||||||
|
"Frame rate of this creation",
|
||||||
|
0.0, G_MAXDOUBLE, 0.0,
|
||||||
|
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OttieCreation:height:
|
||||||
|
*
|
||||||
|
* Height of this creation
|
||||||
|
*/
|
||||||
|
properties[PROP_HEIGHT] =
|
||||||
|
g_param_spec_double ("height",
|
||||||
|
"Height",
|
||||||
|
"Height of this creation",
|
||||||
|
0.0, G_MAXDOUBLE, 0.0,
|
||||||
|
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OttieCreation:name:
|
||||||
|
*
|
||||||
|
* The name of the creation.
|
||||||
|
*/
|
||||||
|
properties[PROP_NAME] =
|
||||||
|
g_param_spec_string ("name",
|
||||||
|
"Name",
|
||||||
|
"The name of the creation",
|
||||||
|
NULL,
|
||||||
|
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OttieCreation:prepared:
|
||||||
|
*
|
||||||
|
* Whether the creation is prepared to render
|
||||||
|
*/
|
||||||
|
properties[PROP_PREPARED] =
|
||||||
|
g_param_spec_boolean ("prepared",
|
||||||
|
"Prepared",
|
||||||
|
"Whether the creation is prepared to render",
|
||||||
|
FALSE,
|
||||||
|
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OttieCreation:start-frame:
|
||||||
|
*
|
||||||
|
* Start frame of the creation
|
||||||
|
*/
|
||||||
|
properties[PROP_START_FRAME] =
|
||||||
|
g_param_spec_double ("start-frame",
|
||||||
|
"Start frame",
|
||||||
|
"Start frame of the creation",
|
||||||
|
0.0, G_MAXDOUBLE, 0.0,
|
||||||
|
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OttieCreation:width:
|
||||||
|
*
|
||||||
|
* Width of this creation
|
||||||
|
*/
|
||||||
|
properties[PROP_WIDTH] =
|
||||||
|
g_param_spec_double ("width",
|
||||||
|
"Width",
|
||||||
|
"Width of this creation",
|
||||||
|
0.0, G_MAXDOUBLE, 0.0,
|
||||||
|
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||||
|
|
||||||
|
g_object_class_install_properties (gobject_class, N_PROPS, properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_creation_init (OttieCreation *self)
|
||||||
|
{
|
||||||
|
self->composition_assets = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ottie_creation_is_loading:
|
||||||
|
* @self: a #OttieCreation
|
||||||
|
*
|
||||||
|
* Returns whether @self is still in the process of loading. This may not just involve
|
||||||
|
* the creation itself, but also any assets that are a part of the creation.
|
||||||
|
*
|
||||||
|
* Returns: %TRUE if the creation is loading
|
||||||
|
*/
|
||||||
|
gboolean
|
||||||
|
ottie_creation_is_loading (OttieCreation *self)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (OTTIE_IS_CREATION (self), FALSE);
|
||||||
|
|
||||||
|
return self->cancellable != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ottie_creation_is_prepared:
|
||||||
|
* @self: a #OttieCreation
|
||||||
|
*
|
||||||
|
* Returns whether @self has successfully loaded a document that it can display.
|
||||||
|
*
|
||||||
|
* Returns: %TRUE if the creation can be used
|
||||||
|
*/
|
||||||
|
gboolean
|
||||||
|
ottie_creation_is_prepared (OttieCreation *self)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (OTTIE_IS_CREATION (self), FALSE);
|
||||||
|
|
||||||
|
return self->frame_rate > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ottie_creation_get_name:
|
||||||
|
* @self: a #OttieCreation
|
||||||
|
*
|
||||||
|
* Returns the name of the current creation or %NULL if the creation is unnamed.
|
||||||
|
*
|
||||||
|
* Returns: (allow-none): The name of the creation
|
||||||
|
*/
|
||||||
|
const char *
|
||||||
|
ottie_creation_get_name (OttieCreation *self)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (OTTIE_IS_CREATION (self), FALSE);
|
||||||
|
|
||||||
|
return self->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_creation_emit_error (OttieCreation *self,
|
||||||
|
const GError *error)
|
||||||
|
{
|
||||||
|
g_print ("Ottie is sad: %s\n", error->message);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char *id;
|
||||||
|
OttieComposition *composition;
|
||||||
|
} OttieParserAsset;
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
ottie_creation_parse_asset (JsonReader *reader,
|
||||||
|
gsize offset,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
OttieParserOption options[] = {
|
||||||
|
{ "id", ottie_parser_option_string, G_STRUCT_OFFSET (OttieParserAsset, id) },
|
||||||
|
{ "layers", ottie_composition_parse_layers, G_STRUCT_OFFSET (OttieParserAsset, composition) },
|
||||||
|
};
|
||||||
|
OttieCreation *self = data;
|
||||||
|
OttieParserAsset asset = { };
|
||||||
|
gboolean result;
|
||||||
|
|
||||||
|
result = ottie_parser_parse_object (reader, "asset", options, G_N_ELEMENTS (options), &asset);
|
||||||
|
|
||||||
|
if (result)
|
||||||
|
{
|
||||||
|
if (asset.id == NULL)
|
||||||
|
ottie_parser_error_syntax (reader, "No name given to asset");
|
||||||
|
else if (asset.composition == NULL)
|
||||||
|
ottie_parser_error_syntax (reader, "No composition layer or image asset defined for name %s", asset.id);
|
||||||
|
else
|
||||||
|
g_hash_table_insert (self->composition_assets, g_strdup (asset.id), g_object_ref (asset.composition));
|
||||||
|
}
|
||||||
|
|
||||||
|
g_clear_pointer (&asset.id, g_free);
|
||||||
|
g_clear_object (&asset.composition);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
ottie_creation_parse_assets (JsonReader *reader,
|
||||||
|
gsize offset,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
return ottie_parser_parse_array (reader, "assets",
|
||||||
|
0, G_MAXUINT, NULL,
|
||||||
|
offset, 0,
|
||||||
|
ottie_creation_parse_asset,
|
||||||
|
data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
ottie_creation_parse_marker (JsonReader *reader,
|
||||||
|
gsize offset,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
ottie_parser_error_unsupported (reader, "Markers are not implemented yet.");
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
ottie_creation_parse_markers (JsonReader *reader,
|
||||||
|
gsize offset,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
return ottie_parser_parse_array (reader, "markers",
|
||||||
|
0, G_MAXUINT, NULL,
|
||||||
|
offset, 0,
|
||||||
|
ottie_creation_parse_marker,
|
||||||
|
data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
ottie_creation_load_from_reader (OttieCreation *self,
|
||||||
|
JsonReader *reader)
|
||||||
|
{
|
||||||
|
OttieParserOption options[] = {
|
||||||
|
{ "fr", ottie_parser_option_double, G_STRUCT_OFFSET (OttieCreation, frame_rate) },
|
||||||
|
{ "w", ottie_parser_option_double, G_STRUCT_OFFSET (OttieCreation, width) },
|
||||||
|
{ "h", ottie_parser_option_double, G_STRUCT_OFFSET (OttieCreation, height) },
|
||||||
|
{ "nm", ottie_parser_option_string, G_STRUCT_OFFSET (OttieCreation, name) },
|
||||||
|
{ "ip", ottie_parser_option_double, G_STRUCT_OFFSET (OttieCreation, start_frame) },
|
||||||
|
{ "op", ottie_parser_option_double, G_STRUCT_OFFSET (OttieCreation, end_frame) },
|
||||||
|
{ "ddd", ottie_parser_option_3d, 0 },
|
||||||
|
{ "v", ottie_parser_option_skip, 0 },
|
||||||
|
{ "layers", ottie_composition_parse_layers, G_STRUCT_OFFSET (OttieCreation, layers) },
|
||||||
|
{ "assets", ottie_creation_parse_assets, 0 },
|
||||||
|
{ "markers", ottie_creation_parse_markers, 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
return ottie_parser_parse_object (reader, "toplevel", options, G_N_ELEMENTS (options), self);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_creation_update_layers (OttieCreation *self)
|
||||||
|
{
|
||||||
|
GHashTableIter iter;
|
||||||
|
gpointer layer;
|
||||||
|
|
||||||
|
g_hash_table_iter_init (&iter, self->composition_assets);
|
||||||
|
|
||||||
|
while (g_hash_table_iter_next (&iter, NULL, &layer))
|
||||||
|
ottie_layer_update (layer, self->composition_assets);
|
||||||
|
|
||||||
|
ottie_layer_update (OTTIE_LAYER (self->layers), self->composition_assets);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_creation_notify_prepared (OttieCreation *self)
|
||||||
|
{
|
||||||
|
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_PREPARED]);
|
||||||
|
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FRAME_RATE]);
|
||||||
|
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_WIDTH]);
|
||||||
|
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_HEIGHT]);
|
||||||
|
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_START_FRAME]);
|
||||||
|
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_END_FRAME]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
ottie_creation_load_from_node (OttieCreation *self,
|
||||||
|
JsonNode *root)
|
||||||
|
{
|
||||||
|
JsonReader *reader = json_reader_new (root);
|
||||||
|
gboolean result;
|
||||||
|
|
||||||
|
result = ottie_creation_load_from_reader (self, reader);
|
||||||
|
|
||||||
|
g_object_unref (reader);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_creation_load_file_parsed (GObject *parser,
|
||||||
|
GAsyncResult *res,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
OttieCreation *self = data;
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
|
if (!json_parser_load_from_stream_finish (JSON_PARSER (parser), res, &error))
|
||||||
|
{
|
||||||
|
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
||||||
|
return;
|
||||||
|
|
||||||
|
ottie_creation_emit_error (self, error);
|
||||||
|
g_error_free (error);
|
||||||
|
ottie_creation_stop_loading (self, TRUE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_object_freeze_notify (G_OBJECT (self));
|
||||||
|
|
||||||
|
if (ottie_creation_load_from_node (self, json_parser_get_root (JSON_PARSER (parser))))
|
||||||
|
{
|
||||||
|
ottie_creation_update_layers (self);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ottie_creation_reset (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
ottie_creation_stop_loading (self, TRUE);
|
||||||
|
ottie_creation_notify_prepared (self);
|
||||||
|
|
||||||
|
g_object_thaw_notify (G_OBJECT (self));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_creation_load_file_open (GObject *file,
|
||||||
|
GAsyncResult *res,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
OttieCreation *self = data;
|
||||||
|
GFileInputStream *stream;
|
||||||
|
GError *error = NULL;
|
||||||
|
JsonParser *parser;
|
||||||
|
|
||||||
|
stream = g_file_read_finish (G_FILE (file), res, &error);
|
||||||
|
if (stream == NULL)
|
||||||
|
{
|
||||||
|
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
||||||
|
return;
|
||||||
|
|
||||||
|
ottie_creation_emit_error (self, error);
|
||||||
|
g_error_free (error);
|
||||||
|
ottie_creation_stop_loading (self, TRUE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
parser = json_parser_new ();
|
||||||
|
json_parser_load_from_stream_async (parser,
|
||||||
|
G_INPUT_STREAM (stream),
|
||||||
|
self->cancellable,
|
||||||
|
ottie_creation_load_file_parsed,
|
||||||
|
self);
|
||||||
|
g_object_unref (parser);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ottie_creation_load_bytes (OttieCreation *self,
|
||||||
|
GBytes *bytes)
|
||||||
|
{
|
||||||
|
GError *error = NULL;
|
||||||
|
JsonParser *parser;
|
||||||
|
|
||||||
|
g_return_if_fail (OTTIE_IS_CREATION (self));
|
||||||
|
g_return_if_fail (bytes != NULL);
|
||||||
|
|
||||||
|
g_object_freeze_notify (G_OBJECT (self));
|
||||||
|
|
||||||
|
ottie_creation_stop_loading (self, FALSE);
|
||||||
|
ottie_creation_reset (self);
|
||||||
|
|
||||||
|
parser = json_parser_new ();
|
||||||
|
if (json_parser_load_from_data (parser,
|
||||||
|
g_bytes_get_data (bytes, NULL),
|
||||||
|
g_bytes_get_size (bytes),
|
||||||
|
&error))
|
||||||
|
{
|
||||||
|
if (ottie_creation_load_from_node (self, json_parser_get_root (JSON_PARSER (parser))))
|
||||||
|
{
|
||||||
|
ottie_creation_update_layers (self);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ottie_creation_reset (self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ottie_creation_emit_error (self, error);
|
||||||
|
g_error_free (error);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_object_unref (parser);
|
||||||
|
|
||||||
|
ottie_creation_notify_prepared (self);
|
||||||
|
|
||||||
|
g_object_thaw_notify (G_OBJECT (self));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ottie_creation_load_file (OttieCreation *self,
|
||||||
|
GFile *file)
|
||||||
|
{
|
||||||
|
g_return_if_fail (OTTIE_IS_CREATION (self));
|
||||||
|
g_return_if_fail (G_IS_FILE (file));
|
||||||
|
|
||||||
|
g_object_freeze_notify (G_OBJECT (self));
|
||||||
|
|
||||||
|
ottie_creation_stop_loading (self, FALSE);
|
||||||
|
if (self->frame_rate)
|
||||||
|
{
|
||||||
|
ottie_creation_reset (self);
|
||||||
|
ottie_creation_notify_prepared (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
self->cancellable = g_cancellable_new ();
|
||||||
|
|
||||||
|
g_file_read_async (file,
|
||||||
|
G_PRIORITY_DEFAULT,
|
||||||
|
self->cancellable,
|
||||||
|
ottie_creation_load_file_open,
|
||||||
|
self);
|
||||||
|
|
||||||
|
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_LOADING]);
|
||||||
|
|
||||||
|
g_object_thaw_notify (G_OBJECT (self));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ottie_creation_load_filename (OttieCreation *self,
|
||||||
|
const char *filename)
|
||||||
|
{
|
||||||
|
GFile *file;
|
||||||
|
|
||||||
|
g_return_if_fail (OTTIE_IS_CREATION (self));
|
||||||
|
g_return_if_fail (filename != NULL);
|
||||||
|
|
||||||
|
file = g_file_new_for_path (filename);
|
||||||
|
|
||||||
|
ottie_creation_load_file (self, file);
|
||||||
|
|
||||||
|
g_clear_object (&file);
|
||||||
|
}
|
||||||
|
|
||||||
|
OttieCreation *
|
||||||
|
ottie_creation_new (void)
|
||||||
|
{
|
||||||
|
return g_object_new (OTTIE_TYPE_CREATION, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
OttieCreation *
|
||||||
|
ottie_creation_new_for_file (GFile *file)
|
||||||
|
{
|
||||||
|
OttieCreation *self;
|
||||||
|
|
||||||
|
g_return_val_if_fail (G_IS_FILE (file), NULL);
|
||||||
|
|
||||||
|
self = g_object_new (OTTIE_TYPE_CREATION, NULL);
|
||||||
|
|
||||||
|
ottie_creation_load_file (self, file);
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
OttieCreation *
|
||||||
|
ottie_creation_new_for_filename (const char *filename)
|
||||||
|
{
|
||||||
|
OttieCreation *self;
|
||||||
|
GFile *file;
|
||||||
|
|
||||||
|
g_return_val_if_fail (filename != NULL, NULL);
|
||||||
|
|
||||||
|
file = g_file_new_for_path (filename);
|
||||||
|
|
||||||
|
self = ottie_creation_new_for_file (file);
|
||||||
|
|
||||||
|
g_clear_object (&file);
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
double
|
||||||
|
ottie_creation_get_frame_rate (OttieCreation *self)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (OTTIE_IS_CREATION (self), 0);
|
||||||
|
|
||||||
|
return self->frame_rate;
|
||||||
|
}
|
||||||
|
|
||||||
|
double
|
||||||
|
ottie_creation_get_start_frame (OttieCreation *self)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (OTTIE_IS_CREATION (self), 0);
|
||||||
|
|
||||||
|
return self->start_frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
double
|
||||||
|
ottie_creation_get_end_frame (OttieCreation *self)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (OTTIE_IS_CREATION (self), 0);
|
||||||
|
|
||||||
|
return self->end_frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
double
|
||||||
|
ottie_creation_get_width (OttieCreation *self)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (OTTIE_IS_CREATION (self), 0);
|
||||||
|
|
||||||
|
return self->width;
|
||||||
|
}
|
||||||
|
|
||||||
|
double
|
||||||
|
ottie_creation_get_height (OttieCreation *self)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (OTTIE_IS_CREATION (self), 0);
|
||||||
|
|
||||||
|
return self->height;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ottie_creation_snapshot (OttieCreation *self,
|
||||||
|
GtkSnapshot *snapshot,
|
||||||
|
double timestamp)
|
||||||
|
{
|
||||||
|
GskRenderNode *node;
|
||||||
|
OttieRender render;
|
||||||
|
|
||||||
|
if (self->layers == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
timestamp = timestamp * self->frame_rate;
|
||||||
|
|
||||||
|
ottie_render_init (&render);
|
||||||
|
|
||||||
|
ottie_layer_render (OTTIE_LAYER (self->layers), &render, timestamp);
|
||||||
|
node = ottie_render_get_node (&render);
|
||||||
|
if (node)
|
||||||
|
{
|
||||||
|
gtk_snapshot_append_node (snapshot, node);
|
||||||
|
gsk_render_node_unref (node);
|
||||||
|
}
|
||||||
|
|
||||||
|
ottie_render_clear (&render);
|
||||||
|
}
|
||||||
|
|
||||||
|
OttieComposition *
|
||||||
|
ottie_creation_get_composition (OttieCreation *self)
|
||||||
|
{
|
||||||
|
return self->layers;
|
||||||
|
}
|
||||||
|
|
||||||
83
ottie/ottiecreation.h
Normal file
83
ottie/ottiecreation.h
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* 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 __OTTIE_CREATION_H__
|
||||||
|
#define __OTTIE_CREATION_H__
|
||||||
|
|
||||||
|
#if !defined (__OTTIE_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||||
|
#error "Only <ottie/ottie.h> can be included directly."
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <gdk/gdk.h>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
#define OTTIE_TYPE_CREATION (ottie_creation_get_type ())
|
||||||
|
#define OTTIE_CREATION(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), OTTIE_TYPE_CREATION, OttieCreation))
|
||||||
|
#define OTTIE_CREATION_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), OTTIE_TYPE_CREATION, OttieCreationClass))
|
||||||
|
#define OTTIE_IS_CREATION(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), OTTIE_TYPE_CREATION))
|
||||||
|
#define OTTIE_IS_CREATION_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), OTTIE_TYPE_CREATION))
|
||||||
|
#define OTTIE_CREATION_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), OTTIE_TYPE_CREATION, OttieCreationClass))
|
||||||
|
|
||||||
|
typedef struct _OttieCreation OttieCreation;
|
||||||
|
typedef struct _OttieCreationClass OttieCreationClass;
|
||||||
|
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
GType ottie_creation_get_type (void) G_GNUC_CONST;
|
||||||
|
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
OttieCreation * ottie_creation_new (void);
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
OttieCreation * ottie_creation_new_for_file (GFile *file);
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
OttieCreation * ottie_creation_new_for_filename (const char *filename);
|
||||||
|
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
void ottie_creation_load_file (OttieCreation *self,
|
||||||
|
GFile *file);
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
void ottie_creation_load_filename (OttieCreation *self,
|
||||||
|
const char *filename);
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
void ottie_creation_load_bytes (OttieCreation *self,
|
||||||
|
GBytes *bytes);
|
||||||
|
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
gboolean ottie_creation_is_loading (OttieCreation *self);
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
gboolean ottie_creation_is_prepared (OttieCreation *self);
|
||||||
|
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
const char * ottie_creation_get_name (OttieCreation *self);
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
double ottie_creation_get_frame_rate (OttieCreation *self);
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
double ottie_creation_get_start_frame (OttieCreation *self);
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
double ottie_creation_get_end_frame (OttieCreation *self);
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
double ottie_creation_get_width (OttieCreation *self);
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
double ottie_creation_get_height (OttieCreation *self);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* __OTTIE_CREATION_H__ */
|
||||||
40
ottie/ottiecreationprivate.h
Normal file
40
ottie/ottiecreationprivate.h
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* 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 __OTTIE_CREATION_PRIVATE_H__
|
||||||
|
#define __OTTIE_CREATION_PRIVATE_H__
|
||||||
|
|
||||||
|
#include "ottiecreation.h"
|
||||||
|
|
||||||
|
#include "ottiecompositionprivate.h"
|
||||||
|
|
||||||
|
#include <gtk/gtk.h>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
|
||||||
|
void ottie_creation_snapshot (OttieCreation *self,
|
||||||
|
GtkSnapshot *snapshot,
|
||||||
|
double timestamp);
|
||||||
|
|
||||||
|
OttieComposition * ottie_creation_get_composition (OttieCreation *self);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* __OTTIE_CREATION_PRIVATE_H__ */
|
||||||
121
ottie/ottiedoublevalue.c
Normal file
121
ottie/ottiedoublevalue.c
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2020 Benjamin Otte
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* Authors: Benjamin Otte <otte@gnome.org>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "ottiedoublevalueprivate.h"
|
||||||
|
|
||||||
|
#include "ottieparserprivate.h"
|
||||||
|
|
||||||
|
#include <glib/gi18n-lib.h>
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
ottie_double_value_parse_value (JsonReader *reader,
|
||||||
|
gsize offset,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
gboolean result, array;
|
||||||
|
|
||||||
|
/* Lottie being Lottie, single values may get dumped into arrays. */
|
||||||
|
array = json_reader_is_array (reader);
|
||||||
|
if (array)
|
||||||
|
json_reader_read_element (reader, 0);
|
||||||
|
|
||||||
|
result = ottie_parser_option_double (reader, offset, data);
|
||||||
|
|
||||||
|
if (array)
|
||||||
|
json_reader_end_element (reader);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static double
|
||||||
|
ottie_double_value_interpolate (double start,
|
||||||
|
double end,
|
||||||
|
double progress)
|
||||||
|
{
|
||||||
|
return start + (end - start) * progress;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define OTTIE_KEYFRAMES_NAME ottie_double_keyframes
|
||||||
|
#define OTTIE_KEYFRAMES_TYPE_NAME OttieDoubleKeyframes
|
||||||
|
#define OTTIE_KEYFRAMES_ELEMENT_TYPE double
|
||||||
|
#define OTTIE_KEYFRAMES_PARSE_FUNC ottie_double_value_parse_value
|
||||||
|
#define OTTIE_KEYFRAMES_INTERPOLATE_FUNC ottie_double_value_interpolate
|
||||||
|
#include "ottiekeyframesimpl.c"
|
||||||
|
|
||||||
|
void
|
||||||
|
ottie_double_value_init (OttieDoubleValue *self,
|
||||||
|
double value)
|
||||||
|
{
|
||||||
|
self->is_static = TRUE;
|
||||||
|
self->static_value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ottie_double_value_clear (OttieDoubleValue *self)
|
||||||
|
{
|
||||||
|
if (!self->is_static)
|
||||||
|
g_clear_pointer (&self->keyframes, ottie_double_keyframes_free);
|
||||||
|
}
|
||||||
|
|
||||||
|
double
|
||||||
|
ottie_double_value_get (OttieDoubleValue *self,
|
||||||
|
double timestamp)
|
||||||
|
{
|
||||||
|
if (self->is_static)
|
||||||
|
return self->static_value;
|
||||||
|
|
||||||
|
return ottie_double_keyframes_get (self->keyframes, timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
ottie_double_value_parse (JsonReader *reader,
|
||||||
|
gsize offset,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
OttieDoubleValue *self = (OttieDoubleValue *) ((guint8 *) data + GPOINTER_TO_SIZE (offset));
|
||||||
|
|
||||||
|
if (json_reader_read_member (reader, "k"))
|
||||||
|
{
|
||||||
|
if (!json_reader_is_array (reader))
|
||||||
|
{
|
||||||
|
self->is_static = TRUE;
|
||||||
|
self->static_value = json_reader_get_double_value (reader);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
self->is_static = FALSE;
|
||||||
|
self->keyframes = ottie_double_keyframes_parse (reader);
|
||||||
|
if (self->keyframes == NULL)
|
||||||
|
{
|
||||||
|
json_reader_end_member (reader);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ottie_parser_error_syntax (reader, "Property is not a number");
|
||||||
|
}
|
||||||
|
json_reader_end_member (reader);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
58
ottie/ottiedoublevalueprivate.h
Normal file
58
ottie/ottiedoublevalueprivate.h
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* 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 __OTTIE_DOUBLE_VALUE_PRIVATE_H__
|
||||||
|
#define __OTTIE_DOUBLE_VALUE_PRIVATE_H__
|
||||||
|
|
||||||
|
#include <json-glib/json-glib.h>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
typedef struct _OttieDoubleValue OttieDoubleValue;
|
||||||
|
|
||||||
|
struct _OttieDoubleValue
|
||||||
|
{
|
||||||
|
gboolean is_static;
|
||||||
|
union {
|
||||||
|
double static_value;
|
||||||
|
gpointer keyframes;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
void ottie_double_value_init (OttieDoubleValue *self,
|
||||||
|
double value);
|
||||||
|
void ottie_double_value_clear (OttieDoubleValue *self);
|
||||||
|
|
||||||
|
static inline gboolean ottie_double_value_is_static (OttieDoubleValue *self);
|
||||||
|
double ottie_double_value_get (OttieDoubleValue *self,
|
||||||
|
double timestamp);
|
||||||
|
|
||||||
|
gboolean ottie_double_value_parse (JsonReader *reader,
|
||||||
|
gsize offset,
|
||||||
|
gpointer data);
|
||||||
|
|
||||||
|
static inline gboolean
|
||||||
|
ottie_double_value_is_static (OttieDoubleValue *self)
|
||||||
|
{
|
||||||
|
return self->is_static;
|
||||||
|
}
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* __OTTIE_DOUBLE_VALUE_PRIVATE_H__ */
|
||||||
138
ottie/ottieellipseshape.c
Normal file
138
ottie/ottieellipseshape.c
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
/*
|
||||||
|
* 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 "ottieellipseshapeprivate.h"
|
||||||
|
|
||||||
|
#include "ottiedoublevalueprivate.h"
|
||||||
|
#include "ottiepointvalueprivate.h"
|
||||||
|
#include "ottieparserprivate.h"
|
||||||
|
#include "ottieshapeprivate.h"
|
||||||
|
|
||||||
|
#include <glib/gi18n-lib.h>
|
||||||
|
|
||||||
|
struct _OttieEllipseShape
|
||||||
|
{
|
||||||
|
OttieShape parent;
|
||||||
|
|
||||||
|
double diellipseion;
|
||||||
|
OttiePointValue position;
|
||||||
|
OttiePointValue size;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _OttieEllipseShapeClass
|
||||||
|
{
|
||||||
|
OttieShapeClass parent_class;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (OttieEllipseShape, ottie_ellipse_shape, OTTIE_TYPE_SHAPE)
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_ellipse_shape_render (OttieShape *shape,
|
||||||
|
OttieRender *render,
|
||||||
|
double timestamp)
|
||||||
|
{
|
||||||
|
OttieEllipseShape *self = OTTIE_ELLIPSE_SHAPE (shape);
|
||||||
|
graphene_point_t p, s;
|
||||||
|
GskPathBuilder *builder;
|
||||||
|
const float weight = sqrt(0.5f);
|
||||||
|
|
||||||
|
ottie_point_value_get (&self->position, timestamp, &p);
|
||||||
|
ottie_point_value_get (&self->size, timestamp, &s);
|
||||||
|
s.x /= 2;
|
||||||
|
s.y /= 2;
|
||||||
|
|
||||||
|
builder = gsk_path_builder_new ();
|
||||||
|
|
||||||
|
gsk_path_builder_move_to (builder,
|
||||||
|
p.x, p.y - s.y);
|
||||||
|
gsk_path_builder_conic_to (builder,
|
||||||
|
p.x + s.x, p.y - s.y,
|
||||||
|
p.x + s.x, p.y,
|
||||||
|
weight);
|
||||||
|
gsk_path_builder_conic_to (builder,
|
||||||
|
p.x + s.x, p.y + s.y,
|
||||||
|
p.x, p.y + s.y,
|
||||||
|
weight);
|
||||||
|
gsk_path_builder_conic_to (builder,
|
||||||
|
p.x - s.x, p.y + s.y,
|
||||||
|
p.x - s.x, p.y,
|
||||||
|
weight);
|
||||||
|
gsk_path_builder_conic_to (builder,
|
||||||
|
p.x - s.x, p.y - s.y,
|
||||||
|
p.x, p.y - s.y,
|
||||||
|
weight);
|
||||||
|
gsk_path_builder_close (builder);
|
||||||
|
|
||||||
|
ottie_render_add_path (render,
|
||||||
|
gsk_path_builder_free_to_path (builder));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_ellipse_shape_dispose (GObject *object)
|
||||||
|
{
|
||||||
|
OttieEllipseShape *self = OTTIE_ELLIPSE_SHAPE (object);
|
||||||
|
|
||||||
|
ottie_point_value_clear (&self->position);
|
||||||
|
ottie_point_value_clear (&self->size);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (ottie_ellipse_shape_parent_class)->dispose (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_ellipse_shape_class_init (OttieEllipseShapeClass *klass)
|
||||||
|
{
|
||||||
|
OttieShapeClass *shape_class = OTTIE_SHAPE_CLASS (klass);
|
||||||
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||||
|
|
||||||
|
shape_class->render = ottie_ellipse_shape_render;
|
||||||
|
|
||||||
|
gobject_class->dispose = ottie_ellipse_shape_dispose;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_ellipse_shape_init (OttieEllipseShape *self)
|
||||||
|
{
|
||||||
|
ottie_point_value_init (&self->position, &GRAPHENE_POINT_INIT (0, 0));
|
||||||
|
ottie_point_value_init (&self->size, &GRAPHENE_POINT_INIT (0, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
OttieShape *
|
||||||
|
ottie_ellipse_shape_parse (JsonReader *reader)
|
||||||
|
{
|
||||||
|
OttieParserOption options[] = {
|
||||||
|
OTTIE_PARSE_OPTIONS_SHAPE,
|
||||||
|
{ "d", ottie_parser_option_double, G_STRUCT_OFFSET (OttieEllipseShape, diellipseion) },
|
||||||
|
{ "p", ottie_point_value_parse, G_STRUCT_OFFSET (OttieEllipseShape, position) },
|
||||||
|
{ "s", ottie_point_value_parse, G_STRUCT_OFFSET (OttieEllipseShape, size) },
|
||||||
|
};
|
||||||
|
OttieEllipseShape *self;
|
||||||
|
|
||||||
|
self = g_object_new (OTTIE_TYPE_ELLIPSE_SHAPE, NULL);
|
||||||
|
|
||||||
|
if (!ottie_parser_parse_object (reader, "ellipse shape", options, G_N_ELEMENTS (options), self))
|
||||||
|
{
|
||||||
|
g_object_unref (self);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return OTTIE_SHAPE (self);
|
||||||
|
}
|
||||||
|
|
||||||
45
ottie/ottieellipseshapeprivate.h
Normal file
45
ottie/ottieellipseshapeprivate.h
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* 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 __OTTIE_ELLIPSE_SHAPE_PRIVATE_H__
|
||||||
|
#define __OTTIE_ELLIPSE_SHAPE_PRIVATE_H__
|
||||||
|
|
||||||
|
#include "ottieshapeprivate.h"
|
||||||
|
|
||||||
|
#include <json-glib/json-glib.h>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
#define OTTIE_TYPE_ELLIPSE_SHAPE (ottie_ellipse_shape_get_type ())
|
||||||
|
#define OTTIE_ELLIPSE_SHAPE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), OTTIE_TYPE_ELLIPSE_SHAPE, OttieEllipseShape))
|
||||||
|
#define OTTIE_ELLIPSE_SHAPE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), OTTIE_TYPE_ELLIPSE_SHAPE, OttieEllipseShapeClass))
|
||||||
|
#define OTTIE_IS_ELLIPSE_SHAPE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), OTTIE_TYPE_ELLIPSE_SHAPE))
|
||||||
|
#define OTTIE_IS_ELLIPSE_SHAPE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), OTTIE_TYPE_ELLIPSE_SHAPE))
|
||||||
|
#define OTTIE_ELLIPSE_SHAPE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), OTTIE_TYPE_ELLIPSE_SHAPE, OttieEllipseShapeClass))
|
||||||
|
|
||||||
|
typedef struct _OttieEllipseShape OttieEllipseShape;
|
||||||
|
typedef struct _OttieEllipseShapeClass OttieEllipseShapeClass;
|
||||||
|
|
||||||
|
GType ottie_ellipse_shape_get_type (void) G_GNUC_CONST;
|
||||||
|
|
||||||
|
OttieShape * ottie_ellipse_shape_parse (JsonReader *reader);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* __OTTIE_ELLIPSE_SHAPE_PRIVATE_H__ */
|
||||||
132
ottie/ottiefillshape.c
Normal file
132
ottie/ottiefillshape.c
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
/*
|
||||||
|
* 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 "ottiefillshapeprivate.h"
|
||||||
|
|
||||||
|
#include "ottiecolorvalueprivate.h"
|
||||||
|
#include "ottiedoublevalueprivate.h"
|
||||||
|
#include "ottieparserprivate.h"
|
||||||
|
#include "ottieshapeprivate.h"
|
||||||
|
|
||||||
|
#include <glib/gi18n-lib.h>
|
||||||
|
#include <gsk/gsk.h>
|
||||||
|
|
||||||
|
struct _OttieFillShape
|
||||||
|
{
|
||||||
|
OttieShape parent;
|
||||||
|
|
||||||
|
OttieDoubleValue opacity;
|
||||||
|
OttieColorValue color;
|
||||||
|
GskBlendMode blend_mode;
|
||||||
|
GskFillRule fill_rule;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _OttieFillShapeClass
|
||||||
|
{
|
||||||
|
OttieShapeClass parent_class;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (OttieFillShape, ottie_fill_shape, OTTIE_TYPE_SHAPE)
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_fill_shape_render (OttieShape *shape,
|
||||||
|
OttieRender *render,
|
||||||
|
double timestamp)
|
||||||
|
{
|
||||||
|
OttieFillShape *self = OTTIE_FILL_SHAPE (shape);
|
||||||
|
GskPath *path;
|
||||||
|
graphene_rect_t bounds;
|
||||||
|
GdkRGBA color;
|
||||||
|
double opacity;
|
||||||
|
GskRenderNode *color_node;
|
||||||
|
|
||||||
|
opacity = ottie_double_value_get (&self->opacity, timestamp);
|
||||||
|
opacity = CLAMP (opacity, 0, 100);
|
||||||
|
ottie_color_value_get (&self->color, timestamp, &color);
|
||||||
|
color.alpha = color.alpha * opacity / 100.f;
|
||||||
|
if (gdk_rgba_is_clear (&color))
|
||||||
|
return;
|
||||||
|
|
||||||
|
path = ottie_render_get_path (render);
|
||||||
|
if (gsk_path_is_empty (path))
|
||||||
|
return;
|
||||||
|
|
||||||
|
gsk_path_get_bounds (path, &bounds);
|
||||||
|
color_node = gsk_color_node_new (&color, &bounds);
|
||||||
|
|
||||||
|
ottie_render_add_node (render, gsk_fill_node_new (color_node, path, self->fill_rule));
|
||||||
|
|
||||||
|
gsk_render_node_unref (color_node);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_fill_shape_dispose (GObject *object)
|
||||||
|
{
|
||||||
|
OttieFillShape *self = OTTIE_FILL_SHAPE (object);
|
||||||
|
|
||||||
|
ottie_double_value_clear (&self->opacity);
|
||||||
|
ottie_color_value_clear (&self->color);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (ottie_fill_shape_parent_class)->dispose (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_fill_shape_class_init (OttieFillShapeClass *klass)
|
||||||
|
{
|
||||||
|
OttieShapeClass *shape_class = OTTIE_SHAPE_CLASS (klass);
|
||||||
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||||
|
|
||||||
|
shape_class->render = ottie_fill_shape_render;
|
||||||
|
|
||||||
|
gobject_class->dispose = ottie_fill_shape_dispose;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_fill_shape_init (OttieFillShape *self)
|
||||||
|
{
|
||||||
|
ottie_double_value_init (&self->opacity, 100);
|
||||||
|
ottie_color_value_init (&self->color, &(GdkRGBA) { 0, 0, 0, 1 });
|
||||||
|
self->fill_rule = GSK_FILL_RULE_WINDING;
|
||||||
|
}
|
||||||
|
|
||||||
|
OttieShape *
|
||||||
|
ottie_fill_shape_parse (JsonReader *reader)
|
||||||
|
{
|
||||||
|
OttieParserOption options[] = {
|
||||||
|
OTTIE_PARSE_OPTIONS_SHAPE,
|
||||||
|
{ "o", ottie_double_value_parse, G_STRUCT_OFFSET (OttieFillShape, opacity) },
|
||||||
|
{ "c", ottie_color_value_parse, G_STRUCT_OFFSET (OttieFillShape, color) },
|
||||||
|
{ "bm", ottie_parser_option_blend_mode, G_STRUCT_OFFSET (OttieFillShape, blend_mode) },
|
||||||
|
{ "r", ottie_parser_option_fill_rule, G_STRUCT_OFFSET (OttieFillShape, fill_rule) },
|
||||||
|
};
|
||||||
|
OttieFillShape *self;
|
||||||
|
|
||||||
|
self = g_object_new (OTTIE_TYPE_FILL_SHAPE, NULL);
|
||||||
|
|
||||||
|
if (!ottie_parser_parse_object (reader, "fill shape", options, G_N_ELEMENTS (options), self))
|
||||||
|
{
|
||||||
|
g_object_unref (self);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return OTTIE_SHAPE (self);
|
||||||
|
}
|
||||||
|
|
||||||
45
ottie/ottiefillshapeprivate.h
Normal file
45
ottie/ottiefillshapeprivate.h
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* 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 __OTTIE_FILL_SHAPE_PRIVATE_H__
|
||||||
|
#define __OTTIE_FILL_SHAPE_PRIVATE_H__
|
||||||
|
|
||||||
|
#include "ottieshapeprivate.h"
|
||||||
|
|
||||||
|
#include <json-glib/json-glib.h>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
#define OTTIE_TYPE_FILL_SHAPE (ottie_fill_shape_get_type ())
|
||||||
|
#define OTTIE_FILL_SHAPE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), OTTIE_TYPE_FILL_SHAPE, OttieFillShape))
|
||||||
|
#define OTTIE_FILL_SHAPE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), OTTIE_TYPE_FILL_SHAPE, OttieFillShapeClass))
|
||||||
|
#define OTTIE_IS_FILL_SHAPE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), OTTIE_TYPE_FILL_SHAPE))
|
||||||
|
#define OTTIE_IS_FILL_SHAPE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), OTTIE_TYPE_FILL_SHAPE))
|
||||||
|
#define OTTIE_FILL_SHAPE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), OTTIE_TYPE_FILL_SHAPE, OttieFillShapeClass))
|
||||||
|
|
||||||
|
typedef struct _OttieFillShape OttieFillShape;
|
||||||
|
typedef struct _OttieFillShapeClass OttieFillShapeClass;
|
||||||
|
|
||||||
|
GType ottie_fill_shape_get_type (void) G_GNUC_CONST;
|
||||||
|
|
||||||
|
OttieShape * ottie_fill_shape_parse (JsonReader *reader);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* __OTTIE_FILL_SHAPE_PRIVATE_H__ */
|
||||||
247
ottie/ottiegroupshape.c
Normal file
247
ottie/ottiegroupshape.c
Normal file
@@ -0,0 +1,247 @@
|
|||||||
|
/*
|
||||||
|
* 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 "ottiegroupshapeprivate.h"
|
||||||
|
|
||||||
|
#include "ottieellipseshapeprivate.h"
|
||||||
|
#include "ottiefillshapeprivate.h"
|
||||||
|
#include "ottieparserprivate.h"
|
||||||
|
#include "ottiepathshapeprivate.h"
|
||||||
|
#include "ottierectshapeprivate.h"
|
||||||
|
#include "ottieshapeprivate.h"
|
||||||
|
#include "ottiestrokeshapeprivate.h"
|
||||||
|
#include "ottietransformprivate.h"
|
||||||
|
#include "ottietrimshapeprivate.h"
|
||||||
|
|
||||||
|
#include <glib/gi18n-lib.h>
|
||||||
|
#include <gsk/gsk.h>
|
||||||
|
|
||||||
|
#define GDK_ARRAY_ELEMENT_TYPE OttieShape *
|
||||||
|
#define GDK_ARRAY_FREE_FUNC g_object_unref
|
||||||
|
#define GDK_ARRAY_TYPE_NAME OttieShapeList
|
||||||
|
#define GDK_ARRAY_NAME ottie_shape_list
|
||||||
|
#define GDK_ARRAY_PREALLOC 4
|
||||||
|
#include "gdk/gdkarrayimpl.c"
|
||||||
|
|
||||||
|
struct _OttieGroupShape
|
||||||
|
{
|
||||||
|
OttieShape parent;
|
||||||
|
|
||||||
|
OttieShapeList shapes;
|
||||||
|
GskBlendMode blend_mode;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _OttieGroupShapeClass
|
||||||
|
{
|
||||||
|
OttieShapeClass parent_class;
|
||||||
|
};
|
||||||
|
|
||||||
|
static GType
|
||||||
|
ottie_group_shape_get_item_type (GListModel *list)
|
||||||
|
{
|
||||||
|
return OTTIE_TYPE_SHAPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static guint
|
||||||
|
ottie_group_shape_get_n_items (GListModel *list)
|
||||||
|
{
|
||||||
|
OttieGroupShape *self = OTTIE_GROUP_SHAPE (list);
|
||||||
|
|
||||||
|
return ottie_shape_list_get_size (&self->shapes);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gpointer
|
||||||
|
ottie_group_shape_get_item (GListModel *list,
|
||||||
|
guint position)
|
||||||
|
{
|
||||||
|
OttieGroupShape *self = OTTIE_GROUP_SHAPE (list);
|
||||||
|
|
||||||
|
if (position >= ottie_shape_list_get_size (&self->shapes))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return g_object_ref (ottie_shape_list_get (&self->shapes, position));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_group_shape_list_model_init (GListModelInterface *iface)
|
||||||
|
{
|
||||||
|
iface->get_item_type = ottie_group_shape_get_item_type;
|
||||||
|
iface->get_n_items = ottie_group_shape_get_n_items;
|
||||||
|
iface->get_item = ottie_group_shape_get_item;
|
||||||
|
}
|
||||||
|
|
||||||
|
G_DEFINE_TYPE_WITH_CODE (OttieGroupShape, ottie_group_shape, OTTIE_TYPE_SHAPE,
|
||||||
|
G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, ottie_group_shape_list_model_init))
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_group_shape_render (OttieShape *shape,
|
||||||
|
OttieRender *render,
|
||||||
|
double timestamp)
|
||||||
|
{
|
||||||
|
OttieGroupShape *self = OTTIE_GROUP_SHAPE (shape);
|
||||||
|
OttieRender child_render;
|
||||||
|
|
||||||
|
ottie_render_init (&child_render);
|
||||||
|
|
||||||
|
for (gsize i = 0; i < ottie_shape_list_get_size (&self->shapes); i++)
|
||||||
|
{
|
||||||
|
ottie_shape_render (ottie_shape_list_get (&self->shapes, i),
|
||||||
|
&child_render,
|
||||||
|
timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
ottie_render_merge (render, &child_render);
|
||||||
|
|
||||||
|
ottie_render_clear (&child_render);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_group_shape_dispose (GObject *object)
|
||||||
|
{
|
||||||
|
OttieGroupShape *self = OTTIE_GROUP_SHAPE (object);
|
||||||
|
|
||||||
|
ottie_shape_list_clear (&self->shapes);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (ottie_group_shape_parent_class)->dispose (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_group_shape_class_init (OttieGroupShapeClass *klass)
|
||||||
|
{
|
||||||
|
OttieShapeClass *shape_class = OTTIE_SHAPE_CLASS (klass);
|
||||||
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||||
|
|
||||||
|
shape_class->render = ottie_group_shape_render;
|
||||||
|
|
||||||
|
gobject_class->dispose = ottie_group_shape_dispose;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_group_shape_init (OttieGroupShape *self)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
ottie_group_shape_parse_shapes (JsonReader *reader,
|
||||||
|
gsize offset,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
OttieGroupShape *self = data;
|
||||||
|
|
||||||
|
if (!json_reader_is_array (reader))
|
||||||
|
{
|
||||||
|
ottie_parser_error_syntax (reader, "Shapes are not an array.");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; ; i++)
|
||||||
|
{
|
||||||
|
OttieShape *shape;
|
||||||
|
const char *type;
|
||||||
|
|
||||||
|
if (!json_reader_read_element (reader, i))
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (!json_reader_is_object (reader))
|
||||||
|
{
|
||||||
|
ottie_parser_error_syntax (reader, "Shape %d is not an object", i);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!json_reader_read_member (reader, "ty"))
|
||||||
|
{
|
||||||
|
ottie_parser_error_syntax (reader, "Shape %d has no type", i);
|
||||||
|
json_reader_end_member (reader);
|
||||||
|
json_reader_end_element (reader);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
type = json_reader_get_string_value (reader);
|
||||||
|
if (type == NULL || json_reader_get_error (reader))
|
||||||
|
{
|
||||||
|
ottie_parser_emit_error (reader, json_reader_get_error (reader));
|
||||||
|
json_reader_end_member (reader);
|
||||||
|
json_reader_end_element (reader);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
json_reader_end_member (reader);
|
||||||
|
|
||||||
|
if (g_str_equal (type, "el"))
|
||||||
|
shape = ottie_ellipse_shape_parse (reader);
|
||||||
|
else if (g_str_equal (type, "fl"))
|
||||||
|
shape = ottie_fill_shape_parse (reader);
|
||||||
|
else if (g_str_equal (type, "gr"))
|
||||||
|
shape = ottie_group_shape_parse (reader);
|
||||||
|
else if (g_str_equal (type, "rc"))
|
||||||
|
shape = ottie_rect_shape_parse (reader);
|
||||||
|
else if (g_str_equal (type, "sh"))
|
||||||
|
shape = ottie_path_shape_parse (reader);
|
||||||
|
else if (g_str_equal (type, "st"))
|
||||||
|
shape = ottie_stroke_shape_parse (reader);
|
||||||
|
else if (g_str_equal (type, "tm"))
|
||||||
|
shape = ottie_trim_shape_parse (reader);
|
||||||
|
else if (g_str_equal (type, "tr"))
|
||||||
|
shape = ottie_transform_parse (reader);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ottie_parser_error_value (reader, "Shape %d has unknown type \"%s\"", i, type);
|
||||||
|
shape = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shape)
|
||||||
|
ottie_shape_list_append (&self->shapes, shape);
|
||||||
|
json_reader_end_element (reader);
|
||||||
|
}
|
||||||
|
|
||||||
|
json_reader_end_element (reader);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
OttieShape *
|
||||||
|
ottie_group_shape_new (void)
|
||||||
|
{
|
||||||
|
return g_object_new (OTTIE_TYPE_GROUP_SHAPE, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
OttieShape *
|
||||||
|
ottie_group_shape_parse (JsonReader *reader)
|
||||||
|
{
|
||||||
|
OttieParserOption options[] = {
|
||||||
|
OTTIE_PARSE_OPTIONS_SHAPE,
|
||||||
|
{ "bm", ottie_parser_option_blend_mode, G_STRUCT_OFFSET (OttieGroupShape, blend_mode) },
|
||||||
|
{ "np", ottie_parser_option_skip_expression, 0 },
|
||||||
|
{ "cix", ottie_parser_option_skip_index, 0 },
|
||||||
|
{ "it", ottie_group_shape_parse_shapes, 0 },
|
||||||
|
};
|
||||||
|
OttieShape *self;
|
||||||
|
|
||||||
|
self = ottie_group_shape_new ();
|
||||||
|
|
||||||
|
if (!ottie_parser_parse_object (reader, "group shape", options, G_N_ELEMENTS (options), self))
|
||||||
|
{
|
||||||
|
g_object_unref (self);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
50
ottie/ottiegroupshapeprivate.h
Normal file
50
ottie/ottiegroupshapeprivate.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 __OTTIE_GROUP_SHAPE_PRIVATE_H__
|
||||||
|
#define __OTTIE_GROUP_SHAPE_PRIVATE_H__
|
||||||
|
|
||||||
|
#include "ottieshapeprivate.h"
|
||||||
|
|
||||||
|
#include <json-glib/json-glib.h>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
#define OTTIE_TYPE_GROUP_SHAPE (ottie_group_shape_get_type ())
|
||||||
|
#define OTTIE_GROUP_SHAPE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), OTTIE_TYPE_GROUP_SHAPE, OttieGroupShape))
|
||||||
|
#define OTTIE_GROUP_SHAPE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), OTTIE_TYPE_GROUP_SHAPE, OttieGroupShapeClass))
|
||||||
|
#define OTTIE_IS_GROUP_SHAPE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), OTTIE_TYPE_GROUP_SHAPE))
|
||||||
|
#define OTTIE_IS_GROUP_SHAPE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), OTTIE_TYPE_GROUP_SHAPE))
|
||||||
|
#define OTTIE_GROUP_SHAPE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), OTTIE_TYPE_GROUP_SHAPE, OttieGroupShapeClass))
|
||||||
|
|
||||||
|
typedef struct _OttieGroupShape OttieGroupShape;
|
||||||
|
typedef struct _OttieGroupShapeClass OttieGroupShapeClass;
|
||||||
|
|
||||||
|
GType ottie_group_shape_get_type (void) G_GNUC_CONST;
|
||||||
|
|
||||||
|
OttieShape * ottie_group_shape_new (void);
|
||||||
|
|
||||||
|
OttieShape * ottie_group_shape_parse (JsonReader *reader);
|
||||||
|
gboolean ottie_group_shape_parse_shapes (JsonReader *reader,
|
||||||
|
gsize offset,
|
||||||
|
gpointer data);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* __OTTIE_GROUP_SHAPE_PRIVATE_H__ */
|
||||||
15
ottie/ottieintl.h
Normal file
15
ottie/ottieintl.h
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
#ifndef __OTTIE_INTL_H__
|
||||||
|
#define __OTTIE_INTL_H__
|
||||||
|
|
||||||
|
#include <glib/gi18n-lib.h>
|
||||||
|
|
||||||
|
#ifdef ENABLE_NLS
|
||||||
|
#define P_(String) g_dgettext(GETTEXT_PACKAGE "-properties",String)
|
||||||
|
#else
|
||||||
|
#define P_(String) (String)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* not really I18N-related, but also a string marker macro */
|
||||||
|
#define I_(string) g_intern_static_string (string)
|
||||||
|
|
||||||
|
#endif
|
||||||
382
ottie/ottiekeyframesimpl.c
Normal file
382
ottie/ottiekeyframesimpl.c
Normal file
@@ -0,0 +1,382 @@
|
|||||||
|
/*
|
||||||
|
* 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 <glib.h>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
#ifndef OTTIE_KEYFRAMES_TYPE_NAME
|
||||||
|
#define OTTIE_KEYFRAMES_TYPE_NAME OttieKeyframes
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef OTTIE_KEYFRAMES_NAME
|
||||||
|
#define OTTIE_KEYFRAMES_NAME ottie_keyframes
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef OTTIE_KEYFRAMES_ELEMENT_TYPE
|
||||||
|
#define OTTIE_KEYFRAMES_ELEMENT_TYPE gpointer
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef OTTIE_KEYFRAMES_DIMENSIONS
|
||||||
|
#define OTTIE_KEYFRAMES_DIMENSIONS 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* make this readable */
|
||||||
|
#define _T_ OTTIE_KEYFRAMES_ELEMENT_TYPE
|
||||||
|
#define OttieKeyframes OTTIE_KEYFRAMES_TYPE_NAME
|
||||||
|
#define OttieKeyframe OTTIE_KEYFRAMES_TYPE_NAME ## Keyframe
|
||||||
|
#define OttieControlPoint OTTIE_KEYFRAMES_TYPE_NAME ## ControlPoint
|
||||||
|
#define ottie_keyframes_paste_more(OTTIE_KEYFRAMES_NAME, func_name) OTTIE_KEYFRAMES_NAME ## _ ## func_name
|
||||||
|
#define ottie_keyframes_paste(OTTIE_KEYFRAMES_NAME, func_name) ottie_keyframes_paste_more (OTTIE_KEYFRAMES_NAME, func_name)
|
||||||
|
#define ottie_keyframes(func_name) ottie_keyframes_paste (OTTIE_KEYFRAMES_NAME, func_name)
|
||||||
|
|
||||||
|
typedef struct OttieControlPoint OttieControlPoint;
|
||||||
|
typedef struct OttieKeyframe OttieKeyframe;
|
||||||
|
typedef struct OttieKeyframes OttieKeyframes;
|
||||||
|
|
||||||
|
struct OttieControlPoint
|
||||||
|
{
|
||||||
|
double x[OTTIE_KEYFRAMES_DIMENSIONS];
|
||||||
|
double y[OTTIE_KEYFRAMES_DIMENSIONS];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct OttieKeyframe
|
||||||
|
{
|
||||||
|
/* Cubic control points, but Lottie names them in and out points */
|
||||||
|
OttieControlPoint in;
|
||||||
|
OttieControlPoint out;
|
||||||
|
double start_time;
|
||||||
|
_T_ start_value;
|
||||||
|
_T_ end_value;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct OttieKeyframes
|
||||||
|
{
|
||||||
|
gsize n_items;
|
||||||
|
OttieKeyframe items[];
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline OttieKeyframes *
|
||||||
|
ottie_keyframes(new) (gsize n_items)
|
||||||
|
{
|
||||||
|
OttieKeyframes *self;
|
||||||
|
|
||||||
|
self = g_malloc0 (sizeof (OttieKeyframes) + n_items * sizeof (OttieKeyframe));
|
||||||
|
self->n_items = n_items;
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
ottie_keyframes(free_value) (_T_ *item)
|
||||||
|
{
|
||||||
|
#ifdef OTTIE_KEYFRAMES_FREE_FUNC
|
||||||
|
#ifdef OTTIE_KEYFRAMES_BY_VALUE
|
||||||
|
OTTIE_KEYFRAMES_FREE_FUNC (item);
|
||||||
|
#else
|
||||||
|
OTTIE_KEYFRAMES_FREE_FUNC (*item);
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
ottie_keyframes(copy_value) (_T_ *dest,
|
||||||
|
_T_ *src)
|
||||||
|
{
|
||||||
|
#ifdef OTTIE_KEYFRAMES_COPY_FUNC
|
||||||
|
# ifdef OTTIE_KEYFRAMES_BY_VALUE
|
||||||
|
OTTIE_KEYFRAMES_COPY_FUNC (dest, src);
|
||||||
|
# else
|
||||||
|
*dest = OTTIE_KEYFRAMES_COPY_FUNC (*src);
|
||||||
|
# endif
|
||||||
|
#else
|
||||||
|
*dest = *src;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/* no G_GNUC_UNUSED here */
|
||||||
|
static inline void
|
||||||
|
ottie_keyframes(free) (OttieKeyframes *self)
|
||||||
|
{
|
||||||
|
#ifdef OTTIE_KEYFRAMES_FREE_FUNC
|
||||||
|
gsize i;
|
||||||
|
|
||||||
|
for (i = 0; i < self->n_items; i++)
|
||||||
|
{
|
||||||
|
ottie_keyframes(free_value) (&self->items[i].start_value);
|
||||||
|
ottie_keyframes(free_value) (&self->items[i].end_value);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
#ifdef OTTIE_KEYFRAMES_BY_VALUE
|
||||||
|
void
|
||||||
|
#else
|
||||||
|
_T_
|
||||||
|
#endif
|
||||||
|
ottie_keyframes(get) (const OttieKeyframes *self,
|
||||||
|
double timestamp
|
||||||
|
#ifdef OTTIE_KEYFRAMES_BY_VALUE
|
||||||
|
, _T_ *out_result
|
||||||
|
#endif
|
||||||
|
)
|
||||||
|
{
|
||||||
|
const OttieKeyframe *keyframe;
|
||||||
|
gsize i;
|
||||||
|
|
||||||
|
for (i = 0; i < self->n_items; i++)
|
||||||
|
{
|
||||||
|
if (self->items[i].start_time > timestamp)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == 0 || i >= self->n_items)
|
||||||
|
{
|
||||||
|
keyframe = &self->items[i == 0 ? 0 : self->n_items - 1];
|
||||||
|
#ifdef OTTIE_KEYFRAMES_BY_VALUE
|
||||||
|
*out_result = keyframe->start_value;
|
||||||
|
return;
|
||||||
|
#else
|
||||||
|
return keyframe->start_value;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
keyframe = &self->items[i - 1];
|
||||||
|
|
||||||
|
double progress = (timestamp - keyframe->start_time) / (self->items[i].start_time - keyframe->start_time);
|
||||||
|
#ifdef OTTIE_KEYFRAMES_BY_VALUE
|
||||||
|
OTTIE_KEYFRAMES_INTERPOLATE_FUNC (&keyframe->start_value, &keyframe->end_value, progress, out_result);
|
||||||
|
#else
|
||||||
|
return OTTIE_KEYFRAMES_INTERPOLATE_FUNC (keyframe->start_value, keyframe->end_value, progress);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
ottie_keyframes(parse_control_point_dimension) (JsonReader *reader,
|
||||||
|
gsize offset,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
double d[OTTIE_KEYFRAMES_DIMENSIONS];
|
||||||
|
|
||||||
|
if (json_reader_is_array (reader))
|
||||||
|
{
|
||||||
|
if (json_reader_count_elements (reader) != OTTIE_KEYFRAMES_DIMENSIONS)
|
||||||
|
ottie_parser_error_value (reader, "control point has %d dimension, not %u", json_reader_count_elements (reader), OTTIE_KEYFRAMES_DIMENSIONS);
|
||||||
|
|
||||||
|
for (int i = 0; i < OTTIE_KEYFRAMES_DIMENSIONS; i++)
|
||||||
|
{
|
||||||
|
if (!json_reader_read_element (reader, i))
|
||||||
|
{
|
||||||
|
ottie_parser_emit_error (reader, json_reader_get_error (reader));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!ottie_parser_option_double (reader, 0, &d[i]))
|
||||||
|
d[i] = 0;
|
||||||
|
}
|
||||||
|
json_reader_end_element (reader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!ottie_parser_option_double (reader, 0, &d[0]))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
for (gsize i = 1; i < OTTIE_KEYFRAMES_DIMENSIONS; i++)
|
||||||
|
d[i] = d[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy ((guint8 *) data + GPOINTER_TO_SIZE (offset), d, sizeof (double) * OTTIE_KEYFRAMES_DIMENSIONS);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
ottie_keyframes(parse_control_point) (JsonReader *reader,
|
||||||
|
gsize offset,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
OttieParserOption options[] = {
|
||||||
|
{ "x", ottie_keyframes(parse_control_point_dimension), G_STRUCT_OFFSET (OttieControlPoint, x) },
|
||||||
|
{ "y", ottie_keyframes(parse_control_point_dimension), G_STRUCT_OFFSET (OttieControlPoint, y) },
|
||||||
|
};
|
||||||
|
OttieControlPoint cp;
|
||||||
|
OttieControlPoint *target = (OttieControlPoint *) ((guint8 *) data + offset);
|
||||||
|
|
||||||
|
if (!ottie_parser_parse_object (reader, "control point", options, G_N_ELEMENTS (options), &cp))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
*target = cp;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
OttieKeyframe keyframe;
|
||||||
|
gboolean has_start_value;
|
||||||
|
gboolean has_end_value;
|
||||||
|
} OttieKeyframeParse;
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
ottie_keyframes(parse_start_value) (JsonReader *reader,
|
||||||
|
gsize pos,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
OttieKeyframeParse *parse = data;
|
||||||
|
|
||||||
|
if (parse->has_start_value)
|
||||||
|
ottie_keyframes(free_value) (&parse->keyframe.start_value);
|
||||||
|
|
||||||
|
parse->has_start_value = OTTIE_KEYFRAMES_PARSE_FUNC (reader, 0, &parse->keyframe.start_value);
|
||||||
|
return parse->has_start_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
ottie_keyframes(parse_end_value) (JsonReader *reader,
|
||||||
|
gsize pos,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
OttieKeyframeParse *parse = data;
|
||||||
|
|
||||||
|
if (parse->has_end_value)
|
||||||
|
ottie_keyframes(free_value) (&parse->keyframe.end_value);
|
||||||
|
|
||||||
|
parse->has_end_value = OTTIE_KEYFRAMES_PARSE_FUNC (reader, 0, &parse->keyframe.end_value);
|
||||||
|
return parse->has_end_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
OttieKeyframes *keyframes;
|
||||||
|
gboolean has_end_value;
|
||||||
|
} OttieKeyframesParse;
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
ottie_keyframes(parse_keyframe) (JsonReader *reader,
|
||||||
|
gsize pos,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
OttieParserOption options[] = {
|
||||||
|
{ "s", ottie_keyframes(parse_start_value), 0, },
|
||||||
|
{ "e", ottie_keyframes(parse_end_value), 0, },
|
||||||
|
{ "t", ottie_parser_option_double, G_STRUCT_OFFSET (OttieKeyframe, start_time) },
|
||||||
|
{ "i", ottie_keyframes(parse_control_point), G_STRUCT_OFFSET (OttieKeyframe, in) },
|
||||||
|
{ "o", ottie_keyframes(parse_control_point), G_STRUCT_OFFSET (OttieKeyframe, out) },
|
||||||
|
{ "ix", ottie_parser_option_skip_index, 0 },
|
||||||
|
};
|
||||||
|
OttieKeyframesParse *self = data;
|
||||||
|
OttieKeyframeParse parse = { 0, };
|
||||||
|
|
||||||
|
if (!ottie_parser_parse_object (reader, "keyframe", options, G_N_ELEMENTS (options), &parse))
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
if (pos == 0)
|
||||||
|
{
|
||||||
|
if (!parse.has_start_value)
|
||||||
|
{
|
||||||
|
ottie_parser_error_syntax (reader, "First keyframe must have a start value");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (parse.keyframe.start_time <= self->keyframes->items[pos - 1].start_time)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
if (!parse.has_start_value)
|
||||||
|
{
|
||||||
|
if (self->has_end_value)
|
||||||
|
{
|
||||||
|
ottie_keyframes(copy_value) (&parse.keyframe.start_value, &self->keyframes->items[pos - 1].end_value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ottie_parser_error_syntax (reader, "Keyframe %zu has no end value and %zu has no start value.", pos - 1, pos);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!self->has_end_value)
|
||||||
|
ottie_keyframes(copy_value) (&self->keyframes->items[pos - 1].end_value, &parse.keyframe.start_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
self->has_end_value = parse.has_end_value;
|
||||||
|
self->keyframes->items[pos] = parse.keyframe;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
self->keyframes->n_items = pos;
|
||||||
|
if (parse.has_start_value)
|
||||||
|
ottie_keyframes(free_value) (&parse.keyframe.start_value);
|
||||||
|
if (parse.has_end_value)
|
||||||
|
ottie_keyframes(free_value) (&parse.keyframe.end_value);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* no G_GNUC_UNUSED here, if you don't use a type, remove it. */
|
||||||
|
static inline OttieKeyframes *
|
||||||
|
ottie_keyframes(parse) (JsonReader *reader)
|
||||||
|
{
|
||||||
|
OttieKeyframesParse parse;
|
||||||
|
OttieKeyframes *self;
|
||||||
|
|
||||||
|
self = ottie_keyframes(new) (json_reader_count_elements (reader));
|
||||||
|
|
||||||
|
parse.keyframes = self;
|
||||||
|
parse.has_end_value = FALSE;
|
||||||
|
|
||||||
|
if (!ottie_parser_parse_array (reader, "keyframes",
|
||||||
|
self->n_items, self->n_items,
|
||||||
|
NULL,
|
||||||
|
0, 1,
|
||||||
|
ottie_keyframes(parse_keyframe),
|
||||||
|
&parse))
|
||||||
|
{
|
||||||
|
/* do a dumb copy so the free has something to free */
|
||||||
|
if (!parse.has_end_value && self->n_items > 0)
|
||||||
|
ottie_keyframes(copy_value) (&self->items[self->n_items - 1].end_value,
|
||||||
|
&self->items[self->n_items - 1].start_value);
|
||||||
|
ottie_keyframes(free) (self);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!parse.has_end_value)
|
||||||
|
ottie_keyframes(copy_value) (&self->items[self->n_items - 1].end_value,
|
||||||
|
&self->items[self->n_items - 1].start_value);
|
||||||
|
|
||||||
|
return parse.keyframes;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef OTTIE_KEYFRAMES_NO_UNDEF
|
||||||
|
|
||||||
|
#undef _T_
|
||||||
|
#undef OttieKeyframes
|
||||||
|
#undef ottie_keyframes_paste_more
|
||||||
|
#undef ottie_keyframes_paste
|
||||||
|
#undef ottie_keyframes
|
||||||
|
|
||||||
|
#undef OTTIE_KEYFRAMES_COPY_FUNC
|
||||||
|
#undef OTTIE_KEYFRAMES_PARSE_FUNC
|
||||||
|
#undef OTTIE_KEYFRAMES_BY_VALUE
|
||||||
|
#undef OTTIE_KEYFRAMES_ELEMENT_TYPE
|
||||||
|
#undef OTTIE_KEYFRAMES_FREE_FUNC
|
||||||
|
#undef OTTIE_KEYFRAMES_NAME
|
||||||
|
#undef OTTIE_KEYFRAMES_TYPE_NAME
|
||||||
|
#endif
|
||||||
96
ottie/ottielayer.c
Normal file
96
ottie/ottielayer.c
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
/*
|
||||||
|
* 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 "ottielayerprivate.h"
|
||||||
|
|
||||||
|
#include <glib/gi18n-lib.h>
|
||||||
|
#include <json-glib/json-glib.h>
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (OttieLayer, ottie_layer, OTTIE_TYPE_OBJECT)
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_layer_default_update (OttieLayer *self,
|
||||||
|
GHashTable *compositions)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_layer_default_render (OttieLayer *self,
|
||||||
|
OttieRender *render,
|
||||||
|
double timestamp)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_layer_dispose (GObject *object)
|
||||||
|
{
|
||||||
|
OttieLayer *self = OTTIE_LAYER (object);
|
||||||
|
|
||||||
|
g_clear_object (&self->transform);
|
||||||
|
g_clear_pointer (&self->layer_name, g_free);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (ottie_layer_parent_class)->dispose (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_layer_class_init (OttieLayerClass *klass)
|
||||||
|
{
|
||||||
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||||
|
|
||||||
|
klass->update = ottie_layer_default_update;
|
||||||
|
klass->render = ottie_layer_default_render;
|
||||||
|
|
||||||
|
gobject_class->dispose = ottie_layer_dispose;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_layer_init (OttieLayer *self)
|
||||||
|
{
|
||||||
|
self->start_frame = -G_MAXDOUBLE;
|
||||||
|
self->end_frame = G_MAXDOUBLE;
|
||||||
|
self->stretch = 1;
|
||||||
|
self->blend_mode = GSK_BLEND_MODE_DEFAULT;
|
||||||
|
self->parent_index = OTTIE_INT_UNSET;
|
||||||
|
self->index = OTTIE_INT_UNSET;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ottie_layer_update (OttieLayer *self,
|
||||||
|
GHashTable *compositions)
|
||||||
|
{
|
||||||
|
OTTIE_LAYER_GET_CLASS (self)->update (self, compositions);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ottie_layer_render (OttieLayer *self,
|
||||||
|
OttieRender *render,
|
||||||
|
double timestamp)
|
||||||
|
{
|
||||||
|
if (timestamp < self->start_frame ||
|
||||||
|
timestamp > self->end_frame)
|
||||||
|
return;
|
||||||
|
|
||||||
|
timestamp -= self->start_time;
|
||||||
|
timestamp /= self->stretch;
|
||||||
|
|
||||||
|
OTTIE_LAYER_GET_CLASS (self)->render (self, render, timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
93
ottie/ottielayerprivate.h
Normal file
93
ottie/ottielayerprivate.h
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
/*
|
||||||
|
* 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 __OTTIE_LAYER_PRIVATE_H__
|
||||||
|
#define __OTTIE_LAYER_PRIVATE_H__
|
||||||
|
|
||||||
|
#include "ottie/ottietransformprivate.h"
|
||||||
|
#include "ottie/ottieobjectprivate.h"
|
||||||
|
#include "ottie/ottieparserprivate.h"
|
||||||
|
#include "ottie/ottierenderprivate.h"
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
#define OTTIE_TYPE_LAYER (ottie_layer_get_type ())
|
||||||
|
#define OTTIE_LAYER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), OTTIE_TYPE_LAYER, OttieLayer))
|
||||||
|
#define OTTIE_LAYER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), OTTIE_TYPE_LAYER, OttieLayerClass))
|
||||||
|
#define OTTIE_IS_LAYER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), OTTIE_TYPE_LAYER))
|
||||||
|
#define OTTIE_IS_LAYER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), OTTIE_TYPE_LAYER))
|
||||||
|
#define OTTIE_LAYER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), OTTIE_TYPE_LAYER, OttieLayerClass))
|
||||||
|
|
||||||
|
typedef struct _OttieLayer OttieLayer;
|
||||||
|
typedef struct _OttieLayerClass OttieLayerClass;
|
||||||
|
|
||||||
|
struct _OttieLayer
|
||||||
|
{
|
||||||
|
OttieObject parent;
|
||||||
|
|
||||||
|
OttieTransform *transform;
|
||||||
|
gboolean auto_orient;
|
||||||
|
GskBlendMode blend_mode;
|
||||||
|
int index;
|
||||||
|
int parent_index;
|
||||||
|
char *layer_name;
|
||||||
|
double start_frame;
|
||||||
|
double end_frame;
|
||||||
|
double start_time;
|
||||||
|
double stretch;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _OttieLayerClass
|
||||||
|
{
|
||||||
|
OttieObjectClass parent_class;
|
||||||
|
|
||||||
|
void (* update) (OttieLayer *layer,
|
||||||
|
GHashTable *compositions);
|
||||||
|
void (* render) (OttieLayer *layer,
|
||||||
|
OttieRender *render,
|
||||||
|
double timestamp);
|
||||||
|
};
|
||||||
|
|
||||||
|
GType ottie_layer_get_type (void) G_GNUC_CONST;
|
||||||
|
|
||||||
|
void ottie_layer_update (OttieLayer *self,
|
||||||
|
GHashTable *compositions);
|
||||||
|
void ottie_layer_render (OttieLayer *self,
|
||||||
|
OttieRender *render,
|
||||||
|
double timestamp);
|
||||||
|
|
||||||
|
#define OTTIE_PARSE_OPTIONS_LAYER \
|
||||||
|
OTTIE_PARSE_OPTIONS_OBJECT, \
|
||||||
|
{ "ao", ottie_parser_option_boolean, G_STRUCT_OFFSET (OttieLayer, auto_orient) }, \
|
||||||
|
{ "bm", ottie_parser_option_blend_mode, G_STRUCT_OFFSET (OttieLayer, blend_mode) }, \
|
||||||
|
{ "ln", ottie_parser_option_string, G_STRUCT_OFFSET (OttieLayer, layer_name) }, \
|
||||||
|
{ "ks", ottie_parser_option_transform, G_STRUCT_OFFSET (OttieLayer, transform) }, \
|
||||||
|
{ "ip", ottie_parser_option_double, G_STRUCT_OFFSET (OttieLayer, start_frame) }, \
|
||||||
|
{ "ind", ottie_parser_option_int, G_STRUCT_OFFSET (OttieLayer, index) }, \
|
||||||
|
{ "parent", ottie_parser_option_int, G_STRUCT_OFFSET (OttieLayer, parent_index) }, \
|
||||||
|
{ "op", ottie_parser_option_double, G_STRUCT_OFFSET (OttieLayer, end_frame) }, \
|
||||||
|
{ "st", ottie_parser_option_double, G_STRUCT_OFFSET (OttieLayer, start_time) }, \
|
||||||
|
{ "sr", ottie_parser_option_double, G_STRUCT_OFFSET (OttieLayer, stretch) }, \
|
||||||
|
{ "ddd", ottie_parser_option_3d, 0 }, \
|
||||||
|
{ "ix", ottie_parser_option_skip_index, 0 }, \
|
||||||
|
{ "ty", ottie_parser_option_skip, 0 }
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* __OTTIE_LAYER_PRIVATE_H__ */
|
||||||
66
ottie/ottienulllayer.c
Normal file
66
ottie/ottienulllayer.c
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* 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 "ottienulllayerprivate.h"
|
||||||
|
|
||||||
|
#include <glib/gi18n-lib.h>
|
||||||
|
|
||||||
|
struct _OttieNullLayer
|
||||||
|
{
|
||||||
|
OttieLayer parent;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _OttieNullLayerClass
|
||||||
|
{
|
||||||
|
OttieLayerClass parent_class;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (OttieNullLayer, ottie_null_layer, OTTIE_TYPE_LAYER)
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_null_layer_class_init (OttieNullLayerClass *klass)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_null_layer_init (OttieNullLayer *self)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
OttieLayer *
|
||||||
|
ottie_null_layer_parse (JsonReader *reader)
|
||||||
|
{
|
||||||
|
OttieParserOption options[] = {
|
||||||
|
OTTIE_PARSE_OPTIONS_LAYER,
|
||||||
|
};
|
||||||
|
OttieNullLayer *self;
|
||||||
|
|
||||||
|
self = g_object_new (OTTIE_TYPE_NULL_LAYER, NULL);
|
||||||
|
|
||||||
|
if (!ottie_parser_parse_object (reader, "null layer", options, G_N_ELEMENTS (options), self))
|
||||||
|
{
|
||||||
|
g_object_unref (self);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return OTTIE_LAYER (self);
|
||||||
|
}
|
||||||
|
|
||||||
45
ottie/ottienulllayerprivate.h
Normal file
45
ottie/ottienulllayerprivate.h
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* 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 __OTTIE_NULL_LAYER_PRIVATE_H__
|
||||||
|
#define __OTTIE_NULL_LAYER_PRIVATE_H__
|
||||||
|
|
||||||
|
#include "ottielayerprivate.h"
|
||||||
|
|
||||||
|
#include <json-glib/json-glib.h>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
#define OTTIE_TYPE_NULL_LAYER (ottie_null_layer_get_type ())
|
||||||
|
#define OTTIE_NULL_LAYER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), OTTIE_TYPE_NULL_LAYER, OttieNullLayer))
|
||||||
|
#define OTTIE_NULL_LAYER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), OTTIE_TYPE_NULL_LAYER, OttieNullLayerClass))
|
||||||
|
#define OTTIE_IS_NULL_LAYER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), OTTIE_TYPE_NULL_LAYER))
|
||||||
|
#define OTTIE_IS_NULL_LAYER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), OTTIE_TYPE_NULL_LAYER))
|
||||||
|
#define OTTIE_NULL_LAYER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), OTTIE_TYPE_NULL_LAYER, OttieNullLayerClass))
|
||||||
|
|
||||||
|
typedef struct _OttieNullLayer OttieNullLayer;
|
||||||
|
typedef struct _OttieNullLayerClass OttieNullLayerClass;
|
||||||
|
|
||||||
|
GType ottie_null_layer_get_type (void) G_GNUC_CONST;
|
||||||
|
|
||||||
|
OttieLayer * ottie_null_layer_parse (JsonReader *reader);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* __OTTIE_NULL_LAYER_PRIVATE_H__ */
|
||||||
175
ottie/ottieobject.c
Normal file
175
ottie/ottieobject.c
Normal file
@@ -0,0 +1,175 @@
|
|||||||
|
/*
|
||||||
|
* 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 "ottieobjectprivate.h"
|
||||||
|
|
||||||
|
#include "ottieintl.h"
|
||||||
|
|
||||||
|
enum {
|
||||||
|
PROP_0,
|
||||||
|
PROP_MATCH_NAME,
|
||||||
|
PROP_NAME,
|
||||||
|
|
||||||
|
N_PROPS,
|
||||||
|
};
|
||||||
|
|
||||||
|
static GParamSpec *properties[N_PROPS] = { NULL, };
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (OttieObject, ottie_object, G_TYPE_OBJECT)
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_object_set_property (GObject *object,
|
||||||
|
guint prop_id,
|
||||||
|
const GValue *value,
|
||||||
|
GParamSpec *pspec)
|
||||||
|
|
||||||
|
{
|
||||||
|
OttieObject *self = OTTIE_OBJECT (object);
|
||||||
|
|
||||||
|
switch (prop_id)
|
||||||
|
{
|
||||||
|
case PROP_MATCH_NAME:
|
||||||
|
ottie_object_set_match_name (self, g_value_get_string (value));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PROP_NAME:
|
||||||
|
ottie_object_set_name (self, g_value_get_string (value));
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_object_get_property (GObject *object,
|
||||||
|
guint prop_id,
|
||||||
|
GValue *value,
|
||||||
|
GParamSpec *pspec)
|
||||||
|
{
|
||||||
|
OttieObject *self = OTTIE_OBJECT (object);
|
||||||
|
|
||||||
|
switch (prop_id)
|
||||||
|
{
|
||||||
|
case PROP_MATCH_NAME:
|
||||||
|
g_value_set_string (value, self->match_name);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PROP_NAME:
|
||||||
|
g_value_set_string (value, self->name);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_object_dispose (GObject *object)
|
||||||
|
{
|
||||||
|
OttieObject *self = OTTIE_OBJECT (object);
|
||||||
|
|
||||||
|
g_clear_pointer (&self->name, g_free);
|
||||||
|
g_clear_pointer (&self->match_name, g_free);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (ottie_object_parent_class)->dispose (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_object_class_init (OttieObjectClass *class)
|
||||||
|
{
|
||||||
|
GObjectClass *gobject_class = G_OBJECT_CLASS (class);
|
||||||
|
|
||||||
|
gobject_class->set_property = ottie_object_set_property;
|
||||||
|
gobject_class->get_property = ottie_object_get_property;
|
||||||
|
gobject_class->dispose = ottie_object_dispose;
|
||||||
|
|
||||||
|
properties[PROP_NAME] =
|
||||||
|
g_param_spec_string ("name",
|
||||||
|
P_("Name"),
|
||||||
|
P_("User-given name"),
|
||||||
|
NULL,
|
||||||
|
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||||
|
|
||||||
|
properties[PROP_MATCH_NAME] =
|
||||||
|
g_param_spec_string ("match-name",
|
||||||
|
P_("Match name"),
|
||||||
|
P_("Name for matching in scripts"),
|
||||||
|
NULL,
|
||||||
|
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||||
|
|
||||||
|
g_object_class_install_properties (gobject_class, N_PROPS, properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_object_init (OttieObject *self)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ottie_object_set_name (OttieObject *self,
|
||||||
|
const char *name)
|
||||||
|
{
|
||||||
|
g_return_if_fail (OTTIE_IS_OBJECT (self));
|
||||||
|
|
||||||
|
if (g_strcmp0 (self->name, name) == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
g_free (self->name);
|
||||||
|
self->name = g_strdup (name);
|
||||||
|
|
||||||
|
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_NAME]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
ottie_object_get_name (OttieObject *self)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (OTTIE_IS_OBJECT (self), NULL);
|
||||||
|
|
||||||
|
return self->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ottie_object_set_match_name (OttieObject *self,
|
||||||
|
const char *match_name)
|
||||||
|
{
|
||||||
|
g_return_if_fail (OTTIE_IS_OBJECT (self));
|
||||||
|
|
||||||
|
if (g_strcmp0 (self->match_name, match_name) == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
g_free (self->match_name);
|
||||||
|
self->match_name = g_strdup (match_name);
|
||||||
|
|
||||||
|
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MATCH_NAME]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
ottie_object_get_match_name (OttieObject *self)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (OTTIE_IS_OBJECT (self), NULL);
|
||||||
|
|
||||||
|
return self->match_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
68
ottie/ottieobjectprivate.h
Normal file
68
ottie/ottieobjectprivate.h
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* 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 __OTTIE_OBJECT_PRIVATE_H__
|
||||||
|
#define __OTTIE_OBJECT_PRIVATE_H__
|
||||||
|
|
||||||
|
#include <glib-object.h>
|
||||||
|
|
||||||
|
#include "ottie/ottierenderprivate.h"
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
#define OTTIE_TYPE_OBJECT (ottie_object_get_type ())
|
||||||
|
#define OTTIE_OBJECT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), OTTIE_TYPE_OBJECT, OttieObject))
|
||||||
|
#define OTTIE_OBJECT_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), OTTIE_TYPE_OBJECT, OttieObjectClass))
|
||||||
|
#define OTTIE_IS_OBJECT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), OTTIE_TYPE_OBJECT))
|
||||||
|
#define OTTIE_IS_OBJECT_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), OTTIE_TYPE_OBJECT))
|
||||||
|
#define OTTIE_OBJECT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), OTTIE_TYPE_OBJECT, OttieObjectClass))
|
||||||
|
|
||||||
|
typedef struct _OttieObject OttieObject;
|
||||||
|
typedef struct _OttieObjectClass OttieObjectClass;
|
||||||
|
|
||||||
|
struct _OttieObject
|
||||||
|
{
|
||||||
|
GObject parent;
|
||||||
|
|
||||||
|
char *name;
|
||||||
|
char *match_name;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _OttieObjectClass
|
||||||
|
{
|
||||||
|
GObjectClass parent_class;
|
||||||
|
};
|
||||||
|
|
||||||
|
GType ottie_object_get_type (void) G_GNUC_CONST;
|
||||||
|
|
||||||
|
void ottie_object_set_name (OttieObject *self,
|
||||||
|
const char *name);
|
||||||
|
const char * ottie_object_get_name (OttieObject *self);
|
||||||
|
|
||||||
|
void ottie_object_set_match_name (OttieObject *self,
|
||||||
|
const char *match_name);
|
||||||
|
const char * ottie_object_get_match_name (OttieObject *self);
|
||||||
|
|
||||||
|
#define OTTIE_PARSE_OPTIONS_OBJECT \
|
||||||
|
{ "nm", ottie_parser_option_string, G_STRUCT_OFFSET (OttieObject, name) }, \
|
||||||
|
{ "mn", ottie_parser_option_string, G_STRUCT_OFFSET (OttieObject, match_name) }
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* __OTTIE_OBJECT_PRIVATE_H__ */
|
||||||
381
ottie/ottiepaintable.c
Normal file
381
ottie/ottiepaintable.c
Normal file
@@ -0,0 +1,381 @@
|
|||||||
|
/*
|
||||||
|
* 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 "ottiepaintable.h"
|
||||||
|
|
||||||
|
#include "ottiecreationprivate.h"
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
#include <glib/gi18n.h>
|
||||||
|
|
||||||
|
struct _OttiePaintable
|
||||||
|
{
|
||||||
|
GObject parent_instance;
|
||||||
|
|
||||||
|
OttieCreation *creation;
|
||||||
|
gint64 timestamp;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _OttiePaintableClass
|
||||||
|
{
|
||||||
|
GObjectClass parent_class;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
PROP_0,
|
||||||
|
PROP_CREATION,
|
||||||
|
PROP_DURATION,
|
||||||
|
PROP_TIMESTAMP,
|
||||||
|
|
||||||
|
N_PROPS,
|
||||||
|
};
|
||||||
|
|
||||||
|
static GParamSpec *properties[N_PROPS] = { NULL, };
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_paintable_paintable_snapshot (GdkPaintable *paintable,
|
||||||
|
GdkSnapshot *snapshot,
|
||||||
|
double width,
|
||||||
|
double height)
|
||||||
|
{
|
||||||
|
OttiePaintable *self = OTTIE_PAINTABLE (paintable);
|
||||||
|
double w, h, timestamp;
|
||||||
|
|
||||||
|
if (!self->creation)
|
||||||
|
return;
|
||||||
|
|
||||||
|
w = ottie_creation_get_width (self->creation);
|
||||||
|
h = ottie_creation_get_height (self->creation);
|
||||||
|
timestamp = (double) self->timestamp / G_USEC_PER_SEC;
|
||||||
|
|
||||||
|
if (w != width || h != height)
|
||||||
|
{
|
||||||
|
gtk_snapshot_save (snapshot);
|
||||||
|
gtk_snapshot_scale (snapshot, width / w, height / h);
|
||||||
|
}
|
||||||
|
|
||||||
|
gtk_snapshot_push_clip (snapshot, &GRAPHENE_RECT_INIT (0, 0, w, h));
|
||||||
|
ottie_creation_snapshot (self->creation, snapshot, timestamp);
|
||||||
|
gtk_snapshot_pop (snapshot);
|
||||||
|
|
||||||
|
if (w != width || h != height)
|
||||||
|
gtk_snapshot_restore (snapshot);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
ottie_paintable_paintable_get_intrinsic_width (GdkPaintable *paintable)
|
||||||
|
{
|
||||||
|
OttiePaintable *self = OTTIE_PAINTABLE (paintable);
|
||||||
|
|
||||||
|
if (!self->creation)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return ceil (ottie_creation_get_width (self->creation));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
ottie_paintable_paintable_get_intrinsic_height (GdkPaintable *paintable)
|
||||||
|
{
|
||||||
|
OttiePaintable *self = OTTIE_PAINTABLE (paintable);
|
||||||
|
|
||||||
|
if (!self->creation)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return ceil (ottie_creation_get_height (self->creation));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_paintable_paintable_init (GdkPaintableInterface *iface)
|
||||||
|
{
|
||||||
|
iface->snapshot = ottie_paintable_paintable_snapshot;
|
||||||
|
iface->get_intrinsic_width = ottie_paintable_paintable_get_intrinsic_width;
|
||||||
|
iface->get_intrinsic_height = ottie_paintable_paintable_get_intrinsic_height;
|
||||||
|
}
|
||||||
|
|
||||||
|
G_DEFINE_TYPE_EXTENDED (OttiePaintable, ottie_paintable, G_TYPE_OBJECT, 0,
|
||||||
|
G_IMPLEMENT_INTERFACE (GDK_TYPE_PAINTABLE,
|
||||||
|
ottie_paintable_paintable_init))
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_paintable_set_property (GObject *object,
|
||||||
|
guint prop_id,
|
||||||
|
const GValue *value,
|
||||||
|
GParamSpec *pspec)
|
||||||
|
|
||||||
|
{
|
||||||
|
OttiePaintable *self = OTTIE_PAINTABLE (object);
|
||||||
|
|
||||||
|
switch (prop_id)
|
||||||
|
{
|
||||||
|
case PROP_CREATION:
|
||||||
|
ottie_paintable_set_creation (self, g_value_get_object (value));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PROP_TIMESTAMP:
|
||||||
|
ottie_paintable_set_timestamp (self, g_value_get_int64 (value));
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_paintable_get_property (GObject *object,
|
||||||
|
guint prop_id,
|
||||||
|
GValue *value,
|
||||||
|
GParamSpec *pspec)
|
||||||
|
{
|
||||||
|
OttiePaintable *self = OTTIE_PAINTABLE (object);
|
||||||
|
|
||||||
|
switch (prop_id)
|
||||||
|
{
|
||||||
|
case PROP_CREATION:
|
||||||
|
g_value_set_object (value, self->creation);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PROP_DURATION:
|
||||||
|
g_value_set_int64 (value, ottie_paintable_get_duration (self));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PROP_TIMESTAMP:
|
||||||
|
g_value_set_int64 (value, self->timestamp);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_paintable_prepared_cb (OttieCreation *creation,
|
||||||
|
GParamSpec *pspec,
|
||||||
|
OttiePaintable *self)
|
||||||
|
{
|
||||||
|
gdk_paintable_invalidate_size (GDK_PAINTABLE (self));
|
||||||
|
gdk_paintable_invalidate_contents (GDK_PAINTABLE (self));
|
||||||
|
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DURATION]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_paintable_unset_creation (OttiePaintable *self)
|
||||||
|
{
|
||||||
|
if (self->creation == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
g_signal_handlers_disconnect_by_func (self->creation, ottie_paintable_prepared_cb, self);
|
||||||
|
g_clear_object (&self->creation);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_paintable_dispose (GObject *object)
|
||||||
|
{
|
||||||
|
OttiePaintable *self = OTTIE_PAINTABLE (object);
|
||||||
|
|
||||||
|
ottie_paintable_unset_creation (self);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (ottie_paintable_parent_class)->dispose (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_paintable_class_init (OttiePaintableClass *klass)
|
||||||
|
{
|
||||||
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||||
|
|
||||||
|
gobject_class->get_property = ottie_paintable_get_property;
|
||||||
|
gobject_class->set_property = ottie_paintable_set_property;
|
||||||
|
gobject_class->dispose = ottie_paintable_dispose;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OttiePaintable:creation
|
||||||
|
*
|
||||||
|
* The displayed creation or %NULL.
|
||||||
|
*/
|
||||||
|
properties[PROP_CREATION] =
|
||||||
|
g_param_spec_object ("creation",
|
||||||
|
_("Creation"),
|
||||||
|
_("The displayed creation"),
|
||||||
|
OTTIE_TYPE_CREATION,
|
||||||
|
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OttiePaintable:duration
|
||||||
|
*
|
||||||
|
* Duration of the displayed creation
|
||||||
|
*/
|
||||||
|
properties[PROP_DURATION] =
|
||||||
|
g_param_spec_int64 ("duration",
|
||||||
|
_("Duration"),
|
||||||
|
_("Duration of the displayed creation"),
|
||||||
|
0, G_MAXINT64, 0,
|
||||||
|
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OttiePaintable:timestamp
|
||||||
|
*
|
||||||
|
* At what timestamp to display the creation.
|
||||||
|
*/
|
||||||
|
properties[PROP_TIMESTAMP] =
|
||||||
|
g_param_spec_int64 ("timestmp",
|
||||||
|
_("Timestamp"),
|
||||||
|
_("At what timestamp to display the creation"),
|
||||||
|
0, G_MAXINT64, 0,
|
||||||
|
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||||
|
|
||||||
|
g_object_class_install_properties (gobject_class, N_PROPS, properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_paintable_init (OttiePaintable *self)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ottie_paintable_new:
|
||||||
|
* @creation: (allow-none) (transfer full): an #OttiePaintable or %NULL
|
||||||
|
*
|
||||||
|
* Creates a new Ottie paintable for the given @creation
|
||||||
|
*
|
||||||
|
* Returns: (transfer full) (type OttiePaintable): a new #OttiePaintable
|
||||||
|
**/
|
||||||
|
OttiePaintable *
|
||||||
|
ottie_paintable_new (OttieCreation *creation)
|
||||||
|
{
|
||||||
|
OttiePaintable *self;
|
||||||
|
|
||||||
|
g_return_val_if_fail (creation == creation || OTTIE_IS_CREATION (creation), NULL);
|
||||||
|
|
||||||
|
self = g_object_new (OTTIE_TYPE_PAINTABLE,
|
||||||
|
"creation", creation,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
g_clear_object (&creation);
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ottie_paintable_get_creation:
|
||||||
|
* @self: an #OttiePaintable
|
||||||
|
*
|
||||||
|
* Returns the creation that shown or %NULL
|
||||||
|
* if none.
|
||||||
|
*
|
||||||
|
* Returns: (transfer none) (nullable): the observed creation.
|
||||||
|
**/
|
||||||
|
OttieCreation *
|
||||||
|
ottie_paintable_get_creation (OttiePaintable *self)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (OTTIE_IS_PAINTABLE (self), NULL);
|
||||||
|
|
||||||
|
return self->creation;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ottie_paintable_set_creation:
|
||||||
|
* @self: an #OttiePaintable
|
||||||
|
* @creation: (allow-none): the creation to show or %NULL
|
||||||
|
*
|
||||||
|
* Sets the creation that should be shown.
|
||||||
|
**/
|
||||||
|
void
|
||||||
|
ottie_paintable_set_creation (OttiePaintable *self,
|
||||||
|
OttieCreation *creation)
|
||||||
|
{
|
||||||
|
g_return_if_fail (OTTIE_IS_PAINTABLE (self));
|
||||||
|
g_return_if_fail (creation == NULL || OTTIE_IS_CREATION (creation));
|
||||||
|
|
||||||
|
if (self->creation == creation)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ottie_paintable_unset_creation (self);
|
||||||
|
|
||||||
|
self->creation = g_object_ref (creation);
|
||||||
|
g_signal_connect (creation, "notify::prepared", G_CALLBACK (ottie_paintable_prepared_cb), self);
|
||||||
|
|
||||||
|
gdk_paintable_invalidate_size (GDK_PAINTABLE (self));
|
||||||
|
gdk_paintable_invalidate_contents (GDK_PAINTABLE (self));
|
||||||
|
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_CREATION]);
|
||||||
|
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DURATION]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ottie_paintable_get_timestamp:
|
||||||
|
* @self: an #OttiePaintable
|
||||||
|
*
|
||||||
|
* Gets the timestamp for the currently displayed image.
|
||||||
|
*
|
||||||
|
* Returns: the timestamp
|
||||||
|
**/
|
||||||
|
gint64
|
||||||
|
ottie_paintable_get_timestamp (OttiePaintable *self)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (OTTIE_IS_PAINTABLE (self), 0);
|
||||||
|
|
||||||
|
return self->timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ottie_paintable_set_timestamp:
|
||||||
|
* @self: an #OttiePaintable
|
||||||
|
* @timestamp: the timestamp to display
|
||||||
|
*
|
||||||
|
* Sets the timestamp to display the creation at.
|
||||||
|
**/
|
||||||
|
void
|
||||||
|
ottie_paintable_set_timestamp (OttiePaintable *self,
|
||||||
|
gint64 timestamp)
|
||||||
|
{
|
||||||
|
g_return_if_fail (OTTIE_IS_PAINTABLE (self));
|
||||||
|
g_return_if_fail (timestamp >= 0);
|
||||||
|
|
||||||
|
if (self->timestamp == timestamp)
|
||||||
|
return;
|
||||||
|
|
||||||
|
self->timestamp = timestamp;
|
||||||
|
|
||||||
|
gdk_paintable_invalidate_contents (GDK_PAINTABLE (self));
|
||||||
|
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_TIMESTAMP]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ottie_paintable_get_duration:
|
||||||
|
* @self: an #OttiePaintable
|
||||||
|
*
|
||||||
|
* Gets the duration of the currently playing creation.
|
||||||
|
*
|
||||||
|
* Returns: The duration in usec.
|
||||||
|
**/
|
||||||
|
gint64
|
||||||
|
ottie_paintable_get_duration (OttiePaintable *self)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (OTTIE_IS_PAINTABLE (self), 0);
|
||||||
|
|
||||||
|
if (self->creation == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return ceil (G_USEC_PER_SEC * ottie_creation_get_end_frame (self->creation)
|
||||||
|
/ ottie_creation_get_frame_rate (self->creation));
|
||||||
|
}
|
||||||
|
|
||||||
54
ottie/ottiepaintable.h
Normal file
54
ottie/ottiepaintable.h
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* 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 __OTTIE_PAINTABLE_H__
|
||||||
|
#define __OTTIE_PAINTABLE_H__
|
||||||
|
|
||||||
|
#if !defined (__OTTIE_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||||
|
#error "Only <ottie/ottie.h> can be included directly."
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <ottie/ottiecreation.h>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
#define OTTIE_TYPE_PAINTABLE (ottie_paintable_get_type ())
|
||||||
|
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
G_DECLARE_FINAL_TYPE (OttiePaintable, ottie_paintable, OTTIE, PAINTABLE, GObject)
|
||||||
|
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
OttiePaintable * ottie_paintable_new (OttieCreation *creation);
|
||||||
|
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
OttieCreation * ottie_paintable_get_creation (OttiePaintable *self);
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
void ottie_paintable_set_creation (OttiePaintable *self,
|
||||||
|
OttieCreation *creation);
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
gint64 ottie_paintable_get_timestamp (OttiePaintable *self);
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
void ottie_paintable_set_timestamp (OttiePaintable *self,
|
||||||
|
gint64 timestamp);
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
gint64 ottie_paintable_get_duration (OttiePaintable *self);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* __OTTIE_PAINTABLE_H__ */
|
||||||
592
ottie/ottieparser.c
Normal file
592
ottie/ottieparser.c
Normal file
@@ -0,0 +1,592 @@
|
|||||||
|
/*
|
||||||
|
* 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 "ottieparserprivate.h"
|
||||||
|
|
||||||
|
#include "ottietransformprivate.h"
|
||||||
|
|
||||||
|
#include <gsk/gsk.h>
|
||||||
|
|
||||||
|
void
|
||||||
|
ottie_parser_emit_error (JsonReader *reader,
|
||||||
|
const GError *error)
|
||||||
|
{
|
||||||
|
g_printerr ("Ottie is sad: %s\n", error->message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ottie_parser_error_syntax (JsonReader *reader,
|
||||||
|
const char *format,
|
||||||
|
...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
GError *error;
|
||||||
|
|
||||||
|
va_start (args, format);
|
||||||
|
error = g_error_new_valist (JSON_PARSER_ERROR,
|
||||||
|
JSON_PARSER_ERROR_INVALID_DATA,
|
||||||
|
format, args);
|
||||||
|
va_end (args);
|
||||||
|
|
||||||
|
ottie_parser_emit_error (reader, error);
|
||||||
|
|
||||||
|
g_error_free (error);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ottie_parser_error_value (JsonReader *reader,
|
||||||
|
const char *format,
|
||||||
|
...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
GError *error;
|
||||||
|
|
||||||
|
va_start (args, format);
|
||||||
|
error = g_error_new_valist (JSON_PARSER_ERROR,
|
||||||
|
JSON_PARSER_ERROR_INVALID_DATA,
|
||||||
|
format, args);
|
||||||
|
va_end (args);
|
||||||
|
|
||||||
|
ottie_parser_emit_error (reader, error);
|
||||||
|
|
||||||
|
g_error_free (error);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ottie_parser_error_unsupported (JsonReader *reader,
|
||||||
|
const char *format,
|
||||||
|
...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
GError *error;
|
||||||
|
|
||||||
|
va_start (args, format);
|
||||||
|
error = g_error_new_valist (JSON_PARSER_ERROR,
|
||||||
|
JSON_PARSER_ERROR_INVALID_DATA,
|
||||||
|
format, args);
|
||||||
|
va_end (args);
|
||||||
|
|
||||||
|
ottie_parser_emit_error (reader, error);
|
||||||
|
|
||||||
|
g_error_free (error);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
ottie_parser_parse_array (JsonReader *reader,
|
||||||
|
const char *debug_name,
|
||||||
|
guint min_items,
|
||||||
|
guint max_items,
|
||||||
|
guint *out_n_items,
|
||||||
|
gsize start_offset,
|
||||||
|
gsize offset_multiplier,
|
||||||
|
OttieParseFunc func,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
guint i;
|
||||||
|
|
||||||
|
if (!json_reader_is_array (reader))
|
||||||
|
{
|
||||||
|
if (min_items > 1)
|
||||||
|
{
|
||||||
|
ottie_parser_error_syntax (reader, "Expected an array when parsing %s", debug_name);
|
||||||
|
if (out_n_items)
|
||||||
|
*out_n_items = 0;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!func (reader, start_offset, data))
|
||||||
|
{
|
||||||
|
if (out_n_items)
|
||||||
|
*out_n_items = 0;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
if (out_n_items)
|
||||||
|
*out_n_items = 1;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (json_reader_count_elements (reader) < min_items)
|
||||||
|
{
|
||||||
|
ottie_parser_error_syntax (reader, "%s needs %u items, but only %u given",
|
||||||
|
debug_name, min_items, json_reader_count_elements (reader));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
max_items = MIN (max_items, json_reader_count_elements (reader));
|
||||||
|
|
||||||
|
for (i = 0; i < max_items; i++)
|
||||||
|
{
|
||||||
|
if (!json_reader_read_element (reader, i) ||
|
||||||
|
!func (reader, start_offset + offset_multiplier * i, data))
|
||||||
|
{
|
||||||
|
json_reader_end_element (reader);
|
||||||
|
if (out_n_items)
|
||||||
|
*out_n_items = i;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
json_reader_end_element (reader);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (out_n_items)
|
||||||
|
*out_n_items = i;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
ottie_parser_parse_object (JsonReader *reader,
|
||||||
|
const char *debug_name,
|
||||||
|
const OttieParserOption *options,
|
||||||
|
gsize n_options,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
if (!json_reader_is_object (reader))
|
||||||
|
{
|
||||||
|
ottie_parser_error_syntax (reader, "Expected an object when parsing %s", debug_name);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; ; i++)
|
||||||
|
{
|
||||||
|
const OttieParserOption *o = NULL;
|
||||||
|
const char *name;
|
||||||
|
|
||||||
|
if (!json_reader_read_element (reader, i))
|
||||||
|
break;
|
||||||
|
|
||||||
|
name = json_reader_get_member_name (reader);
|
||||||
|
|
||||||
|
for (gsize j = 0; j < n_options; j++)
|
||||||
|
{
|
||||||
|
o = &options[j];
|
||||||
|
if (g_str_equal (o->name, name))
|
||||||
|
break;
|
||||||
|
o = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (o)
|
||||||
|
{
|
||||||
|
if (!o->parse_func (reader, o->option_data, data))
|
||||||
|
{
|
||||||
|
json_reader_end_element (reader);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ottie_parser_error_unsupported (reader, "Unsupported %s property \"%s\"", debug_name, json_reader_get_member_name (reader));
|
||||||
|
}
|
||||||
|
|
||||||
|
json_reader_end_element (reader);
|
||||||
|
}
|
||||||
|
|
||||||
|
json_reader_end_element (reader);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
ottie_parser_option_skip (JsonReader *reader,
|
||||||
|
gsize offset,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
ottie_parser_option_boolean (JsonReader *reader,
|
||||||
|
gsize offset,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
gboolean b;
|
||||||
|
|
||||||
|
b = json_reader_get_boolean_value (reader);
|
||||||
|
if (json_reader_get_error (reader))
|
||||||
|
{
|
||||||
|
ottie_parser_emit_error (reader, json_reader_get_error (reader));
|
||||||
|
json_reader_end_element (reader);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
*(gboolean *) ((guint8 *) data + offset) = b;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
ottie_parser_option_double (JsonReader *reader,
|
||||||
|
gsize offset,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
double d;
|
||||||
|
|
||||||
|
d = json_reader_get_double_value (reader);
|
||||||
|
if (json_reader_get_error (reader))
|
||||||
|
{
|
||||||
|
ottie_parser_emit_error (reader, json_reader_get_error (reader));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
*(double *) ((guint8 *) data + offset) = d;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
ottie_parser_option_int (JsonReader *reader,
|
||||||
|
gsize offset,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
gint64 i;
|
||||||
|
|
||||||
|
i = json_reader_get_int_value (reader);
|
||||||
|
if (json_reader_get_error (reader))
|
||||||
|
{
|
||||||
|
ottie_parser_emit_error (reader, json_reader_get_error (reader));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i > G_MAXINT || i < G_MININT)
|
||||||
|
{
|
||||||
|
ottie_parser_error_value (reader, "Integer value %"G_GINT64_FORMAT" out of range", i);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
if (i == OTTIE_INT_UNSET)
|
||||||
|
{
|
||||||
|
ottie_parser_error_unsupported (reader, "The Integer value %d is a magic internal value of Ottie, file a bug", OTTIE_INT_UNSET);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
*(int *) ((guint8 *) data + offset) = i;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
ottie_parser_option_string (JsonReader *reader,
|
||||||
|
gsize offset,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
char **target;
|
||||||
|
const char *s;
|
||||||
|
|
||||||
|
s = json_reader_get_string_value (reader);
|
||||||
|
if (json_reader_get_error (reader))
|
||||||
|
{
|
||||||
|
ottie_parser_emit_error (reader, json_reader_get_error (reader));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
target = (char **) ((guint8 *) data + offset);
|
||||||
|
|
||||||
|
g_clear_pointer (target, g_free);
|
||||||
|
*target = g_strdup (s);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
ottie_parser_option_blend_mode (JsonReader *reader,
|
||||||
|
gsize offset,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
GskBlendMode blend_mode;
|
||||||
|
gint64 i;
|
||||||
|
|
||||||
|
i = json_reader_get_int_value (reader);
|
||||||
|
if (json_reader_get_error (reader))
|
||||||
|
{
|
||||||
|
ottie_parser_emit_error (reader, json_reader_get_error (reader));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (i)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
blend_mode = GSK_BLEND_MODE_DEFAULT;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
blend_mode = GSK_BLEND_MODE_MULTIPLY;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
blend_mode = GSK_BLEND_MODE_SCREEN;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
blend_mode = GSK_BLEND_MODE_OVERLAY;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 4:
|
||||||
|
blend_mode = GSK_BLEND_MODE_DARKEN;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 5:
|
||||||
|
blend_mode = GSK_BLEND_MODE_LIGHTEN;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 6:
|
||||||
|
blend_mode = GSK_BLEND_MODE_COLOR_DODGE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 7:
|
||||||
|
blend_mode = GSK_BLEND_MODE_COLOR_BURN;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 8:
|
||||||
|
blend_mode = GSK_BLEND_MODE_HARD_LIGHT;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 9:
|
||||||
|
blend_mode = GSK_BLEND_MODE_SOFT_LIGHT;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 10:
|
||||||
|
blend_mode = GSK_BLEND_MODE_DIFFERENCE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 11:
|
||||||
|
blend_mode = GSK_BLEND_MODE_EXCLUSION;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 12:
|
||||||
|
blend_mode = GSK_BLEND_MODE_HUE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 13:
|
||||||
|
blend_mode = GSK_BLEND_MODE_SATURATION;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 14:
|
||||||
|
blend_mode = GSK_BLEND_MODE_COLOR;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 15:
|
||||||
|
blend_mode = GSK_BLEND_MODE_LUMINOSITY;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
ottie_parser_error_value (reader, "%"G_GINT64_FORMAT" is not a known blend mode", i);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (blend_mode != GSK_BLEND_MODE_DEFAULT)
|
||||||
|
ottie_parser_error_value (reader, "Blend modes are not implemented yet.");
|
||||||
|
|
||||||
|
*(GskBlendMode *) ((guint8 *) data + offset) = blend_mode;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
ottie_parser_option_3d (JsonReader *reader,
|
||||||
|
gsize offset,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
double d;
|
||||||
|
|
||||||
|
d = json_reader_get_double_value (reader);
|
||||||
|
if (json_reader_get_error (reader))
|
||||||
|
{
|
||||||
|
ottie_parser_emit_error (reader, json_reader_get_error (reader));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (d != 0)
|
||||||
|
{
|
||||||
|
ottie_parser_error_value (reader, "3D is not supported.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
ottie_parser_option_direction (JsonReader *reader,
|
||||||
|
gsize offset,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
OttieDirection direction;
|
||||||
|
gint64 i;
|
||||||
|
|
||||||
|
i = json_reader_get_int_value (reader);
|
||||||
|
if (json_reader_get_error (reader))
|
||||||
|
{
|
||||||
|
ottie_parser_emit_error (reader, json_reader_get_error (reader));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (i)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
ottie_parser_error_value (reader, "%"G_GINT64_FORMAT" is not a known direction", i);
|
||||||
|
G_GNUC_FALLTHROUGH;
|
||||||
|
case 0:
|
||||||
|
direction = OTTIE_DIRECTION_FORWARD;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
case 2:
|
||||||
|
direction = OTTIE_DIRECTION_BACKWARD;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
*(OttieDirection *) ((guint8 *) data + offset) = direction;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
ottie_parser_option_line_cap (JsonReader *reader,
|
||||||
|
gsize offset,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
GskLineCap line_cap;
|
||||||
|
gint64 i;
|
||||||
|
|
||||||
|
i = json_reader_get_int_value (reader);
|
||||||
|
if (json_reader_get_error (reader))
|
||||||
|
{
|
||||||
|
ottie_parser_emit_error (reader, json_reader_get_error (reader));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (i)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
line_cap = GSK_LINE_CAP_BUTT;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
line_cap = GSK_LINE_CAP_ROUND;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
line_cap = GSK_LINE_CAP_SQUARE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
ottie_parser_error_value (reader, "%"G_GINT64_FORMAT" is not a known line cap", i);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
*(GskLineCap *) ((guint8 *) data + offset) = line_cap;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
ottie_parser_option_line_join (JsonReader *reader,
|
||||||
|
gsize offset,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
GskLineJoin line_join;
|
||||||
|
gint64 i;
|
||||||
|
|
||||||
|
i = json_reader_get_int_value (reader);
|
||||||
|
if (json_reader_get_error (reader))
|
||||||
|
{
|
||||||
|
ottie_parser_emit_error (reader, json_reader_get_error (reader));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (i)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
line_join = GSK_LINE_JOIN_MITER;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
line_join = GSK_LINE_JOIN_ROUND;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
line_join = GSK_LINE_JOIN_BEVEL;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
ottie_parser_error_value (reader, "%"G_GINT64_FORMAT" is not a known line join", i);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
*(GskLineJoin *) ((guint8 *) data + offset) = line_join;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
ottie_parser_option_fill_rule (JsonReader *reader,
|
||||||
|
gsize offset,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
GskFillRule fill_rule;
|
||||||
|
gint64 i;
|
||||||
|
|
||||||
|
i = json_reader_get_int_value (reader);
|
||||||
|
if (json_reader_get_error (reader))
|
||||||
|
{
|
||||||
|
ottie_parser_emit_error (reader, json_reader_get_error (reader));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (i)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
fill_rule = GSK_FILL_RULE_WINDING;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
fill_rule = GSK_FILL_RULE_EVEN_ODD;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
ottie_parser_error_value (reader, "%"G_GINT64_FORMAT" is not a known fill rule", i);
|
||||||
|
/* XXX: really? */
|
||||||
|
fill_rule = GSK_FILL_RULE_EVEN_ODD;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
*(GskFillRule *) ((guint8 *) data + offset) = fill_rule;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
ottie_parser_option_transform (JsonReader *reader,
|
||||||
|
gsize offset,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
OttieShape **target;
|
||||||
|
OttieShape *t;
|
||||||
|
|
||||||
|
t = ottie_transform_parse (reader);
|
||||||
|
if (t == NULL)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
target = (OttieShape **) ((guint8 *) data + offset);
|
||||||
|
|
||||||
|
g_clear_object (target);
|
||||||
|
*target = t;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
115
ottie/ottieparserprivate.h
Normal file
115
ottie/ottieparserprivate.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 __OTTIE_PARSER_PRIVATE_H__
|
||||||
|
#define __OTTIE_PARSER_PRIVATE_H__
|
||||||
|
|
||||||
|
#include <json-glib/json-glib.h>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
/* for integers where we want to track that nobody has assigned a value to them */
|
||||||
|
#define OTTIE_INT_UNSET G_MININT
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
OTTIE_DIRECTION_FORWARD,
|
||||||
|
OTTIE_DIRECTION_BACKWARD
|
||||||
|
} OttieDirection;
|
||||||
|
|
||||||
|
typedef struct _OttieParserOption OttieParserOption;
|
||||||
|
|
||||||
|
typedef gboolean (* OttieParseFunc) (JsonReader *reader, gsize offset, gpointer data);
|
||||||
|
|
||||||
|
struct _OttieParserOption
|
||||||
|
{
|
||||||
|
const char *name;
|
||||||
|
OttieParseFunc parse_func;
|
||||||
|
gsize option_data;
|
||||||
|
};
|
||||||
|
|
||||||
|
void ottie_parser_emit_error (JsonReader *reader,
|
||||||
|
const GError *error);
|
||||||
|
void ottie_parser_error_syntax (JsonReader *reader,
|
||||||
|
const char *format,
|
||||||
|
...) G_GNUC_PRINTF (2, 3);
|
||||||
|
void ottie_parser_error_value (JsonReader *reader,
|
||||||
|
const char *format,
|
||||||
|
...) G_GNUC_PRINTF (2, 3);
|
||||||
|
void ottie_parser_error_unsupported (JsonReader *reader,
|
||||||
|
const char *format,
|
||||||
|
...) G_GNUC_PRINTF (2, 3);
|
||||||
|
|
||||||
|
gboolean ottie_parser_parse_array (JsonReader *reader,
|
||||||
|
const char *debug_name,
|
||||||
|
guint min_items,
|
||||||
|
guint max_items,
|
||||||
|
guint *out_n_items,
|
||||||
|
gsize start_offset,
|
||||||
|
gsize offset_multiplier,
|
||||||
|
OttieParseFunc func,
|
||||||
|
gpointer data);
|
||||||
|
|
||||||
|
gboolean ottie_parser_parse_object (JsonReader *reader,
|
||||||
|
const char *debug_name,
|
||||||
|
const OttieParserOption *options,
|
||||||
|
gsize n_options,
|
||||||
|
gpointer data);
|
||||||
|
gboolean ottie_parser_option_skip (JsonReader *reader,
|
||||||
|
gsize offset,
|
||||||
|
gpointer data);
|
||||||
|
#define ottie_parser_option_skip_index ottie_parser_option_skip
|
||||||
|
#define ottie_parser_option_skip_expression ottie_parser_option_skip
|
||||||
|
gboolean ottie_parser_option_boolean (JsonReader *reader,
|
||||||
|
gsize offset,
|
||||||
|
gpointer data);
|
||||||
|
gboolean ottie_parser_option_int (JsonReader *reader,
|
||||||
|
gsize offset,
|
||||||
|
gpointer data);
|
||||||
|
gboolean ottie_parser_option_double (JsonReader *reader,
|
||||||
|
gsize offset,
|
||||||
|
gpointer data);
|
||||||
|
gboolean ottie_parser_option_string (JsonReader *reader,
|
||||||
|
gsize offset,
|
||||||
|
gpointer data);
|
||||||
|
gboolean ottie_parser_option_3d (JsonReader *reader,
|
||||||
|
gsize offset,
|
||||||
|
gpointer data);
|
||||||
|
gboolean ottie_parser_option_direction (JsonReader *reader,
|
||||||
|
gsize offset,
|
||||||
|
gpointer data);
|
||||||
|
gboolean ottie_parser_option_blend_mode (JsonReader *reader,
|
||||||
|
gsize offset,
|
||||||
|
gpointer data);
|
||||||
|
gboolean ottie_parser_option_line_cap (JsonReader *reader,
|
||||||
|
gsize offset,
|
||||||
|
gpointer data);
|
||||||
|
gboolean ottie_parser_option_line_join (JsonReader *reader,
|
||||||
|
gsize offset,
|
||||||
|
gpointer data);
|
||||||
|
gboolean ottie_parser_option_fill_rule (JsonReader *reader,
|
||||||
|
gsize offset,
|
||||||
|
gpointer data);
|
||||||
|
gboolean ottie_parser_option_transform (JsonReader *reader,
|
||||||
|
gsize offset,
|
||||||
|
gpointer data);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* __OTTIE_PARSER_PRIVATE_H__ */
|
||||||
105
ottie/ottiepathshape.c
Normal file
105
ottie/ottiepathshape.c
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
/*
|
||||||
|
* 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 "ottiepathshapeprivate.h"
|
||||||
|
|
||||||
|
#include "ottiepathvalueprivate.h"
|
||||||
|
#include "ottieparserprivate.h"
|
||||||
|
#include "ottieshapeprivate.h"
|
||||||
|
|
||||||
|
#include <glib/gi18n-lib.h>
|
||||||
|
|
||||||
|
struct _OttiePathShape
|
||||||
|
{
|
||||||
|
OttieShape parent;
|
||||||
|
|
||||||
|
double direction;
|
||||||
|
OttiePathValue path;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _OttiePathShapeClass
|
||||||
|
{
|
||||||
|
OttieShapeClass parent_class;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (OttiePathShape, ottie_path_shape, OTTIE_TYPE_SHAPE)
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_path_shape_render (OttieShape *shape,
|
||||||
|
OttieRender *render,
|
||||||
|
double timestamp)
|
||||||
|
{
|
||||||
|
OttiePathShape *self = OTTIE_PATH_SHAPE (shape);
|
||||||
|
|
||||||
|
ottie_render_add_path (render,
|
||||||
|
ottie_path_value_get (&self->path,
|
||||||
|
timestamp,
|
||||||
|
self->direction));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_path_shape_dispose (GObject *object)
|
||||||
|
{
|
||||||
|
OttiePathShape *self = OTTIE_PATH_SHAPE (object);
|
||||||
|
|
||||||
|
ottie_path_value_clear (&self->path);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (ottie_path_shape_parent_class)->dispose (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_path_shape_class_init (OttiePathShapeClass *klass)
|
||||||
|
{
|
||||||
|
OttieShapeClass *shape_class = OTTIE_SHAPE_CLASS (klass);
|
||||||
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||||
|
|
||||||
|
shape_class->render = ottie_path_shape_render;
|
||||||
|
|
||||||
|
gobject_class->dispose = ottie_path_shape_dispose;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_path_shape_init (OttiePathShape *self)
|
||||||
|
{
|
||||||
|
ottie_path_value_init (&self->path);
|
||||||
|
}
|
||||||
|
|
||||||
|
OttieShape *
|
||||||
|
ottie_path_shape_parse (JsonReader *reader)
|
||||||
|
{
|
||||||
|
OttieParserOption options[] = {
|
||||||
|
OTTIE_PARSE_OPTIONS_SHAPE,
|
||||||
|
{ "d", ottie_parser_option_double, G_STRUCT_OFFSET (OttiePathShape, direction) },
|
||||||
|
{ "ks", ottie_path_value_parse, G_STRUCT_OFFSET (OttiePathShape, path) },
|
||||||
|
};
|
||||||
|
OttiePathShape *self;
|
||||||
|
|
||||||
|
self = g_object_new (OTTIE_TYPE_PATH_SHAPE, NULL);
|
||||||
|
|
||||||
|
if (!ottie_parser_parse_object (reader, "path shape", options, G_N_ELEMENTS (options), self))
|
||||||
|
{
|
||||||
|
g_object_unref (self);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return OTTIE_SHAPE (self);
|
||||||
|
}
|
||||||
|
|
||||||
45
ottie/ottiepathshapeprivate.h
Normal file
45
ottie/ottiepathshapeprivate.h
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* 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 __OTTIE_PATH_SHAPE_PRIVATE_H__
|
||||||
|
#define __OTTIE_PATH_SHAPE_PRIVATE_H__
|
||||||
|
|
||||||
|
#include "ottieshapeprivate.h"
|
||||||
|
|
||||||
|
#include <json-glib/json-glib.h>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
#define OTTIE_TYPE_PATH_SHAPE (ottie_path_shape_get_type ())
|
||||||
|
#define OTTIE_PATH_SHAPE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), OTTIE_TYPE_PATH_SHAPE, OttiePathShape))
|
||||||
|
#define OTTIE_PATH_SHAPE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), OTTIE_TYPE_PATH_SHAPE, OttiePathShapeClass))
|
||||||
|
#define OTTIE_IS_PATH_SHAPE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), OTTIE_TYPE_PATH_SHAPE))
|
||||||
|
#define OTTIE_IS_PATH_SHAPE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), OTTIE_TYPE_PATH_SHAPE))
|
||||||
|
#define OTTIE_PATH_SHAPE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), OTTIE_TYPE_PATH_SHAPE, OttiePathShapeClass))
|
||||||
|
|
||||||
|
typedef struct _OttiePathShape OttiePathShape;
|
||||||
|
typedef struct _OttiePathShapeClass OttiePathShapeClass;
|
||||||
|
|
||||||
|
GType ottie_path_shape_get_type (void) G_GNUC_CONST;
|
||||||
|
|
||||||
|
OttieShape * ottie_path_shape_parse (JsonReader *reader);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* __OTTIE_PATH_SHAPE_PRIVATE_H__ */
|
||||||
402
ottie/ottiepathvalue.c
Normal file
402
ottie/ottiepathvalue.c
Normal file
@@ -0,0 +1,402 @@
|
|||||||
|
/*
|
||||||
|
* 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 "ottiepathvalueprivate.h"
|
||||||
|
|
||||||
|
#include "ottieparserprivate.h"
|
||||||
|
|
||||||
|
#include <glib/gi18n-lib.h>
|
||||||
|
|
||||||
|
typedef struct _OttieContour OttieContour;
|
||||||
|
typedef struct _OttieCurve OttieCurve;
|
||||||
|
typedef struct _OttiePath OttiePath;
|
||||||
|
|
||||||
|
struct _OttieCurve {
|
||||||
|
double point[2];
|
||||||
|
double in[2];
|
||||||
|
double out[2];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _OttieContour {
|
||||||
|
gboolean closed;
|
||||||
|
guint n_curves;
|
||||||
|
OttieCurve curves[0];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _OttiePath {
|
||||||
|
guint ref_count;
|
||||||
|
guint n_contours;
|
||||||
|
OttieContour *contours[];
|
||||||
|
};
|
||||||
|
|
||||||
|
static OttieContour *
|
||||||
|
ottie_contour_renew (OttieContour *path,
|
||||||
|
guint n_curves)
|
||||||
|
{
|
||||||
|
OttieContour *self;
|
||||||
|
|
||||||
|
self = g_realloc (path, sizeof (OttieContour) + n_curves * sizeof (OttieCurve));
|
||||||
|
self->n_curves = n_curves;
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
static OttieContour *
|
||||||
|
ottie_contour_new (gboolean closed,
|
||||||
|
guint n_curves)
|
||||||
|
{
|
||||||
|
OttieContour *self;
|
||||||
|
|
||||||
|
self = g_malloc0 (sizeof (OttieContour) + n_curves * sizeof (OttieCurve));
|
||||||
|
self->closed = closed;
|
||||||
|
self->n_curves = n_curves;
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_contour_free (OttieContour *path)
|
||||||
|
{
|
||||||
|
g_free (path);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
ottie_parse_value_parse_numbers (JsonReader *reader,
|
||||||
|
gsize offset,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
return ottie_parser_parse_array (reader, "number",
|
||||||
|
2, 2, NULL,
|
||||||
|
offset, sizeof (double),
|
||||||
|
ottie_parser_option_double, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MAKE_OPEN_CONTOUR (NULL)
|
||||||
|
#define MAKE_CLOSED_CONTOUR GSIZE_TO_POINTER(1)
|
||||||
|
static gboolean
|
||||||
|
ottie_parse_value_parse_curve_array (JsonReader *reader,
|
||||||
|
gsize offset,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
/* Attention: The offset value here is to the point in the curve, not
|
||||||
|
* to the target */
|
||||||
|
OttieContour **target = (OttieContour **) data;
|
||||||
|
OttieContour *path = *target;
|
||||||
|
guint n_curves;
|
||||||
|
|
||||||
|
n_curves = json_reader_count_elements (reader);
|
||||||
|
if (path == MAKE_OPEN_CONTOUR)
|
||||||
|
path = ottie_contour_new (FALSE, n_curves);
|
||||||
|
else if (path == MAKE_CLOSED_CONTOUR)
|
||||||
|
path = ottie_contour_new (TRUE, n_curves);
|
||||||
|
else if (n_curves < path->n_curves)
|
||||||
|
path = ottie_contour_renew (path, n_curves);
|
||||||
|
else if (n_curves > path->n_curves)
|
||||||
|
n_curves = path->n_curves;
|
||||||
|
|
||||||
|
*target = path;
|
||||||
|
|
||||||
|
return ottie_parser_parse_array (reader, "path array",
|
||||||
|
0, path->n_curves, NULL,
|
||||||
|
offset, sizeof (OttieCurve),
|
||||||
|
ottie_parse_value_parse_numbers, &path->curves[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
ottie_parser_value_parse_closed (JsonReader *reader,
|
||||||
|
gsize offset,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
OttieContour **target = (OttieContour **) data;
|
||||||
|
gboolean b;
|
||||||
|
|
||||||
|
b = json_reader_get_boolean_value (reader);
|
||||||
|
if (json_reader_get_error (reader))
|
||||||
|
{
|
||||||
|
ottie_parser_emit_error (reader, json_reader_get_error (reader));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*target == MAKE_OPEN_CONTOUR || *target == MAKE_CLOSED_CONTOUR)
|
||||||
|
{
|
||||||
|
if (b)
|
||||||
|
*target = MAKE_CLOSED_CONTOUR;
|
||||||
|
else
|
||||||
|
*target = MAKE_OPEN_CONTOUR;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
(*target)->closed = b;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
ottie_path_value_parse_contour (JsonReader *reader,
|
||||||
|
gsize offset,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
OttieContour **target = (OttieContour **) ((guint8 *) data + offset);
|
||||||
|
OttieParserOption options[] = {
|
||||||
|
{ "c", ottie_parser_value_parse_closed, 0 },
|
||||||
|
{ "i", ottie_parse_value_parse_curve_array, G_STRUCT_OFFSET (OttieCurve, in) },
|
||||||
|
{ "o", ottie_parse_value_parse_curve_array, G_STRUCT_OFFSET (OttieCurve, out) },
|
||||||
|
{ "v", ottie_parse_value_parse_curve_array, G_STRUCT_OFFSET (OttieCurve, point) },
|
||||||
|
};
|
||||||
|
|
||||||
|
g_assert (*target == NULL);
|
||||||
|
*target = MAKE_CLOSED_CONTOUR;
|
||||||
|
|
||||||
|
if (!ottie_parser_parse_object (reader, "contour", options, G_N_ELEMENTS (options), target))
|
||||||
|
{
|
||||||
|
if (*target != MAKE_OPEN_CONTOUR && *target != MAKE_CLOSED_CONTOUR)
|
||||||
|
g_clear_pointer (target, ottie_contour_free);
|
||||||
|
*target = NULL;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*target == MAKE_OPEN_CONTOUR)
|
||||||
|
*target = ottie_contour_new (FALSE, 0);
|
||||||
|
else if (*target == MAKE_CLOSED_CONTOUR)
|
||||||
|
*target = ottie_contour_new (TRUE, 0);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static OttiePath *
|
||||||
|
ottie_path_new (gsize n_contours)
|
||||||
|
{
|
||||||
|
OttiePath *self;
|
||||||
|
|
||||||
|
self = g_malloc0 (sizeof (OttiePath) + sizeof (OttieContour *) * n_contours);
|
||||||
|
self->ref_count = 1;
|
||||||
|
self->n_contours = n_contours;
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
static OttiePath *
|
||||||
|
ottie_path_ref (OttiePath *self)
|
||||||
|
{
|
||||||
|
self->ref_count++;
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_path_unref (OttiePath *self)
|
||||||
|
{
|
||||||
|
self->ref_count--;
|
||||||
|
if (self->ref_count > 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (guint i = 0; i < self->n_contours; i++)
|
||||||
|
{
|
||||||
|
g_clear_pointer (&self->contours[i], ottie_contour_free);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
ottie_path_value_parse_one (JsonReader *reader,
|
||||||
|
gsize offset,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
OttiePath **target = (OttiePath **) ((guint8 *) data + offset);
|
||||||
|
OttiePath *path;
|
||||||
|
|
||||||
|
if (json_reader_is_array (reader))
|
||||||
|
path = ottie_path_new (json_reader_count_elements (reader));
|
||||||
|
else
|
||||||
|
path = ottie_path_new (1);
|
||||||
|
|
||||||
|
if (!ottie_parser_parse_array (reader, "path",
|
||||||
|
path->n_contours, path->n_contours, NULL,
|
||||||
|
G_STRUCT_OFFSET (OttiePath, contours),
|
||||||
|
sizeof (OttieContour *),
|
||||||
|
ottie_path_value_parse_contour, path))
|
||||||
|
{
|
||||||
|
ottie_path_unref (path);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_clear_pointer (target, ottie_path_unref);
|
||||||
|
*target = path;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static OttieContour *
|
||||||
|
ottie_contour_interpolate (const OttieContour *start,
|
||||||
|
const OttieContour *end,
|
||||||
|
double progress)
|
||||||
|
{
|
||||||
|
OttieContour *self = ottie_contour_new (start->closed || end->closed,
|
||||||
|
MIN (start->n_curves, end->n_curves));
|
||||||
|
|
||||||
|
for (gsize i = 0; i < self->n_curves; i++)
|
||||||
|
{
|
||||||
|
self->curves[i].point[0] = start->curves[i].point[0] + progress * (end->curves[i].point[0] - start->curves[i].point[0]);
|
||||||
|
self->curves[i].point[1] = start->curves[i].point[1] + progress * (end->curves[i].point[1] - start->curves[i].point[1]);
|
||||||
|
self->curves[i].in[0] = start->curves[i].in[0] + progress * (end->curves[i].in[0] - start->curves[i].in[0]);
|
||||||
|
self->curves[i].in[1] = start->curves[i].in[1] + progress * (end->curves[i].in[1] - start->curves[i].in[1]);
|
||||||
|
self->curves[i].out[0] = start->curves[i].out[0] + progress * (end->curves[i].out[0] - start->curves[i].out[0]);
|
||||||
|
self->curves[i].out[1] = start->curves[i].out[1] + progress * (end->curves[i].out[1] - start->curves[i].out[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
static OttiePath *
|
||||||
|
ottie_path_interpolate (const OttiePath *start,
|
||||||
|
const OttiePath *end,
|
||||||
|
double progress)
|
||||||
|
{
|
||||||
|
OttiePath *self = ottie_path_new (MIN (start->n_contours, end->n_contours));
|
||||||
|
|
||||||
|
for (gsize i = 0; i < self->n_contours; i++)
|
||||||
|
{
|
||||||
|
self->contours[i] = ottie_contour_interpolate (start->contours[i],
|
||||||
|
end->contours[i],
|
||||||
|
progress);
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define OTTIE_KEYFRAMES_NAME ottie_path_keyframes
|
||||||
|
#define OTTIE_KEYFRAMES_TYPE_NAME OttieContourKeyframes
|
||||||
|
#define OTTIE_KEYFRAMES_ELEMENT_TYPE OttiePath *
|
||||||
|
#define OTTIE_KEYFRAMES_COPY_FUNC ottie_path_ref
|
||||||
|
#define OTTIE_KEYFRAMES_FREE_FUNC ottie_path_unref
|
||||||
|
#define OTTIE_KEYFRAMES_PARSE_FUNC ottie_path_value_parse_one
|
||||||
|
#define OTTIE_KEYFRAMES_INTERPOLATE_FUNC ottie_path_interpolate
|
||||||
|
#include "ottiekeyframesimpl.c"
|
||||||
|
|
||||||
|
void
|
||||||
|
ottie_path_value_init (OttiePathValue *self)
|
||||||
|
{
|
||||||
|
self->is_static = TRUE;
|
||||||
|
self->static_value = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ottie_path_value_clear (OttiePathValue *self)
|
||||||
|
{
|
||||||
|
if (self->is_static)
|
||||||
|
g_clear_pointer (&self->static_value, ottie_path_unref);
|
||||||
|
else
|
||||||
|
g_clear_pointer (&self->keyframes, ottie_path_keyframes_free);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GskPath *
|
||||||
|
ottie_path_build (OttiePath *self,
|
||||||
|
gboolean reverse)
|
||||||
|
{
|
||||||
|
GskPathBuilder *builder;
|
||||||
|
|
||||||
|
if (reverse)
|
||||||
|
g_warning ("FIXME: Make paths reversible");
|
||||||
|
|
||||||
|
builder = gsk_path_builder_new ();
|
||||||
|
for (gsize i = 0; i < self->n_contours; i++)
|
||||||
|
{
|
||||||
|
OttieContour *contour = self->contours[i];
|
||||||
|
if (contour->n_curves == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
gsk_path_builder_move_to (builder,
|
||||||
|
contour->curves[0].point[0], contour->curves[0].point[1]);
|
||||||
|
for (guint j = 1; j < contour->n_curves; j++)
|
||||||
|
{
|
||||||
|
gsk_path_builder_curve_to (builder,
|
||||||
|
contour->curves[j-1].point[0] + contour->curves[j-1].out[0],
|
||||||
|
contour->curves[j-1].point[1] + contour->curves[j-1].out[1],
|
||||||
|
contour->curves[j].point[0] + contour->curves[j].in[0],
|
||||||
|
contour->curves[j].point[1] + contour->curves[j].in[1],
|
||||||
|
contour->curves[j].point[0],
|
||||||
|
contour->curves[j].point[1]);
|
||||||
|
}
|
||||||
|
if (contour->closed)
|
||||||
|
{
|
||||||
|
gsk_path_builder_curve_to (builder,
|
||||||
|
contour->curves[contour->n_curves-1].point[0] + contour->curves[contour->n_curves-1].out[0],
|
||||||
|
contour->curves[contour->n_curves-1].point[1] + contour->curves[contour->n_curves-1].out[1],
|
||||||
|
contour->curves[0].point[0] + contour->curves[0].in[0],
|
||||||
|
contour->curves[0].point[1] + contour->curves[0].in[1],
|
||||||
|
contour->curves[0].point[0],
|
||||||
|
contour->curves[0].point[1]);
|
||||||
|
gsk_path_builder_close (builder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return gsk_path_builder_free_to_path (builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
GskPath *
|
||||||
|
ottie_path_value_get (OttiePathValue *self,
|
||||||
|
double timestamp,
|
||||||
|
gboolean reverse)
|
||||||
|
{
|
||||||
|
if (self->is_static)
|
||||||
|
return ottie_path_build (self->static_value, reverse);
|
||||||
|
|
||||||
|
return ottie_path_build (ottie_path_keyframes_get (self->keyframes, timestamp), reverse);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
ottie_path_value_parse (JsonReader *reader,
|
||||||
|
gsize offset,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
OttiePathValue *self = (OttiePathValue *) ((guint8 *) data + GPOINTER_TO_SIZE (offset));
|
||||||
|
|
||||||
|
if (json_reader_read_member (reader, "k"))
|
||||||
|
{
|
||||||
|
if (!json_reader_is_array (reader))
|
||||||
|
{
|
||||||
|
if (!ottie_path_value_parse_one (reader, 0, &self->static_value))
|
||||||
|
{
|
||||||
|
json_reader_end_member (reader);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
self->is_static = TRUE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
self->keyframes = ottie_path_keyframes_parse (reader);
|
||||||
|
if (self->keyframes == NULL)
|
||||||
|
{
|
||||||
|
json_reader_end_member (reader);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
self->is_static = FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ottie_parser_error_syntax (reader, "Property is not a path value");
|
||||||
|
}
|
||||||
|
json_reader_end_member (reader);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
53
ottie/ottiepathvalueprivate.h
Normal file
53
ottie/ottiepathvalueprivate.h
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* 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 __OTTIE_PATH_VALUE_PRIVATE_H__
|
||||||
|
#define __OTTIE_PATH_VALUE_PRIVATE_H__
|
||||||
|
|
||||||
|
#include <json-glib/json-glib.h>
|
||||||
|
|
||||||
|
#include <gsk/gsk.h>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
typedef struct _OttiePathValue OttiePathValue;
|
||||||
|
|
||||||
|
struct _OttiePathValue
|
||||||
|
{
|
||||||
|
gboolean is_static;
|
||||||
|
union {
|
||||||
|
gpointer static_value;
|
||||||
|
gpointer keyframes;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
void ottie_path_value_init (OttiePathValue *self);
|
||||||
|
void ottie_path_value_clear (OttiePathValue *self);
|
||||||
|
|
||||||
|
GskPath * ottie_path_value_get (OttiePathValue *self,
|
||||||
|
double timestamp,
|
||||||
|
gboolean reverse);
|
||||||
|
|
||||||
|
gboolean ottie_path_value_parse (JsonReader *reader,
|
||||||
|
gsize offset,
|
||||||
|
gpointer data);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* __OTTIE_PATH_VALUE_PRIVATE_H__ */
|
||||||
490
ottie/ottieplayer.c
Normal file
490
ottie/ottieplayer.c
Normal file
@@ -0,0 +1,490 @@
|
|||||||
|
/*
|
||||||
|
* 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 "ottieplayer.h"
|
||||||
|
|
||||||
|
#include "ottiecreation.h"
|
||||||
|
#include "ottiepaintable.h"
|
||||||
|
|
||||||
|
#include <glib/gi18n.h>
|
||||||
|
|
||||||
|
struct _OttiePlayer
|
||||||
|
{
|
||||||
|
GObject parent_instance;
|
||||||
|
|
||||||
|
GFile *file;
|
||||||
|
|
||||||
|
OttieCreation *creation;
|
||||||
|
OttiePaintable *paintable;
|
||||||
|
gint64 time_offset;
|
||||||
|
guint timer_cb;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _OttiePlayerClass
|
||||||
|
{
|
||||||
|
GObjectClass parent_class;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
PROP_0,
|
||||||
|
PROP_FILE,
|
||||||
|
|
||||||
|
N_PROPS,
|
||||||
|
};
|
||||||
|
|
||||||
|
static GParamSpec *properties[N_PROPS] = { NULL, };
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_player_paintable_snapshot (GdkPaintable *paintable,
|
||||||
|
GdkSnapshot *snapshot,
|
||||||
|
double width,
|
||||||
|
double height)
|
||||||
|
{
|
||||||
|
OttiePlayer *self = OTTIE_PLAYER (paintable);
|
||||||
|
|
||||||
|
gdk_paintable_snapshot (GDK_PAINTABLE (self->paintable), snapshot, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
ottie_player_paintable_get_intrinsic_width (GdkPaintable *paintable)
|
||||||
|
{
|
||||||
|
OttiePlayer *self = OTTIE_PLAYER (paintable);
|
||||||
|
|
||||||
|
return gdk_paintable_get_intrinsic_width (GDK_PAINTABLE (self->paintable));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
ottie_player_paintable_get_intrinsic_height (GdkPaintable *paintable)
|
||||||
|
{
|
||||||
|
OttiePlayer *self = OTTIE_PLAYER (paintable);
|
||||||
|
|
||||||
|
return gdk_paintable_get_intrinsic_height (GDK_PAINTABLE (self->paintable));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_player_paintable_init (GdkPaintableInterface *iface)
|
||||||
|
{
|
||||||
|
iface->snapshot = ottie_player_paintable_snapshot;
|
||||||
|
iface->get_intrinsic_width = ottie_player_paintable_get_intrinsic_width;
|
||||||
|
iface->get_intrinsic_height = ottie_player_paintable_get_intrinsic_height;
|
||||||
|
}
|
||||||
|
|
||||||
|
G_DEFINE_TYPE_EXTENDED (OttiePlayer, ottie_player, GTK_TYPE_MEDIA_STREAM, 0,
|
||||||
|
G_IMPLEMENT_INTERFACE (GDK_TYPE_PAINTABLE,
|
||||||
|
ottie_player_paintable_init))
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
ottie_player_timer_cb (gpointer data)
|
||||||
|
{
|
||||||
|
OttiePlayer *self = OTTIE_PLAYER (data);
|
||||||
|
gint64 timestamp;
|
||||||
|
|
||||||
|
timestamp = g_get_monotonic_time () - self->time_offset;
|
||||||
|
if (timestamp > ottie_paintable_get_duration (self->paintable))
|
||||||
|
{
|
||||||
|
if (gtk_media_stream_get_loop (GTK_MEDIA_STREAM (self)))
|
||||||
|
{
|
||||||
|
timestamp %= ottie_paintable_get_duration (self->paintable);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
timestamp = ottie_paintable_get_duration (self->paintable);
|
||||||
|
gtk_media_stream_ended (GTK_MEDIA_STREAM (self));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ottie_paintable_set_timestamp (self->paintable, timestamp);
|
||||||
|
gtk_media_stream_update (GTK_MEDIA_STREAM (self), timestamp);
|
||||||
|
|
||||||
|
return G_SOURCE_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
ottie_player_play (GtkMediaStream *stream)
|
||||||
|
{
|
||||||
|
OttiePlayer *self = OTTIE_PLAYER (stream);
|
||||||
|
double frame_rate;
|
||||||
|
|
||||||
|
frame_rate = ottie_creation_get_frame_rate (self->creation);
|
||||||
|
if (frame_rate <= 0)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
self->time_offset = g_get_monotonic_time () - ottie_paintable_get_timestamp (self->paintable);
|
||||||
|
self->timer_cb = g_timeout_add (1000 / frame_rate, ottie_player_timer_cb, self);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_player_pause (GtkMediaStream *stream)
|
||||||
|
{
|
||||||
|
OttiePlayer *self = OTTIE_PLAYER (stream);
|
||||||
|
|
||||||
|
g_clear_handle_id (&self->timer_cb, g_source_remove);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_player_seek (GtkMediaStream *stream,
|
||||||
|
gint64 timestamp)
|
||||||
|
{
|
||||||
|
OttiePlayer *self = OTTIE_PLAYER (stream);
|
||||||
|
|
||||||
|
if (!ottie_creation_is_prepared (self->creation))
|
||||||
|
gtk_media_stream_seek_failed (stream);
|
||||||
|
|
||||||
|
ottie_paintable_set_timestamp (self->paintable, timestamp);
|
||||||
|
self->time_offset = g_get_monotonic_time () - timestamp;
|
||||||
|
|
||||||
|
gtk_media_stream_seek_success (stream);
|
||||||
|
gtk_media_stream_update (stream, timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_player_set_property (GObject *object,
|
||||||
|
guint prop_id,
|
||||||
|
const GValue *value,
|
||||||
|
GParamSpec *pspec)
|
||||||
|
|
||||||
|
{
|
||||||
|
OttiePlayer *self = OTTIE_PLAYER (object);
|
||||||
|
|
||||||
|
switch (prop_id)
|
||||||
|
{
|
||||||
|
case PROP_FILE:
|
||||||
|
ottie_player_set_file (self, g_value_get_object (value));
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_player_get_property (GObject *object,
|
||||||
|
guint prop_id,
|
||||||
|
GValue *value,
|
||||||
|
GParamSpec *pspec)
|
||||||
|
{
|
||||||
|
OttiePlayer *self = OTTIE_PLAYER (object);
|
||||||
|
|
||||||
|
switch (prop_id)
|
||||||
|
{
|
||||||
|
case PROP_FILE:
|
||||||
|
g_value_set_object (value, self->file);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_player_dispose (GObject *object)
|
||||||
|
{
|
||||||
|
OttiePlayer *self = OTTIE_PLAYER (object);
|
||||||
|
|
||||||
|
if (self->paintable)
|
||||||
|
{
|
||||||
|
g_signal_handlers_disconnect_by_func (self->paintable, gdk_paintable_invalidate_contents, self);
|
||||||
|
g_signal_handlers_disconnect_by_func (self->paintable, gdk_paintable_invalidate_size, self);
|
||||||
|
g_clear_object (&self->paintable);
|
||||||
|
}
|
||||||
|
g_clear_object (&self->creation);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (ottie_player_parent_class)->dispose (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_player_class_init (OttiePlayerClass *klass)
|
||||||
|
{
|
||||||
|
GtkMediaStreamClass *stream_class = GTK_MEDIA_STREAM_CLASS (klass);
|
||||||
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||||
|
|
||||||
|
stream_class->play = ottie_player_play;
|
||||||
|
stream_class->pause = ottie_player_pause;
|
||||||
|
stream_class->seek = ottie_player_seek;
|
||||||
|
|
||||||
|
gobject_class->get_property = ottie_player_get_property;
|
||||||
|
gobject_class->set_property = ottie_player_set_property;
|
||||||
|
gobject_class->dispose = ottie_player_dispose;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OttiePlayer:file
|
||||||
|
*
|
||||||
|
* The played file or %NULL.
|
||||||
|
*/
|
||||||
|
properties[PROP_FILE] =
|
||||||
|
g_param_spec_object ("file",
|
||||||
|
_("File"),
|
||||||
|
_("The played file"),
|
||||||
|
G_TYPE_FILE,
|
||||||
|
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||||
|
|
||||||
|
g_object_class_install_properties (gobject_class, N_PROPS, properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_player_prepared_cb (OttieCreation *creation,
|
||||||
|
GParamSpec *pspec,
|
||||||
|
OttiePlayer *self)
|
||||||
|
{
|
||||||
|
if (ottie_creation_is_prepared (creation))
|
||||||
|
gtk_media_stream_prepared (GTK_MEDIA_STREAM (self),
|
||||||
|
FALSE,
|
||||||
|
TRUE,
|
||||||
|
TRUE,
|
||||||
|
ottie_paintable_get_duration (self->paintable));
|
||||||
|
else
|
||||||
|
gtk_media_stream_unprepared (GTK_MEDIA_STREAM (self));
|
||||||
|
|
||||||
|
ottie_paintable_set_timestamp (self->paintable, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_player_init (OttiePlayer *self)
|
||||||
|
{
|
||||||
|
self->creation = ottie_creation_new ();
|
||||||
|
g_signal_connect (self->creation, "notify::prepared", G_CALLBACK (ottie_player_prepared_cb), self);
|
||||||
|
self->paintable = ottie_paintable_new (self->creation);
|
||||||
|
g_signal_connect_swapped (self->paintable, "invalidate-contents", G_CALLBACK (gdk_paintable_invalidate_contents), self);
|
||||||
|
g_signal_connect_swapped (self->paintable, "invalidate-size", G_CALLBACK (gdk_paintable_invalidate_size), self);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ottie_player_new:
|
||||||
|
*
|
||||||
|
* Creates a new Ottie player.
|
||||||
|
*
|
||||||
|
* Returns: (transfer full): a new #OttiePlayer
|
||||||
|
**/
|
||||||
|
OttiePlayer *
|
||||||
|
ottie_player_new (void)
|
||||||
|
{
|
||||||
|
return g_object_new (OTTIE_TYPE_PLAYER, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ottie_player_new_for_file:
|
||||||
|
* @file: (nullable): a #GFile
|
||||||
|
*
|
||||||
|
* Creates a new #OttiePlayer playing the given @file. If the file
|
||||||
|
* isn’t found or can’t be loaded, the resulting #OttiePlayer be empty.
|
||||||
|
*
|
||||||
|
* Returns: a new #OttiePlayer
|
||||||
|
**/
|
||||||
|
OttiePlayer*
|
||||||
|
ottie_player_new_for_file (GFile *file)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (file == NULL || G_IS_FILE (file), NULL);
|
||||||
|
|
||||||
|
return g_object_new (OTTIE_TYPE_PLAYER,
|
||||||
|
"file", file,
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ottie_player_new_for_filename:
|
||||||
|
* @filename: (type filename) (nullable): a filename
|
||||||
|
*
|
||||||
|
* Creates a new #OttiePlayer displaying the file @filename.
|
||||||
|
*
|
||||||
|
* This is a utility function that calls ottie_player_new_for_file().
|
||||||
|
* See that function for details.
|
||||||
|
*
|
||||||
|
* Returns: a new #OttiePlayer
|
||||||
|
**/
|
||||||
|
OttiePlayer*
|
||||||
|
ottie_player_new_for_filename (const char *filename)
|
||||||
|
{
|
||||||
|
OttiePlayer *result;
|
||||||
|
GFile *file;
|
||||||
|
|
||||||
|
if (filename)
|
||||||
|
file = g_file_new_for_path (filename);
|
||||||
|
else
|
||||||
|
file = NULL;
|
||||||
|
|
||||||
|
result = ottie_player_new_for_file (file);
|
||||||
|
|
||||||
|
if (file)
|
||||||
|
g_object_unref (file);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ottie_player_new_for_resource:
|
||||||
|
* @resource_path: (nullable): resource path to play back
|
||||||
|
*
|
||||||
|
* Creates a new #OttiePlayer displaying the file @resource_path.
|
||||||
|
*
|
||||||
|
* This is a utility function that calls ottie_player_new_for_file().
|
||||||
|
* See that function for details.
|
||||||
|
*
|
||||||
|
* Returns: a new #OttiePlayer
|
||||||
|
**/
|
||||||
|
OttiePlayer *
|
||||||
|
ottie_player_new_for_resource (const char *resource_path)
|
||||||
|
{
|
||||||
|
OttiePlayer *result;
|
||||||
|
GFile *file;
|
||||||
|
|
||||||
|
if (resource_path)
|
||||||
|
{
|
||||||
|
char *uri, *escaped;
|
||||||
|
|
||||||
|
escaped = g_uri_escape_string (resource_path,
|
||||||
|
G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, FALSE);
|
||||||
|
uri = g_strconcat ("resource://", escaped, NULL);
|
||||||
|
g_free (escaped);
|
||||||
|
|
||||||
|
file = g_file_new_for_uri (uri);
|
||||||
|
g_free (uri);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
file = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = ottie_player_new_for_file (file);
|
||||||
|
|
||||||
|
if (file)
|
||||||
|
g_object_unref (file);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ottie_player_set_file:
|
||||||
|
* @self: a #OttiePlayer
|
||||||
|
* @file: (nullable): a %GFile or %NULL
|
||||||
|
*
|
||||||
|
* Makes @self load and display @file.
|
||||||
|
*
|
||||||
|
* See ottie_player_new_for_file() for details.
|
||||||
|
**/
|
||||||
|
void
|
||||||
|
ottie_player_set_file (OttiePlayer *self,
|
||||||
|
GFile *file)
|
||||||
|
{
|
||||||
|
g_return_if_fail (OTTIE_IS_PLAYER (self));
|
||||||
|
g_return_if_fail (file == NULL || G_IS_FILE (file));
|
||||||
|
|
||||||
|
if (self->file == file)
|
||||||
|
return;
|
||||||
|
|
||||||
|
g_object_freeze_notify (G_OBJECT (self));
|
||||||
|
|
||||||
|
g_set_object (&self->file, file);
|
||||||
|
ottie_creation_load_file (self->creation, file);
|
||||||
|
|
||||||
|
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FILE]);
|
||||||
|
|
||||||
|
g_object_thaw_notify (G_OBJECT (self));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ottie_player_get_file:
|
||||||
|
* @self: a #OttiePlayer
|
||||||
|
*
|
||||||
|
* Gets the #GFile currently displayed if @self is displaying a file.
|
||||||
|
* If @self is not displaying a file, then %NULL is returned.
|
||||||
|
*
|
||||||
|
* Returns: (nullable) (transfer none): The #GFile displayed by @self.
|
||||||
|
**/
|
||||||
|
GFile *
|
||||||
|
ottie_player_get_file (OttiePlayer *self)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (OTTIE_IS_PLAYER (self), FALSE);
|
||||||
|
|
||||||
|
return self->file;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ottie_player_set_filename:
|
||||||
|
* @self: a #OttiePlayer
|
||||||
|
* @filename: (nullable): the filename to play
|
||||||
|
*
|
||||||
|
* Makes @self load and display the given @filename.
|
||||||
|
*
|
||||||
|
* This is a utility function that calls ottie_player_set_file().
|
||||||
|
**/
|
||||||
|
void
|
||||||
|
ottie_player_set_filename (OttiePlayer *self,
|
||||||
|
const char *filename)
|
||||||
|
{
|
||||||
|
GFile *file;
|
||||||
|
|
||||||
|
g_return_if_fail (OTTIE_IS_PLAYER (self));
|
||||||
|
|
||||||
|
if (filename)
|
||||||
|
file = g_file_new_for_path (filename);
|
||||||
|
else
|
||||||
|
file = NULL;
|
||||||
|
|
||||||
|
ottie_player_set_file (self, file);
|
||||||
|
|
||||||
|
if (file)
|
||||||
|
g_object_unref (file);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ottie_player_set_resource:
|
||||||
|
* @self: a #OttiePlayer
|
||||||
|
* @resource_path: (nullable): the resource to set
|
||||||
|
*
|
||||||
|
* Makes @self load and display the resource at the given
|
||||||
|
* @resource_path.
|
||||||
|
*
|
||||||
|
* This is a utility function that calls ottie_player_set_file(),
|
||||||
|
**/
|
||||||
|
void
|
||||||
|
ottie_player_set_resource (OttiePlayer *self,
|
||||||
|
const char *resource_path)
|
||||||
|
{
|
||||||
|
GFile *file;
|
||||||
|
|
||||||
|
g_return_if_fail (OTTIE_IS_PLAYER (self));
|
||||||
|
|
||||||
|
if (resource_path)
|
||||||
|
{
|
||||||
|
char *uri, *escaped;
|
||||||
|
|
||||||
|
escaped = g_uri_escape_string (resource_path,
|
||||||
|
G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, FALSE);
|
||||||
|
uri = g_strconcat ("resource://", escaped, NULL);
|
||||||
|
g_free (escaped);
|
||||||
|
|
||||||
|
file = g_file_new_for_uri (uri);
|
||||||
|
g_free (uri);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
file = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ottie_player_set_file (self, file);
|
||||||
|
|
||||||
|
if (file)
|
||||||
|
g_object_unref (file);
|
||||||
|
}
|
||||||
|
|
||||||
59
ottie/ottieplayer.h
Normal file
59
ottie/ottieplayer.h
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* 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 __OTTIE_PLAYER_H__
|
||||||
|
#define __OTTIE_PLAYER_H__
|
||||||
|
|
||||||
|
#if !defined (__OTTIE_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||||
|
#error "Only <ottie/ottie.h> can be included directly."
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <gtk/gtk.h>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
#define OTTIE_TYPE_PLAYER (ottie_player_get_type ())
|
||||||
|
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
G_DECLARE_FINAL_TYPE (OttiePlayer, ottie_player, OTTIE, PLAYER, GtkMediaStream)
|
||||||
|
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
OttiePlayer * ottie_player_new (void);
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
OttiePlayer * ottie_player_new_for_file (GFile *file);
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
OttiePlayer * ottie_player_new_for_filename (const char *filename);
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
OttiePlayer * ottie_player_new_for_resource (const char *resource_path);
|
||||||
|
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
void ottie_player_set_file (OttiePlayer *self,
|
||||||
|
GFile *file);
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
GFile * ottie_player_get_file (OttiePlayer *self);
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
void ottie_player_set_filename (OttiePlayer *self,
|
||||||
|
const char *filename);
|
||||||
|
GDK_AVAILABLE_IN_ALL
|
||||||
|
void ottie_player_set_resource (OttiePlayer *self,
|
||||||
|
const char *resource_path);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* __OTTIE_PLAYER_H__ */
|
||||||
154
ottie/ottiepoint3dvalue.c
Normal file
154
ottie/ottiepoint3dvalue.c
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
/**
|
||||||
|
* 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 "ottiepoint3dvalueprivate.h"
|
||||||
|
|
||||||
|
#include "ottieparserprivate.h"
|
||||||
|
|
||||||
|
#include <glib/gi18n-lib.h>
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
ottie_point3d_value_parse_value (JsonReader *reader,
|
||||||
|
gsize offset,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
double d[3];
|
||||||
|
guint n_items;
|
||||||
|
|
||||||
|
if (!ottie_parser_parse_array (reader, "point",
|
||||||
|
2, 3, &n_items,
|
||||||
|
0, sizeof (double),
|
||||||
|
ottie_parser_option_double,
|
||||||
|
&d))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (n_items == 2)
|
||||||
|
d[2] = NAN; /* We do fixup below */
|
||||||
|
|
||||||
|
*(graphene_point3d_t *) ((guint8 *) data + offset) = GRAPHENE_POINT3D_INIT (d[0], d[1], d[2]);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define OTTIE_KEYFRAMES_NAME ottie_point_keyframes
|
||||||
|
#define OTTIE_KEYFRAMES_TYPE_NAME OttiePointKeyframes
|
||||||
|
#define OTTIE_KEYFRAMES_ELEMENT_TYPE graphene_point3d_t
|
||||||
|
#define OTTIE_KEYFRAMES_BY_VALUE 1
|
||||||
|
#define OTTIE_KEYFRAMES_DIMENSIONS 3
|
||||||
|
#define OTTIE_KEYFRAMES_PARSE_FUNC ottie_point3d_value_parse_value
|
||||||
|
#define OTTIE_KEYFRAMES_INTERPOLATE_FUNC graphene_point3d_interpolate
|
||||||
|
#include "ottiekeyframesimpl.c"
|
||||||
|
|
||||||
|
void
|
||||||
|
ottie_point3d_value_init (OttiePoint3DValue *self,
|
||||||
|
const graphene_point3d_t *value)
|
||||||
|
{
|
||||||
|
self->is_static = TRUE;
|
||||||
|
self->static_value = *value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ottie_point3d_value_clear (OttiePoint3DValue *self)
|
||||||
|
{
|
||||||
|
if (!self->is_static)
|
||||||
|
g_clear_pointer (&self->keyframes, ottie_point_keyframes_free);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ottie_point3d_value_get (OttiePoint3DValue *self,
|
||||||
|
double timestamp,
|
||||||
|
graphene_point3d_t *value)
|
||||||
|
{
|
||||||
|
if (self->is_static)
|
||||||
|
{
|
||||||
|
*value = self->static_value;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ottie_point_keyframes_get (self->keyframes, timestamp, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
ottie_point3d_value_parse (JsonReader *reader,
|
||||||
|
float default_value,
|
||||||
|
gsize offset,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
OttiePoint3DValue *self = (OttiePoint3DValue *) ((guint8 *) data + offset);
|
||||||
|
|
||||||
|
if (json_reader_read_member (reader, "k"))
|
||||||
|
{
|
||||||
|
gboolean is_static;
|
||||||
|
|
||||||
|
if (!json_reader_is_array (reader))
|
||||||
|
{
|
||||||
|
ottie_parser_error_syntax (reader, "Point value needs an array for its value");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!json_reader_read_element (reader, 0))
|
||||||
|
{
|
||||||
|
ottie_parser_emit_error (reader, json_reader_get_error (reader));
|
||||||
|
json_reader_end_element (reader);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
is_static = !json_reader_is_object (reader);
|
||||||
|
json_reader_end_element (reader);
|
||||||
|
|
||||||
|
if (is_static)
|
||||||
|
{
|
||||||
|
if (!ottie_point3d_value_parse_value (reader, 0, &self->static_value))
|
||||||
|
{
|
||||||
|
json_reader_end_member (reader);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
if (isnan (self->static_value.z))
|
||||||
|
self->static_value.z = default_value;
|
||||||
|
self->is_static = TRUE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
OttiePointKeyframes *keyframes = ottie_point_keyframes_parse (reader);
|
||||||
|
if (keyframes == NULL)
|
||||||
|
{
|
||||||
|
json_reader_end_member (reader);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < keyframes->n_items; i++)
|
||||||
|
{
|
||||||
|
if (isnan (keyframes->items[i].start_value.z))
|
||||||
|
keyframes->items[i].start_value.z = default_value;
|
||||||
|
if (isnan (keyframes->items[i].end_value.z))
|
||||||
|
keyframes->items[i].end_value.z = default_value;
|
||||||
|
}
|
||||||
|
self->is_static = FALSE;
|
||||||
|
self->keyframes = keyframes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ottie_parser_error_syntax (reader, "Point value has no value");
|
||||||
|
}
|
||||||
|
json_reader_end_member (reader);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
54
ottie/ottiepoint3dvalueprivate.h
Normal file
54
ottie/ottiepoint3dvalueprivate.h
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* 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 __OTTIE_POINT3D_VALUE_PRIVATE_H__
|
||||||
|
#define __OTTIE_POINT3D_VALUE_PRIVATE_H__
|
||||||
|
|
||||||
|
#include <json-glib/json-glib.h>
|
||||||
|
#include <graphene.h>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
typedef struct _OttiePoint3DValue OttiePoint3DValue;
|
||||||
|
|
||||||
|
struct _OttiePoint3DValue
|
||||||
|
{
|
||||||
|
gboolean is_static;
|
||||||
|
union {
|
||||||
|
graphene_point3d_t static_value;
|
||||||
|
gpointer keyframes;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
void ottie_point3d_value_init (OttiePoint3DValue *self,
|
||||||
|
const graphene_point3d_t *value);
|
||||||
|
void ottie_point3d_value_clear (OttiePoint3DValue *self);
|
||||||
|
|
||||||
|
void ottie_point3d_value_get (OttiePoint3DValue *self,
|
||||||
|
double timestamp,
|
||||||
|
graphene_point3d_t *value);
|
||||||
|
|
||||||
|
gboolean ottie_point3d_value_parse (JsonReader *reader,
|
||||||
|
float default_value,
|
||||||
|
gsize offset,
|
||||||
|
gpointer data);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* __OTTIE_POINT3D_VALUE_PRIVATE_H__ */
|
||||||
140
ottie/ottiepointvalue.c
Normal file
140
ottie/ottiepointvalue.c
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
/**
|
||||||
|
* 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 "ottiepointvalueprivate.h"
|
||||||
|
|
||||||
|
#include "ottieparserprivate.h"
|
||||||
|
|
||||||
|
#include <glib/gi18n-lib.h>
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
ottie_point_value_parse_value (JsonReader *reader,
|
||||||
|
gsize offset,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
double d[2];
|
||||||
|
|
||||||
|
if (!ottie_parser_parse_array (reader, "point",
|
||||||
|
2, 2, NULL,
|
||||||
|
0, sizeof (double),
|
||||||
|
ottie_parser_option_double,
|
||||||
|
&d))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
*(graphene_point_t *) ((guint8 *) data + offset) = GRAPHENE_POINT_INIT (d[0], d[1]);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define OTTIE_KEYFRAMES_NAME ottie_point_keyframes
|
||||||
|
#define OTTIE_KEYFRAMES_TYPE_NAME OttiePointKeyframes
|
||||||
|
#define OTTIE_KEYFRAMES_ELEMENT_TYPE graphene_point_t
|
||||||
|
#define OTTIE_KEYFRAMES_BY_VALUE 1
|
||||||
|
#define OTTIE_KEYFRAMES_DIMENSIONS 2
|
||||||
|
#define OTTIE_KEYFRAMES_PARSE_FUNC ottie_point_value_parse_value
|
||||||
|
#define OTTIE_KEYFRAMES_INTERPOLATE_FUNC graphene_point_interpolate
|
||||||
|
#include "ottiekeyframesimpl.c"
|
||||||
|
|
||||||
|
void
|
||||||
|
ottie_point_value_init (OttiePointValue *self,
|
||||||
|
const graphene_point_t *value)
|
||||||
|
{
|
||||||
|
self->is_static = TRUE;
|
||||||
|
self->static_value = *value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ottie_point_value_clear (OttiePointValue *self)
|
||||||
|
{
|
||||||
|
if (!self->is_static)
|
||||||
|
g_clear_pointer (&self->keyframes, ottie_point_keyframes_free);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ottie_point_value_get (OttiePointValue *self,
|
||||||
|
double timestamp,
|
||||||
|
graphene_point_t *value)
|
||||||
|
{
|
||||||
|
if (self->is_static)
|
||||||
|
{
|
||||||
|
*value = self->static_value;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ottie_point_keyframes_get (self->keyframes, timestamp, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
ottie_point_value_parse (JsonReader *reader,
|
||||||
|
gsize offset,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
OttiePointValue *self = (OttiePointValue *) ((guint8 *) data + offset);
|
||||||
|
|
||||||
|
if (json_reader_read_member (reader, "k"))
|
||||||
|
{
|
||||||
|
gboolean is_static;
|
||||||
|
|
||||||
|
if (!json_reader_is_array (reader))
|
||||||
|
{
|
||||||
|
ottie_parser_error_syntax (reader, "Point value needs an array for its value");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!json_reader_read_element (reader, 0))
|
||||||
|
{
|
||||||
|
ottie_parser_emit_error (reader, json_reader_get_error (reader));
|
||||||
|
json_reader_end_element (reader);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
is_static = !json_reader_is_object (reader);
|
||||||
|
json_reader_end_element (reader);
|
||||||
|
|
||||||
|
if (is_static)
|
||||||
|
{
|
||||||
|
if (!ottie_point_value_parse_value (reader, 0, &self->static_value))
|
||||||
|
{
|
||||||
|
json_reader_end_member (reader);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
self->is_static = TRUE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
OttiePointKeyframes *keyframes = ottie_point_keyframes_parse (reader);
|
||||||
|
if (keyframes == NULL)
|
||||||
|
{
|
||||||
|
json_reader_end_member (reader);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
self->is_static = FALSE;
|
||||||
|
self->keyframes = keyframes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ottie_parser_error_syntax (reader, "Point value has no value");
|
||||||
|
}
|
||||||
|
json_reader_end_member (reader);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
53
ottie/ottiepointvalueprivate.h
Normal file
53
ottie/ottiepointvalueprivate.h
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* 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 __OTTIE_POINT_VALUE_PRIVATE_H__
|
||||||
|
#define __OTTIE_POINT_VALUE_PRIVATE_H__
|
||||||
|
|
||||||
|
#include <json-glib/json-glib.h>
|
||||||
|
#include <graphene.h>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
typedef struct _OttiePointValue OttiePointValue;
|
||||||
|
|
||||||
|
struct _OttiePointValue
|
||||||
|
{
|
||||||
|
gboolean is_static;
|
||||||
|
union {
|
||||||
|
graphene_point_t static_value;
|
||||||
|
gpointer keyframes;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
void ottie_point_value_init (OttiePointValue *self,
|
||||||
|
const graphene_point_t *value);
|
||||||
|
void ottie_point_value_clear (OttiePointValue *self);
|
||||||
|
|
||||||
|
void ottie_point_value_get (OttiePointValue *self,
|
||||||
|
double timestamp,
|
||||||
|
graphene_point_t *value);
|
||||||
|
|
||||||
|
gboolean ottie_point_value_parse (JsonReader *reader,
|
||||||
|
gsize offset,
|
||||||
|
gpointer data);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* __OTTIE_POINT_VALUE_PRIVATE_H__ */
|
||||||
219
ottie/ottierectshape.c
Normal file
219
ottie/ottierectshape.c
Normal file
@@ -0,0 +1,219 @@
|
|||||||
|
/*
|
||||||
|
* 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 "ottierectshapeprivate.h"
|
||||||
|
|
||||||
|
#include "ottiedoublevalueprivate.h"
|
||||||
|
#include "ottiepointvalueprivate.h"
|
||||||
|
#include "ottieparserprivate.h"
|
||||||
|
#include "ottieshapeprivate.h"
|
||||||
|
|
||||||
|
#include <glib/gi18n-lib.h>
|
||||||
|
|
||||||
|
struct _OttieRectShape
|
||||||
|
{
|
||||||
|
OttieShape parent;
|
||||||
|
|
||||||
|
OttieDirection direction;
|
||||||
|
OttiePointValue position;
|
||||||
|
OttiePointValue size;
|
||||||
|
OttieDoubleValue rounded;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _OttieRectShapeClass
|
||||||
|
{
|
||||||
|
OttieShapeClass parent_class;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (OttieRectShape, ottie_rect_shape, OTTIE_TYPE_SHAPE)
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_rect_shape_render (OttieShape *shape,
|
||||||
|
OttieRender *render,
|
||||||
|
double timestamp)
|
||||||
|
{
|
||||||
|
OttieRectShape *self = OTTIE_RECT_SHAPE (shape);
|
||||||
|
graphene_point_t p, s;
|
||||||
|
double r;
|
||||||
|
GskPathBuilder *builder;
|
||||||
|
|
||||||
|
ottie_point_value_get (&self->position, timestamp, &p);
|
||||||
|
ottie_point_value_get (&self->size, timestamp, &s);
|
||||||
|
r = ottie_double_value_get (&self->rounded, timestamp);
|
||||||
|
s.x /= 2;
|
||||||
|
s.y /= 2;
|
||||||
|
r = MIN (r, MIN (s.x, s.y));
|
||||||
|
|
||||||
|
builder = gsk_path_builder_new ();
|
||||||
|
|
||||||
|
switch (self->direction)
|
||||||
|
{
|
||||||
|
case OTTIE_DIRECTION_FORWARD:
|
||||||
|
if (r <= 0)
|
||||||
|
{
|
||||||
|
gsk_path_builder_move_to (builder, p.x + s.x, p.y - s.y);
|
||||||
|
gsk_path_builder_line_to (builder, p.x - s.x, p.y - s.y);
|
||||||
|
gsk_path_builder_line_to (builder, p.x - s.x, p.y + s.y);
|
||||||
|
gsk_path_builder_line_to (builder, p.x + s.x, p.y + s.y);
|
||||||
|
gsk_path_builder_line_to (builder, p.x + s.x, p.y - s.y);
|
||||||
|
gsk_path_builder_close (builder);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const float weight = sqrt(0.5f);
|
||||||
|
|
||||||
|
gsk_path_builder_move_to (builder,
|
||||||
|
p.x + s.x, p.y - s.y + r);
|
||||||
|
gsk_path_builder_conic_to (builder,
|
||||||
|
p.x + s.x, p.y - s.y,
|
||||||
|
p.x + s.x - r, p.y - s.y,
|
||||||
|
weight);
|
||||||
|
gsk_path_builder_line_to (builder,
|
||||||
|
p.x - s.x + r, p.y - s.y);
|
||||||
|
gsk_path_builder_conic_to (builder,
|
||||||
|
p.x - s.x, p.y - s.y,
|
||||||
|
p.x - s.x, p.y - s.y + r,
|
||||||
|
weight);
|
||||||
|
gsk_path_builder_line_to (builder,
|
||||||
|
p.x - s.x, p.y + s.y - r);
|
||||||
|
gsk_path_builder_conic_to (builder,
|
||||||
|
p.x - s.x, p.y + s.y,
|
||||||
|
p.x - s.x + r, p.y + s.y,
|
||||||
|
weight);
|
||||||
|
gsk_path_builder_line_to (builder,
|
||||||
|
p.x + s.x - r, p.y + s.y);
|
||||||
|
gsk_path_builder_conic_to (builder,
|
||||||
|
p.x + s.x, p.y + s.y,
|
||||||
|
p.x + s.x, p.y + s.y - r,
|
||||||
|
weight);
|
||||||
|
gsk_path_builder_line_to (builder,
|
||||||
|
p.x + s.x, p.y - s.y + r);
|
||||||
|
gsk_path_builder_close (builder);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OTTIE_DIRECTION_BACKWARD:
|
||||||
|
if (r <= 0)
|
||||||
|
{
|
||||||
|
gsk_path_builder_move_to (builder, p.x + s.x, p.y - s.y);
|
||||||
|
gsk_path_builder_line_to (builder, p.x + s.x, p.y + s.y);
|
||||||
|
gsk_path_builder_line_to (builder, p.x - s.x, p.y + s.y);
|
||||||
|
gsk_path_builder_line_to (builder, p.x - s.x, p.y - s.y);
|
||||||
|
gsk_path_builder_line_to (builder, p.x + s.x, p.y - s.y);
|
||||||
|
gsk_path_builder_close (builder);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const float weight = sqrt(0.5f);
|
||||||
|
|
||||||
|
gsk_path_builder_move_to (builder,
|
||||||
|
p.x + s.x, p.y - s.y + r);
|
||||||
|
gsk_path_builder_line_to (builder,
|
||||||
|
p.x + s.x, p.y + s.y - r);
|
||||||
|
gsk_path_builder_conic_to (builder,
|
||||||
|
p.x + s.x, p.y + s.y,
|
||||||
|
p.x + s.x - r, p.y + s.y,
|
||||||
|
weight);
|
||||||
|
gsk_path_builder_line_to (builder,
|
||||||
|
p.x - s.x + r, p.y + s.y);
|
||||||
|
gsk_path_builder_conic_to (builder,
|
||||||
|
p.x - s.x, p.y + s.y,
|
||||||
|
p.x - s.x, p.y + s.y - r,
|
||||||
|
weight);
|
||||||
|
gsk_path_builder_line_to (builder,
|
||||||
|
p.x - s.x, p.y - s.y + r);
|
||||||
|
gsk_path_builder_conic_to (builder,
|
||||||
|
p.x - s.x, p.y - s.y,
|
||||||
|
p.x - s.x + r, p.y - s.y,
|
||||||
|
weight);
|
||||||
|
gsk_path_builder_line_to (builder,
|
||||||
|
p.x + s.x - r, p.y - s.y);
|
||||||
|
gsk_path_builder_conic_to (builder,
|
||||||
|
p.x + s.x, p.y - s.y,
|
||||||
|
p.x + s.x, p.y - s.y + r,
|
||||||
|
weight);
|
||||||
|
gsk_path_builder_close (builder);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
g_assert_not_reached();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ottie_render_add_path (render,
|
||||||
|
gsk_path_builder_free_to_path (builder));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_rect_shape_dispose (GObject *object)
|
||||||
|
{
|
||||||
|
OttieRectShape *self = OTTIE_RECT_SHAPE (object);
|
||||||
|
|
||||||
|
ottie_point_value_clear (&self->position);
|
||||||
|
ottie_point_value_clear (&self->size);
|
||||||
|
ottie_double_value_clear (&self->rounded);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (ottie_rect_shape_parent_class)->dispose (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_rect_shape_class_init (OttieRectShapeClass *klass)
|
||||||
|
{
|
||||||
|
OttieShapeClass *shape_class = OTTIE_SHAPE_CLASS (klass);
|
||||||
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||||
|
|
||||||
|
shape_class->render = ottie_rect_shape_render;
|
||||||
|
|
||||||
|
gobject_class->dispose = ottie_rect_shape_dispose;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ottie_rect_shape_init (OttieRectShape *self)
|
||||||
|
{
|
||||||
|
ottie_point_value_init (&self->position, &GRAPHENE_POINT_INIT (0, 0));
|
||||||
|
ottie_point_value_init (&self->size, &GRAPHENE_POINT_INIT (0, 0));
|
||||||
|
ottie_double_value_init (&self->rounded, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
OttieShape *
|
||||||
|
ottie_rect_shape_parse (JsonReader *reader)
|
||||||
|
{
|
||||||
|
OttieParserOption options[] = {
|
||||||
|
OTTIE_PARSE_OPTIONS_SHAPE,
|
||||||
|
{ "d", ottie_parser_option_direction, G_STRUCT_OFFSET (OttieRectShape, direction) },
|
||||||
|
{ "p", ottie_point_value_parse, G_STRUCT_OFFSET (OttieRectShape, position) },
|
||||||
|
{ "s", ottie_point_value_parse, G_STRUCT_OFFSET (OttieRectShape, size) },
|
||||||
|
{ "r", ottie_double_value_parse, G_STRUCT_OFFSET (OttieRectShape, rounded) },
|
||||||
|
};
|
||||||
|
OttieRectShape *self;
|
||||||
|
|
||||||
|
self = g_object_new (OTTIE_TYPE_RECT_SHAPE, NULL);
|
||||||
|
|
||||||
|
if (!ottie_parser_parse_object (reader, "rect shape", options, G_N_ELEMENTS (options), self))
|
||||||
|
{
|
||||||
|
g_object_unref (self);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return OTTIE_SHAPE (self);
|
||||||
|
}
|
||||||
|
|
||||||
45
ottie/ottierectshapeprivate.h
Normal file
45
ottie/ottierectshapeprivate.h
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* 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 __OTTIE_RECT_SHAPE_PRIVATE_H__
|
||||||
|
#define __OTTIE_RECT_SHAPE_PRIVATE_H__
|
||||||
|
|
||||||
|
#include "ottieshapeprivate.h"
|
||||||
|
|
||||||
|
#include <json-glib/json-glib.h>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
#define OTTIE_TYPE_RECT_SHAPE (ottie_rect_shape_get_type ())
|
||||||
|
#define OTTIE_RECT_SHAPE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), OTTIE_TYPE_RECT_SHAPE, OttieRectShape))
|
||||||
|
#define OTTIE_RECT_SHAPE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), OTTIE_TYPE_RECT_SHAPE, OttieRectShapeClass))
|
||||||
|
#define OTTIE_IS_RECT_SHAPE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), OTTIE_TYPE_RECT_SHAPE))
|
||||||
|
#define OTTIE_IS_RECT_SHAPE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), OTTIE_TYPE_RECT_SHAPE))
|
||||||
|
#define OTTIE_RECT_SHAPE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), OTTIE_TYPE_RECT_SHAPE, OttieRectShapeClass))
|
||||||
|
|
||||||
|
typedef struct _OttieRectShape OttieRectShape;
|
||||||
|
typedef struct _OttieRectShapeClass OttieRectShapeClass;
|
||||||
|
|
||||||
|
GType ottie_rect_shape_get_type (void) G_GNUC_CONST;
|
||||||
|
|
||||||
|
OttieShape * ottie_rect_shape_parse (JsonReader *reader);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* __OTTIE_RECT_SHAPE_PRIVATE_H__ */
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user