tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

cs_clip_box_shadow.glsl (13732B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2  * License, v. 2.0. If a copy of the MPL was not distributed with this
      3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 #include shared,clip_shared,image_source
      6 
      7 varying highp vec4 vLocalPos;
      8 varying highp vec2 vUv;
      9 flat varying highp vec4 vUvBounds;
     10 flat varying mediump vec4 vEdge;
     11 flat varying highp vec4 vUvBounds_NoClamp;
     12 // Clip mode. Packed in to a vector to avoid bug 1630356.
     13 flat varying mediump vec2 vClipMode;
     14 
     15 #define MODE_STRETCH        0
     16 #define MODE_SIMPLE         1
     17 
     18 #ifdef WR_VERTEX_SHADER
     19 
     20 PER_INSTANCE in int aClipDataResourceAddress;
     21 PER_INSTANCE in vec2 aClipSrcRectSize;
     22 PER_INSTANCE in int aClipMode;
     23 PER_INSTANCE in ivec2 aStretchMode;
     24 PER_INSTANCE in vec4 aClipDestRect;
     25 
     26 struct ClipMaskInstanceBoxShadow {
     27     ClipMaskInstanceCommon base;
     28     int resource_address;
     29 };
     30 
     31 ClipMaskInstanceBoxShadow fetch_clip_item() {
     32     ClipMaskInstanceBoxShadow cmi;
     33 
     34     cmi.base = fetch_clip_item_common();
     35     cmi.resource_address = aClipDataResourceAddress;
     36 
     37     return cmi;
     38 }
     39 
     40 struct BoxShadowData {
     41     vec2 src_rect_size;
     42     int clip_mode;
     43     int stretch_mode_x;
     44     int stretch_mode_y;
     45     RectWithEndpoint dest_rect;
     46 };
     47 
     48 BoxShadowData fetch_data() {
     49     BoxShadowData bs_data = BoxShadowData(
     50         aClipSrcRectSize,
     51         aClipMode,
     52         aStretchMode.x,
     53         aStretchMode.y,
     54         RectWithEndpoint(aClipDestRect.xy, aClipDestRect.zw)
     55     );
     56     return bs_data;
     57 }
     58 
     59 void main(void) {
     60     ClipMaskInstanceBoxShadow cmi = fetch_clip_item();
     61     Transform clip_transform = fetch_transform(cmi.base.clip_transform_id);
     62     Transform prim_transform = fetch_transform(cmi.base.prim_transform_id);
     63     BoxShadowData bs_data = fetch_data();
     64     ImageSource res = fetch_image_source(cmi.resource_address);
     65 
     66     RectWithEndpoint dest_rect = bs_data.dest_rect;
     67 
     68     ClipVertexInfo vi = write_clip_tile_vertex(
     69         dest_rect,
     70         prim_transform,
     71         clip_transform,
     72         cmi.base.sub_rect,
     73         cmi.base.task_origin,
     74         cmi.base.screen_origin,
     75         cmi.base.device_pixel_scale
     76     );
     77     vClipMode.x = float(bs_data.clip_mode);
     78 
     79     vec2 texture_size = vec2(TEX_SIZE(sColor0));
     80     vec2 local_pos = vi.local_pos.xy / vi.local_pos.w;
     81     vLocalPos = vi.local_pos;
     82     vec2 dest_rect_size = rect_size(dest_rect);
     83 
     84     switch (bs_data.stretch_mode_x) {
     85         case MODE_STRETCH: {
     86             vEdge.x = 0.5;
     87             vEdge.z = (dest_rect_size.x / bs_data.src_rect_size.x) - 0.5;
     88             vUv.x = (local_pos.x - dest_rect.p0.x) / bs_data.src_rect_size.x;
     89             break;
     90         }
     91         case MODE_SIMPLE:
     92         default: {
     93             vEdge.xz = vec2(1.0);
     94             vUv.x = (local_pos.x - dest_rect.p0.x) / dest_rect_size.x;
     95             break;
     96         }
     97     }
     98 
     99     switch (bs_data.stretch_mode_y) {
    100         case MODE_STRETCH: {
    101             vEdge.y = 0.5;
    102             vEdge.w = (dest_rect_size.y / bs_data.src_rect_size.y) - 0.5;
    103             vUv.y = (local_pos.y - dest_rect.p0.y) / bs_data.src_rect_size.y;
    104             break;
    105         }
    106         case MODE_SIMPLE:
    107         default: {
    108             vEdge.yw = vec2(1.0);
    109             vUv.y = (local_pos.y - dest_rect.p0.y) / dest_rect_size.y;
    110             break;
    111         }
    112     }
    113 
    114     vUv *= vi.local_pos.w;
    115     vec2 uv0 = res.uv_rect.p0;
    116     vec2 uv1 = res.uv_rect.p1;
    117     vUvBounds = vec4(uv0 + vec2(0.5), uv1 - vec2(0.5)) / texture_size.xyxy;
    118     vUvBounds_NoClamp = vec4(uv0, uv1) / texture_size.xyxy;
    119 }
    120 #endif
    121 
    122 #ifdef WR_FRAGMENT_SHADER
    123 void main(void) {
    124     vec2 uv_linear = vUv / vLocalPos.w;
    125     vec2 uv = clamp(uv_linear, vec2(0.0), vEdge.xy);
    126     uv += max(vec2(0.0), uv_linear - vEdge.zw);
    127     uv = mix(vUvBounds_NoClamp.xy, vUvBounds_NoClamp.zw, uv);
    128     uv = clamp(uv, vUvBounds.xy, vUvBounds.zw);
    129 
    130     float in_shadow_rect = rectangle_aa_rough_fragment(vLocalPos.xy / vLocalPos.w);
    131 
    132     float texel = TEX_SAMPLE(sColor0, uv).r;
    133 
    134     float alpha = mix(texel, 1.0 - texel, vClipMode.x);
    135     float result = vLocalPos.w > 0.0 ? mix(vClipMode.x, alpha, in_shadow_rect) : 0.0;
    136 
    137     oFragColor = vec4(result);
    138 }
    139 
    140 #ifdef SWGL_DRAW_SPAN
    141 // As with cs_clip_rectangle, this shader spends a lot of time doing clipping and
    142 // combining for every fragment, even if outside of the primitive to initialize
    143 // the clip tile, or inside the inner bounds of the primitive, where the shadow
    144 // is unnecessary. To alleviate this, the span shader attempts to first intersect
    145 // the the local clip bounds, outside of which we can just use a solid fill
    146 // to initialize those clip tile fragments. Once inside the primitive bounds,
    147 // we further intersect with the inner region where no shadow is necessary either
    148 // so that we can commit entire spans of texture within this nine-patch region
    149 // instead of having to do the work of mapping per fragment.
    150 void swgl_drawSpanR8() {
    151     // Perspective is not supported.
    152     if (swgl_interpStep(vLocalPos).w != 0.0) {
    153         return;
    154     }
    155 
    156     // If the span is completely outside the Z-range and clipped out, just
    157     // output clear so we don't need to consider invalid W in the rest of the
    158     // shader.
    159     float w = swgl_forceScalar(vLocalPos.w);
    160     if (w <= 0.0) {
    161         swgl_commitSolidR8(0.0);
    162         return;
    163     }
    164 
    165     // To start, we evaluate the box shadow in both UV and local space relative
    166     // to the local-space position. This will be interpolated across the span to
    167     // track whether we intersect the nine-patch.
    168     w = 1.0 / w;
    169     vec2 uv_linear = vUv * w;
    170     vec2 uv_linear0 = swgl_forceScalar(uv_linear);
    171     vec2 uv_linear_step = swgl_interpStep(vUv).xy * w;
    172     vec2 local_pos = vLocalPos.xy * w;
    173     vec2 local_pos0 = swgl_forceScalar(local_pos);
    174     vec2 local_step = swgl_interpStep(vLocalPos).xy * w;
    175 
    176     // We need to compute the local-space distance to the bounding box and then
    177     // figure out how many processing steps that maps to. If we are stepping in
    178     // a negative direction on an axis, we need to swap the sides of the box
    179     // which we consider as the start or end. If there is no local-space step
    180     // on an axis (i.e. constant Y), we need to take care to force the steps to
    181     // either the start or end of the span depending on if we are inside or
    182     // outside of the bounding box.
    183     vec4 clip_dist =
    184         mix(vTransformBounds, vTransformBounds.zwxy, lessThan(local_step, vec2(0.0)).xyxy)
    185             - local_pos0.xyxy;
    186     clip_dist =
    187         mix(1.0e6 * step(0.0, clip_dist),
    188             clip_dist * recip(local_step).xyxy,
    189             notEqual(local_step, vec2(0.0)).xyxy);
    190 
    191     // Find the start and end of the shadowed region on this span.
    192     float shadow_start = max(clip_dist.x, clip_dist.y);
    193     float shadow_end = min(clip_dist.z, clip_dist.w);
    194 
    195     // Flip the offsets from the start of the span so we can compare against the
    196     // remaining span length which automatically deducts as we commit fragments.
    197     ivec2 shadow_steps = ivec2(clamp(
    198         swgl_SpanLength - swgl_StepSize * vec2(floor(shadow_start), ceil(shadow_end)),
    199         0.0, swgl_SpanLength));
    200     int shadow_start_len = shadow_steps.x;
    201     int shadow_end_len = shadow_steps.y;
    202 
    203     // Likewise, once inside the primitive bounds, we also need to track which
    204     // sector of the nine-patch we are in which requires intersecting against
    205     // the inner box instead of the outer box.
    206     vec4 opaque_dist =
    207         mix(vEdge, vEdge.zwxy, lessThan(uv_linear_step, vec2(0.0)).xyxy)
    208             - uv_linear0.xyxy;
    209     opaque_dist =
    210         mix(1.0e6 * step(0.0, opaque_dist),
    211             opaque_dist * recip(uv_linear_step).xyxy,
    212             notEqual(uv_linear_step, vec2(0.0)).xyxy);
    213 
    214     // Unlike for the shadow clipping bounds, here we need to rather find the floor of all
    215     // the offsets so that we don't accidentally process any chunks in the transitional areas
    216     // between sectors of the nine-patch.
    217     ivec4 opaque_steps = ivec4(clamp(
    218         swgl_SpanLength -
    219             swgl_StepSize *
    220                 vec4(floor(opaque_dist.x), floor(opaque_dist.y), floor(opaque_dist.z), floor(opaque_dist.w)),
    221         shadow_end_len, swgl_SpanLength));
    222 
    223     // Fill any initial sections of the span that are clipped out based on clip mode.
    224     if (swgl_SpanLength > shadow_start_len) {
    225         int num_before = swgl_SpanLength - shadow_start_len;
    226         swgl_commitPartialSolidR8(num_before, vClipMode.x);
    227         float steps_before = float(num_before / swgl_StepSize);
    228         uv_linear += steps_before * uv_linear_step;
    229         local_pos += steps_before * local_step;
    230     }
    231 
    232     // This loop tries to repeatedly process entire spans of the nine-patch that map
    233     // to a contiguous spans of texture in the source box shadow. First, we process
    234     // a chunk with per-fragment clipping and mapping in case we're starting on a
    235     // transitional region between sectors of the nine-patch which may need to map
    236     // to different spans of texture per-fragment. After, we find the largest span
    237     // within the current sector before we hit the next transitional region, and
    238     // attempt to commit an entire span of texture therein.
    239     while (swgl_SpanLength > 0) {
    240         // Here we might be in a transitional chunk, so do everything per-fragment.
    241         {
    242             vec2 uv = clamp(uv_linear, vec2(0.0), vEdge.xy);
    243             uv += max(vec2(0.0), uv_linear - vEdge.zw);
    244             uv = mix(vUvBounds_NoClamp.xy, vUvBounds_NoClamp.zw, uv);
    245             uv = clamp(uv, vUvBounds.xy, vUvBounds.zw);
    246 
    247             float in_shadow_rect = rectangle_aa_rough_fragment(local_pos);
    248 
    249             float texel = TEX_SAMPLE(sColor0, uv).r;
    250 
    251             float alpha = mix(texel, 1.0 - texel, vClipMode.x);
    252             float result = mix(vClipMode.x, alpha, in_shadow_rect);
    253             swgl_commitColorR8(result);
    254 
    255             uv_linear += uv_linear_step;
    256             local_pos += local_step;
    257         }
    258         // If we now hit the end of the clip bounds, just bail out since there is
    259         // no more shadow to map.
    260         if (swgl_SpanLength <= shadow_end_len) {
    261             break;
    262         }
    263         // By here we've determined to be still inside the nine-patch. We need to
    264         // compare against the inner rectangle thresholds to see which sector of
    265         // the nine-patch to use and thus how to map the box shadow texture. Stop
    266         // at least one step before the end of the shadow region to properly clip
    267         // on the boundary.
    268         int num_inside = swgl_SpanLength - swgl_StepSize - shadow_end_len;
    269         vec4 uv_bounds = vUvBounds;
    270         if (swgl_SpanLength >= opaque_steps.y) {
    271             // We're in the top Y band of the nine-patch.
    272             num_inside = min(num_inside, swgl_SpanLength - opaque_steps.y);
    273         } else if (swgl_SpanLength >= opaque_steps.w) {
    274             // We're in the middle Y band of the nine-patch. Set the UV clamp bounds
    275             // to the vertical center texel of the box shadow.
    276             num_inside = min(num_inside, swgl_SpanLength - opaque_steps.w);
    277             uv_bounds.yw = vec2(clamp(mix(vUvBounds_NoClamp.y, vUvBounds_NoClamp.w, vEdge.y),
    278                                       vUvBounds.y, vUvBounds.w));
    279         }
    280         if (swgl_SpanLength >= opaque_steps.x) {
    281             // We're in the left X column of the nine-patch.
    282             num_inside = min(num_inside, swgl_SpanLength - opaque_steps.x);
    283         } else if (swgl_SpanLength >= opaque_steps.z) {
    284             // We're in the middle X band of the nine-patch. Set the UV clamp bounds
    285             // to the horizontal center texel of the box shadow.
    286             num_inside = min(num_inside, swgl_SpanLength - opaque_steps.z);
    287             uv_bounds.xz = vec2(clamp(mix(vUvBounds_NoClamp.x, vUvBounds_NoClamp.z, vEdge.x),
    288                                       vUvBounds.x, vUvBounds.z));
    289         }
    290         if (num_inside > 0) {
    291             // We have a non-zero span of fragments within the sector. Map to the UV
    292             // start offset of the sector and the UV offset within the sector.
    293             vec2 uv = clamp(uv_linear, vec2(0.0), vEdge.xy);
    294             uv += max(vec2(0.0), uv_linear - vEdge.zw);
    295             uv = mix(vUvBounds_NoClamp.xy, vUvBounds_NoClamp.zw, uv);
    296             // If we're in the center sector of the nine-patch, then we only need to
    297             // sample from a single texel of the box shadow. Just sample that single
    298             // texel once and output it for the entire span. Otherwise, we just need
    299             // to commit an actual span of texture from the box shadow. Depending on
    300             // if we are in clip-out mode, we may need to invert the source texture.
    301             if (uv_bounds.xy == uv_bounds.zw) {
    302                 uv = clamp(uv, uv_bounds.xy, uv_bounds.zw);
    303                 float texel = TEX_SAMPLE(sColor0, uv).r;
    304                 float alpha = mix(texel, 1.0 - texel, vClipMode.x);
    305                 swgl_commitPartialSolidR8(num_inside, alpha);
    306             } else if (vClipMode.x != 0.0) {
    307                 swgl_commitPartialTextureLinearInvertR8(num_inside, sColor0, uv, uv_bounds);
    308             } else {
    309                 swgl_commitPartialTextureLinearR8(num_inside, sColor0, uv, uv_bounds);
    310             }
    311             float steps_inside = float(num_inside / swgl_StepSize);
    312             uv_linear += steps_inside * uv_linear_step;
    313             local_pos += steps_inside * local_step;
    314         }
    315         // By here we're probably in a transitional chunk of the nine-patch that
    316         // requires per-fragment processing, so loop around again to the handler
    317         // for that case.
    318     }
    319 
    320     // Fill any remaining sections of the span that are clipped out.
    321     if (swgl_SpanLength > 0) {
    322         swgl_commitPartialSolidR8(swgl_SpanLength, vClipMode.x);
    323     }
    324 }
    325 #endif
    326 
    327 #endif