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