tor-browser

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

ps_text_run.glsl (13692B)


      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,prim_shared,gpu_buffer
      6 
      7 flat varying mediump vec4 v_color;
      8 flat varying mediump vec3 v_mask_swizzle;
      9 // Normalized bounds of the source image in the texture.
     10 flat varying highp vec4 v_uv_bounds;
     11 
     12 // Interpolated UV coordinates to sample.
     13 varying highp vec2 v_uv;
     14 
     15 
     16 #if defined(WR_FEATURE_GLYPH_TRANSFORM) && !defined(SWGL_CLIP_DIST)
     17 varying highp vec4 v_uv_clip;
     18 #endif
     19 
     20 #ifdef WR_VERTEX_SHADER
     21 
     22 #define VECS_PER_TEXT_RUN           1
     23 #define GLYPHS_PER_GPU_BLOCK        2U
     24 
     25 #ifdef WR_FEATURE_GLYPH_TRANSFORM
     26 RectWithEndpoint transform_rect(RectWithEndpoint rect, mat2 transform) {
     27     vec2 size = rect_size(rect);
     28     vec2 center = transform * (rect.p0 + size * 0.5);
     29     vec2 radius = mat2(abs(transform[0]), abs(transform[1])) * (size * 0.5);
     30     return RectWithEndpoint(center - radius, center + radius);
     31 }
     32 
     33 bool rect_inside_rect(RectWithEndpoint little, RectWithEndpoint big) {
     34     return all(lessThanEqual(vec4(big.p0, little.p1), vec4(little.p0, big.p1)));
     35 }
     36 #endif //WR_FEATURE_GLYPH_TRANSFORM
     37 
     38 struct Glyph {
     39     vec2 offset;
     40 };
     41 
     42 Glyph fetch_glyph(int specific_prim_address,
     43                   int glyph_index) {
     44     // Two glyphs are packed in each texel in the GPU cache.
     45     int glyph_address = specific_prim_address +
     46                         VECS_PER_TEXT_RUN +
     47                         int(uint(glyph_index) / GLYPHS_PER_GPU_BLOCK);
     48     vec4 data = fetch_from_gpu_buffer_1f(glyph_address);
     49     // Select XY or ZW based on glyph index.
     50     vec2 glyph = mix(data.xy, data.zw,
     51                      bvec2(uint(glyph_index) % GLYPHS_PER_GPU_BLOCK == 1U));
     52 
     53     return Glyph(glyph);
     54 }
     55 
     56 struct GlyphResource {
     57     vec4 uv_rect;
     58     vec2 offset;
     59     float scale;
     60 };
     61 
     62 GlyphResource fetch_glyph_resource(int address) {
     63     vec4 data[2] = fetch_from_gpu_buffer_2f(address);
     64     return GlyphResource(data[0], data[1].xy, data[1].z);
     65 }
     66 
     67 struct TextRun {
     68     vec4 color;
     69 };
     70 
     71 TextRun fetch_text_run(int address) {
     72     vec4 data = fetch_from_gpu_buffer_1f(address);
     73     return TextRun(data);
     74 }
     75 
     76 vec2 get_snap_bias(int subpx_dir) {
     77     // In subpixel mode, the subpixel offset has already been
     78     // accounted for while rasterizing the glyph. However, we
     79     // must still round with a subpixel bias rather than rounding
     80     // to the nearest whole pixel, depending on subpixel direciton.
     81     switch (subpx_dir) {
     82         case SUBPX_DIR_NONE:
     83         default:
     84             return vec2(0.5);
     85         case SUBPX_DIR_HORIZONTAL:
     86             // Glyphs positioned [-0.125, 0.125] get a
     87             // subpx position of zero. So include that
     88             // offset in the glyph position to ensure
     89             // we round to the correct whole position.
     90             return vec2(0.125, 0.5);
     91         case SUBPX_DIR_VERTICAL:
     92             return vec2(0.5, 0.125);
     93     }
     94 }
     95 
     96 void main() {
     97     Instance instance = decode_instance_attributes();
     98     PrimitiveHeader ph = fetch_prim_header(instance.prim_header_address);
     99     Transform transform = fetch_transform(ph.transform_id);
    100     ClipArea clip_area = fetch_clip_area(instance.clip_address);
    101     PictureTask task = fetch_picture_task(ph.picture_task_address);
    102 
    103     int glyph_index = instance.segment_index;
    104     int color_mode = instance.flags & 0xF;
    105     int subpx_offset_x = (instance.flags >> 4) & 0x3;
    106     int subpx_offset_y = (instance.flags >> 6) & 0x3;
    107     int subpx_dir = (instance.flags >> 8) & 0x3;
    108     int is_packed_glyph = (instance.flags >> 10) & 0x1;
    109 
    110     // Note that the reference frame relative offset is stored in the prim local
    111     // rect size during batching, instead of the actual size of the primitive.
    112     TextRun text = fetch_text_run(ph.specific_prim_address);
    113     vec2 text_offset = ph.local_rect.p1;
    114 
    115     // Note that the unsnapped reference frame relative offset has already
    116     // been subtracted from the prim local rect origin during batching.
    117     // It was done this way to avoid pushing both the snapped and the
    118     // unsnapped offsets to the shader.
    119     Glyph glyph = fetch_glyph(ph.specific_prim_address, glyph_index);
    120     glyph.offset += ph.local_rect.p0;
    121 
    122     GlyphResource res = fetch_glyph_resource(instance.resource_address);
    123 
    124     // For multi-variant glyphs, adjust the UV rect to select the correct quarter
    125     // of the packed texture based on subpixel offset.
    126     // This must happen before geometry calculations since the glyph rect size depends on the UV rect.
    127     if (is_packed_glyph != 0) {
    128         int variant_index = (subpx_dir == SUBPX_DIR_HORIZONTAL) ? subpx_offset_x : subpx_offset_y;
    129         float quarter_width = (res.uv_rect.z - res.uv_rect.x) * 0.25;
    130         res.uv_rect.x = res.uv_rect.x + float(variant_index) * quarter_width;
    131         res.uv_rect.z = res.uv_rect.x + quarter_width;
    132     }
    133 
    134     vec2 snap_bias = get_snap_bias(subpx_dir);
    135 
    136     // Glyph space refers to the pixel space used by glyph rasterization during frame
    137     // building. If a non-identity transform was used, WR_FEATURE_GLYPH_TRANSFORM will
    138     // be set. Otherwise, regardless of whether the raster space is LOCAL or SCREEN,
    139     // we ignored the transform during glyph rasterization, and need to snap just using
    140     // the device pixel scale and the raster scale.
    141 #ifdef WR_FEATURE_GLYPH_TRANSFORM
    142     // Transform from local space to glyph space.
    143     mat2 glyph_transform = mat2(transform.m) * task.device_pixel_scale;
    144     vec2 glyph_translation = transform.m[3].xy * task.device_pixel_scale;
    145 
    146     // Transform from glyph space back to local space.
    147     mat2 glyph_transform_inv = inverse(glyph_transform);
    148 
    149     // Glyph raster pixels include the impact of the transform. This path can only be
    150     // entered for 3d transforms that can be coerced into a 2d transform; they have no
    151     // perspective, and have a 2d inverse. This is a looser condition than axis aligned
    152     // transforms because it also allows 2d rotations.
    153     vec2 raster_glyph_offset = floor(glyph_transform * glyph.offset + snap_bias);
    154 
    155     // We want to eliminate any subpixel translation in device space to ensure glyph
    156     // snapping is stable for equivalent glyph subpixel positions. Note that we must take
    157     // into account the translation from the transform for snapping purposes.
    158     vec2 raster_text_offset = floor(glyph_transform * text_offset + glyph_translation + 0.5) - glyph_translation;
    159 
    160     vec2 glyph_origin = res.offset + raster_glyph_offset + raster_text_offset;
    161     // Compute the glyph rect in glyph space.
    162     RectWithEndpoint glyph_rect = RectWithEndpoint(
    163         glyph_origin,
    164         glyph_origin + res.uv_rect.zw - res.uv_rect.xy
    165     );
    166 
    167     // The glyph rect is in glyph space, so transform it back to local space.
    168     RectWithEndpoint local_rect = transform_rect(glyph_rect, glyph_transform_inv);
    169 
    170     // Select the corner of the glyph's local space rect that we are processing.
    171     vec2 local_pos = mix(local_rect.p0, local_rect.p1, aPosition.xy);
    172 
    173     // If the glyph's local rect would fit inside the local clip rect, then select a corner from
    174     // the device space glyph rect to reduce overdraw of clipped pixels in the fragment shader.
    175     // Otherwise, fall back to clamping the glyph's local rect to the local clip rect.
    176     if (rect_inside_rect(local_rect, ph.local_clip_rect)) {
    177         local_pos = glyph_transform_inv * mix(glyph_rect.p0, glyph_rect.p1, aPosition.xy);
    178     }
    179 #else
    180     float raster_scale = float(ph.user_data.x) / 65535.0;
    181 
    182     // Scale in which the glyph is snapped when rasterized.
    183     float glyph_raster_scale = raster_scale * task.device_pixel_scale;
    184 
    185     // Scale from glyph space to local space.
    186     float glyph_scale_inv = res.scale / glyph_raster_scale;
    187 
    188     // Glyph raster pixels do not include the impact of the transform. Instead it was
    189     // replaced with an identity transform during glyph rasterization. As such only the
    190     // impact of the raster scale (if in local space) and the device pixel scale (for both
    191     // local and screen space) are included.
    192     //
    193     // This implies one or more of the following conditions:
    194     // - The transform is an identity. In that case, setting WR_FEATURE_GLYPH_TRANSFORM
    195     //   should have the same output result as not. We just distingush which path to use
    196     //   based on the transform used during glyph rasterization. (Screen space).
    197     // - The transform contains an animation. We will imply local raster space in such
    198     //   cases to avoid constantly rerasterizing the glyphs.
    199     // - The transform has perspective or does not have a 2d inverse (Screen or local space).
    200     // - The transform's scale will result in result in very large rasterized glyphs and
    201     //   we clamped the size. This will imply local raster space.
    202     vec2 raster_glyph_offset = floor(glyph.offset * glyph_raster_scale + snap_bias) / res.scale;
    203 
    204     // Compute the glyph rect in local space.
    205     //
    206     // The transform may be animated, so we don't want to do any snapping here for the
    207     // text offset to avoid glyphs wiggling. The text offset should have been snapped
    208     // already for axis aligned transforms excluding any animations during frame building.
    209     vec2 glyph_origin = glyph_scale_inv * (res.offset + raster_glyph_offset) + text_offset;
    210     RectWithEndpoint glyph_rect = RectWithEndpoint(
    211         glyph_origin,
    212         glyph_origin + glyph_scale_inv * (res.uv_rect.zw - res.uv_rect.xy)
    213     );
    214 
    215     // Select the corner of the glyph rect that we are processing.
    216     vec2 local_pos = mix(glyph_rect.p0, glyph_rect.p1, aPosition.xy);
    217 #endif
    218 
    219     VertexInfo vi = write_vertex(
    220         local_pos,
    221         ph.local_clip_rect,
    222         ph.z,
    223         transform,
    224         task
    225     );
    226 
    227 #ifdef WR_FEATURE_GLYPH_TRANSFORM
    228     vec2 f = (glyph_transform * vi.local_pos - glyph_rect.p0) / rect_size(glyph_rect);
    229     #ifdef SWGL_CLIP_DIST
    230         gl_ClipDistance[0] = f.x;
    231         gl_ClipDistance[1] = f.y;
    232         gl_ClipDistance[2] = 1.0 - f.x;
    233         gl_ClipDistance[3] = 1.0 - f.y;
    234     #else
    235         v_uv_clip = vec4(f, 1.0 - f);
    236     #endif
    237 #else
    238     vec2 f = (vi.local_pos - glyph_rect.p0) / rect_size(glyph_rect);
    239 #endif
    240 
    241     write_clip(vi.world_pos, clip_area, task);
    242 
    243     switch (color_mode) {
    244         case COLOR_MODE_ALPHA:
    245             v_mask_swizzle = vec3(0.0, 1.0, 1.0);
    246             v_color = text.color;
    247             break;
    248         case COLOR_MODE_BITMAP_SHADOW:
    249             #ifdef SWGL_BLEND
    250                 swgl_blendDropShadow(text.color);
    251                 v_mask_swizzle = vec3(1.0, 0.0, 0.0);
    252                 v_color = vec4(1.0);
    253             #else
    254                 v_mask_swizzle = vec3(0.0, 1.0, 0.0);
    255                 v_color = text.color;
    256             #endif
    257             break;
    258         case COLOR_MODE_COLOR_BITMAP:
    259             v_mask_swizzle = vec3(1.0, 0.0, 0.0);
    260             v_color = vec4(text.color.a);
    261             break;
    262         case COLOR_MODE_SUBPX_DUAL_SOURCE:
    263             #ifdef SWGL_BLEND
    264                 swgl_blendSubpixelText(text.color);
    265                 v_mask_swizzle = vec3(1.0, 0.0, 0.0);
    266                 v_color = vec4(1.0);
    267             #else
    268                 v_mask_swizzle = vec3(text.color.a, 0.0, 0.0);
    269                 v_color = text.color;
    270             #endif
    271             break;
    272         default:
    273             v_mask_swizzle = vec3(0.0, 0.0, 0.0);
    274             v_color = vec4(1.0);
    275     }
    276 
    277     vec2 texture_size = vec2(TEX_SIZE(sColor0));
    278     vec2 st0 = res.uv_rect.xy / texture_size;
    279     vec2 st1 = res.uv_rect.zw / texture_size;
    280 
    281     v_uv = mix(st0, st1, f);
    282     v_uv_bounds = (res.uv_rect + vec4(0.5, 0.5, -0.5, -0.5)) / texture_size.xyxy;
    283 }
    284 
    285 #endif // WR_VERTEX_SHADER
    286 
    287 #ifdef WR_FRAGMENT_SHADER
    288 
    289 Fragment text_fs(void) {
    290     Fragment frag;
    291 
    292     vec2 tc = clamp(v_uv, v_uv_bounds.xy, v_uv_bounds.zw);
    293     vec4 mask = texture(sColor0, tc);
    294     // v_mask_swizzle.z != 0 means we are using an R8 texture as alpha,
    295     // and therefore must swizzle from the r channel to all channels.
    296     mask = mix(mask, mask.rrrr, bvec4(v_mask_swizzle.z != 0.0));
    297     #ifndef WR_FEATURE_DUAL_SOURCE_BLENDING
    298         mask.rgb = mask.rgb * v_mask_swizzle.x + mask.aaa * v_mask_swizzle.y;
    299     #endif
    300 
    301     #if defined(WR_FEATURE_GLYPH_TRANSFORM) && !defined(SWGL_CLIP_DIST)
    302         mask *= float(all(greaterThanEqual(v_uv_clip, vec4(0.0))));
    303     #endif
    304 
    305     frag.color = v_color * mask;
    306 
    307     #if defined(WR_FEATURE_DUAL_SOURCE_BLENDING) && !defined(SWGL_BLEND)
    308         frag.blend = mask * v_mask_swizzle.x + mask.aaaa * v_mask_swizzle.y;
    309     #endif
    310 
    311     return frag;
    312 }
    313 
    314 
    315 void main() {
    316     Fragment frag = text_fs();
    317 
    318     float clip_mask = do_clip();
    319     frag.color *= clip_mask;
    320 
    321     #if defined(WR_FEATURE_DEBUG_OVERDRAW)
    322         oFragColor = WR_DEBUG_OVERDRAW_COLOR;
    323     #elif defined(WR_FEATURE_DUAL_SOURCE_BLENDING) && !defined(SWGL_BLEND)
    324         oFragColor = frag.color;
    325         oFragBlend = frag.blend * clip_mask;
    326     #else
    327         write_output(frag.color);
    328     #endif
    329 }
    330 
    331 #if defined(SWGL_DRAW_SPAN) && defined(SWGL_BLEND) && defined(SWGL_CLIP_DIST)
    332 void swgl_drawSpanRGBA8() {
    333     // Only support simple swizzles for now. More complex swizzles must either
    334     // be handled by blend overrides or the slow path.
    335     if (v_mask_swizzle.x != 0.0 && v_mask_swizzle.x != 1.0) {
    336         return;
    337     }
    338 
    339     #ifdef WR_FEATURE_DUAL_SOURCE_BLENDING
    340         swgl_commitTextureLinearRGBA8(sColor0, v_uv, v_uv_bounds);
    341     #else
    342         if (swgl_isTextureR8(sColor0)) {
    343             swgl_commitTextureLinearColorR8ToRGBA8(sColor0, v_uv, v_uv_bounds, v_color);
    344         } else {
    345             swgl_commitTextureLinearColorRGBA8(sColor0, v_uv, v_uv_bounds, v_color);
    346         }
    347     #endif
    348 }
    349 #endif
    350 
    351 #endif // WR_FRAGMENT_SHADER