gl renderer: Transform rounded rect on the GPU

Change the RoundedRect struct we use in our shaders so we can transform
it using (affine) matrices.
This commit is contained in:
Timm Bäder 2020-01-19 17:11:57 +01:00
parent f9e613f8fd
commit c8fa1f19db
6 changed files with 94 additions and 69 deletions

View File

@ -535,17 +535,16 @@ transform_rect (GskGLRenderer *self,
RenderOpBuilder *builder,
const GskRoundedRect *rect)
{
const float scale = ops_get_scale (builder);
GskRoundedRect r;
int i;
ops_transform_bounds_modelview (builder, &rect->bounds, &r.bounds);
r.bounds.origin.x = builder->dx + rect->bounds.origin.x;
r.bounds.origin.y = builder->dy + rect->bounds.origin.y;
r.bounds.size = rect->bounds.size;
for (i = 0; i < 4; i ++)
{
r.corner[i].width = rect->corner[i].width * scale;
r.corner[i].height = rect->corner[i].height * scale;
}
r.corner[0] = rect->corner[0];
r.corner[1] = rect->corner[1];
r.corner[2] = rect->corner[2];
r.corner[3] = rect->corner[3];
return r;
}
@ -745,24 +744,22 @@ render_border_node (GskGLRenderer *self,
GskRenderNode *node,
RenderOpBuilder *builder)
{
const float scale = ops_get_scale (builder);
const float min_x = builder->dx + node->bounds.origin.x;
const float min_y = builder->dy + node->bounds.origin.y;
const float max_x = min_x + node->bounds.size.width;
const float max_y = min_y + node->bounds.size.height;
const GdkRGBA *colors = gsk_border_node_peek_colors (node);
const GskRoundedRect *rounded_outline = gsk_border_node_peek_outline (node);
const float *og_widths = gsk_border_node_peek_widths (node);
float widths[4];
const float *widths = gsk_border_node_peek_widths (node);
int i;
struct {
float w;
float h;
} sizes[4];
if (og_widths[0] == og_widths[1] &&
og_widths[0] == og_widths[2] &&
og_widths[0] == og_widths[3] &&
if (widths[0] == widths[1] &&
widths[0] == widths[2] &&
widths[0] == widths[3] &&
gdk_rgba_equal (&colors[0], &colors[1]) &&
gdk_rgba_equal (&colors[0], &colors[2]) &&
gdk_rgba_equal (&colors[0], &colors[3]))
@ -773,7 +770,7 @@ render_border_node (GskGLRenderer *self,
op = ops_begin (builder, OP_CHANGE_INSET_SHADOW);
op->color = &colors[0];
op->outline = transform_rect (self, builder, rounded_outline);
op->spread = og_widths[0] * scale;
op->spread = widths[0];
op->offset[0] = 0;
op->offset[1] = 0;
@ -781,9 +778,6 @@ render_border_node (GskGLRenderer *self,
return;
}
for (i = 0; i < 4; i ++)
widths[i] = og_widths[i];
/* Top left */
if (widths[3] > 0)
sizes[0].w = MAX (widths[3], rounded_outline->corner[0].width);
@ -829,9 +823,6 @@ render_border_node (GskGLRenderer *self,
else
sizes[3].h = 0;
for (i = 0; i < 4; i ++)
widths[i] *= scale;
{
const GskQuadVertex side_data[4][6] = {
/* Top */
@ -1481,7 +1472,6 @@ render_unblurred_inset_shadow_node (GskGLRenderer *self,
GskRenderNode *node,
RenderOpBuilder *builder)
{
const float scale = ops_get_scale (builder);
const float blur_radius = gsk_inset_shadow_node_get_blur_radius (node);
const float dx = gsk_inset_shadow_node_get_dx (node);
const float dy = gsk_inset_shadow_node_get_dy (node);
@ -1494,9 +1484,9 @@ render_unblurred_inset_shadow_node (GskGLRenderer *self,
op = ops_begin (builder, OP_CHANGE_INSET_SHADOW);
op->color = gsk_inset_shadow_node_peek_color (node);
op->outline = transform_rect (self, builder, gsk_inset_shadow_node_peek_outline (node));
op->spread = spread * scale;
op->offset[0] = dx * scale;
op->offset[1] = dy * scale;
op->spread = spread;
op->offset[0] = dx;
op->offset[1] = dy;
load_vertex_data (ops_draw (builder, NULL), node, builder);
}
@ -1647,7 +1637,6 @@ render_unblurred_outset_shadow_node (GskGLRenderer *self,
GskRenderNode *node,
RenderOpBuilder *builder)
{
const float scale = ops_get_scale (builder);
const GskRoundedRect *outline = gsk_outset_shadow_node_peek_outline (node);
const float spread = gsk_outset_shadow_node_get_spread (node);
const float dx = gsk_outset_shadow_node_get_dx (node);
@ -1658,9 +1647,9 @@ render_unblurred_outset_shadow_node (GskGLRenderer *self,
op = ops_begin (builder, OP_CHANGE_UNBLURRED_OUTSET_SHADOW);
op->color = gsk_outset_shadow_node_peek_color (node);
op->outline = transform_rect (self, builder, outline);
op->spread = spread * scale;
op->offset[0] = dx * scale;
op->offset[1] = dy * scale;
op->spread = spread;
op->offset[0] = dx;
op->offset[1] = dy;
load_vertex_data (ops_draw (builder, NULL), node, builder);
}

View File

@ -25,6 +25,9 @@ void main() {
RoundedRect outside = create_rect(u_outline_rect);
RoundedRect inside = rounded_rect_shrink (outside, u_widths);
rounded_rect_transform(outside, u_modelview);
rounded_rect_transform(inside, u_modelview);
float alpha = clamp (rounded_rect_coverage (outside, f.xy) -
rounded_rect_coverage (inside, f.xy),
0.0, 1.0);

View File

@ -20,15 +20,21 @@ _IN_ vec4 final_color;
void main() {
vec4 f = gl_FragCoord;
vec4 color;
f.x += u_viewport.x;
f.y = (u_viewport.y + u_viewport.w) - f.y;
RoundedRect outside = create_rect(u_outline_rect);
RoundedRect inside = rounded_rect_shrink(outside, vec4(u_spread));
color = final_color * clamp (rounded_rect_coverage (outside, f.xy) -
rounded_rect_coverage (inside, f.xy - u_offset),
0.0, 1.0);
setOutputColor(color);
rounded_rect_offset(inside, u_offset);
rounded_rect_transform(outside, u_modelview);
rounded_rect_transform(inside, u_modelview);
float alpha = clamp (rounded_rect_coverage (outside, f.xy) -
rounded_rect_coverage (inside, f.xy),
0.0, 1.0);
setOutputColor(final_color * alpha);
}

View File

@ -26,6 +26,7 @@ void main() {
f.y = (u_viewport.y + u_viewport.w) - f.y;
RoundedRect outline = create_rect(u_outline_rect);
rounded_rect_transform(outline, u_modelview);
float alpha = Texture(u_source, vUv).a;
alpha *= (1.0 - clamp(rounded_rect_coverage(outline, f.xy), 0.0, 1.0));

View File

@ -6,7 +6,6 @@ precision highp float;
precision highp float;
#endif
uniform sampler2D u_source;
uniform mat4 u_projection;
uniform mat4 u_modelview;
@ -33,18 +32,24 @@ _IN_ vec2 vUv;
struct RoundedRect
{
vec4 bounds;
vec4 corner_widths;
vec4 corner_heights;
// Look, arrays can't be in structs if you want to return the struct
// from a function in gles or whatever. Just kill me.
vec4 corner_points1; // xy = top left, zw = top right
vec4 corner_points2; // xy = bottom right, zw = bottom left
};
// Transform from a GskRoundedRect to a RoundedRect as we need it.
RoundedRect
create_rect(vec4 data[3])
create_rect(vec4[3] data)
{
vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
vec4 widths = vec4(data[1].x, data[1].z, data[2].x, data[2].z);
vec4 heights = vec4(data[1].y, data[1].w, data[2].y, data[2].w);
return RoundedRect(bounds, widths, heights);
vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
bounds.xw + vec2(data[2].zw * vec2(1, -1)));
return RoundedRect(bounds, corner_points1, corner_points2);
}
float
@ -73,15 +78,15 @@ rounded_rect_coverage (RoundedRect r, vec2 p)
p.x >= r.bounds.z || p.y >= r.bounds.w)
return 0.0;
vec2 rad_tl = vec2(r.corner_widths.x, r.corner_heights.x);
vec2 rad_tr = vec2(r.corner_widths.y, r.corner_heights.y);
vec2 rad_br = vec2(r.corner_widths.z, r.corner_heights.z);
vec2 rad_bl = vec2(r.corner_widths.w, r.corner_heights.w);
vec2 rad_tl = r.corner_points1.xy - r.bounds.xy;
vec2 rad_tr = r.corner_points1.zw - r.bounds.zy;
vec2 rad_br = r.corner_points2.xy - r.bounds.zw;
vec2 rad_bl = r.corner_points2.zw - r.bounds.xw;
vec2 ref_tl = r.bounds.xy + vec2( r.corner_widths.x, r.corner_heights.x);
vec2 ref_tr = r.bounds.zy + vec2(-r.corner_widths.y, r.corner_heights.y);
vec2 ref_br = r.bounds.zw + vec2(-r.corner_widths.z, -r.corner_heights.z);
vec2 ref_bl = r.bounds.xw + vec2( r.corner_widths.w, -r.corner_heights.w);
vec2 ref_tl = r.corner_points1.xy;
vec2 ref_tr = r.corner_points1.zw;
vec2 ref_br = r.corner_points2.xy;
vec2 ref_bl = r.corner_points2.zw;
float d_tl = ellipsis_coverage(p, ref_tl, rad_tl);
float d_tr = ellipsis_coverage(p, ref_tr, rad_tr);
@ -103,26 +108,38 @@ RoundedRect
rounded_rect_shrink (RoundedRect r, vec4 amount)
{
vec4 new_bounds = r.bounds + vec4(1.0,1.0,-1.0,-1.0) * amount.wxyz;
vec4 new_widths = vec4(0);
vec4 new_heights = vec4(0);
vec4 new_corner_points1 = r.corner_points1;
vec4 new_corner_points2 = r.corner_points2;
// Left top
if (r.corner_widths.x > 0.0) new_widths.x = r.corner_widths.x - amount.w;
if (r.corner_heights.x > 0.0) new_heights.x = r.corner_heights.x - amount.x;
if (r.corner_points1.xy == r.bounds.xy) new_corner_points1.xy = new_bounds.xy;
if (r.corner_points1.zw == r.bounds.zy) new_corner_points1.zw = new_bounds.zy;
if (r.corner_points2.xy == r.bounds.zw) new_corner_points2.xy = new_bounds.zw;
if (r.corner_points2.zw == r.bounds.xw) new_corner_points2.zw = new_bounds.xw;
// Top right
if (r.corner_widths.y > 0.0) new_widths.y = r.corner_widths.y - amount.y;
if (r.corner_heights.y > 0.0) new_heights.y = r.corner_heights.y - amount.x;
return RoundedRect (new_bounds, new_corner_points1, new_corner_points2);
}
// Bottom right
if (r.corner_widths.z > 0.0) new_widths.z = r.corner_widths.z - amount.y;
if (r.corner_heights.z > 0.0) new_heights.z = r.corner_heights.z - amount.z;
void
rounded_rect_offset(inout RoundedRect r, vec2 offset)
{
r.bounds.xy += offset;
r.bounds.zw += offset;
r.corner_points1.xy += offset;
r.corner_points1.zw += offset;
r.corner_points2.xy += offset;
r.corner_points2.zw += offset;
}
// Bottom left
if (r.corner_widths.w > 0.0) new_widths.w = r.corner_widths.w - amount.w;
if (r.corner_heights.w > 0.0) new_heights.w = r.corner_heights.w - amount.z;
void rounded_rect_transform(inout RoundedRect r, mat4 mat)
{
r.bounds.xy = (mat * vec4(r.bounds.xy, 0.0, 1.0)).xy;
r.bounds.zw = (mat * vec4(r.bounds.zw, 0.0, 1.0)).xy;
return RoundedRect (new_bounds, new_widths, new_heights);
r.corner_points1.xy = (mat * vec4(r.corner_points1.xy, 0.0, 1.0)).xy;
r.corner_points1.zw = (mat * vec4(r.corner_points1.zw, 0.0, 1.0)).xy;
r.corner_points2.xy = (mat * vec4(r.corner_points2.xy, 0.0, 1.0)).xy;
r.corner_points2.zw = (mat * vec4(r.corner_points2.zw, 0.0, 1.0)).xy;
}
vec4 Texture(sampler2D sampler, vec2 texCoords) {
@ -141,6 +158,9 @@ void setOutputColor(vec4 color) {
f.x += u_viewport.x;
f.y = (u_viewport.y + u_viewport.w) - f.y;
// We do *NOT* transform the clip rect here since we already
// need to do that on the CPU.
#if GSK_GLES
gl_FragColor = color * rounded_rect_coverage(create_rect(u_clip_rect), f.xy);
#elif GSK_LEGACY

View File

@ -20,15 +20,21 @@ _IN_ vec4 final_color;
void main() {
vec4 f = gl_FragCoord;
vec4 color;
f.x += u_viewport.x;
f.y = (u_viewport.y + u_viewport.w) - f.y;
RoundedRect inside = create_rect(u_outline_rect);
RoundedRect outside = rounded_rect_shrink(inside, vec4(- u_spread));
color = final_color * clamp (rounded_rect_coverage (outside, f.xy - u_offset) -
rounded_rect_coverage (inside, f.xy),
0.0, 1.0);
setOutputColor(color);
rounded_rect_offset(outside, u_offset);
rounded_rect_transform(outside, u_modelview);
rounded_rect_transform(inside, u_modelview);
float alpha = clamp (rounded_rect_coverage (outside, f.xy) -
rounded_rect_coverage (inside, f.xy),
0.0, 1.0);
setOutputColor(final_color * alpha);
}