Compare commits
24 Commits
3.99.5
...
misc-measure
| Author | SHA1 | Date | |
|---|---|---|---|
| 5efa3400d9 | |||
| 41c6bfec67 | |||
| 5a4d19644f | |||
| c9e9f6184d | |||
| 3a3c16c475 | |||
| dfb25771a7 | |||
| b286b04b7d | |||
| 22aa12ade5 | |||
| 79ba157664 | |||
| c21c63c357 | |||
| 78f04ae486 | |||
| bd4dcc54fb | |||
| 580d3057ce | |||
| 0060734103 | |||
| 95ce8aafb4 | |||
| 0603bc2f45 | |||
| 07864c3070 | |||
| 95d5b9a057 | |||
| d5fe88a787 | |||
| 343324c205 | |||
| 12dde7b4a1 | |||
| c07afdf1d7 | |||
| 7959ed96e2 | |||
| 43360a1cfa |
@@ -8,6 +8,7 @@
|
||||
#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)
|
||||
@@ -60,7 +61,7 @@ gtk_path_paintable_snapshot (GdkPaintable *paintable,
|
||||
#if 0
|
||||
gtk_snapshot_push_fill (snapshot, self->path, GSK_FILL_RULE_WINDING);
|
||||
#else
|
||||
GskStroke *stroke = gsk_stroke_new (4.0);
|
||||
GskStroke *stroke = gsk_stroke_new (2.0);
|
||||
gtk_snapshot_push_stroke (snapshot, self->path, stroke);
|
||||
gsk_stroke_free (stroke);
|
||||
#endif
|
||||
@@ -215,6 +216,46 @@ create_path_from_text (GtkWidget *widget)
|
||||
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,
|
||||
@@ -225,12 +266,26 @@ update_path (GtkWidget *widget,
|
||||
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),
|
||||
@@ -240,6 +295,7 @@ update_path (GtkWidget *widget,
|
||||
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))),
|
||||
|
||||
@@ -103,6 +103,7 @@ static gboolean
|
||||
gtk_path_transform_op (GskPathOperation op,
|
||||
const graphene_point_t *pts,
|
||||
gsize n_pts,
|
||||
float weight,
|
||||
gpointer data)
|
||||
{
|
||||
GtkPathTransform *transform = data;
|
||||
@@ -135,6 +136,15 @@ gtk_path_transform_op (GskPathOperation op,
|
||||
}
|
||||
break;
|
||||
|
||||
case GSK_PATH_CONIC:
|
||||
{
|
||||
graphene_point_t res[2];
|
||||
gtk_path_transform_point (transform->measure, &pts[1], transform->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;
|
||||
@@ -160,7 +170,7 @@ gtk_path_transform (GskPathMeasure *measure,
|
||||
else
|
||||
transform.scale = 1.0f;
|
||||
|
||||
gsk_path_foreach (path, GSK_PATH_FOREACH_ALLOW_CURVES, gtk_path_transform_op, &transform);
|
||||
gsk_path_foreach (path, GSK_PATH_FOREACH_ALLOW_CURVE, gtk_path_transform_op, &transform);
|
||||
|
||||
return gsk_path_builder_free_to_path (transform.builder);
|
||||
}
|
||||
|
||||
@@ -304,6 +304,7 @@ gsk_path_builder_free_to_path
|
||||
gsk_path_builder_get_current_point
|
||||
<SUBSECTION>
|
||||
gsk_path_builder_add_rect
|
||||
gsk_path_builder_add_rounded_rect
|
||||
gsk_path_builder_add_circle
|
||||
<SUBSECTION>
|
||||
gsk_path_builder_move_to
|
||||
@@ -312,6 +313,8 @@ 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
|
||||
|
||||
+141
-207
@@ -21,6 +21,7 @@
|
||||
|
||||
#include "gskcontourprivate.h"
|
||||
|
||||
#include "gskcurveprivate.h"
|
||||
#include "gskpathbuilder.h"
|
||||
#include "gskpathprivate.h"
|
||||
#include "gsksplineprivate.h"
|
||||
@@ -217,11 +218,11 @@ gsk_rect_contour_foreach (const GskContour *contour,
|
||||
GRAPHENE_POINT_INIT (self->x, self->y)
|
||||
};
|
||||
|
||||
return func (GSK_PATH_MOVE, &pts[0], 1, user_data)
|
||||
&& func (GSK_PATH_LINE, &pts[0], 2, user_data)
|
||||
&& func (GSK_PATH_LINE, &pts[1], 2, user_data)
|
||||
&& func (GSK_PATH_LINE, &pts[2], 2, user_data)
|
||||
&& func (GSK_PATH_CLOSE, &pts[3], 2, user_data);
|
||||
return func (GSK_PATH_MOVE, &pts[0], 1, 0, user_data)
|
||||
&& func (GSK_PATH_LINE, &pts[0], 2, 0, user_data)
|
||||
&& func (GSK_PATH_LINE, &pts[1], 2, 0, user_data)
|
||||
&& func (GSK_PATH_LINE, &pts[2], 2, 0, user_data)
|
||||
&& func (GSK_PATH_CLOSE, &pts[3], 2, 0, user_data);
|
||||
}
|
||||
|
||||
static gpointer
|
||||
@@ -601,7 +602,7 @@ gsk_circle_contour_curve (const graphene_point_t curve[4],
|
||||
{
|
||||
ForeachWrapper *wrapper = data;
|
||||
|
||||
return wrapper->func (GSK_PATH_CURVE, curve, 4, wrapper->user_data);
|
||||
return wrapper->func (GSK_PATH_CURVE, curve, 4, 0, wrapper->user_data);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
@@ -613,7 +614,7 @@ gsk_circle_contour_foreach (const GskContour *contour,
|
||||
const GskCircleContour *self = (const GskCircleContour *) contour;
|
||||
graphene_point_t start = GSK_CIRCLE_POINT_INIT (self, self->start_angle);
|
||||
|
||||
if (!func (GSK_PATH_MOVE, &start, 1, user_data))
|
||||
if (!func (GSK_PATH_MOVE, &start, 1, 0, user_data))
|
||||
return FALSE;
|
||||
|
||||
if (!gsk_spline_decompose_arc (&self->center,
|
||||
@@ -627,7 +628,7 @@ gsk_circle_contour_foreach (const GskContour *contour,
|
||||
|
||||
if (fabs (self->start_angle - self->end_angle) >= 360)
|
||||
{
|
||||
if (!func (GSK_PATH_CLOSE, (graphene_point_t[2]) { start, start }, 2, user_data))
|
||||
if (!func (GSK_PATH_CLOSE, (graphene_point_t[2]) { start, start }, 2, 0, user_data))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@@ -871,7 +872,7 @@ struct _GskStandardContour
|
||||
gsize n_ops;
|
||||
gsize n_points;
|
||||
graphene_point_t *points;
|
||||
GskStandardOperation ops[];
|
||||
gskpathop ops[];
|
||||
};
|
||||
|
||||
static gsize
|
||||
@@ -879,7 +880,7 @@ gsk_standard_contour_compute_size (gsize n_ops,
|
||||
gsize n_points)
|
||||
{
|
||||
return sizeof (GskStandardContour)
|
||||
+ sizeof (GskStandardOperation) * n_ops
|
||||
+ sizeof (gskpathop) * n_ops
|
||||
+ sizeof (graphene_point_t) * n_points;
|
||||
}
|
||||
|
||||
@@ -899,16 +900,10 @@ gsk_standard_contour_foreach (const GskContour *contour,
|
||||
{
|
||||
const GskStandardContour *self = (const GskStandardContour *) contour;
|
||||
gsize i;
|
||||
const gsize n_points[] = {
|
||||
[GSK_PATH_MOVE] = 1,
|
||||
[GSK_PATH_CLOSE] = 2,
|
||||
[GSK_PATH_LINE] = 2,
|
||||
[GSK_PATH_CURVE] = 4
|
||||
};
|
||||
|
||||
for (i = 0; i < self->n_ops; i ++)
|
||||
{
|
||||
if (!func (self->ops[i].op, &self->points[self->ops[i].point], n_points[self->ops[i].op], user_data))
|
||||
if (!gsk_pathop_foreach (self->ops[i], func, user_data))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@@ -932,9 +927,9 @@ gsk_standard_contour_print (const GskContour *contour,
|
||||
|
||||
for (i = 0; i < self->n_ops; i ++)
|
||||
{
|
||||
graphene_point_t *pt = &self->points[self->ops[i].point];
|
||||
const graphene_point_t *pt = gsk_pathop_points (self->ops[i]);
|
||||
|
||||
switch (self->ops[i].op)
|
||||
switch (gsk_pathop_op (self->ops[i]))
|
||||
{
|
||||
case GSK_PATH_MOVE:
|
||||
g_string_append (string, "M ");
|
||||
@@ -959,6 +954,16 @@ gsk_standard_contour_print (const GskContour *contour,
|
||||
_g_string_append_point (string, &pt[3]);
|
||||
break;
|
||||
|
||||
case GSK_PATH_CONIC:
|
||||
/* This is not valid SVG */
|
||||
g_string_append (string, " O ");
|
||||
_g_string_append_point (string, &pt[1]);
|
||||
g_string_append (string, ", ");
|
||||
_g_string_append_point (string, &pt[3]);
|
||||
g_string_append (string, ", ");
|
||||
_g_string_append_double (string, pt[2].x);
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
return;
|
||||
@@ -1043,7 +1048,7 @@ typedef struct
|
||||
GskStandardContourMeasure measure;
|
||||
} LengthDecompose;
|
||||
|
||||
static void
|
||||
static gboolean
|
||||
gsk_standard_contour_measure_add_point (const graphene_point_t *from,
|
||||
const graphene_point_t *to,
|
||||
float from_progress,
|
||||
@@ -1054,6 +1059,9 @@ gsk_standard_contour_measure_add_point (const graphene_point_t *from,
|
||||
float seg_length;
|
||||
|
||||
seg_length = graphene_point_distance (from, to, NULL, NULL);
|
||||
if (seg_length == 0)
|
||||
return TRUE;
|
||||
|
||||
decomp->measure.end += seg_length;
|
||||
decomp->measure.start_progress = from_progress;
|
||||
decomp->measure.end_progress = to_progress;
|
||||
@@ -1062,6 +1070,8 @@ gsk_standard_contour_measure_add_point (const graphene_point_t *from,
|
||||
g_array_append_val (decomp->array, decomp->measure);
|
||||
|
||||
decomp->measure.start += seg_length;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gpointer
|
||||
@@ -1071,7 +1081,7 @@ gsk_standard_contour_init_measure (const GskContour *contour,
|
||||
{
|
||||
const GskStandardContour *self = (const GskStandardContour *) contour;
|
||||
gsize i;
|
||||
float length, seg_length;
|
||||
float length;
|
||||
GArray *array;
|
||||
|
||||
array = g_array_new (FALSE, FALSE, sizeof (GskStandardContourMeasure));
|
||||
@@ -1079,42 +1089,12 @@ gsk_standard_contour_init_measure (const GskContour *contour,
|
||||
|
||||
for (i = 1; i < self->n_ops; i ++)
|
||||
{
|
||||
graphene_point_t *pt = &self->points[self->ops[i].point];
|
||||
GskCurve curve;
|
||||
LengthDecompose decomp = { array, { length, length, 0, 0, { }, i } };
|
||||
|
||||
switch (self->ops[i].op)
|
||||
{
|
||||
case GSK_PATH_MOVE:
|
||||
break;
|
||||
|
||||
case GSK_PATH_CLOSE:
|
||||
case GSK_PATH_LINE:
|
||||
seg_length = graphene_point_distance (&pt[0], &pt[1], NULL, NULL);
|
||||
if (seg_length > 0)
|
||||
{
|
||||
g_array_append_vals (array,
|
||||
&(GskStandardContourMeasure) {
|
||||
length,
|
||||
length + seg_length,
|
||||
0, 1,
|
||||
pt[1],
|
||||
i,
|
||||
}, 1);
|
||||
length += seg_length;
|
||||
}
|
||||
break;
|
||||
|
||||
case GSK_PATH_CURVE:
|
||||
{
|
||||
LengthDecompose decomp = { array, { length, length, 0, 0, pt[0], i } };
|
||||
gsk_spline_decompose_cubic (pt, tolerance, gsk_standard_contour_measure_add_point, &decomp);
|
||||
length = decomp.measure.start;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
return NULL;
|
||||
}
|
||||
gsk_curve_init (&curve, self->ops[i]);
|
||||
gsk_curve_decompose (&curve, tolerance, gsk_standard_contour_measure_add_point, &decomp);
|
||||
length = decomp.measure.start;
|
||||
}
|
||||
|
||||
*out_length = length;
|
||||
@@ -1144,40 +1124,6 @@ gsk_standard_contour_find_measure (gconstpointer m,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_standard_contour_measure_get_point (GskStandardContour *self,
|
||||
gsize op,
|
||||
float progress,
|
||||
graphene_point_t *pos,
|
||||
graphene_vec2_t *tangent)
|
||||
{
|
||||
const graphene_point_t *pts;
|
||||
|
||||
pts = &self->points[self->ops[op].point];
|
||||
switch (self->ops[op].op)
|
||||
{
|
||||
case GSK_PATH_LINE:
|
||||
case GSK_PATH_CLOSE:
|
||||
if (pos)
|
||||
graphene_point_interpolate (&pts[0], &pts[1], progress, pos);
|
||||
if (tangent)
|
||||
{
|
||||
graphene_vec2_init (tangent, pts[1].x - pts[0].x, pts[1].y - pts[0].y);
|
||||
graphene_vec2_normalize (tangent, tangent);
|
||||
}
|
||||
break;
|
||||
|
||||
case GSK_PATH_CURVE:
|
||||
gsk_spline_get_point_cubic (pts, progress, pos, tangent);
|
||||
break;
|
||||
|
||||
case GSK_PATH_MOVE:
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_standard_contour_get_point (const GskContour *contour,
|
||||
gpointer measure_data,
|
||||
@@ -1190,11 +1136,12 @@ gsk_standard_contour_get_point (const GskContour *contour,
|
||||
guint index;
|
||||
float progress;
|
||||
GskStandardContourMeasure *measure;
|
||||
GskCurve curve;
|
||||
|
||||
if (array->len == 0)
|
||||
{
|
||||
g_assert (distance == 0);
|
||||
g_assert (self->ops[0].op == GSK_PATH_MOVE);
|
||||
g_assert (gsk_pathop_op (self->ops[0]) == GSK_PATH_MOVE);
|
||||
if (pos)
|
||||
*pos = self->points[0];
|
||||
if (tangent)
|
||||
@@ -1209,7 +1156,12 @@ gsk_standard_contour_get_point (const GskContour *contour,
|
||||
progress = measure->start_progress + (measure->end_progress - measure->start_progress) * progress;
|
||||
g_assert (progress >= 0 && progress <= 1);
|
||||
|
||||
gsk_standard_contour_measure_get_point (self, measure->op, progress, pos, tangent);
|
||||
gsk_curve_init (&curve, self->ops[measure->op]);
|
||||
|
||||
if (pos)
|
||||
gsk_curve_get_point (&curve, progress, pos);
|
||||
if (tangent)
|
||||
gsk_curve_get_tangent (&curve, progress, tangent);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
@@ -1231,7 +1183,7 @@ gsk_standard_contour_get_closest_point (const GskContour *contour,
|
||||
gsize i;
|
||||
gboolean result = FALSE;
|
||||
|
||||
g_assert (self->ops[0].op == GSK_PATH_MOVE);
|
||||
g_assert (gsk_pathop_op (self->ops[0]) == GSK_PATH_MOVE);
|
||||
last_point = self->points[0];
|
||||
|
||||
if (array->len == 0)
|
||||
@@ -1272,15 +1224,45 @@ gsk_standard_contour_get_closest_point (const GskContour *contour,
|
||||
//g_print ("%zu: (%g-%g) dist %g\n", i, measure->start, measure->end, dist);
|
||||
if (dist <= threshold + 1.0f)
|
||||
{
|
||||
graphene_vec2_t t;
|
||||
float found_progress;
|
||||
GskCurve curve;
|
||||
graphene_point_t p2;
|
||||
float found_progress, test_progress, test_dist;
|
||||
const float step = 1/1024.f;
|
||||
|
||||
gsk_curve_init (&curve, self->ops[measure->op]);
|
||||
|
||||
found_progress = measure->start_progress + (measure->end_progress - measure->start_progress) * progress;
|
||||
|
||||
gsk_standard_contour_measure_get_point (self, measure->op, found_progress, &p, out_tangent ? &t : NULL);
|
||||
gsk_curve_get_point (&curve, found_progress, &p);
|
||||
dist = graphene_point_distance (point, &p, NULL, NULL);
|
||||
//g_print ("!!! %zu: (%g-%g @ %g) dist %g\n", i, measure->start_progress, measure->end_progress, progress, dist);
|
||||
|
||||
/* The progress is non-uniform, so simple translation of progress doesn't work.
|
||||
* Check if larger values inch closer towards minimal distance. */
|
||||
while (progress + step < 1.0f) {
|
||||
test_progress = measure->start_progress + (measure->end_progress - measure->start_progress) * (progress + step);
|
||||
gsk_curve_get_point (&curve, test_progress, &p2);
|
||||
test_dist = graphene_point_distance (point, &p2, NULL, NULL);
|
||||
if (test_dist > dist)
|
||||
break;
|
||||
progress += step;
|
||||
p = p2;
|
||||
found_progress = test_progress;
|
||||
dist = test_dist;
|
||||
}
|
||||
/* Also check smaller ones */
|
||||
while (progress - step > 0.0f) {
|
||||
test_progress = measure->start_progress + (measure->end_progress - measure->start_progress) * (progress - step);
|
||||
gsk_curve_get_point (&curve, test_progress, &p2);
|
||||
test_dist = graphene_point_distance (point, &p2, NULL, NULL);
|
||||
if (test_dist > dist)
|
||||
break;
|
||||
progress -= step;
|
||||
p = p2;
|
||||
found_progress = test_progress;
|
||||
dist = test_dist;
|
||||
}
|
||||
//g_print ("!!! %zu: (%g-%g @ %g) dist %g\n", i, measure->start_progress, measure->end_progress, progress, dist);
|
||||
/* double check that the point actually is closer */
|
||||
//g_print ("!!! %zu: (%g-%g) dist %g\n", i, measure->start, measure->end, dist);
|
||||
if (dist <= threshold)
|
||||
{
|
||||
if (out_distance)
|
||||
@@ -1290,7 +1272,7 @@ gsk_standard_contour_get_closest_point (const GskContour *contour,
|
||||
if (out_offset)
|
||||
*out_offset = measure->start + (measure->end - measure->start) * progress;
|
||||
if (out_tangent)
|
||||
*out_tangent = t;
|
||||
gsk_curve_get_tangent (&curve, found_progress, out_tangent);
|
||||
result = TRUE;
|
||||
if (tolerance >= dist)
|
||||
return TRUE;
|
||||
@@ -1303,12 +1285,13 @@ gsk_standard_contour_get_closest_point (const GskContour *contour,
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_standard_contour_init (GskContour *contour,
|
||||
GskPathFlags flags,
|
||||
const GskStandardOperation *ops,
|
||||
gsize n_ops,
|
||||
gsk_standard_contour_init (GskContour *contour,
|
||||
GskPathFlags flags,
|
||||
const graphene_point_t *points,
|
||||
gsize n_points);
|
||||
gsize n_points,
|
||||
const gskpathop *ops,
|
||||
gsize n_ops,
|
||||
ptrdiff_t offset);
|
||||
|
||||
static void
|
||||
gsk_standard_contour_copy (const GskContour *contour,
|
||||
@@ -1316,7 +1299,7 @@ gsk_standard_contour_copy (const GskContour *contour,
|
||||
{
|
||||
const GskStandardContour *self = (const GskStandardContour *) contour;
|
||||
|
||||
gsk_standard_contour_init (dest, self->flags, self->ops, self->n_ops, self->points, self->n_points);
|
||||
gsk_standard_contour_init (dest, self->flags, self->points, self->n_points, self->ops, self->n_ops, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -1365,111 +1348,52 @@ gsk_standard_contour_add_segment (const GskContour *contour,
|
||||
* taking care that first and last operation might be identical */
|
||||
if (start_measure)
|
||||
{
|
||||
switch (self->ops[start_measure->op].op)
|
||||
{
|
||||
case GSK_PATH_CLOSE:
|
||||
case GSK_PATH_LINE:
|
||||
{
|
||||
graphene_point_t *pts = &self->points[self->ops[start_measure->op].point];
|
||||
graphene_point_t point;
|
||||
GskCurve curve, cut;
|
||||
const graphene_point_t *start_point;
|
||||
|
||||
graphene_point_interpolate (&pts[0], &pts[1], start_progress, &point);
|
||||
gsk_path_builder_move_to (builder, point.x, point.y);
|
||||
if (end_measure && end_measure->op == start_measure->op)
|
||||
{
|
||||
graphene_point_interpolate (&pts[0], &pts[1], end_progress, &point);
|
||||
gsk_path_builder_line_to (builder, point.x, point.y);
|
||||
return;
|
||||
}
|
||||
gsk_path_builder_line_to (builder, pts[1].x, pts[1].y);
|
||||
}
|
||||
break;
|
||||
gsk_curve_init (&curve, self->ops[start_measure->op]);
|
||||
|
||||
case GSK_PATH_CURVE:
|
||||
{
|
||||
graphene_point_t *pts = &self->points[self->ops[start_measure->op].point];
|
||||
graphene_point_t curve[4], discard[4];
|
||||
|
||||
gsk_spline_split_cubic (pts, discard, curve, start_progress);
|
||||
if (end_measure && end_measure->op == start_measure->op)
|
||||
{
|
||||
graphene_point_t tiny[4];
|
||||
gsk_spline_split_cubic (curve, tiny, discard, (end_progress - start_progress) / (1 - start_progress));
|
||||
gsk_path_builder_move_to (builder, tiny[0].x, tiny[0].y);
|
||||
gsk_path_builder_curve_to (builder, tiny[1].x, tiny[1].y, tiny[2].x, tiny[2].y, tiny[3].x, tiny[3].y);
|
||||
return;
|
||||
}
|
||||
gsk_path_builder_move_to (builder, curve[0].x, curve[0].y);
|
||||
gsk_path_builder_curve_to (builder, curve[1].x, curve[1].y, curve[2].x, curve[2].y, curve[3].x, curve[3].y);
|
||||
}
|
||||
break;
|
||||
|
||||
case GSK_PATH_MOVE:
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
if (end_measure && end_measure->op == start_measure->op)
|
||||
{
|
||||
gsk_curve_segment (&curve, start_progress, end_progress, &cut);
|
||||
start_point = gsk_curve_get_start_point (&cut);
|
||||
gsk_path_builder_move_to (builder, start_point->x, start_point->y);
|
||||
gsk_curve_builder_to (&cut, builder);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
gsk_curve_split (&curve, start_progress, NULL, &cut);
|
||||
|
||||
start_point = gsk_curve_get_start_point (&cut);
|
||||
gsk_path_builder_move_to (builder, start_point->x, start_point->y);
|
||||
gsk_curve_builder_to (&cut, builder);
|
||||
i = start_measure->op + 1;
|
||||
}
|
||||
else
|
||||
i = 0;
|
||||
|
||||
for (; i < (end_measure ? end_measure->op : self->n_ops); i++)
|
||||
for (; i < (end_measure ? end_measure->op : self->n_ops - 1); i++)
|
||||
{
|
||||
graphene_point_t *pt = &self->points[self->ops[i].point];
|
||||
|
||||
switch (self->ops[i].op)
|
||||
{
|
||||
case GSK_PATH_MOVE:
|
||||
gsk_path_builder_move_to (builder, pt[0].x, pt[0].y);
|
||||
break;
|
||||
|
||||
case GSK_PATH_LINE:
|
||||
case GSK_PATH_CLOSE:
|
||||
gsk_path_builder_line_to (builder, pt[1].x, pt[1].y);
|
||||
break;
|
||||
|
||||
case GSK_PATH_CURVE:
|
||||
gsk_path_builder_curve_to (builder, pt[1].x, pt[1].y, pt[2].x, pt[2].y, pt[3].x, pt[3].y);
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
return;
|
||||
}
|
||||
gsk_path_builder_pathop_to (builder, self->ops[i]);
|
||||
}
|
||||
|
||||
/* Add the last partial operation */
|
||||
if (end_measure)
|
||||
{
|
||||
switch (self->ops[end_measure->op].op)
|
||||
{
|
||||
case GSK_PATH_CLOSE:
|
||||
case GSK_PATH_LINE:
|
||||
{
|
||||
graphene_point_t *pts = &self->points[self->ops[end_measure->op].point];
|
||||
graphene_point_t point;
|
||||
GskCurve curve, cut;
|
||||
|
||||
graphene_point_interpolate (&pts[0], &pts[1], end_progress, &point);
|
||||
gsk_path_builder_line_to (builder, point.x, point.y);
|
||||
}
|
||||
break;
|
||||
gsk_curve_init (&curve, self->ops[end_measure->op]);
|
||||
|
||||
case GSK_PATH_CURVE:
|
||||
{
|
||||
graphene_point_t *pts = &self->points[self->ops[end_measure->op].point];
|
||||
graphene_point_t curve[4], discard[4];
|
||||
|
||||
gsk_spline_split_cubic (pts, curve, discard, end_progress);
|
||||
gsk_path_builder_curve_to (builder, curve[1].x, curve[1].y, curve[2].x, curve[2].y, curve[3].x, curve[3].y);
|
||||
}
|
||||
break;
|
||||
|
||||
case GSK_PATH_MOVE:
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
return;
|
||||
}
|
||||
gsk_curve_split (&curve, end_progress, &cut, NULL);
|
||||
gsk_curve_builder_to (&cut, builder);
|
||||
}
|
||||
else if (i == self->n_ops - 1)
|
||||
{
|
||||
gskpathop op = self->ops[i];
|
||||
if (gsk_pathop_op (op) == GSK_PATH_CLOSE)
|
||||
gsk_path_builder_pathop_to (builder, gsk_pathop_encode (GSK_PATH_LINE, gsk_pathop_points (op)));
|
||||
else
|
||||
gsk_path_builder_pathop_to (builder, op);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1569,37 +1493,47 @@ static const GskContourClass GSK_STANDARD_CONTOUR_CLASS =
|
||||
* see gsk_standard_contour_compute_size()
|
||||
*/
|
||||
static void
|
||||
gsk_standard_contour_init (GskContour *contour,
|
||||
GskPathFlags flags,
|
||||
const GskStandardOperation *ops,
|
||||
gsize n_ops,
|
||||
const graphene_point_t *points,
|
||||
gsize n_points)
|
||||
gsk_standard_contour_init (GskContour *contour,
|
||||
GskPathFlags flags,
|
||||
const graphene_point_t *points,
|
||||
gsize n_points,
|
||||
const gskpathop *ops,
|
||||
gsize n_ops,
|
||||
gssize offset)
|
||||
|
||||
{
|
||||
GskStandardContour *self = (GskStandardContour *) contour;
|
||||
gsize i;
|
||||
|
||||
self->contour.klass = &GSK_STANDARD_CONTOUR_CLASS;
|
||||
|
||||
self->flags = flags;
|
||||
self->n_ops = n_ops;
|
||||
memcpy (self->ops, ops, sizeof (GskStandardOperation) * n_ops);
|
||||
self->n_points = n_points;
|
||||
self->points = (graphene_point_t *) &self->ops[n_ops];
|
||||
memcpy (self->points, points, sizeof (graphene_point_t) * n_points);
|
||||
|
||||
offset += self->points - points;
|
||||
for (i = 0; i < n_ops; i++)
|
||||
{
|
||||
self->ops[i] = gsk_pathop_encode (gsk_pathop_op (ops[i]),
|
||||
gsk_pathop_points (ops[i]) + offset);
|
||||
}
|
||||
}
|
||||
|
||||
GskContour *
|
||||
gsk_standard_contour_new (GskPathFlags flags,
|
||||
const GskStandardOperation *ops,
|
||||
gsize n_ops,
|
||||
const graphene_point_t *points,
|
||||
gsize n_points)
|
||||
gsk_standard_contour_new (GskPathFlags flags,
|
||||
const graphene_point_t *points,
|
||||
gsize n_points,
|
||||
const gskpathop *ops,
|
||||
gsize n_ops,
|
||||
gssize offset)
|
||||
{
|
||||
GskContour *contour;
|
||||
|
||||
contour = g_malloc0 (gsk_standard_contour_compute_size (n_ops, n_points));
|
||||
|
||||
gsk_standard_contour_init (contour, flags, ops, n_ops, points, n_points);
|
||||
gsk_standard_contour_init (contour, flags, points, n_points, ops, n_ops, offset);
|
||||
|
||||
return contour;
|
||||
}
|
||||
|
||||
+6
-10
@@ -23,6 +23,8 @@
|
||||
|
||||
#include <gskpath.h>
|
||||
|
||||
#include "gskpathopprivate.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef enum
|
||||
@@ -33,23 +35,17 @@ typedef enum
|
||||
|
||||
typedef struct _GskContour GskContour;
|
||||
|
||||
typedef struct _GskStandardOperation GskStandardOperation;
|
||||
|
||||
struct _GskStandardOperation {
|
||||
GskPathOperation op;
|
||||
gsize point; /* index into points array of the start point (last point of previous op) */
|
||||
};
|
||||
|
||||
GskContour * gsk_rect_contour_new (const graphene_rect_t *rect);
|
||||
GskContour * gsk_circle_contour_new (const graphene_point_t *center,
|
||||
float radius,
|
||||
float start_angle,
|
||||
float end_angle);
|
||||
GskContour * gsk_standard_contour_new (GskPathFlags flags,
|
||||
const GskStandardOperation *ops,
|
||||
const graphene_point_t *points,
|
||||
gsize n_points,
|
||||
const gskpathop *ops,
|
||||
gsize n_ops,
|
||||
const graphene_point_t *points,
|
||||
gsize n_points);
|
||||
gssize offset);
|
||||
|
||||
void gsk_contour_copy (GskContour * dest,
|
||||
const GskContour *src);
|
||||
|
||||
+978
@@ -0,0 +1,978 @@
|
||||
/*
|
||||
* Copyright © 2020 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gskcurveprivate.h"
|
||||
|
||||
#define MIN_PROGRESS (1/1024.f)
|
||||
|
||||
typedef struct _GskCurveClass GskCurveClass;
|
||||
|
||||
struct _GskCurveClass
|
||||
{
|
||||
void (* init) (GskCurve *curve,
|
||||
gskpathop op);
|
||||
void (* init_foreach) (GskCurve *curve,
|
||||
GskPathOperation op,
|
||||
const graphene_point_t *pts,
|
||||
gsize n_pts,
|
||||
float weight);
|
||||
void (* get_point) (const GskCurve *curve,
|
||||
float t,
|
||||
graphene_point_t *pos);
|
||||
void (* get_tangent) (const GskCurve *curve,
|
||||
float t,
|
||||
graphene_vec2_t *tangent);
|
||||
void (* split) (const GskCurve *curve,
|
||||
float progress,
|
||||
GskCurve *result1,
|
||||
GskCurve *result2);
|
||||
void (* segment) (const GskCurve *curve,
|
||||
float start,
|
||||
float end,
|
||||
GskCurve *segment);
|
||||
gboolean (* decompose) (const GskCurve *curve,
|
||||
float tolerance,
|
||||
GskCurveAddLineFunc add_line_func,
|
||||
gpointer user_data);
|
||||
gskpathop (* pathop) (const GskCurve *curve);
|
||||
const graphene_point_t * (* get_start_point) (const GskCurve *curve);
|
||||
const graphene_point_t * (* get_end_point) (const GskCurve *curve);
|
||||
void (* get_start_tangent) (const GskCurve *curve,
|
||||
graphene_vec2_t *tangent);
|
||||
void (* get_end_tangent) (const GskCurve *curve,
|
||||
graphene_vec2_t *tangent);
|
||||
};
|
||||
|
||||
static void
|
||||
get_tangent (const graphene_point_t *p0,
|
||||
const graphene_point_t *p1,
|
||||
graphene_vec2_t *t)
|
||||
{
|
||||
graphene_vec2_init (t, p1->x - p0->x, p1->y - p0->y);
|
||||
graphene_vec2_normalize (t, t);
|
||||
}
|
||||
|
||||
/** LINE **/
|
||||
|
||||
static void
|
||||
gsk_line_curve_init_from_points (GskLineCurve *self,
|
||||
GskPathOperation op,
|
||||
const graphene_point_t *start,
|
||||
const graphene_point_t *end)
|
||||
{
|
||||
self->op = op;
|
||||
self->points[0] = *start;
|
||||
self->points[1] = *end;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_line_curve_init (GskCurve *curve,
|
||||
gskpathop op)
|
||||
{
|
||||
GskLineCurve *self = &curve->line;
|
||||
const graphene_point_t *pts = gsk_pathop_points (op);
|
||||
|
||||
gsk_line_curve_init_from_points (self, gsk_pathop_op (op), &pts[0], &pts[1]);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_line_curve_init_foreach (GskCurve *curve,
|
||||
GskPathOperation op,
|
||||
const graphene_point_t *pts,
|
||||
gsize n_pts,
|
||||
float weight)
|
||||
{
|
||||
GskLineCurve *self = &curve->line;
|
||||
|
||||
g_assert (n_pts == 2);
|
||||
|
||||
gsk_line_curve_init_from_points (self, op, &pts[0], &pts[1]);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_line_curve_get_point (const GskCurve *curve,
|
||||
float t,
|
||||
graphene_point_t *pos)
|
||||
{
|
||||
const GskLineCurve *self = &curve->line;
|
||||
|
||||
graphene_point_interpolate (&self->points[0], &self->points[1], t, pos);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_line_curve_get_tangent (const GskCurve *curve,
|
||||
float t,
|
||||
graphene_vec2_t *tangent)
|
||||
{
|
||||
const GskLineCurve *self = &curve->line;
|
||||
|
||||
get_tangent (&self->points[0], &self->points[1], tangent);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_line_curve_split (const GskCurve *curve,
|
||||
float progress,
|
||||
GskCurve *start,
|
||||
GskCurve *end)
|
||||
{
|
||||
const GskLineCurve *self = &curve->line;
|
||||
graphene_point_t point;
|
||||
|
||||
graphene_point_interpolate (&self->points[0], &self->points[1], progress, &point);
|
||||
|
||||
if (start)
|
||||
gsk_line_curve_init_from_points (&start->line, GSK_PATH_LINE, &self->points[0], &point);
|
||||
if (end)
|
||||
gsk_line_curve_init_from_points (&end->line, GSK_PATH_LINE, &point, &self->points[1]);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_line_curve_segment (const GskCurve *curve,
|
||||
float start,
|
||||
float end,
|
||||
GskCurve *segment)
|
||||
{
|
||||
const GskLineCurve *self = &curve->line;
|
||||
graphene_point_t start_point, end_point;
|
||||
|
||||
graphene_point_interpolate (&self->points[0], &self->points[1], start, &start_point);
|
||||
graphene_point_interpolate (&self->points[0], &self->points[1], end, &end_point);
|
||||
|
||||
gsk_line_curve_init_from_points (&segment->line, GSK_PATH_LINE, &start_point, &end_point);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gsk_line_curve_decompose (const GskCurve *curve,
|
||||
float tolerance,
|
||||
GskCurveAddLineFunc add_line_func,
|
||||
gpointer user_data)
|
||||
{
|
||||
const GskLineCurve *self = &curve->line;
|
||||
|
||||
return add_line_func (&self->points[0], &self->points[1], 0.0f, 1.0f, user_data);
|
||||
}
|
||||
|
||||
static gskpathop
|
||||
gsk_line_curve_pathop (const GskCurve *curve)
|
||||
{
|
||||
const GskLineCurve *self = &curve->line;
|
||||
|
||||
return gsk_pathop_encode (self->op, self->points);
|
||||
}
|
||||
|
||||
static const graphene_point_t *
|
||||
gsk_line_curve_get_start_point (const GskCurve *curve)
|
||||
{
|
||||
const GskLineCurve *self = &curve->line;
|
||||
|
||||
return &self->points[0];
|
||||
}
|
||||
|
||||
static const graphene_point_t *
|
||||
gsk_line_curve_get_end_point (const GskCurve *curve)
|
||||
{
|
||||
const GskLineCurve *self = &curve->line;
|
||||
|
||||
return &self->points[1];
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_line_curve_get_start_end_tangent (const GskCurve *curve,
|
||||
graphene_vec2_t *tangent)
|
||||
{
|
||||
const GskLineCurve *self = &curve->line;
|
||||
|
||||
get_tangent (&self->points[0], &self->points[1], tangent);
|
||||
}
|
||||
|
||||
static const GskCurveClass GSK_LINE_CURVE_CLASS = {
|
||||
gsk_line_curve_init,
|
||||
gsk_line_curve_init_foreach,
|
||||
gsk_line_curve_get_point,
|
||||
gsk_line_curve_get_tangent,
|
||||
gsk_line_curve_split,
|
||||
gsk_line_curve_segment,
|
||||
gsk_line_curve_decompose,
|
||||
gsk_line_curve_pathop,
|
||||
gsk_line_curve_get_start_point,
|
||||
gsk_line_curve_get_end_point,
|
||||
gsk_line_curve_get_start_end_tangent,
|
||||
gsk_line_curve_get_start_end_tangent
|
||||
};
|
||||
|
||||
/** CURVE **/
|
||||
|
||||
static void
|
||||
gsk_curve_curve_init_from_points (GskCurveCurve *self,
|
||||
const graphene_point_t pts[4])
|
||||
{
|
||||
self->op = GSK_PATH_CURVE;
|
||||
self->has_coefficients = FALSE;
|
||||
memcpy (self->points, pts, sizeof (graphene_point_t) * 4);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_curve_curve_init (GskCurve *curve,
|
||||
gskpathop op)
|
||||
{
|
||||
GskCurveCurve *self = &curve->curve;
|
||||
|
||||
gsk_curve_curve_init_from_points (self, gsk_pathop_points (op));
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_curve_curve_init_foreach (GskCurve *curve,
|
||||
GskPathOperation op,
|
||||
const graphene_point_t *pts,
|
||||
gsize n_pts,
|
||||
float weight)
|
||||
{
|
||||
GskCurveCurve *self = &curve->curve;
|
||||
|
||||
g_assert (n_pts == 4);
|
||||
|
||||
gsk_curve_curve_init_from_points (self, pts);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_curve_curve_ensure_coefficients (const GskCurveCurve *curve)
|
||||
{
|
||||
GskCurveCurve *self = (GskCurveCurve *) curve;
|
||||
const graphene_point_t *pts = &self->points[0];
|
||||
|
||||
if (self->has_coefficients)
|
||||
return;
|
||||
|
||||
self->coeffs[0] = GRAPHENE_POINT_INIT (pts[3].x - 3.0f * pts[2].x + 3.0f * pts[1].x - pts[0].x,
|
||||
pts[3].y - 3.0f * pts[2].y + 3.0f * pts[1].y - pts[0].y);
|
||||
self->coeffs[1] = GRAPHENE_POINT_INIT (3.0f * pts[2].x - 6.0f * pts[1].x + 3.0f * pts[0].x,
|
||||
3.0f * pts[2].y - 6.0f * pts[1].y + 3.0f * pts[0].y);
|
||||
self->coeffs[2] = GRAPHENE_POINT_INIT (3.0f * pts[1].x - 3.0f * pts[0].x,
|
||||
3.0f * pts[1].y - 3.0f * pts[0].y);
|
||||
self->coeffs[3] = pts[0];
|
||||
|
||||
self->has_coefficients = TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_curve_curve_get_point (const GskCurve *curve,
|
||||
float t,
|
||||
graphene_point_t *pos)
|
||||
{
|
||||
const GskCurveCurve *self = &curve->curve;
|
||||
const graphene_point_t *c = self->coeffs;
|
||||
|
||||
gsk_curve_curve_ensure_coefficients (self);
|
||||
|
||||
*pos = GRAPHENE_POINT_INIT (((c[0].x * t + c[1].x) * t +c[2].x) * t + c[3].x,
|
||||
((c[0].y * t + c[1].y) * t +c[2].y) * t + c[3].y);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_curve_curve_get_tangent (const GskCurve *curve,
|
||||
float t,
|
||||
graphene_vec2_t *tangent)
|
||||
{
|
||||
const GskCurveCurve *self = &curve->curve;
|
||||
const graphene_point_t *c = self->coeffs;
|
||||
|
||||
gsk_curve_curve_ensure_coefficients (self);
|
||||
|
||||
graphene_vec2_init (tangent,
|
||||
(3.0f * c[0].x * t + 2.0f * c[1].x) * t + c[2].x,
|
||||
(3.0f * c[0].y * t + 2.0f * c[1].y) * t + c[2].y);
|
||||
graphene_vec2_normalize (tangent, tangent);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_curve_curve_split (const GskCurve *curve,
|
||||
float progress,
|
||||
GskCurve *start,
|
||||
GskCurve *end)
|
||||
{
|
||||
const GskCurveCurve *self = &curve->curve;
|
||||
const graphene_point_t *pts = self->points;
|
||||
graphene_point_t ab, bc, cd;
|
||||
graphene_point_t abbc, bccd;
|
||||
graphene_point_t final;
|
||||
|
||||
graphene_point_interpolate (&pts[0], &pts[1], progress, &ab);
|
||||
graphene_point_interpolate (&pts[1], &pts[2], progress, &bc);
|
||||
graphene_point_interpolate (&pts[2], &pts[3], progress, &cd);
|
||||
graphene_point_interpolate (&ab, &bc, progress, &abbc);
|
||||
graphene_point_interpolate (&bc, &cd, progress, &bccd);
|
||||
graphene_point_interpolate (&abbc, &bccd, progress, &final);
|
||||
|
||||
if (start)
|
||||
gsk_curve_curve_init_from_points (&start->curve, (graphene_point_t[4]) { pts[0], ab, abbc, final });
|
||||
if (end)
|
||||
gsk_curve_curve_init_from_points (&end->curve, (graphene_point_t[4]) { final, bccd, cd, pts[3] });
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_curve_curve_segment (const GskCurve *curve,
|
||||
float start,
|
||||
float end,
|
||||
GskCurve *segment)
|
||||
{
|
||||
GskCurve tmp;
|
||||
|
||||
gsk_curve_curve_split (curve, start, NULL, &tmp);
|
||||
gsk_curve_curve_split (&tmp, (end - start) / (1.0f - start), segment, NULL);
|
||||
}
|
||||
|
||||
/* taken from Skia, including the very descriptive name */
|
||||
static gboolean
|
||||
gsk_curve_curve_too_curvy (const GskCurveCurve *self,
|
||||
float tolerance)
|
||||
{
|
||||
const graphene_point_t *pts = self->points;
|
||||
graphene_point_t p;
|
||||
|
||||
graphene_point_interpolate (&pts[0], &pts[3], 1.0f / 3, &p);
|
||||
if (ABS (p.x - pts[1].x) + ABS (p.y - pts[1].y) > tolerance)
|
||||
return TRUE;
|
||||
|
||||
graphene_point_interpolate (&pts[0], &pts[3], 2.0f / 3, &p);
|
||||
if (ABS (p.x - pts[2].x) + ABS (p.y - pts[2].y) > tolerance)
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gsk_curce_curve_decompose_step (const GskCurve *curve,
|
||||
float start_progress,
|
||||
float end_progress,
|
||||
float tolerance,
|
||||
GskCurveAddLineFunc add_line_func,
|
||||
gpointer user_data)
|
||||
{
|
||||
const GskCurveCurve *self = &curve->curve;
|
||||
GskCurve left, right;
|
||||
float mid_progress;
|
||||
|
||||
if (!gsk_curve_curve_too_curvy (self, tolerance) || end_progress - start_progress <= MIN_PROGRESS)
|
||||
return add_line_func (&self->points[0], &self->points[3], start_progress, end_progress, user_data);
|
||||
|
||||
gsk_curve_curve_split ((const GskCurve *) self, 0.5, &left, &right);
|
||||
mid_progress = (start_progress + end_progress) / 2;
|
||||
|
||||
return gsk_curce_curve_decompose_step (&left, start_progress, mid_progress, tolerance, add_line_func, user_data)
|
||||
&& gsk_curce_curve_decompose_step (&right, mid_progress, end_progress, tolerance, add_line_func, user_data);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gsk_curve_curve_decompose (const GskCurve *curve,
|
||||
float tolerance,
|
||||
GskCurveAddLineFunc add_line_func,
|
||||
gpointer user_data)
|
||||
{
|
||||
return gsk_curce_curve_decompose_step (curve, 0.0, 1.0, tolerance, add_line_func, user_data);
|
||||
}
|
||||
|
||||
static gskpathop
|
||||
gsk_curve_curve_pathop (const GskCurve *curve)
|
||||
{
|
||||
const GskCurveCurve *self = &curve->curve;
|
||||
|
||||
return gsk_pathop_encode (self->op, self->points);
|
||||
}
|
||||
|
||||
static const graphene_point_t *
|
||||
gsk_curve_curve_get_start_point (const GskCurve *curve)
|
||||
{
|
||||
const GskCurveCurve *self = &curve->curve;
|
||||
|
||||
return &self->points[0];
|
||||
}
|
||||
|
||||
static const graphene_point_t *
|
||||
gsk_curve_curve_get_end_point (const GskCurve *curve)
|
||||
{
|
||||
const GskCurveCurve *self = &curve->curve;
|
||||
|
||||
return &self->points[3];
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_curve_curve_get_start_tangent (const GskCurve *curve,
|
||||
graphene_vec2_t *tangent)
|
||||
{
|
||||
const GskCurveCurve *self = &curve->curve;
|
||||
|
||||
get_tangent (&self->points[0], &self->points[1], tangent);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_curve_curve_get_end_tangent (const GskCurve *curve,
|
||||
graphene_vec2_t *tangent)
|
||||
{
|
||||
const GskCurveCurve *self = &curve->curve;
|
||||
|
||||
get_tangent (&self->points[2], &self->points[3], tangent);
|
||||
}
|
||||
|
||||
static const GskCurveClass GSK_CURVE_CURVE_CLASS = {
|
||||
gsk_curve_curve_init,
|
||||
gsk_curve_curve_init_foreach,
|
||||
gsk_curve_curve_get_point,
|
||||
gsk_curve_curve_get_tangent,
|
||||
gsk_curve_curve_split,
|
||||
gsk_curve_curve_segment,
|
||||
gsk_curve_curve_decompose,
|
||||
gsk_curve_curve_pathop,
|
||||
gsk_curve_curve_get_start_point,
|
||||
gsk_curve_curve_get_end_point,
|
||||
gsk_curve_curve_get_start_tangent,
|
||||
gsk_curve_curve_get_end_tangent
|
||||
};
|
||||
|
||||
/** CONIC **/
|
||||
|
||||
static void
|
||||
gsk_conic_curve_init_from_points (GskConicCurve *self,
|
||||
const graphene_point_t pts[4])
|
||||
{
|
||||
self->op = GSK_PATH_CONIC;
|
||||
self->has_coefficients = FALSE;
|
||||
|
||||
memcpy (self->points, pts, sizeof (graphene_point_t) * 4);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_conic_curve_init (GskCurve *curve,
|
||||
gskpathop op)
|
||||
{
|
||||
GskConicCurve *self = &curve->conic;
|
||||
|
||||
gsk_conic_curve_init_from_points (self, gsk_pathop_points (op));
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_conic_curve_init_foreach (GskCurve *curve,
|
||||
GskPathOperation op,
|
||||
const graphene_point_t *pts,
|
||||
gsize n_pts,
|
||||
float weight)
|
||||
{
|
||||
GskConicCurve *self = &curve->conic;
|
||||
|
||||
g_assert (n_pts == 3);
|
||||
|
||||
gsk_conic_curve_init_from_points (self,
|
||||
(graphene_point_t[4]) {
|
||||
pts[0],
|
||||
pts[1],
|
||||
GRAPHENE_POINT_INIT (weight, 0),
|
||||
pts[2]
|
||||
});
|
||||
}
|
||||
|
||||
static inline float
|
||||
gsk_conic_curve_get_weight (const GskConicCurve *self)
|
||||
{
|
||||
return self->points[2].x;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_conic_curve_ensure_coefficents (const GskConicCurve *curve)
|
||||
{
|
||||
GskConicCurve *self = (GskConicCurve *) curve;
|
||||
float w = gsk_conic_curve_get_weight (self);
|
||||
const graphene_point_t *pts = self->points;
|
||||
graphene_point_t pw = GRAPHENE_POINT_INIT (w * pts[1].x, w * pts[1].y);
|
||||
|
||||
if (self->has_coefficients)
|
||||
return;
|
||||
|
||||
self->num[2] = pts[0];
|
||||
self->num[1] = GRAPHENE_POINT_INIT (2 * (pw.x - pts[0].x),
|
||||
2 * (pw.y - pts[0].y));
|
||||
self->num[0] = GRAPHENE_POINT_INIT (pts[3].x - 2 * pw.x + pts[0].x,
|
||||
pts[3].y - 2 * pw.y + pts[0].y);
|
||||
|
||||
self->denom[2] = GRAPHENE_POINT_INIT (1, 1);
|
||||
self->denom[1] = GRAPHENE_POINT_INIT (2 * (w - 1), 2 * (w - 1));
|
||||
self->denom[0] = GRAPHENE_POINT_INIT (-self->denom[1].x, -self->denom[1].y);
|
||||
|
||||
self->has_coefficients = TRUE;
|
||||
}
|
||||
|
||||
static inline void
|
||||
gsk_curve_eval_quad (const graphene_point_t quad[3],
|
||||
float progress,
|
||||
graphene_point_t *result)
|
||||
{
|
||||
*result = GRAPHENE_POINT_INIT ((quad[0].x * progress + quad[1].x) * progress + quad[2].x,
|
||||
(quad[0].y * progress + quad[1].y) * progress + quad[2].y);
|
||||
}
|
||||
|
||||
static inline void
|
||||
gsk_conic_curve_eval_point (const GskConicCurve *self,
|
||||
float progress,
|
||||
graphene_point_t *point)
|
||||
{
|
||||
graphene_point_t num, denom;
|
||||
|
||||
gsk_curve_eval_quad (self->num, progress, &num);
|
||||
gsk_curve_eval_quad (self->denom, progress, &denom);
|
||||
|
||||
*point = GRAPHENE_POINT_INIT (num.x / denom.x, num.y / denom.y);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_conic_curve_get_point (const GskCurve *curve,
|
||||
float t,
|
||||
graphene_point_t *pos)
|
||||
{
|
||||
const GskConicCurve *self = &curve->conic;
|
||||
|
||||
gsk_conic_curve_ensure_coefficents (self);
|
||||
|
||||
gsk_conic_curve_eval_point (self, t, pos);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_conic_curve_get_tangent (const GskCurve *curve,
|
||||
float t,
|
||||
graphene_vec2_t *tangent)
|
||||
{
|
||||
const GskConicCurve *self = &curve->conic;
|
||||
graphene_point_t tmp;
|
||||
float w = gsk_conic_curve_get_weight (self);
|
||||
const graphene_point_t *pts = self->points;
|
||||
|
||||
/* The tangent will be 0 in these corner cases, just
|
||||
* treat it like a line here. */
|
||||
if ((t <= 0.f && graphene_point_equal (&pts[0], &pts[1])) ||
|
||||
(t >= 1.f && graphene_point_equal (&pts[1], &pts[3])))
|
||||
{
|
||||
graphene_vec2_init (tangent, pts[3].x - pts[0].x, pts[3].y - pts[0].y);
|
||||
return;
|
||||
}
|
||||
|
||||
gsk_curve_eval_quad ((graphene_point_t[3]) {
|
||||
GRAPHENE_POINT_INIT ((w - 1) * (pts[3].x - pts[0].x),
|
||||
(w - 1) * (pts[3].y - pts[0].y)),
|
||||
GRAPHENE_POINT_INIT (pts[3].x - pts[0].x - 2 * w * (pts[1].x - pts[0].x),
|
||||
pts[3].y - pts[0].y - 2 * w * (pts[1].y - pts[0].y)),
|
||||
GRAPHENE_POINT_INIT (w * (pts[1].x - pts[0].x),
|
||||
w * (pts[1].y - pts[0].y))
|
||||
},
|
||||
t,
|
||||
&tmp);
|
||||
graphene_vec2_init (tangent, tmp.x, tmp.y);
|
||||
graphene_vec2_normalize (tangent, tangent);
|
||||
}
|
||||
|
||||
static void
|
||||
split_bezier3d_recurse (const graphene_point3d_t *p,
|
||||
int l,
|
||||
float t,
|
||||
graphene_point3d_t *left,
|
||||
graphene_point3d_t *right,
|
||||
int *lpos,
|
||||
int *rpos)
|
||||
{
|
||||
if (l == 1)
|
||||
{
|
||||
left[*lpos] = p[0];
|
||||
right[*rpos] = p[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
graphene_point3d_t *np;
|
||||
int i;
|
||||
|
||||
np = g_alloca (sizeof (graphene_point3d_t) * (l - 1));
|
||||
for (i = 0; i < l - 1; i++)
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
left[*lpos] = p[i];
|
||||
(*lpos)++;
|
||||
}
|
||||
if (i + 1 == l - 1)
|
||||
{
|
||||
right[*rpos] = p[i + 1];
|
||||
(*rpos)--;
|
||||
}
|
||||
graphene_point3d_interpolate (&p[i], &p[i + 1], t, &np[i]);
|
||||
}
|
||||
split_bezier3d_recurse (np, l - 1, t, left, right, lpos, rpos);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
split_bezier3d (const graphene_point3d_t *p,
|
||||
int l,
|
||||
float t,
|
||||
graphene_point3d_t *left,
|
||||
graphene_point3d_t *right)
|
||||
{
|
||||
int lpos = 0;
|
||||
int rpos = l - 1;
|
||||
split_bezier3d_recurse (p, l, t, left, right, &lpos, &rpos);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_conic_curve_split (const GskCurve *curve,
|
||||
float progress,
|
||||
GskCurve *start,
|
||||
GskCurve *end)
|
||||
{
|
||||
const GskConicCurve *self = &curve->conic;
|
||||
graphene_point3d_t p[3];
|
||||
graphene_point3d_t l[3], r[3];
|
||||
graphene_point_t left[4], right[4];
|
||||
float w;
|
||||
|
||||
/* do de Casteljau in homogeneous coordinates... */
|
||||
w = self->points[2].x;
|
||||
p[0] = GRAPHENE_POINT3D_INIT (self->points[0].x, self->points[0].y, 1);
|
||||
p[1] = GRAPHENE_POINT3D_INIT (self->points[1].x * w, self->points[1].y * w, w);
|
||||
p[2] = GRAPHENE_POINT3D_INIT (self->points[3].x, self->points[3].y, 1);
|
||||
|
||||
split_bezier3d (p, 3, progress, l, r);
|
||||
|
||||
/* then project the control points down */
|
||||
left[0] = GRAPHENE_POINT_INIT (l[0].x / l[0].z, l[0].y / l[0].z);
|
||||
left[1] = GRAPHENE_POINT_INIT (l[1].x / l[1].z, l[1].y / l[1].z);
|
||||
left[3] = GRAPHENE_POINT_INIT (l[2].x / l[2].z, l[2].y / l[2].z);
|
||||
|
||||
right[0] = GRAPHENE_POINT_INIT (r[0].x / r[0].z, r[0].y / r[0].z);
|
||||
right[1] = GRAPHENE_POINT_INIT (r[1].x / r[1].z, r[1].y / r[1].z);
|
||||
right[3] = GRAPHENE_POINT_INIT (r[2].x / r[2].z, r[2].y / r[2].z);
|
||||
|
||||
/* normalize the outer weights to be 1 by using
|
||||
* the fact that weights w_i and c*w_i are equivalent
|
||||
* for any nonzero constant c
|
||||
*/
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
l[i].z /= l[0].z;
|
||||
r[i].z /= r[2].z;
|
||||
}
|
||||
|
||||
/* normalize the inner weight to be 1 by using
|
||||
* the fact that w_0*w_2/w_1^2 is a constant for
|
||||
* all equivalent weights.
|
||||
*/
|
||||
left[2] = GRAPHENE_POINT_INIT (l[1].z / sqrt (l[2].z), 0);
|
||||
right[2] = GRAPHENE_POINT_INIT (r[1].z / sqrt (r[0].z), 0);
|
||||
|
||||
if (start)
|
||||
gsk_curve_init (start, gsk_pathop_encode (GSK_PATH_CONIC, left));
|
||||
if (end)
|
||||
gsk_curve_init (end, gsk_pathop_encode (GSK_PATH_CONIC, right));
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_conic_curve_segment (const GskCurve *curve,
|
||||
float start,
|
||||
float end,
|
||||
GskCurve *segment)
|
||||
{
|
||||
const GskConicCurve *self = &curve->conic;
|
||||
graphene_point_t start_num, start_denom;
|
||||
graphene_point_t mid_num, mid_denom;
|
||||
graphene_point_t end_num, end_denom;
|
||||
graphene_point_t ctrl_num, ctrl_denom;
|
||||
float mid;
|
||||
|
||||
if (start <= 0.0f)
|
||||
return gsk_conic_curve_split (curve, end, segment, NULL);
|
||||
else if (end >= 1.0f)
|
||||
return gsk_conic_curve_split (curve, start, NULL, segment);
|
||||
|
||||
gsk_conic_curve_ensure_coefficents (self);
|
||||
|
||||
gsk_curve_eval_quad (self->num, start, &start_num);
|
||||
gsk_curve_eval_quad (self->denom, start, &start_denom);
|
||||
mid = (start + end) / 2;
|
||||
gsk_curve_eval_quad (self->num, mid, &mid_num);
|
||||
gsk_curve_eval_quad (self->denom, mid, &mid_denom);
|
||||
gsk_curve_eval_quad (self->num, end, &end_num);
|
||||
gsk_curve_eval_quad (self->denom, end, &end_denom);
|
||||
ctrl_num = GRAPHENE_POINT_INIT (2 * mid_num.x - (start_num.x + end_num.x) / 2,
|
||||
2 * mid_num.y - (start_num.y + end_num.y) / 2);
|
||||
ctrl_denom = GRAPHENE_POINT_INIT (2 * mid_denom.x - (start_denom.x + end_denom.x) / 2,
|
||||
2 * mid_denom.y - (start_denom.y + end_denom.y) / 2);
|
||||
|
||||
gsk_conic_curve_init_from_points (&segment->conic,
|
||||
(graphene_point_t[4]) {
|
||||
GRAPHENE_POINT_INIT (start_num.x / start_denom.x,
|
||||
start_num.y / start_denom.y),
|
||||
GRAPHENE_POINT_INIT (ctrl_num.x / ctrl_denom.x,
|
||||
ctrl_num.y / ctrl_denom.y),
|
||||
GRAPHENE_POINT_INIT (ctrl_denom.x / sqrtf (start_denom.x * end_denom.x),
|
||||
0),
|
||||
GRAPHENE_POINT_INIT (end_num.x / end_denom.x,
|
||||
end_num.y / end_denom.y)
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/* taken from Skia, including the very descriptive name */
|
||||
static gboolean
|
||||
gsk_conic_curve_too_curvy (const graphene_point_t *start,
|
||||
const graphene_point_t *mid,
|
||||
const graphene_point_t *end,
|
||||
float tolerance)
|
||||
{
|
||||
return fabs ((start->x + end->x) * 0.5 - mid->x) > tolerance
|
||||
|| fabs ((start->y + end->y) * 0.5 - mid->y) > tolerance;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gsk_conic_curve_decompose_subdivide (const GskConicCurve *self,
|
||||
float tolerance,
|
||||
const graphene_point_t *start,
|
||||
float start_progress,
|
||||
const graphene_point_t *end,
|
||||
float end_progress,
|
||||
GskCurveAddLineFunc add_line_func,
|
||||
gpointer user_data)
|
||||
{
|
||||
graphene_point_t mid;
|
||||
float mid_progress;
|
||||
|
||||
mid_progress = (start_progress + end_progress) / 2;
|
||||
gsk_conic_curve_eval_point (self, mid_progress, &mid);
|
||||
|
||||
if (end_progress - start_progress <= MIN_PROGRESS ||
|
||||
!gsk_conic_curve_too_curvy (start, &mid, end, tolerance))
|
||||
{
|
||||
return add_line_func (start, end, start_progress, end_progress, user_data);
|
||||
}
|
||||
|
||||
return gsk_conic_curve_decompose_subdivide (self, tolerance,
|
||||
start, start_progress, &mid, mid_progress,
|
||||
add_line_func, user_data)
|
||||
&& gsk_conic_curve_decompose_subdivide (self, tolerance,
|
||||
&mid, mid_progress, end, end_progress,
|
||||
add_line_func, user_data);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gsk_conic_curve_decompose (const GskCurve *curve,
|
||||
float tolerance,
|
||||
GskCurveAddLineFunc add_line_func,
|
||||
gpointer user_data)
|
||||
{
|
||||
const GskConicCurve *self = &curve->conic;
|
||||
graphene_point_t mid;
|
||||
|
||||
gsk_conic_curve_ensure_coefficents (self);
|
||||
|
||||
gsk_conic_curve_eval_point (self, 0.5, &mid);
|
||||
|
||||
return gsk_conic_curve_decompose_subdivide (self,
|
||||
tolerance,
|
||||
&self->points[0],
|
||||
0.0f,
|
||||
&mid,
|
||||
0.5f,
|
||||
add_line_func,
|
||||
user_data)
|
||||
&& gsk_conic_curve_decompose_subdivide (self,
|
||||
tolerance,
|
||||
&mid,
|
||||
0.5f,
|
||||
&self->points[3],
|
||||
1.0f,
|
||||
add_line_func,
|
||||
user_data);
|
||||
}
|
||||
|
||||
static gskpathop
|
||||
gsk_conic_curve_pathop (const GskCurve *curve)
|
||||
{
|
||||
const GskConicCurve *self = &curve->conic;
|
||||
|
||||
return gsk_pathop_encode (self->op, self->points);
|
||||
}
|
||||
|
||||
static const graphene_point_t *
|
||||
gsk_conic_curve_get_start_point (const GskCurve *curve)
|
||||
{
|
||||
const GskConicCurve *self = &curve->conic;
|
||||
|
||||
return &self->points[0];
|
||||
}
|
||||
|
||||
static const graphene_point_t *
|
||||
gsk_conic_curve_get_end_point (const GskCurve *curve)
|
||||
{
|
||||
const GskConicCurve *self = &curve->conic;
|
||||
|
||||
return &self->points[3];
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_conic_curve_get_start_tangent (const GskCurve *curve,
|
||||
graphene_vec2_t *tangent)
|
||||
{
|
||||
const GskConicCurve *self = &curve->conic;
|
||||
|
||||
get_tangent (&self->points[0], &self->points[1], tangent);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_conic_curve_get_end_tangent (const GskCurve *curve,
|
||||
graphene_vec2_t *tangent)
|
||||
{
|
||||
const GskConicCurve *self = &curve->conic;
|
||||
|
||||
get_tangent (&self->points[1], &self->points[3], tangent);
|
||||
}
|
||||
|
||||
static const GskCurveClass GSK_CONIC_CURVE_CLASS = {
|
||||
gsk_conic_curve_init,
|
||||
gsk_conic_curve_init_foreach,
|
||||
gsk_conic_curve_get_point,
|
||||
gsk_conic_curve_get_tangent,
|
||||
gsk_conic_curve_split,
|
||||
gsk_conic_curve_segment,
|
||||
gsk_conic_curve_decompose,
|
||||
gsk_conic_curve_pathop,
|
||||
gsk_conic_curve_get_start_point,
|
||||
gsk_conic_curve_get_end_point,
|
||||
gsk_conic_curve_get_start_tangent,
|
||||
gsk_conic_curve_get_end_tangent
|
||||
};
|
||||
|
||||
/** API **/
|
||||
|
||||
static const GskCurveClass *
|
||||
get_class (GskPathOperation op)
|
||||
{
|
||||
const GskCurveClass *klasses[] = {
|
||||
[GSK_PATH_CLOSE] = &GSK_LINE_CURVE_CLASS,
|
||||
[GSK_PATH_LINE] = &GSK_LINE_CURVE_CLASS,
|
||||
[GSK_PATH_CURVE] = &GSK_CURVE_CURVE_CLASS,
|
||||
[GSK_PATH_CONIC] = &GSK_CONIC_CURVE_CLASS,
|
||||
};
|
||||
|
||||
g_assert (op < G_N_ELEMENTS (klasses) && klasses[op] != NULL);
|
||||
|
||||
return klasses[op];
|
||||
}
|
||||
|
||||
void
|
||||
gsk_curve_init (GskCurve *curve,
|
||||
gskpathop op)
|
||||
{
|
||||
get_class (gsk_pathop_op (op))->init (curve, op);
|
||||
}
|
||||
|
||||
void
|
||||
gsk_curve_init_foreach (GskCurve *curve,
|
||||
GskPathOperation op,
|
||||
const graphene_point_t *pts,
|
||||
gsize n_pts,
|
||||
float weight)
|
||||
{
|
||||
get_class (op)->init_foreach (curve, op, pts, n_pts, weight);
|
||||
}
|
||||
|
||||
void
|
||||
gsk_curve_get_point (const GskCurve *curve,
|
||||
float progress,
|
||||
graphene_point_t *pos)
|
||||
{
|
||||
get_class (curve->op)->get_point (curve, progress, pos);
|
||||
}
|
||||
|
||||
void
|
||||
gsk_curve_get_tangent (const GskCurve *curve,
|
||||
float progress,
|
||||
graphene_vec2_t *tangent)
|
||||
{
|
||||
get_class (curve->op)->get_tangent (curve, progress, tangent);
|
||||
}
|
||||
|
||||
void
|
||||
gsk_curve_split (const GskCurve *curve,
|
||||
float progress,
|
||||
GskCurve *start,
|
||||
GskCurve *end)
|
||||
{
|
||||
get_class (curve->op)->split (curve, progress, start, end);
|
||||
}
|
||||
|
||||
void
|
||||
gsk_curve_segment (const GskCurve *curve,
|
||||
float start,
|
||||
float end,
|
||||
GskCurve *segment)
|
||||
{
|
||||
if (start <= 0 && end >= 1)
|
||||
{
|
||||
*segment = *curve;
|
||||
return;
|
||||
}
|
||||
|
||||
get_class (curve->op)->segment (curve, start, end, segment);
|
||||
}
|
||||
|
||||
gboolean
|
||||
gsk_curve_decompose (const GskCurve *curve,
|
||||
float tolerance,
|
||||
GskCurveAddLineFunc add_line_func,
|
||||
gpointer user_data)
|
||||
{
|
||||
return get_class (curve->op)->decompose (curve, tolerance, add_line_func, user_data);
|
||||
}
|
||||
|
||||
gskpathop
|
||||
gsk_curve_pathop (const GskCurve *curve)
|
||||
{
|
||||
return get_class (curve->op)->pathop (curve);
|
||||
}
|
||||
|
||||
const graphene_point_t *
|
||||
gsk_curve_get_start_point (const GskCurve *curve)
|
||||
{
|
||||
return get_class (curve->op)->get_start_point (curve);
|
||||
}
|
||||
|
||||
const graphene_point_t *
|
||||
gsk_curve_get_end_point (const GskCurve *curve)
|
||||
{
|
||||
return get_class (curve->op)->get_end_point (curve);
|
||||
}
|
||||
|
||||
void
|
||||
gsk_curve_get_start_tangent (const GskCurve *curve,
|
||||
graphene_vec2_t *tangent)
|
||||
{
|
||||
get_class (curve->op)->get_start_tangent (curve, tangent);
|
||||
}
|
||||
|
||||
void
|
||||
gsk_curve_get_end_tangent (const GskCurve *curve,
|
||||
graphene_vec2_t *tangent)
|
||||
{
|
||||
get_class (curve->op)->get_end_tangent (curve, tangent);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* 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);
|
||||
|
||||
void gsk_curve_init (GskCurve *curve,
|
||||
gskpathop op);
|
||||
void gsk_curve_init_foreach (GskCurve *curve,
|
||||
GskPathOperation op,
|
||||
const graphene_point_t *pts,
|
||||
gsize n_pts,
|
||||
float weight);
|
||||
|
||||
void gsk_curve_get_point (const GskCurve *curve,
|
||||
float progress,
|
||||
graphene_point_t *pos);
|
||||
void gsk_curve_get_tangent (const GskCurve *curve,
|
||||
float progress,
|
||||
graphene_vec2_t *tangent);
|
||||
void gsk_curve_split (const GskCurve *curve,
|
||||
float progress,
|
||||
GskCurve *start,
|
||||
GskCurve *end);
|
||||
void gsk_curve_segment (const GskCurve *curve,
|
||||
float start,
|
||||
float end,
|
||||
GskCurve *segment);
|
||||
gboolean gsk_curve_decompose (const GskCurve *curve,
|
||||
float tolerance,
|
||||
GskCurveAddLineFunc add_line_func,
|
||||
gpointer user_data);
|
||||
gskpathop gsk_curve_pathop (const GskCurve *curve);
|
||||
#define gsk_curve_builder_to(curve, builder) gsk_path_builder_pathop_to ((builder), gsk_curve_pathop (curve))
|
||||
const graphene_point_t *gsk_curve_get_start_point (const GskCurve *curve);
|
||||
const graphene_point_t *gsk_curve_get_end_point (const GskCurve *curve);
|
||||
void gsk_curve_get_start_tangent (const GskCurve *curve,
|
||||
graphene_vec2_t *tangent);
|
||||
void gsk_curve_get_end_tangent (const GskCurve *curve,
|
||||
graphene_vec2_t *tangent);
|
||||
|
||||
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GSK_CURVE_PRIVATE_H__ */
|
||||
|
||||
@@ -251,6 +251,9 @@ typedef enum {
|
||||
* @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.
|
||||
*
|
||||
@@ -261,6 +264,7 @@ typedef enum {
|
||||
GSK_PATH_CLOSE,
|
||||
GSK_PATH_LINE,
|
||||
GSK_PATH_CURVE,
|
||||
GSK_PATH_CONIC,
|
||||
} GskPathOperation;
|
||||
|
||||
/**
|
||||
|
||||
+88
-34
@@ -21,8 +21,8 @@
|
||||
|
||||
#include "gskpathprivate.h"
|
||||
|
||||
#include "gskcurveprivate.h"
|
||||
#include "gskpathbuilder.h"
|
||||
#include "gsksplineprivate.h"
|
||||
|
||||
/**
|
||||
* SECTION:gskpath
|
||||
@@ -212,7 +212,11 @@ gsk_path_get_contour (GskPath *path,
|
||||
* Converts @self into a human-readable string representation suitable
|
||||
* for printing.
|
||||
*
|
||||
* The string is compatible with SVG paths.
|
||||
* The string is compatible with
|
||||
* [SVG path syntax](https://www.w3.org/TR/SVG11/paths.html#PathData),
|
||||
* with the exception that conic curves will generate a string of the
|
||||
* form "O x1 y1, x2 y2, w" where x1, y1 is the control point, x2, y2
|
||||
* is the end point, and w is the weight.
|
||||
**/
|
||||
void
|
||||
gsk_path_print (GskPath *self,
|
||||
@@ -262,6 +266,7 @@ static gboolean
|
||||
gsk_path_to_cairo_add_op (GskPathOperation op,
|
||||
const graphene_point_t *pts,
|
||||
gsize n_pts,
|
||||
float weight,
|
||||
gpointer cr)
|
||||
{
|
||||
switch (op)
|
||||
@@ -282,6 +287,7 @@ gsk_path_to_cairo_add_op (GskPathOperation op,
|
||||
cairo_curve_to (cr, pts[1].x, pts[1].y, pts[2].x, pts[2].y, pts[3].x, pts[3].y);
|
||||
break;
|
||||
|
||||
case GSK_PATH_CONIC:
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
return FALSE;
|
||||
@@ -299,7 +305,7 @@ gsk_path_to_cairo_add_op (GskPathOperation op,
|
||||
* with Cairo.
|
||||
*
|
||||
* This may cause some suboptimal conversions to be performed as Cairo
|
||||
* may not perform all features of #GskPath.
|
||||
* may not support all features of #GskPath.
|
||||
*
|
||||
* This function does not clear the existing Cairo path. Call
|
||||
* cairo_new_path() if you want this.
|
||||
@@ -312,7 +318,7 @@ gsk_path_to_cairo (GskPath *self,
|
||||
g_return_if_fail (cr != NULL);
|
||||
|
||||
gsk_path_foreach_with_tolerance (self,
|
||||
GSK_PATH_FOREACH_ALLOW_CURVES,
|
||||
GSK_PATH_FOREACH_ALLOW_CURVE,
|
||||
cairo_get_tolerance (cr),
|
||||
gsk_path_to_cairo_add_op,
|
||||
cr);
|
||||
@@ -364,10 +370,9 @@ gsk_path_is_empty (GskPath *path)
|
||||
* axis-aligned line.
|
||||
*
|
||||
* If the path is empty, %FALSE is returned and @bounds are set to
|
||||
* graphene_rect_zero().
|
||||
* This is different from the case where the path is a signle point at
|
||||
* the origin, where the @bounds will also be set to the zero rectangle
|
||||
* but 0 will be returned.
|
||||
* graphene_rect_zero(). This is different from the case where the path
|
||||
* is a single point at the origin, where the @bounds will also be set to
|
||||
* the zero rectangle but 0 will be returned.
|
||||
*
|
||||
* Returns: %TRUE if the path has bounds, %FALSE if the path is known
|
||||
* to be empty and have no bounds.
|
||||
@@ -440,31 +445,29 @@ struct _GskPathForeachTrampoline
|
||||
double tolerance;
|
||||
GskPathForeachFunc func;
|
||||
gpointer user_data;
|
||||
gboolean retval;
|
||||
};
|
||||
|
||||
static void
|
||||
gsk_path_foreach_trampoline_add_point (const graphene_point_t *from,
|
||||
const graphene_point_t *to,
|
||||
float from_progress,
|
||||
float to_progress,
|
||||
gpointer data)
|
||||
static gboolean
|
||||
gsk_path_foreach_trampoline_add_line (const graphene_point_t *from,
|
||||
const graphene_point_t *to,
|
||||
float from_progress,
|
||||
float to_progress,
|
||||
gpointer data)
|
||||
{
|
||||
GskPathForeachTrampoline *trampoline = data;
|
||||
|
||||
if (!trampoline->retval)
|
||||
return;
|
||||
|
||||
trampoline->retval = trampoline->func (GSK_PATH_LINE,
|
||||
(graphene_point_t[2]) { *from, *to },
|
||||
2,
|
||||
trampoline->user_data);
|
||||
return trampoline->func (GSK_PATH_LINE,
|
||||
(graphene_point_t[2]) { *from, *to },
|
||||
2,
|
||||
0.0f,
|
||||
trampoline->user_data);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gsk_path_foreach_trampoline (GskPathOperation op,
|
||||
const graphene_point_t *pts,
|
||||
gsize n_pts,
|
||||
float weight,
|
||||
gpointer data)
|
||||
{
|
||||
GskPathForeachTrampoline *trampoline = data;
|
||||
@@ -474,17 +477,37 @@ gsk_path_foreach_trampoline (GskPathOperation op,
|
||||
case GSK_PATH_MOVE:
|
||||
case GSK_PATH_CLOSE:
|
||||
case GSK_PATH_LINE:
|
||||
return trampoline->func (op, pts, n_pts, trampoline->user_data);
|
||||
return trampoline->func (op, pts, n_pts, weight, trampoline->user_data);
|
||||
|
||||
case GSK_PATH_CURVE:
|
||||
if (trampoline->flags & GSK_PATH_FOREACH_ALLOW_CURVES)
|
||||
return trampoline->func (op, pts, n_pts, trampoline->user_data);
|
||||
|
||||
gsk_spline_decompose_cubic (pts,
|
||||
trampoline->tolerance,
|
||||
gsk_path_foreach_trampoline_add_point,
|
||||
trampoline);
|
||||
return trampoline->retval;
|
||||
{
|
||||
GskCurve curve;
|
||||
|
||||
if (trampoline->flags & GSK_PATH_FOREACH_ALLOW_CURVE)
|
||||
return trampoline->func (op, pts, n_pts, weight, trampoline->user_data);
|
||||
|
||||
gsk_curve_init (&curve, gsk_pathop_encode (GSK_PATH_CURVE, pts));
|
||||
return gsk_curve_decompose (&curve,
|
||||
trampoline->tolerance,
|
||||
gsk_path_foreach_trampoline_add_line,
|
||||
trampoline);
|
||||
}
|
||||
|
||||
case GSK_PATH_CONIC:
|
||||
{
|
||||
GskCurve curve;
|
||||
|
||||
if (trampoline->flags & GSK_PATH_FOREACH_ALLOW_CONIC)
|
||||
return trampoline->func (op, pts, n_pts, weight, trampoline->user_data);
|
||||
|
||||
/* XXX: decompose into curves if allowed */
|
||||
gsk_curve_init (&curve, gsk_pathop_encode (GSK_PATH_CONIC, (graphene_point_t[4]) { pts[0], pts[1], { weight, }, pts[2] } ));
|
||||
return gsk_curve_decompose (&curve,
|
||||
trampoline->tolerance,
|
||||
gsk_path_foreach_trampoline_add_line,
|
||||
trampoline);
|
||||
}
|
||||
|
||||
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
@@ -503,9 +526,9 @@ gsk_path_foreach_with_tolerance (GskPath *self,
|
||||
gsize i;
|
||||
|
||||
/* If we need to massage the data, set up a trampoline here */
|
||||
if (flags != GSK_PATH_FOREACH_ALLOW_CURVES)
|
||||
if (flags != (GSK_PATH_FOREACH_ALLOW_CURVE | GSK_PATH_FOREACH_ALLOW_CONIC))
|
||||
{
|
||||
trampoline = (GskPathForeachTrampoline) { flags, tolerance, func, user_data, TRUE };
|
||||
trampoline = (GskPathForeachTrampoline) { flags, tolerance, func, user_data };
|
||||
func = gsk_path_foreach_trampoline;
|
||||
user_data = &trampoline;
|
||||
}
|
||||
@@ -628,7 +651,7 @@ parse_command (const char **p,
|
||||
if (*cmd == 'X')
|
||||
allowed = "mM";
|
||||
else
|
||||
allowed = "mMhHvVzZlLcCsStTqQaA";
|
||||
allowed = "mMhHvVzZlLcCsStTqQoOaA";
|
||||
|
||||
skip_whitespace (p);
|
||||
s = strchr (allowed, **p);
|
||||
@@ -1057,6 +1080,37 @@ gsk_path_parse (const char *string)
|
||||
}
|
||||
break;
|
||||
|
||||
case 'O':
|
||||
case 'o':
|
||||
{
|
||||
double x1, y1, x2, y2, weight;
|
||||
|
||||
if (parse_coordinate_pair (&p, &x1, &y1) &&
|
||||
parse_coordinate_pair (&p, &x2, &y2) &&
|
||||
parse_nonnegative_number (&p, &weight))
|
||||
{
|
||||
if (cmd == 'c')
|
||||
{
|
||||
x1 += x;
|
||||
y1 += y;
|
||||
x2 += x;
|
||||
y2 += y;
|
||||
}
|
||||
if (strchr ("zZ", prev_cmd))
|
||||
{
|
||||
gsk_path_builder_move_to (builder, x, y);
|
||||
path_x = x;
|
||||
path_y = y;
|
||||
}
|
||||
gsk_path_builder_conic_to (builder, x1, y1, x2, y2, weight);
|
||||
x = x2;
|
||||
y = y2;
|
||||
}
|
||||
else
|
||||
goto error;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'A':
|
||||
case 'a':
|
||||
{
|
||||
|
||||
+7
-2
@@ -31,7 +31,9 @@ G_BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* GskPathForeachFlags:
|
||||
* @GSK_PATH_FOREACH_ALLOW_CURVES: Allow emission of %GSK_PATH_CURVE
|
||||
* @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
|
||||
@@ -43,7 +45,8 @@ G_BEGIN_DECLS
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
GSK_PATH_FOREACH_ALLOW_CURVES = (1 << 0)
|
||||
GSK_PATH_FOREACH_ALLOW_CURVE = (1 << 0),
|
||||
GSK_PATH_FOREACH_ALLOW_CONIC = (1 << 1)
|
||||
} GskPathForeachFlags;
|
||||
|
||||
/**
|
||||
@@ -51,6 +54,7 @@ typedef enum
|
||||
* @op: The operation to perform
|
||||
* @pts: The points of the operation
|
||||
* @n_pts: The number of points
|
||||
* @weight: The weight for conic curves, or unused if not a conic curve.
|
||||
* @user_data: The user data provided with the function
|
||||
*
|
||||
* Prototype of the callback to iterate throught the operations of
|
||||
@@ -62,6 +66,7 @@ typedef enum
|
||||
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 ())
|
||||
|
||||
+160
-7
@@ -105,7 +105,7 @@ gsk_path_builder_new (void)
|
||||
builder = g_slice_new0 (GskPathBuilder);
|
||||
builder->ref_count = 1;
|
||||
|
||||
builder->ops = g_array_new (FALSE, FALSE, sizeof (GskStandardOperation));
|
||||
builder->ops = g_array_new (FALSE, FALSE, sizeof (gskpathop));
|
||||
builder->points = g_array_new (FALSE, FALSE, sizeof (graphene_point_t));
|
||||
|
||||
/* Be explicit here */
|
||||
@@ -137,6 +137,19 @@ gsk_path_builder_ref (GskPathBuilder *builder)
|
||||
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)
|
||||
{
|
||||
@@ -144,7 +157,7 @@ gsk_path_builder_ensure_current (GskPathBuilder *builder)
|
||||
return;
|
||||
|
||||
builder->flags = GSK_PATH_FLAT;
|
||||
g_array_append_vals (builder->ops, &(GskStandardOperation) { GSK_PATH_MOVE, 0 }, 1);
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -156,7 +169,7 @@ gsk_path_builder_append_current (GskPathBuilder *builder,
|
||||
{
|
||||
gsk_path_builder_ensure_current (builder);
|
||||
|
||||
g_array_append_vals (builder->ops, &(GskStandardOperation) { op, builder->points->len - 1 }, 1);
|
||||
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];
|
||||
@@ -171,10 +184,11 @@ gsk_path_builder_end_current (GskPathBuilder *builder)
|
||||
return;
|
||||
|
||||
contour = gsk_standard_contour_new (builder->flags,
|
||||
(GskStandardOperation *) builder->ops->data,
|
||||
builder->ops->len,
|
||||
(graphene_point_t *) builder->points->data,
|
||||
builder->points->len);
|
||||
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);
|
||||
@@ -353,6 +367,73 @@ gsk_path_builder_add_rect (GskPathBuilder *builder,
|
||||
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
|
||||
@@ -549,11 +630,83 @@ gsk_path_builder_rel_curve_to (GskPathBuilder *builder,
|
||||
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.
|
||||
* 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
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#endif
|
||||
|
||||
|
||||
#include <gsk/gskroundedrect.h>
|
||||
#include <gsk/gsktypes.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
@@ -55,6 +56,9 @@ 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);
|
||||
@@ -99,6 +103,20 @@ void gsk_path_builder_rel_curve_to (GskPathBuilder
|
||||
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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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__ */
|
||||
|
||||
@@ -166,6 +166,34 @@ gsk_path_measure_unref (GskPathMeasure *self)
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
return self->tolerance;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_measure_get_length:
|
||||
* @self: a #GskPathMeasure
|
||||
|
||||
@@ -44,6 +44,11 @@ GskPathMeasure * gsk_path_measure_ref (GskPathMeasure
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_path_measure_unref (GskPathMeasure *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskPath * gsk_path_measure_get_path (GskPathMeasure *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
float gsk_path_measure_get_tolerance (GskPathMeasure *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
float gsk_path_measure_get_length (GskPathMeasure *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
|
||||
@@ -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__ */
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "gskpath.h"
|
||||
|
||||
#include "gskcontourprivate.h"
|
||||
#include "gskpathopprivate.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
|
||||
+1
-210
@@ -25,215 +25,6 @@
|
||||
|
||||
#include <math.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
graphene_point_t last_point;
|
||||
float last_progress;
|
||||
float tolerance;
|
||||
GskSplineAddPointFunc func;
|
||||
gpointer user_data;
|
||||
} GskCubicDecomposition;
|
||||
|
||||
static void
|
||||
gsk_spline_decompose_add_point (GskCubicDecomposition *decomp,
|
||||
const graphene_point_t *pt,
|
||||
float progress)
|
||||
{
|
||||
if (graphene_point_equal (&decomp->last_point, pt))
|
||||
return;
|
||||
|
||||
decomp->func (&decomp->last_point, pt, decomp->last_progress, decomp->last_progress + progress, decomp->user_data);
|
||||
decomp->last_point = *pt;
|
||||
decomp->last_progress += progress;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_spline_cubic_get_coefficients (graphene_point_t coeffs[4],
|
||||
const graphene_point_t pts[4])
|
||||
{
|
||||
coeffs[0] = GRAPHENE_POINT_INIT (pts[3].x - 3.0f * pts[2].x + 3.0f * pts[1].x - pts[0].x,
|
||||
pts[3].y - 3.0f * pts[2].y + 3.0f * pts[1].y - pts[0].y);
|
||||
coeffs[1] = GRAPHENE_POINT_INIT (3.0f * pts[2].x - 6.0f * pts[1].x + 3.0f * pts[0].x,
|
||||
3.0f * pts[2].y - 6.0f * pts[1].y + 3.0f * pts[0].y);
|
||||
coeffs[2] = GRAPHENE_POINT_INIT (3.0f * pts[1].x - 3.0f * pts[0].x,
|
||||
3.0f * pts[1].y - 3.0f * pts[0].y);
|
||||
coeffs[3] = pts[0];
|
||||
}
|
||||
|
||||
void
|
||||
gsk_spline_get_point_cubic (const graphene_point_t pts[4],
|
||||
float progress,
|
||||
graphene_point_t *pos,
|
||||
graphene_vec2_t *tangent)
|
||||
{
|
||||
graphene_point_t c[4];
|
||||
|
||||
gsk_spline_cubic_get_coefficients (c, pts);
|
||||
|
||||
if (pos)
|
||||
*pos = GRAPHENE_POINT_INIT (((c[0].x * progress + c[1].x) * progress +c[2].x) * progress + c[3].x,
|
||||
((c[0].y * progress + c[1].y) * progress +c[2].y) * progress + c[3].y);
|
||||
if (tangent)
|
||||
{
|
||||
graphene_vec2_init (tangent,
|
||||
(3.0f * c[0].x * progress + 2.0f * c[1].x) * progress + c[2].x,
|
||||
(3.0f * c[0].y * progress + 2.0f * c[1].y) * progress + c[2].y);
|
||||
graphene_vec2_normalize (tangent, tangent);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gsk_spline_split_cubic (const graphene_point_t pts[4],
|
||||
graphene_point_t result1[4],
|
||||
graphene_point_t result2[4],
|
||||
float progress)
|
||||
{
|
||||
graphene_point_t ab, bc, cd;
|
||||
graphene_point_t abbc, bccd;
|
||||
graphene_point_t final;
|
||||
|
||||
graphene_point_interpolate (&pts[0], &pts[1], progress, &ab);
|
||||
graphene_point_interpolate (&pts[1], &pts[2], progress, &bc);
|
||||
graphene_point_interpolate (&pts[2], &pts[3], progress, &cd);
|
||||
graphene_point_interpolate (&ab, &bc, progress, &abbc);
|
||||
graphene_point_interpolate (&bc, &cd, progress, &bccd);
|
||||
graphene_point_interpolate (&abbc, &bccd, progress, &final);
|
||||
|
||||
memcpy (result1, (graphene_point_t[4]) { pts[0], ab, abbc, final }, sizeof (graphene_point_t[4]));
|
||||
memcpy (result2, (graphene_point_t[4]) { final, bccd, cd, pts[3] }, sizeof (graphene_point_t[4]));
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* Return an upper bound on the error (squared) that could result from
|
||||
* approximating a spline as a line segment connecting the two endpoints. */
|
||||
static float
|
||||
gsk_spline_error_squared (const graphene_point_t pts[4])
|
||||
{
|
||||
float bdx, bdy, berr;
|
||||
float cdx, cdy, cerr;
|
||||
|
||||
/* We are going to compute the distance (squared) between each of the the b
|
||||
* and c control points and the segment a-b. The maximum of these two
|
||||
* distances will be our approximation error. */
|
||||
|
||||
bdx = pts[1].x - pts[0].x;
|
||||
bdy = pts[1].y - pts[0].y;
|
||||
|
||||
cdx = pts[2].x - pts[0].x;
|
||||
cdy = pts[2].y - pts[0].y;
|
||||
|
||||
if (!graphene_point_equal (&pts[0], &pts[3]))
|
||||
{
|
||||
float dx, dy, u, v;
|
||||
|
||||
/* Intersection point (px):
|
||||
* px = p1 + u(p2 - p1)
|
||||
* (p - px) ∙ (p2 - p1) = 0
|
||||
* Thus:
|
||||
* u = ((p - p1) ∙ (p2 - p1)) / ∥p2 - p1∥²;
|
||||
*/
|
||||
|
||||
dx = pts[3].x - pts[0].x;
|
||||
dy = pts[3].y - pts[0].y;
|
||||
v = dx * dx + dy * dy;
|
||||
|
||||
u = bdx * dx + bdy * dy;
|
||||
if (u <= 0)
|
||||
{
|
||||
/* bdx -= 0;
|
||||
* bdy -= 0;
|
||||
*/
|
||||
}
|
||||
else if (u >= v)
|
||||
{
|
||||
bdx -= dx;
|
||||
bdy -= dy;
|
||||
}
|
||||
else
|
||||
{
|
||||
bdx -= u/v * dx;
|
||||
bdy -= u/v * dy;
|
||||
}
|
||||
|
||||
u = cdx * dx + cdy * dy;
|
||||
if (u <= 0)
|
||||
{
|
||||
/* cdx -= 0;
|
||||
* cdy -= 0;
|
||||
*/
|
||||
}
|
||||
else if (u >= v)
|
||||
{
|
||||
cdx -= dx;
|
||||
cdy -= dy;
|
||||
}
|
||||
else
|
||||
{
|
||||
cdx -= u/v * dx;
|
||||
cdy -= u/v * dy;
|
||||
}
|
||||
}
|
||||
|
||||
berr = bdx * bdx + bdy * bdy;
|
||||
cerr = cdx * cdx + cdy * cdy;
|
||||
if (berr > cerr)
|
||||
return berr;
|
||||
else
|
||||
return cerr;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* taken from Skia, including the very descriptive name */
|
||||
static gboolean
|
||||
gsk_spline_cubic_too_curvy (const graphene_point_t pts[4],
|
||||
float tolerance)
|
||||
{
|
||||
graphene_point_t p;
|
||||
|
||||
graphene_point_interpolate (&pts[0], &pts[3], 1.0f / 3, &p);
|
||||
if (ABS (p.x - pts[1].x) + ABS (p.y - pts[1].y) > tolerance)
|
||||
return TRUE;
|
||||
|
||||
graphene_point_interpolate (&pts[0], &pts[3], 2.0f / 3, &p);
|
||||
if (ABS (p.x - pts[2].x) + ABS (p.y - pts[2].y) > tolerance)
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_spline_decompose_into (GskCubicDecomposition *decomp,
|
||||
const graphene_point_t pts[4],
|
||||
float progress)
|
||||
{
|
||||
graphene_point_t left[4], right[4];
|
||||
|
||||
if (!gsk_spline_cubic_too_curvy (pts, decomp->tolerance) || progress < 1 / 1024.f)
|
||||
{
|
||||
gsk_spline_decompose_add_point (decomp, &pts[3], progress);
|
||||
return;
|
||||
}
|
||||
|
||||
gsk_spline_split_cubic (pts, left, right, 0.5);
|
||||
|
||||
gsk_spline_decompose_into (decomp, left, progress / 2);
|
||||
gsk_spline_decompose_into (decomp, right, progress / 2);
|
||||
}
|
||||
|
||||
void
|
||||
gsk_spline_decompose_cubic (const graphene_point_t pts[4],
|
||||
float tolerance,
|
||||
GskSplineAddPointFunc add_point_func,
|
||||
gpointer user_data)
|
||||
{
|
||||
GskCubicDecomposition decomp = { pts[0], 0.0f, tolerance, add_point_func, user_data };
|
||||
|
||||
gsk_spline_decompose_into (&decomp, pts, 1.0f);
|
||||
|
||||
g_assert (graphene_point_equal (&decomp.last_point, &pts[3]));
|
||||
g_assert (decomp.last_progress == 1.0f || decomp.last_progress == 0.0f);
|
||||
}
|
||||
|
||||
/* Spline deviation from the circle in radius would be given by:
|
||||
|
||||
error = sqrt (x**2 + y**2) - 1
|
||||
@@ -401,7 +192,7 @@ gsk_spline_decompose_arc (const graphene_point_t *center,
|
||||
}
|
||||
else if (ABS (step) < tolerance)
|
||||
{
|
||||
return TRUE;
|
||||
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);
|
||||
|
||||
@@ -25,25 +25,6 @@
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef void (* GskSplineAddPointFunc) (const graphene_point_t *from,
|
||||
const graphene_point_t *to,
|
||||
float from_progress,
|
||||
float to_progress,
|
||||
gpointer user_data);
|
||||
|
||||
void gsk_spline_get_point_cubic (const graphene_point_t pts[4],
|
||||
float progress,
|
||||
graphene_point_t *pos,
|
||||
graphene_vec2_t *tangent);
|
||||
void gsk_spline_split_cubic (const graphene_point_t pts[4],
|
||||
graphene_point_t result1[4],
|
||||
graphene_point_t result2[4],
|
||||
float progress);
|
||||
void gsk_spline_decompose_cubic (const graphene_point_t pts[4],
|
||||
float tolerance,
|
||||
GskSplineAddPointFunc add_point_func,
|
||||
gpointer user_data);
|
||||
|
||||
typedef gboolean (* GskSplineAddCurveFunc) (const graphene_point_t curve[4],
|
||||
gpointer user_data);
|
||||
gboolean gsk_spline_decompose_arc (const graphene_point_t *center,
|
||||
|
||||
+3
-1
@@ -21,11 +21,12 @@ gsk_private_gl_shaders = [
|
||||
]
|
||||
|
||||
gsk_public_sources = files([
|
||||
'gskdiff.c',
|
||||
'gskcairorenderer.c',
|
||||
'gskdiff.c',
|
||||
'gskglshader.c',
|
||||
'gskpath.c',
|
||||
'gskpathbuilder.c',
|
||||
'gskpathdash.c',
|
||||
'gskpathmeasure.c',
|
||||
'gskrenderer.c',
|
||||
'gskrendernode.c',
|
||||
@@ -40,6 +41,7 @@ gsk_public_sources = files([
|
||||
gsk_private_sources = files([
|
||||
'gskcairoblur.c',
|
||||
'gskcontour.c',
|
||||
'gskcurve.c',
|
||||
'gskdebug.c',
|
||||
'gskprivate.c',
|
||||
'gskprofiler.c',
|
||||
|
||||
@@ -1414,6 +1414,7 @@ create_arrow_render_node (GtkPopover *popover)
|
||||
|
||||
stroke = gsk_stroke_new (border.bottom + 1);
|
||||
gtk_snapshot_push_stroke (snapshot, path, stroke);
|
||||
gsk_stroke_free (stroke);
|
||||
|
||||
gsk_path_get_bounds (path, &bounds);
|
||||
gtk_snapshot_append_color (snapshot, border_color, &bounds);
|
||||
|
||||
+141
-74
@@ -357,16 +357,16 @@ snapshot_frame_fill (GtkSnapshot *snapshot,
|
||||
gtk_snapshot_append_border (snapshot, outline, border_width, colors);
|
||||
}
|
||||
|
||||
static void
|
||||
set_stroke_style (cairo_t *cr,
|
||||
double line_width,
|
||||
GtkBorderStyle style,
|
||||
double length)
|
||||
static GskStroke *
|
||||
create_stroke_style (double line_width,
|
||||
GtkBorderStyle style,
|
||||
double length)
|
||||
{
|
||||
double segments[2];
|
||||
GskStroke *stroke;
|
||||
float segments[2];
|
||||
double n;
|
||||
|
||||
cairo_set_line_width (cr, line_width);
|
||||
stroke = gsk_stroke_new (line_width);
|
||||
|
||||
if (style == GTK_BORDER_STYLE_DOTTED)
|
||||
{
|
||||
@@ -374,12 +374,12 @@ set_stroke_style (cairo_t *cr,
|
||||
|
||||
segments[0] = 0;
|
||||
segments[1] = n ? length / n : 2;
|
||||
cairo_set_dash (cr, segments, G_N_ELEMENTS (segments), 0);
|
||||
gsk_stroke_set_dash (stroke, segments, 2);
|
||||
|
||||
cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
|
||||
cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
|
||||
gsk_stroke_set_line_cap (stroke, GSK_LINE_CAP_ROUND);
|
||||
gsk_stroke_set_line_join (stroke, GSK_LINE_JOIN_ROUND);
|
||||
}
|
||||
else
|
||||
else if (style == GTK_BORDER_STYLE_DASHED)
|
||||
{
|
||||
n = length / line_width;
|
||||
/* Optimize the common case of an integer-sized rectangle
|
||||
@@ -397,32 +397,33 @@ set_stroke_style (cairo_t *cr,
|
||||
segments[0] = n ? (1. / 3) * length / n : 1;
|
||||
segments[1] = 2 * segments[0];
|
||||
}
|
||||
cairo_set_dash (cr, segments, G_N_ELEMENTS (segments), 0);
|
||||
gsk_stroke_set_dash (stroke, segments, 2);
|
||||
|
||||
cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
|
||||
cairo_set_line_join (cr, CAIRO_LINE_JOIN_MITER);
|
||||
gsk_stroke_set_line_cap (stroke, GSK_LINE_CAP_SQUARE);
|
||||
gsk_stroke_set_line_join (stroke, GSK_LINE_JOIN_MITER);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
return stroke;
|
||||
}
|
||||
|
||||
static void
|
||||
render_frame_stroke (cairo_t *cr,
|
||||
const GskRoundedRect *border_box,
|
||||
const double border_width[4],
|
||||
GdkRGBA colors[4],
|
||||
guint hidden_side,
|
||||
GtkBorderStyle stroke_style)
|
||||
snapshot_frame_stroke (GtkSnapshot *snapshot,
|
||||
const GskRoundedRect *border_box,
|
||||
const float border_width[4],
|
||||
GdkRGBA colors[4],
|
||||
guint hidden_side,
|
||||
GtkBorderStyle stroke_style)
|
||||
{
|
||||
gboolean different_colors, different_borders;
|
||||
GskRoundedRect stroke_box;
|
||||
GskPathBuilder *builder;
|
||||
GskPath *path;
|
||||
GskStroke *stroke;
|
||||
guint i;
|
||||
|
||||
different_colors = !gdk_rgba_equal (&colors[0], &colors[1]) ||
|
||||
!gdk_rgba_equal (&colors[0], &colors[2]) ||
|
||||
!gdk_rgba_equal (&colors[0], &colors[3]);
|
||||
different_borders = border_width[0] != border_width[1] ||
|
||||
border_width[0] != border_width[2] ||
|
||||
border_width[0] != border_width[3] ;
|
||||
|
||||
stroke_box = *border_box;
|
||||
gsk_rounded_rect_shrink (&stroke_box,
|
||||
border_width[GTK_CSS_TOP] / 2.0,
|
||||
@@ -430,32 +431,36 @@ render_frame_stroke (cairo_t *cr,
|
||||
border_width[GTK_CSS_BOTTOM] / 2.0,
|
||||
border_width[GTK_CSS_LEFT] / 2.0);
|
||||
|
||||
if (!different_colors && !different_borders && hidden_side == 0)
|
||||
if (border_width[0] == border_width[1] &&
|
||||
border_width[0] == border_width[2] &&
|
||||
border_width[0] == border_width[3] &&
|
||||
hidden_side == 0)
|
||||
{
|
||||
double length = 0;
|
||||
|
||||
/* FAST PATH:
|
||||
* Mostly expected to trigger for focus rectangles */
|
||||
for (i = 0; i < 4; i++)
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
length += _gtk_rounded_box_guess_length (&stroke_box, i);
|
||||
}
|
||||
|
||||
gsk_rounded_rect_path (&stroke_box, cr);
|
||||
gdk_cairo_set_source_rgba (cr, &colors[0]);
|
||||
set_stroke_style (cr, border_width[0], stroke_style, length);
|
||||
cairo_stroke (cr);
|
||||
builder = gsk_path_builder_new ();
|
||||
gsk_path_builder_add_rounded_rect (builder, &stroke_box);
|
||||
path = gsk_path_builder_free_to_path (builder);
|
||||
stroke = create_stroke_style (border_width[0],
|
||||
stroke_style, length);
|
||||
gtk_snapshot_push_stroke (snapshot, path, stroke);
|
||||
gsk_stroke_free (stroke);
|
||||
gsk_path_unref (path);
|
||||
|
||||
gtk_snapshot_append_border (snapshot, border_box, border_width, colors);
|
||||
|
||||
gtk_snapshot_pop (snapshot);
|
||||
}
|
||||
else
|
||||
{
|
||||
GskRoundedRect padding_box;
|
||||
|
||||
padding_box = *border_box;
|
||||
gsk_rounded_rect_shrink (&padding_box,
|
||||
border_width[GTK_CSS_TOP],
|
||||
border_width[GTK_CSS_RIGHT],
|
||||
border_width[GTK_CSS_BOTTOM],
|
||||
border_width[GTK_CSS_LEFT]);
|
||||
const float weight = sqrtf(2)/2.0;
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
@@ -465,49 +470,111 @@ render_frame_stroke (cairo_t *cr,
|
||||
if (border_width[i] == 0)
|
||||
continue;
|
||||
|
||||
cairo_save (cr);
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
if (i == 0)
|
||||
_gtk_rounded_box_path_top (border_box, &padding_box, cr);
|
||||
{
|
||||
/* top */
|
||||
gsk_path_builder_move_to (builder,
|
||||
stroke_box.bounds.origin.x + stroke_box.corner[GSK_CORNER_TOP_LEFT].width / 2,
|
||||
stroke_box.bounds.origin.y + stroke_box.corner[GSK_CORNER_TOP_LEFT].height / 2);
|
||||
gsk_path_builder_conic_to (builder,
|
||||
stroke_box.bounds.origin.x,
|
||||
stroke_box.bounds.origin.y,
|
||||
stroke_box.bounds.origin.x + stroke_box.corner[GSK_CORNER_TOP_LEFT].width,
|
||||
stroke_box.bounds.origin.y,
|
||||
weight);
|
||||
gsk_path_builder_line_to (builder,
|
||||
stroke_box.bounds.origin.x + stroke_box.bounds.size.width - stroke_box.corner[GSK_CORNER_TOP_RIGHT].width,
|
||||
stroke_box.bounds.origin.y);
|
||||
gsk_path_builder_conic_to (builder,
|
||||
stroke_box.bounds.origin.x + stroke_box.bounds.size.width,
|
||||
stroke_box.bounds.origin.y,
|
||||
stroke_box.bounds.origin.x + stroke_box.bounds.size.width - stroke_box.corner[GSK_CORNER_TOP_RIGHT].width / 2,
|
||||
stroke_box.bounds.origin.y + stroke_box.corner[GSK_CORNER_TOP_RIGHT].height / 2,
|
||||
weight);
|
||||
}
|
||||
else if (i == 1)
|
||||
_gtk_rounded_box_path_right (border_box, &padding_box, cr);
|
||||
{
|
||||
/* right */
|
||||
gsk_path_builder_move_to (builder,
|
||||
stroke_box.bounds.origin.x + stroke_box.bounds.size.width - stroke_box.corner[GSK_CORNER_TOP_RIGHT].width / 2,
|
||||
stroke_box.bounds.origin.y + stroke_box.corner[GSK_CORNER_TOP_RIGHT].height / 2);
|
||||
gsk_path_builder_conic_to (builder,
|
||||
stroke_box.bounds.origin.x + stroke_box.bounds.size.width,
|
||||
stroke_box.bounds.origin.y,
|
||||
stroke_box.bounds.origin.x + stroke_box.bounds.size.width,
|
||||
stroke_box.bounds.origin.y + stroke_box.corner[GSK_CORNER_TOP_RIGHT].height,
|
||||
weight);
|
||||
gsk_path_builder_line_to (builder,
|
||||
stroke_box.bounds.origin.x + stroke_box.bounds.size.width,
|
||||
stroke_box.bounds.origin.y + stroke_box.bounds.size.height - stroke_box.corner[GSK_CORNER_BOTTOM_RIGHT].height);
|
||||
gsk_path_builder_conic_to (builder,
|
||||
stroke_box.bounds.origin.x + stroke_box.bounds.size.width,
|
||||
stroke_box.bounds.origin.y + stroke_box.bounds.size.height,
|
||||
stroke_box.bounds.origin.x + stroke_box.bounds.size.width - stroke_box.corner[GSK_CORNER_BOTTOM_RIGHT].width / 2,
|
||||
stroke_box.bounds.origin.y + stroke_box.bounds.size.height - stroke_box.corner[GSK_CORNER_BOTTOM_RIGHT].height / 2,
|
||||
weight);
|
||||
}
|
||||
else if (i == 2)
|
||||
_gtk_rounded_box_path_bottom (border_box, &padding_box, cr);
|
||||
{
|
||||
/* bottom */
|
||||
gsk_path_builder_move_to (builder,
|
||||
stroke_box.bounds.origin.x + stroke_box.bounds.size.width - stroke_box.corner[GSK_CORNER_BOTTOM_RIGHT].width / 2,
|
||||
stroke_box.bounds.origin.y + stroke_box.bounds.size.height - stroke_box.corner[GSK_CORNER_BOTTOM_RIGHT].height / 2);
|
||||
gsk_path_builder_conic_to (builder,
|
||||
stroke_box.bounds.origin.x + stroke_box.bounds.size.width,
|
||||
stroke_box.bounds.origin.y + stroke_box.bounds.size.height,
|
||||
stroke_box.bounds.origin.x + stroke_box.bounds.size.width - stroke_box.corner[GSK_CORNER_BOTTOM_RIGHT].width,
|
||||
stroke_box.bounds.origin.y + stroke_box.bounds.size.height,
|
||||
weight);
|
||||
gsk_path_builder_line_to (builder,
|
||||
stroke_box.bounds.origin.x + stroke_box.corner[GSK_CORNER_BOTTOM_LEFT].width,
|
||||
stroke_box.bounds.origin.y + stroke_box.bounds.size.height);
|
||||
gsk_path_builder_conic_to (builder,
|
||||
stroke_box.bounds.origin.x,
|
||||
stroke_box.bounds.origin.y + stroke_box.bounds.size.height,
|
||||
stroke_box.bounds.origin.x + stroke_box.corner[GSK_CORNER_BOTTOM_LEFT].width / 2,
|
||||
stroke_box.bounds.origin.y + stroke_box.bounds.size.height - stroke_box.corner[GSK_CORNER_BOTTOM_LEFT].height / 2,
|
||||
weight);
|
||||
}
|
||||
else if (i == 3)
|
||||
_gtk_rounded_box_path_left (border_box, &padding_box, cr);
|
||||
cairo_clip (cr);
|
||||
{
|
||||
/* left */
|
||||
gsk_path_builder_move_to (builder,
|
||||
stroke_box.bounds.origin.x + stroke_box.corner[GSK_CORNER_BOTTOM_LEFT].width / 2,
|
||||
stroke_box.bounds.origin.y + stroke_box.bounds.size.height - stroke_box.corner[GSK_CORNER_BOTTOM_LEFT].height / 2);
|
||||
gsk_path_builder_conic_to (builder,
|
||||
stroke_box.bounds.origin.x,
|
||||
stroke_box.bounds.origin.y + stroke_box.bounds.size.height,
|
||||
stroke_box.bounds.origin.x,
|
||||
stroke_box.bounds.origin.y + stroke_box.bounds.size.height - stroke_box.corner[GSK_CORNER_BOTTOM_LEFT].height,
|
||||
weight);
|
||||
gsk_path_builder_line_to (builder,
|
||||
stroke_box.bounds.origin.x,
|
||||
stroke_box.bounds.origin.y + stroke_box.corner[GSK_CORNER_TOP_LEFT].height);
|
||||
gsk_path_builder_conic_to (builder,
|
||||
stroke_box.bounds.origin.x,
|
||||
stroke_box.bounds.origin.y,
|
||||
stroke_box.bounds.origin.x + stroke_box.corner[GSK_CORNER_TOP_LEFT].width,
|
||||
stroke_box.bounds.origin.y,
|
||||
weight);
|
||||
}
|
||||
|
||||
_gtk_rounded_box_path_side (&stroke_box, cr, i);
|
||||
path = gsk_path_builder_free_to_path (builder);
|
||||
stroke = create_stroke_style (border_width[i],
|
||||
stroke_style,
|
||||
_gtk_rounded_box_guess_length (&stroke_box, i));
|
||||
gtk_snapshot_push_stroke (snapshot, path, stroke);
|
||||
gsk_stroke_free (stroke);
|
||||
gsk_path_unref (path);
|
||||
|
||||
gdk_cairo_set_source_rgba (cr, &colors[i]);
|
||||
set_stroke_style (cr,
|
||||
border_width[i],
|
||||
stroke_style,
|
||||
_gtk_rounded_box_guess_length (&stroke_box, i));
|
||||
cairo_stroke (cr);
|
||||
gtk_snapshot_append_border (snapshot, border_box, border_width, colors);
|
||||
|
||||
cairo_restore (cr);
|
||||
gtk_snapshot_pop (snapshot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
snapshot_frame_stroke (GtkSnapshot *snapshot,
|
||||
const GskRoundedRect *outline,
|
||||
const float border_width[4],
|
||||
GdkRGBA colors[4],
|
||||
guint hidden_side,
|
||||
GtkBorderStyle stroke_style)
|
||||
{
|
||||
double double_width[4] = { border_width[0], border_width[1], border_width[2], border_width[3] };
|
||||
cairo_t *cr;
|
||||
|
||||
cr = gtk_snapshot_append_cairo (snapshot,
|
||||
&outline->bounds);
|
||||
render_frame_stroke (cr, outline, double_width, colors, hidden_side, stroke_style);
|
||||
cairo_destroy (cr);
|
||||
}
|
||||
|
||||
static void
|
||||
color_shade (const GdkRGBA *color,
|
||||
double factor,
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright © 2020 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "gsk/gskcurveprivate.h"
|
||||
|
||||
static gboolean
|
||||
measure_segment (const graphene_point_t *from,
|
||||
const graphene_point_t *to,
|
||||
float from_t,
|
||||
float to_t,
|
||||
gpointer data)
|
||||
{
|
||||
float *length = data;
|
||||
|
||||
*length += graphene_point_distance (from, to, NULL, NULL);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static float
|
||||
measure_length (const GskCurve *curve)
|
||||
{
|
||||
float result = 0;
|
||||
|
||||
gsk_curve_decompose (curve, 0.5, measure_segment, &result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* This is a pretty nasty conic that makes it obvious that split()
|
||||
* does not respect the progress values, so split() twice with
|
||||
* scaled factor won't work.
|
||||
*/
|
||||
static void
|
||||
test_conic_segment (void)
|
||||
{
|
||||
GskCurve c, s, e, m;
|
||||
graphene_point_t pts[4] = {
|
||||
GRAPHENE_POINT_INIT (-1856.131591796875, 46.217609405517578125),
|
||||
GRAPHENE_POINT_INIT (-1555.9866943359375, 966.0810546875),
|
||||
GRAPHENE_POINT_INIT (98.94945526123046875, 0),
|
||||
GRAPHENE_POINT_INIT (-1471.33154296875, 526.701171875)
|
||||
};
|
||||
float start = 0.02222645096480846405029296875;
|
||||
float end = 0.982032716274261474609375;
|
||||
|
||||
gsk_curve_init (&c, gsk_pathop_encode (GSK_PATH_CONIC, pts));
|
||||
|
||||
gsk_curve_split (&c, start, &s, NULL);
|
||||
gsk_curve_segment (&c, start, end, &m);
|
||||
gsk_curve_split (&c, end, NULL, &e);
|
||||
|
||||
g_assert_cmpfloat_with_epsilon (measure_length (&c), measure_length (&s) + measure_length (&m) + measure_length (&e), 0.03125);
|
||||
}
|
||||
|
||||
static void
|
||||
test_curve_tangents (void)
|
||||
{
|
||||
GskCurve c;
|
||||
graphene_point_t p[4];
|
||||
graphene_vec2_t t;
|
||||
|
||||
graphene_point_init (&p[0], 0, 0);
|
||||
graphene_point_init (&p[1], 100, 0);
|
||||
gsk_curve_init (&c, gsk_pathop_encode (GSK_PATH_LINE, p));
|
||||
|
||||
gsk_curve_get_start_tangent (&c, &t);
|
||||
g_assert_true (graphene_vec2_near (&t, graphene_vec2_x_axis (), 0.0001));
|
||||
gsk_curve_get_end_tangent (&c, &t);
|
||||
g_assert_true (graphene_vec2_near (&t, graphene_vec2_x_axis (), 0.0001));
|
||||
|
||||
|
||||
graphene_point_init (&p[0], 0, 0);
|
||||
graphene_point_init (&p[1], 0, 100);
|
||||
gsk_curve_init (&c, gsk_pathop_encode (GSK_PATH_LINE, p));
|
||||
|
||||
gsk_curve_get_start_tangent (&c, &t);
|
||||
g_assert_true (graphene_vec2_near (&t, graphene_vec2_y_axis (), 0.0001));
|
||||
gsk_curve_get_end_tangent (&c, &t);
|
||||
g_assert_true (graphene_vec2_near (&t, graphene_vec2_y_axis (), 0.0001));
|
||||
|
||||
graphene_point_init (&p[0], 0, 0);
|
||||
graphene_point_init (&p[1], 100, 0);
|
||||
p[2] = GRAPHENE_POINT_INIT (g_test_rand_double_range (0, 20), 0);
|
||||
graphene_point_init (&p[3], 100, 100);
|
||||
gsk_curve_init (&c, gsk_pathop_encode (GSK_PATH_CONIC, p));
|
||||
|
||||
gsk_curve_get_start_tangent (&c, &t);
|
||||
g_assert_true (graphene_vec2_near (&t, graphene_vec2_x_axis (), 0.0001));
|
||||
gsk_curve_get_end_tangent (&c, &t);
|
||||
g_assert_true (graphene_vec2_near (&t, graphene_vec2_y_axis (), 0.0001));
|
||||
|
||||
graphene_point_init (&p[0], 0, 0);
|
||||
graphene_point_init (&p[1], 50, 0);
|
||||
graphene_point_init (&p[2], 100, 50);
|
||||
graphene_point_init (&p[3], 100, 100);
|
||||
gsk_curve_init (&c, gsk_pathop_encode (GSK_PATH_CURVE, p));
|
||||
|
||||
gsk_curve_get_start_tangent (&c, &t);
|
||||
g_assert_true (graphene_vec2_near (&t, graphene_vec2_x_axis (), 0.0001));
|
||||
gsk_curve_get_end_tangent (&c, &t);
|
||||
g_assert_true (graphene_vec2_near (&t, graphene_vec2_y_axis (), 0.0001));
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc,
|
||||
char *argv[])
|
||||
{
|
||||
gtk_test_init (&argc, &argv, NULL);
|
||||
|
||||
g_test_add_func ("/curve/special/conic-segment", test_conic_segment);
|
||||
g_test_add_func ("/curve/special/tangents", test_curve_tangents);
|
||||
|
||||
return g_test_run ();
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
#include <gtk/gtk.h>
|
||||
#include "gsk/gskcurveprivate.h"
|
||||
|
||||
static void
|
||||
init_random_point (graphene_point_t *p)
|
||||
{
|
||||
p->x = g_test_rand_double_range (0, 1000);
|
||||
p->y = g_test_rand_double_range (0, 1000);
|
||||
}
|
||||
|
||||
static float
|
||||
random_weight (void)
|
||||
{
|
||||
if (g_test_rand_bit ())
|
||||
return g_test_rand_double_range (0, 100);
|
||||
else
|
||||
return 1.0 / g_test_rand_double_range (1, 100);
|
||||
}
|
||||
|
||||
static void
|
||||
init_random_curve (GskCurve *curve)
|
||||
{
|
||||
switch (g_test_rand_int_range (GSK_PATH_LINE, GSK_PATH_CONIC + 1))
|
||||
{
|
||||
case GSK_PATH_LINE:
|
||||
{
|
||||
graphene_point_t p[2];
|
||||
|
||||
init_random_point (&p[0]);
|
||||
init_random_point (&p[1]);
|
||||
gsk_curve_init (curve, gsk_pathop_encode (GSK_PATH_LINE, p));
|
||||
}
|
||||
break;
|
||||
|
||||
case GSK_PATH_CURVE:
|
||||
{
|
||||
graphene_point_t p[4];
|
||||
|
||||
init_random_point (&p[0]);
|
||||
init_random_point (&p[1]);
|
||||
init_random_point (&p[2]);
|
||||
init_random_point (&p[3]);
|
||||
gsk_curve_init (curve, gsk_pathop_encode (GSK_PATH_CURVE, p));
|
||||
}
|
||||
break;
|
||||
|
||||
case GSK_PATH_CONIC:
|
||||
{
|
||||
graphene_point_t p[4];
|
||||
|
||||
init_random_point (&p[0]);
|
||||
init_random_point (&p[1]);
|
||||
p[2] = GRAPHENE_POINT_INIT (random_weight(), 0);
|
||||
init_random_point (&p[3]);
|
||||
gsk_curve_init (curve, gsk_pathop_encode (GSK_PATH_CONIC, p));
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_curve_tangents (void)
|
||||
{
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
GskCurve c;
|
||||
graphene_vec2_t vec, exact;
|
||||
|
||||
init_random_curve (&c);
|
||||
|
||||
gsk_curve_get_tangent (&c, 0, &vec);
|
||||
g_assert_cmpfloat_with_epsilon (graphene_vec2_length (&vec), 1.0f, 0.00001);
|
||||
gsk_curve_get_start_tangent (&c, &exact);
|
||||
g_assert_cmpfloat_with_epsilon (graphene_vec2_length (&exact), 1.0f, 0.00001);
|
||||
g_assert_true (graphene_vec2_near (&vec, &exact, 0.05));
|
||||
|
||||
gsk_curve_get_tangent (&c, 1, &vec);
|
||||
g_assert_cmpfloat_with_epsilon (graphene_vec2_length (&vec), 1.0f, 0.00001);
|
||||
gsk_curve_get_end_tangent (&c, &exact);
|
||||
g_assert_cmpfloat_with_epsilon (graphene_vec2_length (&exact), 1.0f, 0.00001);
|
||||
g_assert_true (graphene_vec2_near (&vec, &exact, 0.05));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_curve_points (void)
|
||||
{
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
GskCurve c;
|
||||
graphene_point_t p;
|
||||
|
||||
init_random_curve (&c);
|
||||
|
||||
/* We can assert equality here because evaluating the polynomials with 0
|
||||
* has no effect on accuracy.
|
||||
*/
|
||||
gsk_curve_get_point (&c, 0, &p);
|
||||
g_assert_true (graphene_point_equal (gsk_curve_get_start_point (&c), &p));
|
||||
/* But here we evaluate the polynomials with 1 which gives the highest possible
|
||||
* accuracy error. So we'll just be generous here.
|
||||
*/
|
||||
gsk_curve_get_point (&c, 1, &p);
|
||||
g_assert_true (graphene_point_near (gsk_curve_get_end_point (&c), &p, 0.05));
|
||||
}
|
||||
}
|
||||
|
||||
/* at this point the subdivision stops and the decomposer
|
||||
* violates tolerance rules
|
||||
*/
|
||||
#define MIN_PROGRESS (1/1024.f)
|
||||
|
||||
typedef struct
|
||||
{
|
||||
graphene_point_t p;
|
||||
float t;
|
||||
} PointOnLine;
|
||||
|
||||
static gboolean
|
||||
add_line_to_array (const graphene_point_t *from,
|
||||
const graphene_point_t *to,
|
||||
float from_progress,
|
||||
float to_progress,
|
||||
gpointer user_data)
|
||||
{
|
||||
GArray *array = user_data;
|
||||
PointOnLine *last = &g_array_index (array, PointOnLine, array->len - 1);
|
||||
|
||||
g_assert (array->len > 0);
|
||||
g_assert_cmpfloat (from_progress, >=, 0.0f);
|
||||
g_assert_cmpfloat (from_progress, <, to_progress);
|
||||
g_assert_cmpfloat (to_progress, <=, 1.0f);
|
||||
|
||||
g_assert_true (graphene_point_equal (&last->p, from));
|
||||
g_assert_cmpfloat (last->t, ==, from_progress);
|
||||
|
||||
g_array_append_vals (array, (PointOnLine[1]) { { *to, to_progress } }, 1);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
test_curve_decompose (void)
|
||||
{
|
||||
static const float tolerance = 0.5;
|
||||
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
GArray *array;
|
||||
GskCurve c;
|
||||
|
||||
init_random_curve (&c);
|
||||
|
||||
array = g_array_new (FALSE, FALSE, sizeof (PointOnLine));
|
||||
g_array_append_vals (array, (PointOnLine[1]) { { *gsk_curve_get_start_point (&c), 0.f } }, 1);
|
||||
|
||||
g_assert_true (gsk_curve_decompose (&c, tolerance, add_line_to_array, array));
|
||||
|
||||
g_assert_cmpint (array->len, >=, 2); /* We at least got a line to the end */
|
||||
g_assert_cmpfloat (g_array_index (array, PointOnLine, array->len - 1).t, ==, 1.0);
|
||||
|
||||
for (int j = 0; j < array->len; j++)
|
||||
{
|
||||
PointOnLine *pol = &g_array_index (array, PointOnLine, j);
|
||||
graphene_point_t p;
|
||||
|
||||
/* Check that the points we got are actually on the line */
|
||||
gsk_curve_get_point (&c, pol->t, &p);
|
||||
g_assert_true (graphene_point_near (&pol->p, &p, 0.05));
|
||||
|
||||
/* Check that the mid point is not further than the tolerance */
|
||||
if (j > 0)
|
||||
{
|
||||
PointOnLine *last = &g_array_index (array, PointOnLine, j - 1);
|
||||
graphene_point_t mid;
|
||||
|
||||
if (pol->t - last->t > MIN_PROGRESS)
|
||||
{
|
||||
graphene_point_interpolate (&last->p, &pol->p, 0.5, &mid);
|
||||
gsk_curve_get_point (&c, (pol->t + last->t) / 2, &p);
|
||||
/* The decomposer does this cheaper Manhattan distance test,
|
||||
* so graphene_point_near() does not work */
|
||||
g_assert_cmpfloat (fabs (mid.x - p.x), <=, tolerance);
|
||||
g_assert_cmpfloat (fabs (mid.y - p.y), <=, tolerance);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
g_test_init (&argc, &argv, NULL);
|
||||
|
||||
g_test_add_func ("/curve/points", test_curve_points);
|
||||
g_test_add_func ("/curve/tangents", test_curve_tangents);
|
||||
g_test_add_func ("/curve/decompose", test_curve_decompose);
|
||||
|
||||
return g_test_run ();
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
* Copyright © 2020 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "gsk/gskpathdashprivate.h"
|
||||
|
||||
static gboolean
|
||||
build_path (GskPathOperation op,
|
||||
const graphene_point_t *pts,
|
||||
gsize n_pts,
|
||||
float weight,
|
||||
gpointer user_data)
|
||||
{
|
||||
GskPathBuilder *builder = user_data;
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case GSK_PATH_MOVE:
|
||||
gsk_path_builder_move_to (builder, pts[0].x, pts[0].y);
|
||||
break;
|
||||
|
||||
case GSK_PATH_CLOSE:
|
||||
gsk_path_builder_close (builder);
|
||||
break;
|
||||
|
||||
case GSK_PATH_LINE:
|
||||
gsk_path_builder_line_to (builder, pts[1].x, pts[1].y);
|
||||
break;
|
||||
|
||||
case GSK_PATH_CURVE:
|
||||
gsk_path_builder_curve_to (builder, pts[1].x, pts[1].y, pts[2].x, pts[2].y, pts[3].x, pts[3].y);
|
||||
break;
|
||||
|
||||
case GSK_PATH_CONIC:
|
||||
gsk_path_builder_conic_to (builder, pts[1].x, pts[1].y, pts[2].x, pts[2].y, weight);
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
break;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
test_simple (void)
|
||||
{
|
||||
const struct {
|
||||
const char *test;
|
||||
float dash[4];
|
||||
gsize n_dash;
|
||||
float dash_offset;
|
||||
const char *result;
|
||||
} tests[] = {
|
||||
/* a line with a dash of a quarter its size, very simple test */
|
||||
{
|
||||
"M 0 0 L 20 0",
|
||||
{ 5, }, 1, 0.f,
|
||||
"M 0 0 L 5 0 M 10 0 L 15 0",
|
||||
},
|
||||
/* a square with a dash of half its size, another simple test */
|
||||
{
|
||||
"M 0 0 h 10 v 10 h -10 z",
|
||||
{ 5, }, 1, 0.f,
|
||||
"M 10 0 L 10 5 M 10 10 L 5 10 M 0 10 L 0 5 M 0 0 L 5 0"
|
||||
},
|
||||
/* a square smaller than the dash, make sure it closes */
|
||||
{
|
||||
"M 0 0 h 10 v 10 h -10 z",
|
||||
{ 50, }, 1, 0.f,
|
||||
"M 0 0 L 10 0 L 10 10 L 0 10 Z"
|
||||
},
|
||||
/* a square exactly the dash's size, make sure it still closes */
|
||||
{
|
||||
"M 0 0 h 10 v 10 h -10 z",
|
||||
{ 40, }, 1, 0.f,
|
||||
"M 0 0 L 10 0 L 10 10 L 0 10 Z"
|
||||
},
|
||||
/* a dash with offset */
|
||||
{
|
||||
"M 0 0 h 10 v 10 h -10 z",
|
||||
{ 5, }, 1, 2.5f,
|
||||
"M 7.5 0 L 10 0 L 10 2.5 M 10 7.5 L 10 10 L 7.5 10 M 2.5 10 L 0 10 L 0 7.5 M 0 2.5 L 0 0 L 2.5 0"
|
||||
},
|
||||
/* a dash with offset, but this time the rect isn't closed */
|
||||
{
|
||||
"M 0 0 L 10 0 L 10 10 L 0 10 L 0 0",
|
||||
{ 5, }, 1, 2.5f,
|
||||
"M 0 0 L 2.5 0 M 7.5 0 L 10 0 L 10 2.5 M 10 7.5 L 10 10 L 7.5 10 M 2.5 10 L 0 10 L 0 7.5 M 0 2.5 L 0 0"
|
||||
},
|
||||
/* a dash with offset into an empty dash */
|
||||
{
|
||||
"M 0 0 h 10 v 10 h -10 z",
|
||||
{ 5, }, 1, 7.5f,
|
||||
"M 2.5 0 L 7.5 0 M 10 2.5 L 10 7.5 M 7.5 10 L 2.5 10 M 0 7.5 L 0 2.5"
|
||||
},
|
||||
/* a dash with offset where the whole rectangle fits into one element - make sure it closes */
|
||||
{
|
||||
"M 0 0 h 10 v 10 h -10 z",
|
||||
{ 1, 1, 100 }, 3, 3.f,
|
||||
"M 0 0 L 10 0 L 10 10 L 0 10 Z"
|
||||
},
|
||||
/* a dash with 0-length elements, aka dotting */
|
||||
{
|
||||
"M 0 0 h 10 v 10 h -10 z",
|
||||
{ 0, 5 }, 2, 0.f,
|
||||
"M 5 0 M 10 0 M 10 5 M 10 10 M 5 10 M 0 10 M 0 5 M 0 0"
|
||||
},
|
||||
/* a dash of a circle */
|
||||
{
|
||||
"M 10 5 O 10 10, 5 10, 0.70710676908493042 O 0 10, 0 5, 0.70710676908493042 O 0 0, 5 0, 0.70710676908493042 O 10 0, 10 5, 0.70710676908493042 Z",
|
||||
{ 32, }, 1, 0.f,
|
||||
"M 10 5 O 10 10, 5 10, 0.70710676908493042 O 0 10, 0 5, 0.70710676908493042 O 0 0, 5 0, 0.70710676908493042 O 10 0, 10 5, 0.70710676908493042 Z",
|
||||
},
|
||||
/* a dash with offset and 2 contours */
|
||||
{
|
||||
"M 10 10 h 10 v 10 h -10 z M 20 20 h 10 v 10 h -10 z",
|
||||
{ 5, }, 1, 2.5f,
|
||||
"M 17.5 10 L 20 10 L 20 12.5 M 20 17.5 L 20 20 L 17.5 20 M 12.5 20 L 10 20 L 10 17.5 M 10 12.5 L 10 10 L 12.5 10 "
|
||||
"M 27.5 20 L 30 20 L 30 22.5 M 30 27.5 L 30 30 L 27.5 30 M 22.5 30 L 20 30 L 20 27.5 M 20 22.5 L 20 20 L 22.5 20"
|
||||
},
|
||||
};
|
||||
GskPath *path, *result;
|
||||
GskPathBuilder *builder;
|
||||
GskStroke *stroke;
|
||||
char *s;
|
||||
|
||||
for (gsize i = 0; i < G_N_ELEMENTS(tests); i++)
|
||||
{
|
||||
stroke = gsk_stroke_new (1);
|
||||
gsk_stroke_set_dash (stroke, tests[i].dash, tests[i].n_dash);
|
||||
gsk_stroke_set_dash_offset (stroke, tests[i].dash_offset);
|
||||
|
||||
path = gsk_path_parse (tests[i].test);
|
||||
g_assert_nonnull (path);
|
||||
s = gsk_path_to_string (path);
|
||||
g_assert_cmpstr (s, ==, tests[i].test);
|
||||
g_free (s);
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
gsk_path_dash (path, stroke, 0.5, build_path, builder);
|
||||
result = gsk_path_builder_free_to_path (builder);
|
||||
|
||||
s = gsk_path_to_string (result);
|
||||
g_assert_cmpstr (s, ==, tests[i].result);
|
||||
g_free (s);
|
||||
|
||||
gsk_path_unref (result);
|
||||
gsk_stroke_free (stroke);
|
||||
gsk_path_unref (path);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc,
|
||||
char *argv[])
|
||||
{
|
||||
gtk_test_init (&argc, &argv, NULL);
|
||||
|
||||
g_test_add_func ("/dash/simple", test_simple);
|
||||
|
||||
return g_test_run ();
|
||||
}
|
||||
@@ -188,6 +188,9 @@ foreach test : node_parser_tests
|
||||
endforeach
|
||||
|
||||
tests = [
|
||||
['curve', ['../../gsk/gskcurve.c'], ['-DGTK_COMPILATION']],
|
||||
['curve-special-cases', ['../../gsk/gskcurve.c'], ['-DGTK_COMPILATION']],
|
||||
['dash'],
|
||||
['path'],
|
||||
['path-special-cases'],
|
||||
['rounded-rect'],
|
||||
|
||||
+51
-1
@@ -178,6 +178,15 @@ add_shape_contour (GskPathBuilder *builder)
|
||||
}
|
||||
}
|
||||
|
||||
static float
|
||||
random_weight (void)
|
||||
{
|
||||
if (g_test_rand_bit ())
|
||||
return g_test_rand_double_range (0, 100);
|
||||
else
|
||||
return 1.0 / g_test_rand_double_range (1, 100);
|
||||
}
|
||||
|
||||
static void
|
||||
add_standard_contour (GskPathBuilder *builder)
|
||||
{
|
||||
@@ -200,7 +209,7 @@ add_standard_contour (GskPathBuilder *builder)
|
||||
n = g_test_rand_int_range (1, 20);
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
switch (g_test_rand_int_range (0, 4))
|
||||
switch (g_test_rand_int_range (0, 6))
|
||||
{
|
||||
case 0:
|
||||
gsk_path_builder_line_to (builder,
|
||||
@@ -234,6 +243,24 @@ add_standard_contour (GskPathBuilder *builder)
|
||||
g_test_rand_double_range (-1000, 1000));
|
||||
break;
|
||||
|
||||
case 4:
|
||||
gsk_path_builder_conic_to (builder,
|
||||
g_test_rand_double_range (-1000, 1000),
|
||||
g_test_rand_double_range (-1000, 1000),
|
||||
g_test_rand_double_range (-1000, 1000),
|
||||
g_test_rand_double_range (-1000, 1000),
|
||||
random_weight ());
|
||||
break;
|
||||
|
||||
case 5:
|
||||
gsk_path_builder_rel_conic_to (builder,
|
||||
g_test_rand_double_range (-1000, 1000),
|
||||
g_test_rand_double_range (-1000, 1000),
|
||||
g_test_rand_double_range (-1000, 1000),
|
||||
g_test_rand_double_range (-1000, 1000),
|
||||
random_weight ());
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
break;
|
||||
@@ -324,6 +351,16 @@ path_operation_print (const PathOperation *p,
|
||||
_g_string_append_point (string, &p->pts[3]);
|
||||
break;
|
||||
|
||||
case GSK_PATH_CONIC:
|
||||
/* This is not valid SVG */
|
||||
g_string_append (string, " O ");
|
||||
_g_string_append_point (string, &p->pts[1]);
|
||||
g_string_append (string, ", ");
|
||||
_g_string_append_point (string, &p->pts[2]);
|
||||
g_string_append (string, ", ");
|
||||
_g_string_append_double (string, p->weight);
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
return;
|
||||
@@ -354,6 +391,11 @@ path_operation_equal (const PathOperation *p1,
|
||||
&& graphene_point_near (&p1->pts[2], &p2->pts[2], epsilon)
|
||||
&& graphene_point_near (&p1->pts[3], &p2->pts[3], epsilon);
|
||||
|
||||
case GSK_PATH_CONIC:
|
||||
return graphene_point_near (&p1->pts[1], &p2->pts[1], epsilon)
|
||||
&& graphene_point_near (&p1->pts[2], &p2->pts[2], epsilon)
|
||||
&& G_APPROX_VALUE (p1->weight, p2->weight, epsilon);
|
||||
|
||||
default:
|
||||
g_return_val_if_reached (FALSE);
|
||||
}
|
||||
@@ -363,6 +405,7 @@ static gboolean
|
||||
collect_path_operation_cb (GskPathOperation op,
|
||||
const graphene_point_t *pts,
|
||||
gsize n_pts,
|
||||
float weight,
|
||||
gpointer user_data)
|
||||
{
|
||||
g_array_append_vals (user_data,
|
||||
@@ -377,6 +420,7 @@ collect_path_operation_cb (GskPathOperation op,
|
||||
GRAPHENE_POINT_INIT(n_pts > 3 ? pts[3].x : 0,
|
||||
n_pts > 3 ? pts[3].y : 0)
|
||||
},
|
||||
weight
|
||||
},
|
||||
1);
|
||||
return TRUE;
|
||||
@@ -943,6 +987,7 @@ static gboolean
|
||||
rotate_path_cb (GskPathOperation op,
|
||||
const graphene_point_t *pts,
|
||||
gsize n_pts,
|
||||
float weight,
|
||||
gpointer user_data)
|
||||
{
|
||||
GskPathBuilder **builders = user_data;
|
||||
@@ -969,6 +1014,11 @@ rotate_path_cb (GskPathOperation op,
|
||||
gsk_path_builder_curve_to (builders[1], pts[1].y, -pts[1].x, pts[2].y, -pts[2].x, pts[3].y, -pts[3].x);
|
||||
break;
|
||||
|
||||
case GSK_PATH_CONIC:
|
||||
gsk_path_builder_conic_to (builders[0], pts[2].x, pts[2].y, pts[3].x, pts[3].y, weight);
|
||||
gsk_path_builder_conic_to (builders[1], pts[2].y, -pts[2].x, pts[3].y, -pts[3].x, weight);
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
return FALSE;
|
||||
|
||||
Reference in New Issue
Block a user