Add gsk_path_stroke
Implement stroking for paths. The current implementation does not try to handle short segments in the vicinity of sharp joins in any special way, so there can be some artifacts in that situation. There are special-case implementations for rectangle and circle contours, since in many cases the outlines of these contours just consist of two of the same shapes.
This commit is contained in:
213
gsk/gskcontour.c
213
gsk/gskcontour.c
@@ -94,6 +94,13 @@ struct _GskContourClass
|
||||
float (* get_distance) (const GskContour *contour,
|
||||
GskPathPoint *point,
|
||||
gpointer measure_data);
|
||||
void (* add_stroke) (const GskContour *contour,
|
||||
GskPathBuilder *builder,
|
||||
GskStroke *stroke);
|
||||
void (* offset) (const GskContour *contour,
|
||||
GskPathBuilder *builder,
|
||||
float distance,
|
||||
GskStroke *stroke);
|
||||
};
|
||||
|
||||
static gsize
|
||||
@@ -727,6 +734,23 @@ gsk_standard_contour_get_winding (const GskContour *contour,
|
||||
return winding;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_standard_contour_add_stroke (const GskContour *contour,
|
||||
GskPathBuilder *builder,
|
||||
GskStroke *stroke)
|
||||
{
|
||||
gsk_contour_default_add_stroke (contour, builder, stroke);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_standard_contour_offset (const GskContour *contour,
|
||||
GskPathBuilder *builder,
|
||||
float distance,
|
||||
GskStroke *stroke)
|
||||
{
|
||||
gsk_contour_default_offset (contour, builder, distance, stroke);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gsk_standard_contour_get_closest_point (const GskContour *contour,
|
||||
const graphene_point_t *point,
|
||||
@@ -922,6 +946,8 @@ static const GskContourClass GSK_STANDARD_CONTOUR_CLASS =
|
||||
gsk_standard_contour_add_segment,
|
||||
gsk_standard_contour_get_point,
|
||||
gsk_standard_contour_get_distance,
|
||||
gsk_standard_contour_add_stroke,
|
||||
gsk_standard_contour_offset,
|
||||
};
|
||||
|
||||
/* You must ensure the contour has enough size allocated,
|
||||
@@ -1385,6 +1411,68 @@ gsk_rect_contour_get_winding (const GskContour *contour,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
stroke_is_simple (GskStroke *stroke)
|
||||
{
|
||||
if (stroke->line_join == GSK_LINE_JOIN_ROUND ||
|
||||
stroke->line_join == GSK_LINE_JOIN_BEVEL)
|
||||
return FALSE;
|
||||
|
||||
if (stroke->miter_limit < 1.5)
|
||||
return FALSE;
|
||||
|
||||
if (stroke->dash_length != 0)
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_rect_contour_add_stroke (const GskContour *contour,
|
||||
GskPathBuilder *builder,
|
||||
GskStroke *stroke)
|
||||
{
|
||||
const GskRectContour *self = (const GskRectContour *) contour;
|
||||
|
||||
if (stroke_is_simple (stroke))
|
||||
{
|
||||
graphene_rect_t rect;
|
||||
|
||||
graphene_rect_init (&rect, self->x, self->y, self->width, self->height);
|
||||
|
||||
graphene_rect_inset (&rect, stroke->line_width / 2, stroke->line_width / 2);
|
||||
gsk_path_builder_add_rect (builder, &rect);
|
||||
|
||||
graphene_rect_inset (&rect, - stroke->line_width, - stroke->line_width);
|
||||
rect.origin.x += rect.size.width;
|
||||
rect.size.width = - rect.size.width;
|
||||
gsk_path_builder_add_rect (builder, &rect);
|
||||
}
|
||||
else
|
||||
gsk_contour_default_add_stroke (contour, builder, stroke);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_rect_contour_offset (const GskContour *contour,
|
||||
GskPathBuilder *builder,
|
||||
float distance,
|
||||
GskStroke *stroke)
|
||||
{
|
||||
const GskRectContour *self = (const GskRectContour *) contour;
|
||||
|
||||
if (stroke_is_simple (stroke))
|
||||
{
|
||||
graphene_rect_t rect;
|
||||
|
||||
graphene_rect_init (&rect, self->x, self->y, self->width, self->height);
|
||||
|
||||
graphene_rect_inset (&rect, distance, distance);
|
||||
gsk_path_builder_add_rect (builder, &rect);
|
||||
}
|
||||
else
|
||||
gsk_contour_default_offset (contour, builder, distance, stroke);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gsk_rect_contour_get_closest_point (const GskContour *contour,
|
||||
const graphene_point_t *point,
|
||||
@@ -1475,6 +1563,8 @@ static const GskContourClass GSK_RECT_CONTOUR_CLASS =
|
||||
gsk_rect_contour_add_segment,
|
||||
gsk_rect_contour_get_point,
|
||||
gsk_rect_contour_get_distance,
|
||||
gsk_rect_contour_add_stroke,
|
||||
gsk_rect_contour_offset,
|
||||
};
|
||||
|
||||
GskContour *
|
||||
@@ -1822,6 +1912,54 @@ gsk_circle_contour_get_winding (const GskContour *contour,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_circle_contour_add_stroke (const GskContour *contour,
|
||||
GskPathBuilder *builder,
|
||||
GskStroke *stroke)
|
||||
{
|
||||
const GskCircleContour *self = (const GskCircleContour *) contour;
|
||||
|
||||
if (stroke->dash_length == 0 &&
|
||||
fabs (self->start_angle - self->end_angle) >= 360)
|
||||
{
|
||||
GskContour *c;
|
||||
|
||||
c = gsk_circle_contour_new (&self->center, self->radius + stroke->line_width / 2,
|
||||
self->start_angle,
|
||||
self->end_angle);
|
||||
gsk_path_builder_add_contour (builder, c);
|
||||
|
||||
c = gsk_circle_contour_new (&self->center, self->radius - stroke->line_width / 2,
|
||||
self->end_angle,
|
||||
self->start_angle);
|
||||
gsk_path_builder_add_contour (builder, c);
|
||||
}
|
||||
else
|
||||
gsk_contour_default_add_stroke (contour, builder, stroke);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_circle_contour_offset (const GskContour *contour,
|
||||
GskPathBuilder *builder,
|
||||
float distance,
|
||||
GskStroke *stroke)
|
||||
{
|
||||
const GskCircleContour *self = (const GskCircleContour *) contour;
|
||||
|
||||
if (stroke->dash_length == 0 &&
|
||||
fabs (self->start_angle - self->end_angle) >= 360)
|
||||
{
|
||||
GskContour *c;
|
||||
|
||||
c = gsk_circle_contour_new (&self->center, self->radius + distance,
|
||||
self->start_angle,
|
||||
self->end_angle);
|
||||
gsk_path_builder_add_contour (builder, c);
|
||||
}
|
||||
else
|
||||
gsk_contour_default_offset (contour, builder, distance, stroke);
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
float angle;
|
||||
@@ -1931,6 +2069,8 @@ static const GskContourClass GSK_CIRCLE_CONTOUR_CLASS =
|
||||
gsk_circle_contour_add_segment,
|
||||
gsk_circle_contour_get_point,
|
||||
gsk_circle_contour_get_distance,
|
||||
gsk_circle_contour_add_stroke,
|
||||
gsk_circle_contour_offset,
|
||||
};
|
||||
|
||||
GskContour *
|
||||
@@ -2327,6 +2467,60 @@ gsk_rounded_rect_contour_get_winding (const GskContour *contour,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_rounded_rect_contour_add_stroke (const GskContour *contour,
|
||||
GskPathBuilder *builder,
|
||||
GskStroke *stroke)
|
||||
{
|
||||
const GskRoundedRectContour *self = (const GskRoundedRectContour *) contour;
|
||||
|
||||
if (stroke_is_simple (stroke))
|
||||
{
|
||||
GskRoundedRect rect;
|
||||
GskContour *c;
|
||||
|
||||
gsk_rounded_rect_init_copy (&rect, &self->rect);
|
||||
|
||||
gsk_rounded_rect_shrink (&rect,
|
||||
stroke->line_width / 2,
|
||||
stroke->line_width / 2,
|
||||
stroke->line_width / 2,
|
||||
stroke->line_width / 2);
|
||||
c = gsk_rounded_rect_contour_new (&rect);
|
||||
gsk_path_builder_add_contour (builder, c);
|
||||
|
||||
gsk_rounded_rect_init_copy (&rect, &self->rect);
|
||||
gsk_rounded_rect_shrink (&rect,
|
||||
- stroke->line_width / 2,
|
||||
- stroke->line_width / 2,
|
||||
- stroke->line_width / 2,
|
||||
- stroke->line_width / 2);
|
||||
c = gsk_rounded_rect_contour_new (&rect);
|
||||
((GskRoundedRectContour *)c)->ccw = TRUE;
|
||||
gsk_path_builder_add_contour (builder, c);
|
||||
}
|
||||
else
|
||||
gsk_contour_default_add_stroke (contour, builder, stroke);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_rounded_rect_contour_offset (const GskContour *contour,
|
||||
GskPathBuilder *builder,
|
||||
float distance,
|
||||
GskStroke *stroke)
|
||||
{
|
||||
const GskRoundedRectContour *self = (const GskRoundedRectContour *) contour;
|
||||
GskRoundedRect rect;
|
||||
GskContour *c;
|
||||
|
||||
gsk_rounded_rect_init_copy (&rect, &self->rect);
|
||||
if (self->ccw)
|
||||
distance = - distance;
|
||||
gsk_rounded_rect_shrink (&rect, - distance, - distance, - distance, - distance);
|
||||
c = gsk_rounded_rect_contour_new (&rect);
|
||||
gsk_path_builder_add_contour (builder, c);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gsk_rounded_rect_contour_get_closest_point (const GskContour *contour,
|
||||
const graphene_point_t *point,
|
||||
@@ -2424,6 +2618,8 @@ static const GskContourClass GSK_ROUNDED_RECT_CONTOUR_CLASS =
|
||||
gsk_rounded_rect_contour_add_segment,
|
||||
gsk_rounded_rect_contour_get_point,
|
||||
gsk_rounded_rect_contour_get_distance,
|
||||
gsk_rounded_rect_contour_add_stroke,
|
||||
gsk_rounded_rect_contour_offset,
|
||||
};
|
||||
|
||||
GskContour *
|
||||
@@ -2604,6 +2800,23 @@ gsk_contour_get_distance (const GskContour *self,
|
||||
return self->klass->get_distance (self, point, measure_data);
|
||||
}
|
||||
|
||||
void
|
||||
gsk_contour_add_stroke (const GskContour *self,
|
||||
GskPathBuilder *builder,
|
||||
GskStroke *stroke)
|
||||
{
|
||||
self->klass->add_stroke (self, builder, stroke);
|
||||
}
|
||||
|
||||
void
|
||||
gsk_contour_offset (const GskContour *self,
|
||||
GskPathBuilder *builder,
|
||||
float distance,
|
||||
GskStroke *stroke)
|
||||
{
|
||||
self->klass->offset (self, builder, distance, stroke);
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
|
||||
/* vim:set foldmethod=marker expandtab: */
|
||||
|
||||
@@ -112,4 +112,22 @@ gboolean gsk_contour_dash (const GskContou
|
||||
GskPathForeachFunc func,
|
||||
gpointer user_data);
|
||||
|
||||
void gsk_contour_add_stroke (const GskContour *contour,
|
||||
GskPathBuilder *builder,
|
||||
GskStroke *stroke);
|
||||
|
||||
void gsk_contour_default_add_stroke (const GskContour *contour,
|
||||
GskPathBuilder *builder,
|
||||
GskStroke *stroke);
|
||||
|
||||
void gsk_contour_offset (const GskContour *contour,
|
||||
GskPathBuilder *builder,
|
||||
float distance,
|
||||
GskStroke *stroke);
|
||||
|
||||
void gsk_contour_default_offset (const GskContour *contour,
|
||||
GskPathBuilder *builder,
|
||||
float distance,
|
||||
GskStroke *stroke);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
* The [struct@Gsk.PathBuilder] structure is meant to help in this endeavor.
|
||||
*/
|
||||
|
||||
|
||||
struct _GskPath
|
||||
{
|
||||
/*< private >*/
|
||||
@@ -1324,3 +1325,58 @@ error:
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_stroke:
|
||||
* @self: a `GskPath`
|
||||
* @stroke: stroke parameters
|
||||
*
|
||||
* Create a new path that follows the outline of the area
|
||||
* that would be affected by stroking along @self with
|
||||
* the given stroke parameters.
|
||||
*
|
||||
* Returns: a new `GskPath`
|
||||
*/
|
||||
GskPath *
|
||||
gsk_path_stroke (GskPath *self,
|
||||
GskStroke *stroke)
|
||||
{
|
||||
GskPathBuilder *builder;
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
|
||||
for (int i = 0; i < self->n_contours; i++)
|
||||
gsk_contour_add_stroke (gsk_path_get_contour (self, i), builder, stroke);
|
||||
|
||||
return gsk_path_builder_free_to_path (builder);
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_offset:
|
||||
* @self: a `GskPath`
|
||||
* @distance: the distance to offset the path by
|
||||
* @stroke: stroke parameters
|
||||
*
|
||||
* Create a new path that is offset from @self by @distance.
|
||||
*
|
||||
* The offset can be positive (to the right) or negative
|
||||
* (to the left). The line-join and miter-limit of the @stroke
|
||||
* influence how joins between the offset path segments
|
||||
* are made.
|
||||
*
|
||||
* Returns: a new `GskPath`
|
||||
*/
|
||||
GskPath *
|
||||
gsk_path_offset (GskPath *self,
|
||||
float distance,
|
||||
GskStroke *stroke)
|
||||
{
|
||||
GskPathBuilder *builder;
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
|
||||
for (int i = 0; i < self->n_contours; i++)
|
||||
gsk_contour_offset (gsk_path_get_contour (self, i), builder, distance, stroke);
|
||||
|
||||
return gsk_path_builder_free_to_path (builder);
|
||||
}
|
||||
|
||||
@@ -124,6 +124,15 @@ gboolean gsk_path_dash (GskPath
|
||||
GskPathForeachFunc func,
|
||||
gpointer user_data);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskPath * gsk_path_stroke (GskPath *self,
|
||||
GskStroke *stroke);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskPath * gsk_path_offset (GskPath *self,
|
||||
float distance,
|
||||
GskStroke *stroke);
|
||||
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GskPath, gsk_path_unref)
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "gskpathmeasure.h"
|
||||
#include "gskpathbuilder.h"
|
||||
|
||||
#include "gskpathbuilder.h"
|
||||
#include "gskpathpointprivate.h"
|
||||
|
||||
2634
gsk/gskpathstroke.c
Normal file
2634
gsk/gskpathstroke.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -30,6 +30,7 @@ gsk_public_sources = files([
|
||||
'gskpathbuilder.c',
|
||||
'gskpathmeasure.c',
|
||||
'gskpathpoint.c',
|
||||
'gskpathstroke.c',
|
||||
'gskrenderer.c',
|
||||
'gskrendernode.c',
|
||||
'gskrendernodeimpl.c',
|
||||
|
||||
Reference in New Issue
Block a user