diff options
Diffstat (limited to 'gfx/wr/webrender/res/brush_image.glsl')
-rw-r--r-- | gfx/wr/webrender/res/brush_image.glsl | 423 |
1 files changed, 423 insertions, 0 deletions
diff --git a/gfx/wr/webrender/res/brush_image.glsl b/gfx/wr/webrender/res/brush_image.glsl new file mode 100644 index 0000000000..47a6e8bdcc --- /dev/null +++ b/gfx/wr/webrender/res/brush_image.glsl @@ -0,0 +1,423 @@ +/* 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/. */ + +#define VECS_PER_SPECIFIC_BRUSH 3 + +#include shared,prim_shared,brush + +#ifdef WR_FEATURE_ALPHA_PASS +varying vec2 v_local_pos; +#endif + +// Interpolated UV coordinates to sample. +varying vec2 v_uv; + +#ifdef WR_FEATURE_ALPHA_PASS +flat varying vec4 v_color; +flat varying vec2 v_mask_swizzle; +flat varying vec2 v_tile_repeat; +#endif + +// Normalized bounds of the source image in the texture. +flat varying vec4 v_uv_bounds; +// Normalized bounds of the source image in the texture, adjusted to avoid +// sampling artifacts. +flat varying vec4 v_uv_sample_bounds; +// x: Layer index to sample. +// y: Flag to allow perspective interpolation of UV. +flat varying vec2 v_layer_and_perspective; + +#ifdef WR_VERTEX_SHADER + +// Must match the AlphaType enum. +#define BLEND_MODE_ALPHA 0 +#define BLEND_MODE_PREMUL_ALPHA 1 + +struct ImageBrushData { + vec4 color; + vec4 background_color; + vec2 stretch_size; +}; + +ImageBrushData fetch_image_data(int address) { + vec4[3] raw_data = fetch_from_gpu_cache_3(address); + ImageBrushData data = ImageBrushData( + raw_data[0], + raw_data[1], + raw_data[2].xy + ); + return data; +} + +void brush_vs( + VertexInfo vi, + int prim_address, + RectWithSize prim_rect, + RectWithSize segment_rect, + ivec4 prim_user_data, + int specific_resource_address, + mat4 transform, + PictureTask pic_task, + int brush_flags, + vec4 segment_data +) { + ImageBrushData image_data = fetch_image_data(prim_address); + + // If this is in WR_FEATURE_TEXTURE_RECT mode, the rect and size use + // non-normalized texture coordinates. +#ifdef WR_FEATURE_TEXTURE_RECT + vec2 texture_size = vec2(1, 1); +#else + vec2 texture_size = vec2(textureSize(sColor0, 0)); +#endif + + ImageResource res = fetch_image_resource(specific_resource_address); + vec2 uv0 = res.uv_rect.p0; + vec2 uv1 = res.uv_rect.p1; + + RectWithSize local_rect = prim_rect; + vec2 stretch_size = image_data.stretch_size; + if (stretch_size.x < 0.0) { + stretch_size = local_rect.size; + } + + // If this segment should interpolate relative to the + // segment, modify the parameters for that. + if ((brush_flags & BRUSH_FLAG_SEGMENT_RELATIVE) != 0) { + local_rect = segment_rect; + stretch_size = local_rect.size; + + if ((brush_flags & BRUSH_FLAG_TEXEL_RECT) != 0) { + // If the extra data is a texel rect, modify the UVs. + vec2 uv_size = res.uv_rect.p1 - res.uv_rect.p0; + uv0 = res.uv_rect.p0 + segment_data.xy * uv_size; + uv1 = res.uv_rect.p0 + segment_data.zw * uv_size; + } + + #ifdef WR_FEATURE_REPETITION + // TODO(bug 1609893): Move this logic to the CPU as well as other sources of + // branchiness in this shader. + if ((brush_flags & BRUSH_FLAG_TEXEL_RECT) != 0) { + // Value of the stretch size with repetition. We have to compute it for + // both axis even if we only repeat on one axis because the value for + // each axis depends on what the repeated value would have been for the + // other axis. + vec2 repeated_stretch_size = stretch_size; + // Size of the uv rect of the segment we are considering when computing + // the repetitions. For the fill area it is a tad more complicated as we + // have to use the uv size of the top-middle segment to drive horizontal + // repetitions, and the size of the left-middle segment to drive vertical + // repetitions. So we track the reference sizes for both axis separately + // even though in the common case (the border segments) they are the same. + vec2 horizontal_uv_size = uv1 - uv0; + vec2 vertical_uv_size = uv1 - uv0; + // We use top and left sizes by default and fall back to bottom and right + // when a size is empty. + if ((brush_flags & BRUSH_FLAG_SEGMENT_NINEPATCH_MIDDLE) != 0) { + repeated_stretch_size = segment_rect.p0 - prim_rect.p0; + + float epsilon = 0.001; + + // Adjust the the referecne uv size to compute vertical repetitions for + // the fill area. + vertical_uv_size.x = uv0.x - res.uv_rect.p0.x; + if (vertical_uv_size.x < epsilon || repeated_stretch_size.x < epsilon) { + vertical_uv_size.x = res.uv_rect.p1.x - uv1.x; + repeated_stretch_size.x = prim_rect.p0.x + prim_rect.size.x + - segment_rect.p0.x - segment_rect.size.x; + } + + // Adjust the the referecne uv size to compute horizontal repetitions + // for the fill area. + horizontal_uv_size.y = uv0.y - res.uv_rect.p0.y; + if (horizontal_uv_size.y < epsilon || repeated_stretch_size.y < epsilon) { + horizontal_uv_size.y = res.uv_rect.p1.y - uv1.y; + repeated_stretch_size.y = prim_rect.p0.y + prim_rect.size.y + - segment_rect.p0.y - segment_rect.size.y; + } + } + + if ((brush_flags & BRUSH_FLAG_SEGMENT_REPEAT_X) != 0) { + float uv_ratio = horizontal_uv_size.x / horizontal_uv_size.y; + stretch_size.x = repeated_stretch_size.y * uv_ratio; + } + if ((brush_flags & BRUSH_FLAG_SEGMENT_REPEAT_Y) != 0) { + float uv_ratio = vertical_uv_size.y / vertical_uv_size.x; + stretch_size.y = repeated_stretch_size.x * uv_ratio; + } + + } else { + if ((brush_flags & BRUSH_FLAG_SEGMENT_REPEAT_X) != 0) { + stretch_size.x = segment_data.z - segment_data.x; + } + if ((brush_flags & BRUSH_FLAG_SEGMENT_REPEAT_Y) != 0) { + stretch_size.y = segment_data.w - segment_data.y; + } + } + if ((brush_flags & BRUSH_FLAG_SEGMENT_REPEAT_X_ROUND) != 0) { + float nx = max(1.0, round(segment_rect.size.x / stretch_size.x)); + stretch_size.x = segment_rect.size.x / nx; + } + if ((brush_flags & BRUSH_FLAG_SEGMENT_REPEAT_Y_ROUND) != 0) { + float ny = max(1.0, round(segment_rect.size.y / stretch_size.y)); + stretch_size.y = segment_rect.size.y / ny; + } + #endif + } + + float perspective_interpolate = (brush_flags & BRUSH_FLAG_PERSPECTIVE_INTERPOLATION) != 0 ? 1.0 : 0.0; + v_layer_and_perspective = vec2(res.layer, perspective_interpolate); + + // Handle case where the UV coords are inverted (e.g. from an + // external image). + vec2 min_uv = min(uv0, uv1); + vec2 max_uv = max(uv0, uv1); + + v_uv_sample_bounds = vec4( + min_uv + vec2(0.5), + max_uv - vec2(0.5) + ) / texture_size.xyxy; + + vec2 f = (vi.local_pos - local_rect.p0) / local_rect.size; + +#ifdef WR_FEATURE_ALPHA_PASS + int color_mode = prim_user_data.x & 0xffff; + int blend_mode = prim_user_data.x >> 16; + int raster_space = prim_user_data.y; + + if (color_mode == COLOR_MODE_FROM_PASS) { + color_mode = uMode; + } + + // Derive the texture coordinates for this image, based on + // whether the source image is a local-space or screen-space + // image. + switch (raster_space) { + case RASTER_SCREEN: { + // Since the screen space UVs specify an arbitrary quad, do + // a bilinear interpolation to get the correct UV for this + // local position. + f = get_image_quad_uv(specific_resource_address, f); + break; + } + default: + break; + } +#endif + + // Offset and scale v_uv here to avoid doing it in the fragment shader. + vec2 repeat = local_rect.size / stretch_size; + v_uv = mix(uv0, uv1, f) - min_uv; + v_uv /= texture_size; + v_uv *= repeat.xy; + if (perspective_interpolate == 0.0) { + v_uv *= vi.world_pos.w; + } + +#ifdef WR_FEATURE_TEXTURE_RECT + v_uv_bounds = vec4(0.0, 0.0, vec2(textureSize(sColor0))); +#else + v_uv_bounds = vec4(min_uv, max_uv) / texture_size.xyxy; +#endif + +#ifdef WR_FEATURE_REPETITION + // Normalize UV to 0..1 scale only if using repetition. Otherwise, leave + // UVs unnormalized since we won't compute a modulus without repetition + // enabled. + v_uv /= (v_uv_bounds.zw - v_uv_bounds.xy); +#endif + +#ifdef WR_FEATURE_ALPHA_PASS + v_tile_repeat = repeat.xy; + + float opacity = float(prim_user_data.z) / 65535.0; + switch (blend_mode) { + case BLEND_MODE_ALPHA: + image_data.color.a *= opacity; + break; + case BLEND_MODE_PREMUL_ALPHA: + default: + image_data.color *= opacity; + break; + } + + switch (color_mode) { + case COLOR_MODE_ALPHA: + case COLOR_MODE_BITMAP: + v_mask_swizzle = vec2(0.0, 1.0); + v_color = image_data.color; + break; + case COLOR_MODE_SUBPX_BG_PASS2: + case COLOR_MODE_SUBPX_DUAL_SOURCE: + case COLOR_MODE_IMAGE: + v_mask_swizzle = vec2(1.0, 0.0); + v_color = image_data.color; + break; + case COLOR_MODE_SUBPX_CONST_COLOR: + case COLOR_MODE_SUBPX_BG_PASS0: + case COLOR_MODE_COLOR_BITMAP: + v_mask_swizzle = vec2(1.0, 0.0); + v_color = vec4(image_data.color.a); + break; + case COLOR_MODE_SUBPX_BG_PASS1: + v_mask_swizzle = vec2(-1.0, 1.0); + v_color = vec4(image_data.color.a) * image_data.background_color; + break; + default: + v_mask_swizzle = vec2(0.0); + v_color = vec4(1.0); + } + + v_local_pos = vi.local_pos; +#endif +} +#endif + +#ifdef WR_FRAGMENT_SHADER + +vec2 compute_repeated_uvs(float perspective_divisor) { + vec2 uv_size = v_uv_bounds.zw - v_uv_bounds.xy; + +#ifdef WR_FEATURE_ALPHA_PASS + // This prevents the uv on the top and left parts of the primitive that was inflated + // for anti-aliasing purposes from going beyound the range covered by the regular + // (non-inflated) primitive. + vec2 local_uv = max(v_uv * perspective_divisor, vec2(0.0)); + + // Handle horizontal and vertical repetitions. + vec2 repeated_uv = fract(local_uv) * uv_size + v_uv_bounds.xy; + + // This takes care of the bottom and right inflated parts. + // We do it after the modulo because the latter wraps around the values exactly on + // the right and bottom edges, which we do not want. + if (local_uv.x >= v_tile_repeat.x) { + repeated_uv.x = v_uv_bounds.z; + } + if (local_uv.y >= v_tile_repeat.y) { + repeated_uv.y = v_uv_bounds.w; + } +#else + vec2 repeated_uv = fract(v_uv * perspective_divisor) * uv_size + v_uv_bounds.xy; +#endif + + return repeated_uv; +} + +Fragment brush_fs() { + float perspective_divisor = mix(gl_FragCoord.w, 1.0, v_layer_and_perspective.y); + +#ifdef WR_FEATURE_REPETITION + vec2 repeated_uv = compute_repeated_uvs(perspective_divisor); +#else + vec2 repeated_uv = v_uv * perspective_divisor + v_uv_bounds.xy; +#endif + + // Clamp the uvs to avoid sampling artifacts. + vec2 uv = clamp(repeated_uv, v_uv_sample_bounds.xy, v_uv_sample_bounds.zw); + + vec4 texel = TEX_SAMPLE(sColor0, vec3(uv, v_layer_and_perspective.x)); + + Fragment frag; + +#ifdef WR_FEATURE_ALPHA_PASS + #ifdef WR_FEATURE_ANTIALIASING + float alpha = init_transform_fs(v_local_pos); + #else + float alpha = 1.0; + #endif + texel.rgb = texel.rgb * v_mask_swizzle.x + texel.aaa * v_mask_swizzle.y; + + vec4 alpha_mask = texel * alpha; + frag.color = v_color * alpha_mask; + + #ifdef WR_FEATURE_DUAL_SOURCE_BLENDING + frag.blend = alpha_mask * v_color.a; + #endif +#else + frag.color = texel; +#endif + + return frag; +} + +#if defined(SWGL) && (!defined(WR_FEATURE_ALPHA_PASS) || !defined(WR_FEATURE_DUAL_SOURCE_BLENDING)) +void swgl_drawSpanRGBA8() { + if (!swgl_isTextureRGBA8(sColor0) || !swgl_isTextureLinear(sColor0)) { + return; + } + + #ifdef WR_FEATURE_ALPHA_PASS + if (v_mask_swizzle != vec2(1.0, 0.0)) { + return; + } + #endif + + int layer = swgl_textureLayerOffset(sColor0, v_layer_and_perspective.x); + + float perspective_divisor = mix(swgl_forceScalar(gl_FragCoord.w), 1.0, v_layer_and_perspective.y); + + #ifndef WR_FEATURE_REPETITION + vec2 uv = v_uv * perspective_divisor + v_uv_bounds.xy; + + #ifndef WR_FEATURE_ANTIALIASING + if (swgl_allowTextureNearest(sColor0, uv)) { + #ifdef WR_FEATURE_ALPHA_PASS + if (v_color != vec4(1.0)) { + swgl_commitTextureNearestColorRGBA8(sColor0, uv, v_uv_sample_bounds, v_color, layer); + return; + } + #endif + swgl_commitTextureNearestRGBA8(sColor0, uv, v_uv_sample_bounds, layer); + return; + } + #endif + + uv = swgl_linearQuantize(sColor0, uv); + vec2 min_uv = swgl_linearQuantize(sColor0, v_uv_sample_bounds.xy); + vec2 max_uv = swgl_linearQuantize(sColor0, v_uv_sample_bounds.zw); + vec2 step_uv = swgl_linearQuantizeStep(sColor0, swgl_interpStep(v_uv)) * perspective_divisor; + #endif + + #ifdef WR_FEATURE_ALPHA_PASS + #ifdef WR_FEATURE_ANTIALIASING + { + #else + if (v_color != vec4(1.0)) { + #endif + while (swgl_SpanLength > 0) { + vec4 color = v_color; + #ifdef WR_FEATURE_ANTIALIASING + color *= init_transform_fs(v_local_pos); + v_local_pos += swgl_interpStep(v_local_pos); + #endif + #ifdef WR_FEATURE_REPETITION + vec2 repeated_uv = compute_repeated_uvs(perspective_divisor); + vec2 uv = clamp(repeated_uv, v_uv_sample_bounds.xy, v_uv_sample_bounds.zw); + swgl_commitTextureLinearColorRGBA8(sColor0, swgl_linearQuantize(sColor0, uv), color, layer); + v_uv += swgl_interpStep(v_uv); + #else + swgl_commitTextureLinearColorRGBA8(sColor0, clamp(uv, min_uv, max_uv), color, layer); + uv += step_uv; + #endif + } + return; + } + // No clip or color scaling required, so just fall through to a normal textured span... + #endif + + while (swgl_SpanLength > 0) { + #ifdef WR_FEATURE_REPETITION + vec2 repeated_uv = compute_repeated_uvs(perspective_divisor); + vec2 uv = clamp(repeated_uv, v_uv_sample_bounds.xy, v_uv_sample_bounds.zw); + swgl_commitTextureLinearRGBA8(sColor0, swgl_linearQuantize(sColor0, uv), layer); + v_uv += swgl_interpStep(v_uv); + #else + swgl_commitTextureLinearRGBA8(sColor0, clamp(uv, min_uv, max_uv), layer); + uv += step_uv; + #endif + } +} +#endif + +#endif |