summaryrefslogtreecommitdiff
path: root/gsk
diff options
context:
space:
mode:
authorMatthias Clasen <mclasen@redhat.com>2023-05-01 09:52:46 -0400
committerMatthias Clasen <mclasen@redhat.com>2023-05-01 13:45:20 -0400
commite453a989dfc211f7c5fbf1873b7d90324e7805cc (patch)
tree021c79439f1155ec1f1ad3d438610c6dc4b38af5 /gsk
parentf56328a6cc3cb1f2fb8a2692eedc6dd0db4f44c8 (diff)
downloadgtk+-e453a989dfc211f7c5fbf1873b7d90324e7805cc.tar.gz
Improve rounded rect intersection
Move the rounded rect/rect intersection code to gskroundedrect.c, make it more precise, and add tests for it. Update the callers in the GL renderer.
Diffstat (limited to 'gsk')
-rw-r--r--gsk/gl/gskglrenderjob.c111
-rw-r--r--gsk/gskroundedrect.c171
-rw-r--r--gsk/gskroundedrectprivate.h10
3 files changed, 198 insertions, 94 deletions
diff --git a/gsk/gl/gskglrenderjob.c b/gsk/gl/gskglrenderjob.c
index df19d98209..96637054d2 100644
--- a/gsk/gl/gskglrenderjob.c
+++ b/gsk/gl/gskglrenderjob.c
@@ -54,27 +54,6 @@
/* Make sure gradient stops fits in packed array_count */
G_STATIC_ASSERT ((MAX_GRADIENT_STOPS * 5) < (1 << GSK_GL_UNIFORM_ARRAY_BITS));
-#define rounded_rect_top_left(r) \
- (GRAPHENE_RECT_INIT(r->bounds.origin.x, \
- r->bounds.origin.y, \
- r->corner[0].width, r->corner[0].height))
-#define rounded_rect_top_right(r) \
- (GRAPHENE_RECT_INIT(r->bounds.origin.x + r->bounds.size.width - r->corner[1].width, \
- r->bounds.origin.y, \
- r->corner[1].width, r->corner[1].height))
-#define rounded_rect_bottom_right(r) \
- (GRAPHENE_RECT_INIT(r->bounds.origin.x + r->bounds.size.width - r->corner[2].width, \
- r->bounds.origin.y + r->bounds.size.height - r->corner[2].height, \
- r->corner[2].width, r->corner[2].height))
-#define rounded_rect_bottom_left(r) \
- (GRAPHENE_RECT_INIT(r->bounds.origin.x, \
- r->bounds.origin.y + r->bounds.size.height - r->corner[2].height, \
- r->corner[3].width, r->corner[3].height))
-#define rounded_rect_corner0(r) rounded_rect_top_left(r)
-#define rounded_rect_corner1(r) rounded_rect_top_right(r)
-#define rounded_rect_corner2(r) rounded_rect_bottom_right(r)
-#define rounded_rect_corner3(r) rounded_rect_bottom_left(r)
-#define rounded_rect_corner(r, i) (rounded_rect_corner##i(r))
#define ALPHA_IS_CLEAR(alpha) ((alpha) < ((float) 0x00ff / (float) 0xffff))
#define RGBA_IS_CLEAR(rgba) ALPHA_IS_CLEAR((rgba)->alpha)
@@ -429,70 +408,6 @@ rect_intersects (const graphene_rect_t *r1,
return TRUE;
}
-static inline gboolean
-rounded_rect_has_corner (const GskRoundedRect *r,
- guint i)
-{
- return r->corner[i].width > 0 && r->corner[i].height > 0;
-}
-
-/* Current clip is NOT rounded but new one is definitely! */
-static inline gboolean
-intersect_rounded_rectilinear (const graphene_rect_t *non_rounded,
- const GskRoundedRect *rounded,
- GskRoundedRect *result)
-{
- gboolean corners[4];
-
- /* Intersects with top left corner? */
- corners[0] = rounded_rect_has_corner (rounded, 0) &&
- rect_intersects (non_rounded,
- &rounded_rect_corner (rounded, 0));
- if (corners[0] && !rect_contains_rect (non_rounded,
- &rounded_rect_corner (rounded, 0)))
- return FALSE;
-
- /* top right ? */
- corners[1] = rounded_rect_has_corner (rounded, 1) &&
- rect_intersects (non_rounded,
- &rounded_rect_corner (rounded, 1));
- if (corners[1] && !rect_contains_rect (non_rounded,
- &rounded_rect_corner (rounded, 1)))
- return FALSE;
-
- /* bottom right ? */
- corners[2] = rounded_rect_has_corner (rounded, 2) &&
- rect_intersects (non_rounded,
- &rounded_rect_corner (rounded, 2));
- if (corners[2] && !rect_contains_rect (non_rounded,
- &rounded_rect_corner (rounded, 2)))
- return FALSE;
-
- /* bottom left ? */
- corners[3] = rounded_rect_has_corner (rounded, 3) &&
- rect_intersects (non_rounded,
- &rounded_rect_corner (rounded, 3));
- if (corners[3] && !rect_contains_rect (non_rounded,
- &rounded_rect_corner (rounded, 3)))
- return FALSE;
-
- /* We do intersect with at least one of the corners, but in such a way that the
- * intersection between the two clips can still be represented by a single rounded
- * rect in a trivial way. do that.
- */
- graphene_rect_intersection (non_rounded, &rounded->bounds, &result->bounds);
-
- for (guint i = 0; i < 4; i++)
- {
- if (corners[i])
- result->corner[i] = rounded->corner[i];
- else
- result->corner[i].width = result->corner[i].height = 0;
- }
-
- return TRUE;
-}
-
static inline void
init_projection_matrix (graphene_matrix_t *projection,
const graphene_rect_t *viewport)
@@ -1729,6 +1644,7 @@ gsk_gl_render_job_visit_clipped_child (GskGLRenderJob *job,
{
graphene_rect_t transformed_clip;
GskRoundedRect intersection;
+ GskRoundedRectIntersection result;
gsk_gl_render_job_transform_bounds (job, clip, &transformed_clip);
@@ -1742,10 +1658,17 @@ gsk_gl_render_job_visit_clipped_child (GskGLRenderJob *job,
gsk_gl_render_job_push_clip (job, &intersection);
gsk_gl_render_job_visit_node (job, child);
gsk_gl_render_job_pop_clip (job);
+ return;
}
- else if (intersect_rounded_rectilinear (&transformed_clip,
- &job->current_clip->rect,
- &intersection))
+
+ result = gsk_rounded_rect_intersect_with_rect (&job->current_clip->rect,
+ &transformed_clip,
+ &intersection);
+
+ if (result == GSK_INTERSECTION_EMPTY)
+ return;
+
+ if (result == GSK_INTERSECTION_NONEMPTY)
{
gsk_gl_render_job_push_clip (job, &intersection);
gsk_gl_render_job_visit_node (job, child);
@@ -1802,10 +1725,16 @@ gsk_gl_render_job_visit_rounded_clip_node (GskGLRenderJob *job,
if (job->current_clip->is_rectilinear)
{
GskRoundedRect intersected_clip;
+ GskRoundedRectIntersection result;
+
+ result = gsk_rounded_rect_intersect_with_rect (&transformed_clip,
+ &job->current_clip->rect.bounds,
+ &intersected_clip);
+
+ if (result == GSK_INTERSECTION_EMPTY)
+ return;
- if (intersect_rounded_rectilinear (&job->current_clip->rect.bounds,
- &transformed_clip,
- &intersected_clip))
+ if (result == GSK_INTERSECTION_NONEMPTY)
{
gsk_gl_render_job_push_clip (job, &intersected_clip);
gsk_gl_render_job_visit_node (job, child);
diff --git a/gsk/gskroundedrect.c b/gsk/gskroundedrect.c
index 42df2bf06c..7c0733fbe9 100644
--- a/gsk/gskroundedrect.c
+++ b/gsk/gskroundedrect.c
@@ -519,8 +519,174 @@ gsk_rounded_rect_intersects_rect (const GskRoundedRect *self,
gsk_rounded_rect_locate_point (self, &GRAPHENE_POINT_INIT (rect->origin.x, rect->origin.y + rect->size.height)) == OUTSIDE_TOP_RIGHT ||
gsk_rounded_rect_locate_point (self, &GRAPHENE_POINT_INIT (rect->origin.x + rect->size.width, rect->origin.y + rect->size.height)) == OUTSIDE_TOP_LEFT)
return FALSE;
+return TRUE;
+}
- return TRUE;
+#define rect_point0(r) ((r)->origin)
+#define rect_point1(r) (GRAPHENE_POINT_INIT ((r)->origin.x + (r)->size.width, (r)->origin.y))
+#define rect_point2(r) (GRAPHENE_POINT_INIT ((r)->origin.x + (r)->size.width, (r)->origin.y + (r)->size.height))
+#define rect_point3(r) (GRAPHENE_POINT_INIT ((r)->origin.x, (r)->origin.y + (r)->size.height))
+
+#define rounded_rect_corner0(r) \
+ (GRAPHENE_RECT_INIT((r)->bounds.origin.x, \
+ (r)->bounds.origin.y, \
+ (r)->corner[0].width, (r)->corner[0].height))
+#define rounded_rect_corner1(r) \
+ (GRAPHENE_RECT_INIT((r)->bounds.origin.x + (r)->bounds.size.width - (r)->corner[1].width, \
+ (r)->bounds.origin.y, \
+ (r)->corner[1].width, (r)->corner[1].height))
+#define rounded_rect_corner2(r) \
+ (GRAPHENE_RECT_INIT((r)->bounds.origin.x + (r)->bounds.size.width - (r)->corner[2].width, \
+ (r)->bounds.origin.y + (r)->bounds.size.height - (r)->corner[2].height, \
+ (r)->corner[2].width, (r)->corner[2].height))
+#define rounded_rect_corner3(r) \
+ (GRAPHENE_RECT_INIT((r)->bounds.origin.x, \
+ (r)->bounds.origin.y + (r)->bounds.size.height - (r)->corner[3].height, \
+ (r)->corner[3].width, (r)->corner[3].height))
+
+enum {
+ BELOW,
+ INNER,
+ ABOVE
+};
+
+static inline void
+classify_point (const graphene_point_t *p, const graphene_rect_t *rect, int *px, int *py)
+{
+ if (p->x <= rect->origin.x)
+ *px = BELOW;
+ else if (p->x >= rect->origin.x + rect->size.width)
+ *px = ABOVE;
+ else
+ *px = INNER;
+
+ if (p->y <= rect->origin.y)
+ *py = BELOW;
+ else if (p->y >= rect->origin.y + rect->size.height)
+ *py = ABOVE;
+ else
+ *py = INNER;
+}
+
+GskRoundedRectIntersection
+gsk_rounded_rect_intersect_with_rect (const GskRoundedRect *self,
+ const graphene_rect_t *rect,
+ GskRoundedRect *result)
+{
+ int px, py, qx, qy;
+
+ if (!graphene_rect_intersection (&self->bounds, rect, &result->bounds))
+ return GSK_INTERSECTION_EMPTY;
+
+ classify_point (&rect_point0 (rect), &rounded_rect_corner0 (self), &px, &py);
+
+ if (px == BELOW && py == BELOW)
+ {
+ classify_point (&rect_point2 (rect), &rounded_rect_corner0 (self), &qx, &qy);
+
+ if (qx == BELOW || qy == BELOW)
+ return GSK_INTERSECTION_EMPTY;
+ else if (qx == INNER && qy == INNER &&
+ gsk_rounded_rect_locate_point (self, &rect_point2 (rect)) != INSIDE)
+ return GSK_INTERSECTION_EMPTY;
+ else if (qx == ABOVE && qy == ABOVE)
+ result->corner[0] = self->corner[0];
+ else
+ return GSK_INTERSECTION_NOT_REPRESENTABLE;
+ }
+ else if ((px == INNER || py == INNER) &&
+ gsk_rounded_rect_locate_point (self, &rect_point0 (rect)) != INSIDE)
+ {
+ if (gsk_rounded_rect_locate_point (self, &rect_point2 (rect)) == OUTSIDE_TOP_LEFT)
+ return GSK_INTERSECTION_EMPTY;
+ else
+ return GSK_INTERSECTION_NOT_REPRESENTABLE;
+ }
+ else
+ result->corner[0].width = result->corner[0].height = 0;
+
+ classify_point (&rect_point1 (rect), &rounded_rect_corner1 (self), &px, &py);
+
+ if (px == ABOVE && py == BELOW)
+ {
+ classify_point (&rect_point3 (rect), &rounded_rect_corner1 (self), &qx, &qy);
+
+ if (qx == ABOVE || qy == BELOW)
+ return GSK_INTERSECTION_EMPTY;
+ else if (qx == INNER && qy == INNER &&
+ gsk_rounded_rect_locate_point (self, &rect_point3 (rect)) != INSIDE)
+ return GSK_INTERSECTION_EMPTY;
+ else if (qx == BELOW && qy == ABOVE)
+ result->corner[1] = self->corner[1];
+ else
+ return GSK_INTERSECTION_NOT_REPRESENTABLE;
+ }
+ else if ((px == INNER || py == INNER) &&
+ gsk_rounded_rect_locate_point (self, &rect_point1 (rect)) != INSIDE)
+ {
+ if (gsk_rounded_rect_locate_point (self, &rect_point3 (rect)) == OUTSIDE_TOP_RIGHT)
+ return GSK_INTERSECTION_EMPTY;
+ else
+ return GSK_INTERSECTION_NOT_REPRESENTABLE;
+ }
+ else
+ result->corner[1].width = result->corner[1].height = 0;
+
+ classify_point (&rect_point2 (rect), &rounded_rect_corner2 (self), &px, &py);
+
+ if (px == ABOVE && py == ABOVE)
+ {
+ classify_point (&rect_point0 (rect), &rounded_rect_corner2 (self), &qx, &qy);
+
+ if (qx == ABOVE || qy == ABOVE)
+ return GSK_INTERSECTION_EMPTY;
+ else if (qx == INNER && qy == INNER &&
+ gsk_rounded_rect_locate_point (self, &rect_point0 (rect)) != INSIDE)
+ return GSK_INTERSECTION_EMPTY;
+ else if (qx == BELOW && qy == BELOW)
+ result->corner[2] = self->corner[2];
+ else
+ return GSK_INTERSECTION_NOT_REPRESENTABLE;
+ }
+ else if ((px == INNER || py == INNER) &&
+ gsk_rounded_rect_locate_point (self, &rect_point2 (rect)) != INSIDE)
+ {
+ if (gsk_rounded_rect_locate_point (self, &rect_point0 (rect)) == OUTSIDE_BOTTOM_RIGHT)
+ return GSK_INTERSECTION_EMPTY;
+ else
+ return GSK_INTERSECTION_NOT_REPRESENTABLE;
+ }
+ else
+ result->corner[2].width = result->corner[2].height = 0;
+
+ classify_point (&rect_point3 (rect), &rounded_rect_corner3 (self), &px, &py);
+
+ if (px == BELOW && py == ABOVE)
+ {
+ classify_point (&rect_point1 (rect), &rounded_rect_corner3 (self), &qx, &qy);
+
+ if (qx == BELOW || qy == ABOVE)
+ return GSK_INTERSECTION_EMPTY;
+ else if (qx == INNER && qy == INNER &&
+ gsk_rounded_rect_locate_point (self, &rect_point1 (rect)) != INSIDE)
+ return GSK_INTERSECTION_EMPTY;
+ else if (qx == ABOVE && qy == BELOW)
+ result->corner[3] = self->corner[3];
+ else
+ return GSK_INTERSECTION_NOT_REPRESENTABLE;
+ }
+ else if ((px == INNER || py == INNER) &&
+ gsk_rounded_rect_locate_point (self, &rect_point3 (rect)) != INSIDE)
+ {
+ if (gsk_rounded_rect_locate_point (self, &rect_point1 (rect)) == OUTSIDE_BOTTOM_LEFT)
+ return GSK_INTERSECTION_EMPTY;
+ else
+ return GSK_INTERSECTION_NOT_REPRESENTABLE;
+ }
+ else
+ result->corner[3].width = result->corner[3].height = 0;
+
+ return GSK_INTERSECTION_NONEMPTY;
}
static void
@@ -565,7 +731,7 @@ gsk_rounded_rect_path (const GskRoundedRect *self,
self->corner[GSK_CORNER_TOP_LEFT].width,
self->corner[GSK_CORNER_TOP_LEFT].height,
G_PI, 3 * G_PI_2);
- _cairo_ellipsis (cr,
+ _cairo_ellipsis (cr,
self->bounds.origin.x + self->bounds.size.width - self->corner[GSK_CORNER_TOP_RIGHT].width,
self->bounds.origin.y + self->corner[GSK_CORNER_TOP_RIGHT].height,
self->corner[GSK_CORNER_TOP_RIGHT].width,
@@ -649,5 +815,4 @@ gsk_rounded_rect_to_string (const GskRoundedRect *self)
self->corner[2].height,
self->corner[3].width,
self->corner[3].height);
-
}
diff --git a/gsk/gskroundedrectprivate.h b/gsk/gskroundedrectprivate.h
index 5be1a4ab0c..b9afb754cc 100644
--- a/gsk/gskroundedrectprivate.h
+++ b/gsk/gskroundedrectprivate.h
@@ -34,6 +34,16 @@ gboolean gsk_rounded_rect_equal (gconstpointer
gconstpointer rect2) G_GNUC_PURE;
char * gsk_rounded_rect_to_string (const GskRoundedRect *self) G_GNUC_MALLOC;
+typedef enum {
+ GSK_INTERSECTION_EMPTY,
+ GSK_INTERSECTION_NONEMPTY,
+ GSK_INTERSECTION_NOT_REPRESENTABLE
+} GskRoundedRectIntersection;
+
+GskRoundedRectIntersection gsk_rounded_rect_intersect_with_rect (const GskRoundedRect *self,
+ const graphene_rect_t *rect,
+ GskRoundedRect *result) G_GNUC_PURE;
+
G_END_DECLS