Source code

Revision control

Copy as Markdown

Other Tools

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/// Box-shadow blur rendering via the quad infrastructure.
///
/// GPU buffer layout at pattern_input.x (5 blocks):
/// [0] alloc_size.x, alloc_size.y, dest_rect_size.x, dest_rect_size.y
/// [1] dest_rect_offset.x, dest_rect_offset.y, clip_mode (0=outset, 1=inset), 0
/// [2] element_offset_rel_prim.x, element_offset_rel_prim.y, element_size.x, element_size.y
/// [3] element_radius.tl.w, element_radius.tl.h, element_radius.tr.w, element_radius.tr.h
/// [4] element_radius.br.w, element_radius.br.h, element_radius.bl.w, element_radius.bl.h
///
/// For outset: prim_rect == dest_rect, element_offset_rel_prim is typically negative
/// (element sits inside the inflated shadow rect).
/// For inset: prim_rect is the element rect; dest_rect is the shadow area
/// (offset and potentially smaller), and blur alpha is inverted.
///
/// Element clipping (clip-out for outset, clip-in for inset) is handled analytically
/// in this shader via a rounded-rect SDF, enabling Direct rendering for the common case.
#include ps_quad,ellipse,shared
// xy: position relative to dest_rect.p0, for nine-patch UV sampling.
// zw: position in local (primitive) space, for element clip SDF evaluation.
varying highp vec4 v_shadow_pos_local_pos;
// xy: 1 / alloc_size — UV denominator.
// z: 1.0 for inset (blur alpha inverted, element clip-in), 0.0 for outset (clip-out).
// Packed in to a vector to work around bug 1630356.
// w: unused.
flat varying highp vec4 v_uv_scale_inset;
// Nine-patch edges: .xy = 0.5 (near), .zw = dest_rect_size/alloc_size - 0.5 (far).
flat varying highp vec4 v_edge;
// Atlas UV rect (normalized) and sample bounds.
flat varying highp vec4 v_uv_rect;
flat varying highp vec4 v_uv_bounds;
// Element clip SDF data: corner ellipse centers (xy) and radii (zw).
// Plane normals are reconstructed from radii in the fragment shader;
// only the plane constants are passed separately (see vElemPlaneConstants).
flat varying highp vec4 vElemCenter_Radius_TL;
flat varying highp vec4 vElemCenter_Radius_TR;
flat varying highp vec4 vElemCenter_Radius_BR;
flat varying highp vec4 vElemCenter_Radius_BL;
// Corner half-space plane constants (one per corner, xyzw = TL TR BR BL).
// Normals are derived from the corner radii stored in vElemCenter_Radius_*.zw.
flat varying highp vec4 vElemPlaneConstants;
// Element rect bounds in local space (for signed_distance_rect fallback).
flat varying highp vec4 vElemBounds;
#ifdef WR_VERTEX_SHADER
void pattern_vertex(PrimitiveInfo info) {
vec4 data0 = fetch_from_gpu_buffer_1f(info.pattern_input.x);
vec4 data1 = fetch_from_gpu_buffer_1f(info.pattern_input.x + 1);
vec4 data2 = fetch_from_gpu_buffer_1f(info.pattern_input.x + 2);
vec4 data3 = fetch_from_gpu_buffer_1f(info.pattern_input.x + 3);
vec4 data4 = fetch_from_gpu_buffer_1f(info.pattern_input.x + 4);
vec2 alloc_size = data0.xy;
vec2 dest_rect_size = data0.zw;
vec2 dest_rect_off = data1.xy;
v_uv_scale_inset = vec4(vec2(1.0) / alloc_size, data1.z, 0.0);
v_shadow_pos_local_pos = vec4(
info.local_pos - info.local_prim_rect.p0 - dest_rect_off,
info.local_pos
);
v_edge = vec4(
0.5,
0.5,
dest_rect_size.x / alloc_size.x - 0.5,
dest_rect_size.y / alloc_size.y - 0.5
);
vec2 texture_size = vec2(TEX_SIZE(sColor0));
v_uv_rect = vec4(info.segment.uv_rect.p0, info.segment.uv_rect.p1) / texture_size.xyxy;
v_uv_bounds = vec4(
info.segment.uv_rect.p0 + vec2(0.5),
info.segment.uv_rect.p1 - vec2(0.5)
) / texture_size.xyxy;
// Element clip: compute corner centers, radii, and half-space plane constants.
vec2 elem_p0 = info.local_prim_rect.p0 + data2.xy;
vec2 elem_p1 = elem_p0 + data2.zw;
vElemBounds = vec4(elem_p0, elem_p1);
vec2 r_tl = data3.xy;
vec2 r_tr = data3.zw;
vec2 r_br = data4.xy;
vec2 r_bl = data4.zw;
vElemCenter_Radius_TL = vec4(elem_p0 + r_tl, r_tl);
vElemCenter_Radius_TR = vec4(elem_p1.x - r_tr.x, elem_p0.y + r_tr.y, r_tr);
vElemCenter_Radius_BR = vec4(elem_p1 - r_br, r_br);
vElemCenter_Radius_BL = vec4(elem_p0.x + r_bl.x, elem_p1.y - r_bl.y, r_bl);
// Plane normals are n = ±radius.yx (reconstructed in the fragment shader from the
// stored radii). Only the plane constants are needed as varyings.
vec2 n_tl = -r_tl.yx;
vec2 n_tr = vec2(r_tr.y, -r_tr.x);
vec2 n_br = r_br.yx;
vec2 n_bl = vec2(-r_bl.y, r_bl.x);
vElemPlaneConstants = vec4(
dot(n_tl, vec2(elem_p0.x, elem_p0.y + r_tl.y)),
dot(n_tr, vec2(elem_p1.x - r_tr.x, elem_p0.y)),
dot(n_br, vec2(elem_p1.x, elem_p1.y - r_br.y)),
dot(n_bl, vec2(elem_p0.x + r_bl.x, elem_p1.y))
);
}
#endif
#ifdef WR_FRAGMENT_SHADER
vec4 pattern_fragment(vec4 base_color) {
vec2 shadow_pos = v_shadow_pos_local_pos.xy;
vec2 local_pos = v_shadow_pos_local_pos.zw;
vec2 uv_scale = v_uv_scale_inset.xy;
float inset = v_uv_scale_inset.z;
vec2 uv_linear = shadow_pos * uv_scale;
vec2 uv = clamp(uv_linear, vec2(0.0), v_edge.xy);
uv += max(vec2(0.0), uv_linear - v_edge.zw);
uv = mix(v_uv_rect.xy, v_uv_rect.zw, uv);
uv = clamp(uv, v_uv_bounds.xy, v_uv_bounds.zw);
float alpha = TEX_SAMPLE(sColor0, uv).r;
// Inset shadows: the blur texture encodes the shadow shape interior
// (alpha=1 inside shadow_rect). We invert to get alpha=1 at the element
// boundary fading toward zero at the shadow_rect center.
alpha = mix(alpha, 1.0 - alpha, inset);
// Element clip: clip-out for outset (inset=0), clip-in for inset (inset=1).
// distance_to_rounded_rect returns negative inside the element rect, positive outside.
// distance_aa returns 1 when dist < 0 (inside) and 0 when dist > 0 (outside).
float aa_range = compute_aa_range(local_pos);
vec2 r_tl = vElemCenter_Radius_TL.zw;
vec2 r_tr = vElemCenter_Radius_TR.zw;
vec2 r_br = vElemCenter_Radius_BR.zw;
vec2 r_bl = vElemCenter_Radius_BL.zw;
// Reconstruct plane normals from the stored radii.
vec2 n_tl = -r_tl.yx;
vec2 n_tr = vec2(r_tr.y, -r_tr.x);
vec2 n_br = r_br.yx;
vec2 n_bl = vec2(-r_bl.y, r_bl.x);
vec3 elem_plane_tl = vec3(n_tl, vElemPlaneConstants.x);
vec3 elem_plane_tr = vec3(n_tr, vElemPlaneConstants.y);
vec3 elem_plane_br = vec3(n_br, vElemPlaneConstants.z);
vec3 elem_plane_bl = vec3(n_bl, vElemPlaneConstants.w);
float elem_dist = distance_to_rounded_rect(
local_pos,
elem_plane_tl, vec4(vElemCenter_Radius_TL.xy, inverse_radii_squared(r_tl)),
elem_plane_tr, vec4(vElemCenter_Radius_TR.xy, inverse_radii_squared(r_tr)),
elem_plane_br, vec4(vElemCenter_Radius_BR.xy, inverse_radii_squared(r_br)),
elem_plane_bl, vec4(vElemCenter_Radius_BL.xy, inverse_radii_squared(r_bl)),
vElemBounds
);
// Outset (inset=0): dist < 0 = inside element → should be clipped out → use -elem_dist.
// Inset (inset=1): dist < 0 = inside element → should be kept → use elem_dist.
float element_clip = distance_aa(aa_range, mix(-elem_dist, elem_dist, inset));
return base_color * alpha * element_clip;
}
#endif