diff options
Diffstat (limited to 'gsk/gl/gskglrenderer.c')
-rw-r--r-- | gsk/gl/gskglrenderer.c | 302 |
1 files changed, 181 insertions, 121 deletions
diff --git a/gsk/gl/gskglrenderer.c b/gsk/gl/gskglrenderer.c index 130971e7b9..9df466a248 100644 --- a/gsk/gl/gskglrenderer.c +++ b/gsk/gl/gskglrenderer.c @@ -70,7 +70,6 @@ typedef enum RESET_CLIP = 1 << 1, RESET_OPACITY = 1 << 2, DUMP_FRAMEBUFFER = 1 << 3, - CENTER_CHILD = 1 << 4, NO_CACHE_PLZ = 1 << 5, } OffscreenFlags; @@ -906,6 +905,7 @@ upload_texture (GskGLRenderer *self, } else { + out_region->texture_id = gsk_gl_driver_get_texture_for_texture (self->gl_driver, texture, @@ -1152,6 +1152,59 @@ rounded_inner_rect_contains_rect (const GskRoundedRect *rounded, return graphene_rect_contains_rect (&inner, rect); } +/* Current clip is NOT rounded but new one is definitely! */ +static inline bool +intersect_rounded_rectilinear (const graphene_rect_t *non_rounded, + const GskRoundedRect *rounded, + GskRoundedRect *result) +{ + int n_corners = 0; + bool corners[4]; + + /* Intersects with top left corner? */ + n_corners += corners[0] = rounded_rect_has_corner (rounded, 0) && + graphene_rect_intersection (non_rounded, + &rounded_rect_corner (rounded, 0), NULL); + /* top right? */ + n_corners += corners[1] = rounded_rect_has_corner (rounded, 1) && + graphene_rect_intersection (non_rounded, + &rounded_rect_corner (rounded, 1), NULL); + /* bottom right? */ + n_corners += corners[2] = rounded_rect_has_corner (rounded, 2) && + graphene_rect_intersection (non_rounded, + &rounded_rect_corner (rounded, 2), NULL); + /* bottom left */ + n_corners += corners[3] = rounded_rect_has_corner (rounded, 3) && + graphene_rect_intersection (non_rounded, + &rounded_rect_corner (rounded, 3), NULL); + + if (corners[0] && !graphene_rect_contains_rect (non_rounded, &rounded_rect_corner (rounded, 0))) + return false; + if (corners[1] && !graphene_rect_contains_rect (non_rounded, &rounded_rect_corner (rounded, 1))) + return false; + if (corners[2] && !graphene_rect_contains_rect (non_rounded, &rounded_rect_corner (rounded, 2))) + return false; + if (corners[3] && !graphene_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 (int 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; +} + +/* This code intersects the current (maybe rounded) clip with the new + * non-rounded clip */ static inline void render_clipped_child (GskGLRenderer *self, RenderOpBuilder *builder, @@ -1159,112 +1212,57 @@ render_clipped_child (GskGLRenderer *self, GskRenderNode *child) { graphene_rect_t transformed_clip; - GskRoundedRect child_clip; + GskRoundedRect intersection; ops_transform_bounds_modelview (builder, clip, &transformed_clip); if (builder->clip_is_rectilinear) - goto trivial; - - { - const GskRoundedRect *cur_clip = builder->current_clip; - int n_corners = 0; - bool corners[4]; - - /* Intersects with top left corner? */ - n_corners += corners[0] = rounded_rect_has_corner (cur_clip, 0) && - graphene_rect_intersection (&transformed_clip, - &rounded_rect_corner (cur_clip, 0), NULL); - /* top right? */ - n_corners += corners[1] = rounded_rect_has_corner (cur_clip, 1) && - graphene_rect_intersection (&transformed_clip, - &rounded_rect_corner (cur_clip, 1), NULL); - /* bottom right? */ - n_corners += corners[2] = rounded_rect_has_corner (cur_clip, 2) && - graphene_rect_intersection (&transformed_clip, - &rounded_rect_corner (cur_clip, 2), NULL); - /* bottom left */ - n_corners += corners[3] = rounded_rect_has_corner (cur_clip, 3) && - graphene_rect_intersection (&transformed_clip, - &rounded_rect_corner (cur_clip, 3), NULL); - - if (n_corners == 0) - goto trivial; - - if (corners[0] && !graphene_rect_contains_rect (&transformed_clip, &rounded_rect_corner (cur_clip, 0))) - goto rtt; - if (corners[1] && !graphene_rect_contains_rect (&transformed_clip, &rounded_rect_corner (cur_clip, 1))) - goto rtt; - if (corners[2] && !graphene_rect_contains_rect (&transformed_clip, &rounded_rect_corner (cur_clip, 2))) - goto rtt; - if (corners[3] && !graphene_rect_contains_rect (&transformed_clip, &rounded_rect_corner (cur_clip, 3))) - goto rtt; - - /* 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. */ - { - GskRoundedRect real_intersection; - - graphene_rect_intersection (&transformed_clip, &cur_clip->bounds, &real_intersection.bounds); - - for (int i = 0; i < 4; i++) - { - if (corners[i]) - real_intersection.corner[i] = cur_clip->corner[i]; - else - real_intersection.corner[i].width = real_intersection.corner[i].height = 0; - } + { + memset (&intersection, 0, sizeof (GskRoundedRect)); + graphene_rect_intersection (&transformed_clip, + &builder->current_clip->bounds, + &intersection.bounds); - /* Draw with that new clip */ - ops_push_clip (builder, &real_intersection); + ops_push_clip (builder, &intersection); gsk_gl_renderer_add_render_ops (self, child, builder); ops_pop_clip (builder); } - return; - } - -rtt: - { - /* well fuck */ - const float scale = ops_get_scale (builder); - gboolean is_offscreen; - TextureRegion region; - GskRoundedRect scaled_clip; - - memset (&scaled_clip, 0, sizeof (GskRoundedRect)); - - scaled_clip.bounds.origin.x = clip->origin.x * scale; - scaled_clip.bounds.origin.y = clip->origin.y * scale; - scaled_clip.bounds.size.width = clip->size.width * scale; - scaled_clip.bounds.size.height = clip->size.height * scale; - - ops_push_clip (builder, &scaled_clip); - if (!add_offscreen_ops (self, builder, &child->bounds, - child, - ®ion, &is_offscreen, - RESET_OPACITY | FORCE_OFFSCREEN)) - g_assert_not_reached (); - ops_pop_clip (builder); + else if (intersect_rounded_rectilinear (&transformed_clip, + builder->current_clip, + &intersection)) + { + ops_push_clip (builder, &intersection); + gsk_gl_renderer_add_render_ops (self, child, builder); + ops_pop_clip (builder); + } + else + { + /* well fuck */ + const float scale = ops_get_scale (builder); + gboolean is_offscreen; + TextureRegion region; + GskRoundedRect scaled_clip; - ops_set_program (builder, &self->programs->blit_program); - ops_set_texture (builder, region.texture_id); + memset (&scaled_clip, 0, sizeof (GskRoundedRect)); - load_offscreen_vertex_data (ops_draw (builder, NULL), child, builder); - return; - } + scaled_clip.bounds.origin.x = clip->origin.x * scale; + scaled_clip.bounds.origin.y = clip->origin.y * scale; + scaled_clip.bounds.size.width = clip->size.width * scale; + scaled_clip.bounds.size.height = clip->size.height * scale; + ops_push_clip (builder, &scaled_clip); + if (!add_offscreen_ops (self, builder, &child->bounds, + child, + ®ion, &is_offscreen, + RESET_OPACITY | FORCE_OFFSCREEN)) + g_assert_not_reached (); + ops_pop_clip (builder); -trivial: - memset (&child_clip, 0, sizeof (GskRoundedRect)); - graphene_rect_intersection (&transformed_clip, - &builder->current_clip->bounds, - &child_clip.bounds); + ops_set_program (builder, &self->programs->blit_program); + ops_set_texture (builder, region.texture_id); - ops_push_clip (builder, &child_clip); - gsk_gl_renderer_add_render_ops (self, child, builder); - ops_pop_clip (builder); - return; + load_offscreen_vertex_data (ops_draw (builder, NULL), child, builder); + } } static inline void @@ -1294,6 +1292,29 @@ render_rounded_clip_node (GskGLRenderer *self, return; ops_transform_bounds_modelview (builder, &clip->bounds, &transformed_clip.bounds); + for (i = 0; i < 4; i ++) + { + transformed_clip.corner[i].width = clip->corner[i].width * scale; + transformed_clip.corner[i].height = clip->corner[i].height * scale; + } + + if (builder->clip_is_rectilinear) + { + GskRoundedRect intersected_clip; + + if (intersect_rounded_rectilinear (&builder->current_clip->bounds, + &transformed_clip, + &intersected_clip)) + { + ops_push_clip (builder, &intersected_clip); + gsk_gl_renderer_add_render_ops (self, child, builder); + ops_pop_clip (builder); + return; + } + } + + /* After this point we are really working with a new and a current clip + * which both have rounded corners. */ if (!ops_has_clip (builder)) need_offscreen = FALSE; @@ -1307,12 +1328,16 @@ render_rounded_clip_node (GskGLRenderer *self, { /* If they don't intersect at all, we can simply set * the new clip and add the render ops */ - for (i = 0; i < 4; i ++) + + /* If the new clip entirely contains the current clip, the intersection is simply + * the current clip, so we can ignore the new one */ + if (rounded_inner_rect_contains_rect (&transformed_clip, &builder->current_clip->bounds)) { - transformed_clip.corner[i].width = clip->corner[i].width * scale; - transformed_clip.corner[i].height = clip->corner[i].height * scale; + gsk_gl_renderer_add_render_ops (self, child, builder); + return; } + /* TODO: Intersect current and new clip */ ops_push_clip (builder, &transformed_clip); gsk_gl_renderer_add_render_ops (self, child, builder); ops_pop_clip (builder); @@ -1354,7 +1379,6 @@ render_rounded_clip_node (GskGLRenderer *self, load_offscreen_vertex_data (ops_draw (builder, NULL), node, builder); } - /* Else this node is entirely out of the current clip node and we don't draw it anyway. */ } static inline void @@ -1762,7 +1786,6 @@ render_unblurred_outset_shadow_node (GskGLRenderer *self, } - static GdkRGBA COLOR_WHITE = { 1, 1, 1, 1 }; static inline void render_outset_shadow_node (GskGLRenderer *self, @@ -1783,19 +1806,32 @@ render_outset_shadow_node (GskGLRenderer *self, OpShadow *shadow; int blurred_texture_id; int cached_tid; + bool do_slicing; /* scaled_outline is the minimal outline we need to draw the given drop shadow, * enlarged by the spread and offset by the blur radius. */ scaled_outline = *outline; - /* Shrink our outline to the minimum size that can still hold all the border radii */ - gsk_rounded_rect_shrink_to_minimum (&scaled_outline); - /* Increase by the spread */ - gsk_rounded_rect_shrink (&scaled_outline, -spread, -spread, -spread, -spread); - /* Grow bounds but don't grow corners */ - graphene_rect_inset (&scaled_outline.bounds, - extra_blur_pixels, - extra_blur_pixels); - /* For the center part, we add a few pixels */ - scaled_outline.bounds.size.width += SHADOW_EXTRA_SIZE; - scaled_outline.bounds.size.height += SHADOW_EXTRA_SIZE; + + if (outline->bounds.size.width < blur_extra || + outline->bounds.size.height < blur_extra) + { + do_slicing = false; + gsk_rounded_rect_shrink (&scaled_outline, -spread, -spread, -spread, -spread); + } + else + { + /* Shrink our outline to the minimum size that can still hold all the border radii */ + gsk_rounded_rect_shrink_to_minimum (&scaled_outline); + /* Increase by the spread */ + gsk_rounded_rect_shrink (&scaled_outline, -spread, -spread, -spread, -spread); + /* Grow bounds but don't grow corners */ + graphene_rect_inset (&scaled_outline.bounds, - blur_extra / 2.0, - blur_extra / 2.0); + /* For the center part, we add a few pixels */ + scaled_outline.bounds.size.width += SHADOW_EXTRA_SIZE; + scaled_outline.bounds.size.height += SHADOW_EXTRA_SIZE; + + do_slicing = true; + } texture_width = (int)ceil ((scaled_outline.bounds.size.width + blur_extra) * scale); texture_height = (int)ceil ((scaled_outline.bounds.size.height + blur_extra) * scale); @@ -1880,6 +1916,41 @@ render_outset_shadow_node (GskGLRenderer *self, blurred_texture_id = cached_tid; } + + if (!do_slicing) + { + const float min_x = floorf (builder->dx + outline->bounds.origin.x - spread - (blur_extra / 2.0) + dx); + const float min_y = floorf (builder->dy + outline->bounds.origin.y - spread - (blur_extra / 2.0) + dy); + float x1, x2, y1, y2, tx1, tx2, ty1, ty2; + + ops_set_program (builder, &self->programs->outset_shadow_program); + ops_set_color (builder, color); + ops_set_texture (builder, blurred_texture_id); + + shadow = ops_begin (builder, OP_CHANGE_OUTSET_SHADOW); + shadow->outline = transform_rect (self, builder, outline); + + tx1 = 0; tx2 = 1; + ty1 = 0; ty2 = 1; + + x1 = min_x; + x2 = min_x + texture_width / scale; + y1 = min_y; + y2 = min_y + texture_height / scale; + + ops_draw (builder, (GskQuadVertex[GL_N_VERTICES]) { + { { x1, y1 }, { tx1, ty2 }, }, + { { x1, y2 }, { tx1, ty1 }, }, + { { x2, y1 }, { tx2, ty2 }, }, + + { { x2, y2 }, { tx2, ty1 }, }, + { { x1, y2 }, { tx1, ty1 }, }, + { { x2, y1 }, { tx2, ty2 }, }, + }); + return; + } + + ops_set_program (builder, &self->programs->outset_shadow_program); ops_set_color (builder, color); ops_set_texture (builder, blurred_texture_id); @@ -3317,19 +3388,8 @@ add_offscreen_ops (GskGLRenderer *self, bounds->origin.y * scale, width, height)); - if (flags & CENTER_CHILD) - { - ops_offset (builder, - (bounds->size.width - child_node->bounds.size.width) / 2.0 - - child_node->bounds.origin.x, - (bounds->size.height - child_node->bounds.size.height) / 2.0 - - child_node->bounds.origin.y); - } - else - { - builder->dx = 0; - builder->dy = 0; - } + builder->dx = 0; + builder->dy = 0; if (flags & RESET_OPACITY) prev_opacity = ops_set_opacity (builder, 1.0); |