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; - if (intersect_rounded_rectilinear (&job->current_clip->rect.bounds, - &transformed_clip, - &intersected_clip)) + result = gsk_rounded_rect_intersect_with_rect (&transformed_clip, + &job->current_clip->rect.bounds, + &intersected_clip); + + if (result == GSK_INTERSECTION_EMPTY) + return; + + 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 diff --git a/testsuite/gsk/rounded-rect.c b/testsuite/gsk/rounded-rect.c index 0127d1cb54..3112ec26e3 100644 --- a/testsuite/gsk/rounded-rect.c +++ b/testsuite/gsk/rounded-rect.c @@ -160,6 +160,78 @@ test_to_float (void) g_assert_true (flt[10] == 9. && flt[11] == 11.); } +#define ROUNDED_RECT_INIT_FULL(x,y,w,h,w0,h0,w1,h1,w2,h2,w3,h3) \ + (GskRoundedRect) { .bounds = GRAPHENE_RECT_INIT (x, y, w, h), \ + .corner = { \ + GRAPHENE_SIZE_INIT (w0, h0), \ + GRAPHENE_SIZE_INIT (w1, h1), \ + GRAPHENE_SIZE_INIT (w2, h2), \ + GRAPHENE_SIZE_INIT (w3, h3), \ + }} + +#define ROUNDED_RECT_INIT(x,y,w,h,r) \ + ROUNDED_RECT_INIT_FULL (x, y, w, h, r, r, r, r, r, r, r, r) + +#define ROUNDED_RECT_INIT_UNIFORM(x,y,w,h,r1,r2,r3,r4) \ + ROUNDED_RECT_INIT_FULL (x, y, w, h, r1, r1, r2, r2, r3, r3, r4, r4) + + +static void +test_intersect_with_rect (void) +{ + struct { + GskRoundedRect rounded; + graphene_rect_t rect; + GskRoundedRect expected; + GskRoundedRectIntersection result; + } test[] = { + { ROUNDED_RECT_INIT (20, 50, 100, 100, 50), GRAPHENE_RECT_INIT (60, 80, 60, 70), + ROUNDED_RECT_INIT (0, 0, 0, 0, 0), GSK_INTERSECTION_NOT_REPRESENTABLE }, + { ROUNDED_RECT_INIT (0, 0, 100, 100, 10), GRAPHENE_RECT_INIT (0, 0, 100, 100), + ROUNDED_RECT_INIT (0, 0, 100, 100, 10), GSK_INTERSECTION_NONEMPTY }, + { ROUNDED_RECT_INIT (0, 0, 100, 100, 10), GRAPHENE_RECT_INIT (0, 0, 80, 80), + ROUNDED_RECT_INIT_UNIFORM (0, 0, 80, 80, 10, 0, 0, 0), GSK_INTERSECTION_NONEMPTY }, + { ROUNDED_RECT_INIT (0, 0, 100, 100, 10), GRAPHENE_RECT_INIT (10, 10, 80, 80), + ROUNDED_RECT_INIT (10, 10, 80, 80, 0), GSK_INTERSECTION_NONEMPTY }, + { ROUNDED_RECT_INIT (0, 0, 100, 100, 10), GRAPHENE_RECT_INIT (10, 15, 100, 70), + ROUNDED_RECT_INIT (10, 15, 90, 70, 0), GSK_INTERSECTION_NONEMPTY }, + { ROUNDED_RECT_INIT (0, 0, 100, 100, 10), GRAPHENE_RECT_INIT (110, 0, 10, 10), + ROUNDED_RECT_INIT (0, 0, 0, 0, 0), GSK_INTERSECTION_EMPTY }, + { ROUNDED_RECT_INIT (0, 0, 100, 100, 10), GRAPHENE_RECT_INIT (5, 5, 90, 90), + ROUNDED_RECT_INIT (5, 5, 90, 90, 0), GSK_INTERSECTION_NONEMPTY }, + { ROUNDED_RECT_INIT (0, 0, 100, 100, 10), GRAPHENE_RECT_INIT (1, 1, 1, 1), + ROUNDED_RECT_INIT (0, 0, 0, 0, 0), GSK_INTERSECTION_EMPTY }, + { ROUNDED_RECT_INIT (0, 0, 100, 100, 10), GRAPHENE_RECT_INIT (5, -5, 10, 20), + ROUNDED_RECT_INIT (0, 0, 0, 0, 0), GSK_INTERSECTION_NOT_REPRESENTABLE }, + { ROUNDED_RECT_INIT_UNIFORM (-200, 0, 200, 100, 0, 0, 0, 40), GRAPHENE_RECT_INIT (-200, 0, 160, 100), + ROUNDED_RECT_INIT_UNIFORM (-200, 0, 160, 100, 0, 0, 0, 40), GSK_INTERSECTION_NONEMPTY }, + }; + + for (unsigned int i = 0; i < G_N_ELEMENTS (test); i++) + { + GskRoundedRect out; + GskRoundedRectIntersection res; + + if (g_test_verbose ()) + g_test_message ("intersection test %u", i); + + memset (&out, 0, sizeof (GskRoundedRect)); + + res = gsk_rounded_rect_intersect_with_rect (&test[i].rounded, &test[i].rect, &out); + g_assert_true (res == test[i].result); + if (res == GSK_INTERSECTION_NONEMPTY) + { + if (!gsk_rounded_rect_equal (&out, &test[i].expected)) + { + char *s = gsk_rounded_rect_to_string (&test[i].expected); + char *s2 = gsk_rounded_rect_to_string (&out); + g_test_message ("expected %s, got %s\n", s, s2); + } + g_assert_true (gsk_rounded_rect_equal (&out, &test[i].expected)); + } + } +} + int main (int argc, char *argv[]) @@ -171,6 +243,7 @@ main (int argc, g_test_add_func ("/rounded-rect/contains-point", test_contains_point); g_test_add_func ("/rounded-rect/is-circular", test_is_circular); g_test_add_func ("/rounded-rect/to-float", test_to_float); + g_test_add_func ("/rounded-rect/intersect-with-rect", test_intersect_with_rect); return g_test_run (); }