tor-browser

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

brush_image.glsl (16487B)


      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 #define VECS_PER_SPECIFIC_BRUSH 3
      6 
      7 #include shared,prim_shared,brush,image_source
      8 
      9 // Interpolated UV coordinates to sample.
     10 varying highp vec2 v_uv;
     11 
     12 #ifdef WR_FEATURE_ALPHA_PASS
     13 flat varying mediump vec4 v_color;
     14 flat varying mediump vec2 v_mask_swizzle;
     15 flat varying mediump vec2 v_tile_repeat_bounds;
     16 #endif
     17 
     18 // Normalized bounds of the source image in the texture.
     19 flat varying highp vec4 v_uv_bounds;
     20 // Normalized bounds of the source image in the texture, adjusted to avoid
     21 // sampling artifacts.
     22 flat varying highp vec4 v_uv_sample_bounds;
     23 
     24 // Flag to allow perspective interpolation of UV.
     25 // Packed in to vector to work around bug 1630356.
     26 flat varying mediump vec2 v_perspective;
     27 
     28 #ifdef WR_VERTEX_SHADER
     29 
     30 // Must match the AlphaType enum.
     31 #define BLEND_MODE_ALPHA            0
     32 #define BLEND_MODE_PREMUL_ALPHA     1
     33 
     34 struct ImageBrushPrimitiveData {
     35     vec4 color;
     36     vec4 background_color;
     37     vec2 stretch_size;
     38 };
     39 
     40 ImageBrushPrimitiveData fetch_image_data(int address) {
     41     vec4[3] raw_data = fetch_from_gpu_buffer_3f(address);
     42     ImageBrushPrimitiveData data = ImageBrushPrimitiveData(
     43         raw_data[0],
     44         raw_data[1],
     45         raw_data[2].xy
     46     );
     47     return data;
     48 }
     49 
     50 vec2 modf2(vec2 x, vec2 y) {
     51     return x - y * floor(x/y);
     52 }
     53 
     54 void brush_vs(
     55     VertexInfo vi,
     56     int prim_address,
     57     RectWithEndpoint prim_rect,
     58     RectWithEndpoint segment_rect,
     59     ivec4 prim_user_data,
     60     int specific_resource_address,
     61     mat4 transform,
     62     PictureTask pic_task,
     63     int brush_flags,
     64     vec4 segment_data
     65 ) {
     66     ImageBrushPrimitiveData image_data = fetch_image_data(prim_address);
     67 
     68     // If this is in WR_FEATURE_TEXTURE_RECT mode, the rect and size use
     69     // non-normalized texture coordinates.
     70 #ifdef WR_FEATURE_TEXTURE_RECT
     71     vec2 texture_size = vec2(1, 1);
     72 #else
     73     vec2 texture_size = vec2(TEX_SIZE(sColor0));
     74 #endif
     75 
     76     ImageSource res = fetch_image_source(specific_resource_address);
     77     vec2 uv0 = res.uv_rect.p0;
     78     vec2 uv1 = res.uv_rect.p1;
     79 
     80     RectWithEndpoint local_rect = prim_rect;
     81     vec2 stretch_size = image_data.stretch_size;
     82     if (stretch_size.x < 0.0) {
     83         stretch_size = rect_size(local_rect);
     84     }
     85 
     86     // If this segment should interpolate relative to the
     87     // segment, modify the parameters for that.
     88     if ((brush_flags & BRUSH_FLAG_SEGMENT_RELATIVE) != 0) {
     89         local_rect = segment_rect;
     90         stretch_size = rect_size(local_rect);
     91 
     92         if ((brush_flags & BRUSH_FLAG_TEXEL_RECT) != 0) {
     93             // If the extra data is a texel rect, modify the UVs.
     94             vec2 uv_size = res.uv_rect.p1 - res.uv_rect.p0;
     95             uv0 = res.uv_rect.p0 + segment_data.xy * uv_size;
     96             uv1 = res.uv_rect.p0 + segment_data.zw * uv_size;
     97         }
     98 
     99         #ifdef WR_FEATURE_REPETITION
    100             // TODO(bug 1609893): Move this logic to the CPU as well as other sources of
    101             // branchiness in this shader.
    102             if ((brush_flags & BRUSH_FLAG_TEXEL_RECT) != 0) {
    103                 // Value of the stretch size with repetition. We have to compute it for
    104                 // both axis even if we only repeat on one axis because the value for
    105                 // each axis depends on what the repeated value would have been for the
    106                 // other axis.
    107                 vec2 repeated_stretch_size = stretch_size;
    108                 // Size of the uv rect of the segment we are considering when computing
    109                 // the repetitions. For the fill area it is a tad more complicated as we
    110                 // have to use the uv size of the top-middle segment to drive horizontal
    111                 // repetitions, and the size of the left-middle segment to drive vertical
    112                 // repetitions. So we track the reference sizes for both axis separately
    113                 // even though in the common case (the border segments) they are the same.
    114                 vec2 horizontal_uv_size = uv1 - uv0;
    115                 vec2 vertical_uv_size = uv1 - uv0;
    116                 // We use top and left sizes by default and fall back to bottom and right
    117                 // when a size is empty.
    118                 if ((brush_flags & BRUSH_FLAG_SEGMENT_NINEPATCH_MIDDLE) != 0) {
    119                     repeated_stretch_size = segment_rect.p0 - prim_rect.p0;
    120 
    121                     float epsilon = 0.001;
    122 
    123                     // Adjust the the referecne uv size to compute vertical repetitions for
    124                     // the fill area.
    125                     vertical_uv_size.x = uv0.x - res.uv_rect.p0.x;
    126                     if (vertical_uv_size.x < epsilon || repeated_stretch_size.x < epsilon) {
    127                         vertical_uv_size.x = res.uv_rect.p1.x - uv1.x;
    128                         repeated_stretch_size.x = prim_rect.p1.x - segment_rect.p1.x;
    129                     }
    130 
    131                     // Adjust the the referecne uv size to compute horizontal repetitions
    132                     // for the fill area.
    133                     horizontal_uv_size.y = uv0.y - res.uv_rect.p0.y;
    134                     if (horizontal_uv_size.y < epsilon || repeated_stretch_size.y < epsilon) {
    135                         horizontal_uv_size.y = res.uv_rect.p1.y - uv1.y;
    136                         repeated_stretch_size.y = prim_rect.p1.y - segment_rect.p1.y;
    137                     }
    138                 }
    139 
    140                 if ((brush_flags & BRUSH_FLAG_SEGMENT_REPEAT_X) != 0) {
    141                     float uv_ratio = horizontal_uv_size.x / horizontal_uv_size.y;
    142                     stretch_size.x = repeated_stretch_size.y * uv_ratio;
    143                 }
    144                 if ((brush_flags & BRUSH_FLAG_SEGMENT_REPEAT_Y) != 0) {
    145                     float uv_ratio = vertical_uv_size.y / vertical_uv_size.x;
    146                     stretch_size.y = repeated_stretch_size.x * uv_ratio;
    147                 }
    148 
    149             } else {
    150                 if ((brush_flags & BRUSH_FLAG_SEGMENT_REPEAT_X) != 0) {
    151                     stretch_size.x = segment_data.z - segment_data.x;
    152                 }
    153                 if ((brush_flags & BRUSH_FLAG_SEGMENT_REPEAT_Y) != 0) {
    154                     stretch_size.y = segment_data.w - segment_data.y;
    155                 }
    156             }
    157             if ((brush_flags & BRUSH_FLAG_SEGMENT_REPEAT_X_ROUND) != 0) {
    158                 float segment_rect_width = segment_rect.p1.x - segment_rect.p0.x;
    159                 float nx = max(1.0, round(segment_rect_width / stretch_size.x));
    160                 stretch_size.x = segment_rect_width / nx;
    161             }
    162             if ((brush_flags & BRUSH_FLAG_SEGMENT_REPEAT_Y_ROUND) != 0) {
    163                 float segment_rect_height = segment_rect.p1.y - segment_rect.p0.y;
    164                 float ny = max(1.0, round(segment_rect_height / stretch_size.y));
    165                 stretch_size.y = segment_rect_height / ny;
    166             }
    167         #endif
    168     }
    169 
    170     float perspective_interpolate = (brush_flags & BRUSH_FLAG_PERSPECTIVE_INTERPOLATION) != 0 ? 1.0 : 0.0;
    171     v_perspective.x = perspective_interpolate;
    172 
    173     // We deliberately only support the normalized UVs flag when *not* using
    174     // repetition, as the presence of this code resulted in a miscompilation
    175     // in the repetition variant on some devices, causing incorrect rendering
    176     // even when the flag is unset. Perhaps due to the excessive number of
    177     // branches in the repetition code, and this one broke the camel's back?
    178     // See bug 1932416 and bug 1929799.
    179 #ifndef WR_FEATURE_REPETITION
    180     vec2 uv_scale = mix(vec2(1.0), texture_size,
    181                         bvec2((brush_flags & BRUSH_FLAG_NORMALIZED_UVS) != 0));
    182     uv0 *= uv_scale;
    183     uv1 *= uv_scale;
    184 #endif
    185 
    186     // Handle case where the UV coords are inverted (e.g. from an
    187     // external image).
    188     vec2 min_uv = min(uv0, uv1);
    189     vec2 max_uv = max(uv0, uv1);
    190 
    191     v_uv_sample_bounds = vec4(
    192         min_uv + vec2(0.5),
    193         max_uv - vec2(0.5)
    194     ) / texture_size.xyxy;
    195 
    196     vec2 f = (vi.local_pos - local_rect.p0) / rect_size(local_rect);
    197 
    198 #ifdef WR_FEATURE_ALPHA_PASS
    199     int color_mode = prim_user_data.x & 0xffff;
    200     int blend_mode = prim_user_data.x >> 16;
    201 
    202 #endif
    203 
    204     // Derive the texture coordinates for this image, based on
    205     // whether the source image is a local-space or screen-space
    206     // image.
    207     int raster_space = prim_user_data.y;
    208     if (raster_space == RASTER_SCREEN) {
    209         // Since the screen space UVs specify an arbitrary quad, do
    210         // a bilinear interpolation to get the correct UV for this
    211         // local position.
    212         f = get_image_quad_uv(specific_resource_address, f);
    213     }
    214 
    215     // Offset and scale v_uv here to avoid doing it in the fragment shader.
    216     vec2 repeat = rect_size(local_rect) / stretch_size;
    217     v_uv = mix(uv0, uv1, f) - min_uv;
    218     v_uv *= repeat.xy;
    219 
    220     vec2 normalized_offset = vec2(0.0);
    221 #ifdef WR_FEATURE_REPETITION
    222     // In the case of border-image-repeat: repeat, we must apply an offset so that
    223     // the first tile is centered.
    224     //
    225     // This is derived from:
    226     //   uv_size = max_uv - min_uv
    227     //   repeat = local_rect.size / stetch_size
    228     //   layout_offset = local_rect.size / 2 - strecth_size / 2
    229     //   texel_offset = layout_offset * uv_size / stretch_size
    230     //   texel_offset = uv_size / 2 * (local_rect.size / stretch_size - stretch_size / stretch_size)
    231     //   texel_offset = uv_size / 2 * (repeat - 1)
    232     //
    233     // The offset is then adjusted so that it loops in the [0, uv_size] range.
    234     // In principle this is simply a modulo:
    235     //
    236     //   adjusted_offset = fact((repeat - 1)/2) * uv_size
    237     //
    238     // However we don't want fract's behavior with negative numbers which happens when the pattern
    239     // is larger than the local rect (repeat is between 0 and 1), so we shift the content by 1 to
    240     // remain positive.
    241     //
    242     //   adjusted_offset = fract(repeat/2 - 1/2 + 1) * uv_size
    243     //
    244     // `uv - offset` will go through another modulo in the fragment shader for which we again don't
    245     // want the behavior for nagative numbers. We rearrange this here in the form
    246     // `uv + (uv_size - offset)` to prevent that.
    247     //
    248     //   adjusted_offset = (1 - fract(repeat/2 - 1/2 + 1)) * uv_size
    249     //
    250     // We then separate the normalized part of the offset which we also need elsewhere.
    251     bvec2 centered = bvec2(brush_flags & BRUSH_FLAG_SEGMENT_REPEAT_X_CENTERED,
    252                            brush_flags & BRUSH_FLAG_SEGMENT_REPEAT_Y_CENTERED);
    253     // Use mix() rather than if statements due to a miscompilation on Adreno 3xx. See bug 1853573.
    254     normalized_offset = mix(vec2(0.0), 1.0 - fract(repeat * 0.5 + 0.5), centered);
    255     v_uv += normalized_offset * (max_uv - min_uv);
    256 #endif
    257     v_uv /= texture_size;
    258     if (perspective_interpolate == 0.0) {
    259         v_uv *= vi.world_pos.w;
    260     }
    261 
    262 #ifdef WR_FEATURE_TEXTURE_RECT
    263     v_uv_bounds = vec4(0.0, 0.0, vec2(textureSize(sColor0)));
    264 #else
    265     v_uv_bounds = vec4(min_uv, max_uv) / texture_size.xyxy;
    266 #endif
    267 
    268 #ifdef WR_FEATURE_REPETITION
    269     // Normalize UV to 0..1 scale only if using repetition. Otherwise, leave
    270     // UVs unnormalized since we won't compute a modulus without repetition
    271     // enabled.
    272     v_uv /= (v_uv_bounds.zw - v_uv_bounds.xy);
    273 #endif
    274 
    275 #ifdef WR_FEATURE_ALPHA_PASS
    276     v_tile_repeat_bounds = repeat.xy + normalized_offset;
    277 
    278     float opacity = float(prim_user_data.z) / 65535.0;
    279     switch (blend_mode) {
    280         case BLEND_MODE_ALPHA:
    281             image_data.color.a *= opacity;
    282             break;
    283         case BLEND_MODE_PREMUL_ALPHA:
    284         default:
    285             image_data.color *= opacity;
    286             break;
    287     }
    288 
    289     switch (color_mode) {
    290         case COLOR_MODE_ALPHA:
    291         case COLOR_MODE_BITMAP_SHADOW:
    292             #ifdef SWGL_BLEND
    293                 swgl_blendDropShadow(image_data.color);
    294                 v_mask_swizzle = vec2(1.0, 0.0);
    295                 v_color = vec4(1.0);
    296             #else
    297                 v_mask_swizzle = vec2(0.0, 1.0);
    298                 v_color = image_data.color;
    299             #endif
    300             break;
    301         case COLOR_MODE_IMAGE:
    302             v_mask_swizzle = vec2(1.0, 0.0);
    303             v_color = image_data.color;
    304             break;
    305         case COLOR_MODE_COLOR_BITMAP:
    306             v_mask_swizzle = vec2(1.0, 0.0);
    307             v_color = vec4(image_data.color.a);
    308             break;
    309         case COLOR_MODE_SUBPX_DUAL_SOURCE:
    310             v_mask_swizzle = vec2(image_data.color.a, 0.0);
    311             v_color = image_data.color;
    312             break;
    313         case COLOR_MODE_MULTIPLY_DUAL_SOURCE:
    314             v_mask_swizzle = vec2(-image_data.color.a, image_data.color.a);
    315             v_color = image_data.color;
    316             break;
    317         default:
    318             v_mask_swizzle = vec2(0.0);
    319             v_color = vec4(1.0);
    320     }
    321 #endif
    322 }
    323 #endif
    324 
    325 #ifdef WR_FRAGMENT_SHADER
    326 
    327 vec2 compute_repeated_uvs(float perspective_divisor) {
    328 #ifdef WR_FEATURE_REPETITION
    329     vec2 uv_size = v_uv_bounds.zw - v_uv_bounds.xy;
    330 
    331     #ifdef WR_FEATURE_ALPHA_PASS
    332     vec2 local_uv = v_uv * perspective_divisor;
    333     // This prevents the uv on the top and left parts of the primitive that was inflated
    334     // for anti-aliasing purposes from going beyound the range covered by the regular
    335     // (non-inflated) primitive.
    336     local_uv = max(local_uv, vec2(0.0));
    337 
    338     // Handle horizontal and vertical repetitions.
    339     vec2 repeated_uv = fract(local_uv) * uv_size + v_uv_bounds.xy;
    340 
    341     // This takes care of the bottom and right inflated parts.
    342     // We do it after the modulo because the latter wraps around the values exactly on
    343     // the right and bottom edges, which we do not want.
    344     if (local_uv.x >= v_tile_repeat_bounds.x) {
    345         repeated_uv.x = v_uv_bounds.z;
    346     }
    347     if (local_uv.y >= v_tile_repeat_bounds.y) {
    348         repeated_uv.y = v_uv_bounds.w;
    349     }
    350     #else
    351     vec2 repeated_uv = fract(v_uv * perspective_divisor) * uv_size + v_uv_bounds.xy;
    352     #endif
    353 
    354     return repeated_uv;
    355 #else
    356     return v_uv * perspective_divisor + v_uv_bounds.xy;
    357 #endif
    358 }
    359 
    360 Fragment brush_fs() {
    361     float perspective_divisor = mix(gl_FragCoord.w, 1.0, v_perspective.x);
    362     vec2 repeated_uv = compute_repeated_uvs(perspective_divisor);
    363 
    364     // Clamp the uvs to avoid sampling artifacts.
    365     vec2 uv = clamp(repeated_uv, v_uv_sample_bounds.xy, v_uv_sample_bounds.zw);
    366 
    367     vec4 texel = TEX_SAMPLE(sColor0, uv);
    368 
    369     Fragment frag;
    370 
    371 #ifdef WR_FEATURE_ALPHA_PASS
    372     #ifdef WR_FEATURE_ANTIALIASING
    373         float alpha = antialias_brush();
    374     #else
    375         float alpha = 1.0;
    376     #endif
    377     #ifndef WR_FEATURE_DUAL_SOURCE_BLENDING
    378         texel.rgb = texel.rgb * v_mask_swizzle.x + texel.aaa * v_mask_swizzle.y;
    379     #endif
    380 
    381     vec4 alpha_mask = texel * alpha;
    382     frag.color = v_color * alpha_mask;
    383 
    384     #ifdef WR_FEATURE_DUAL_SOURCE_BLENDING
    385         frag.blend = alpha_mask * v_mask_swizzle.x + alpha_mask.aaaa * v_mask_swizzle.y;
    386     #endif
    387 #else
    388     frag.color = texel;
    389 #endif
    390 
    391     return frag;
    392 }
    393 
    394 #if defined(SWGL_DRAW_SPAN) && (!defined(WR_FEATURE_ALPHA_PASS) || !defined(WR_FEATURE_DUAL_SOURCE_BLENDING))
    395 void swgl_drawSpanRGBA8() {
    396     if (!swgl_isTextureRGBA8(sColor0)) {
    397         return;
    398     }
    399 
    400     #ifdef WR_FEATURE_ALPHA_PASS
    401         if (v_mask_swizzle != vec2(1.0, 0.0)) {
    402             return;
    403         }
    404     #endif
    405 
    406     float perspective_divisor = mix(swgl_forceScalar(gl_FragCoord.w), 1.0, v_perspective.x);
    407 
    408     #ifdef WR_FEATURE_REPETITION
    409         // Get the UVs before any repetition, scaling, or offsetting has occurred...
    410         vec2 uv = v_uv * perspective_divisor;
    411     #else
    412         vec2 uv = compute_repeated_uvs(perspective_divisor);
    413     #endif
    414 
    415     #ifdef WR_FEATURE_ALPHA_PASS
    416     if (v_color != vec4(1.0)) {
    417         #ifdef WR_FEATURE_REPETITION
    418             swgl_commitTextureRepeatColorRGBA8(sColor0, uv, v_tile_repeat_bounds, v_uv_bounds, v_uv_sample_bounds, v_color);
    419         #else
    420             swgl_commitTextureColorRGBA8(sColor0, uv, v_uv_sample_bounds, v_color);
    421         #endif
    422         return;
    423     }
    424     // No color scaling required, so just fall through to a normal textured span...
    425     #endif
    426 
    427     #ifdef WR_FEATURE_REPETITION
    428         #ifdef WR_FEATURE_ALPHA_PASS
    429             swgl_commitTextureRepeatRGBA8(sColor0, uv, v_tile_repeat_bounds, v_uv_bounds, v_uv_sample_bounds);
    430         #else
    431             swgl_commitTextureRepeatRGBA8(sColor0, uv, vec2(0.0), v_uv_bounds, v_uv_sample_bounds);
    432         #endif
    433     #else
    434         swgl_commitTextureRGBA8(sColor0, uv, v_uv_sample_bounds);
    435     #endif
    436 }
    437 #endif
    438 
    439 #endif