Bug 1857841 - pt 3. Add a new page kind named "fresh" r=glandium
[gecko.git] / gfx / wr / webrender / res / brush_image.glsl
blob4a0cfe2229af42686ac0f0f98580973936489b91
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/. */
5 #define VECS_PER_SPECIFIC_BRUSH 3
7 #include shared,prim_shared,brush
9 // Interpolated UV coordinates to sample.
10 varying highp vec2 v_uv;
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
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;
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;
28 #ifdef WR_VERTEX_SHADER
30 // Must match the AlphaType enum.
31 #define BLEND_MODE_ALPHA            0
32 #define BLEND_MODE_PREMUL_ALPHA     1
34 struct ImageBrushData {
35     vec4 color;
36     vec4 background_color;
37     vec2 stretch_size;
40 ImageBrushData fetch_image_data(int address) {
41     vec4[3] raw_data = fetch_from_gpu_cache_3(address);
42     ImageBrushData data = ImageBrushData(
43         raw_data[0],
44         raw_data[1],
45         raw_data[2].xy
46     );
47     return data;
50 vec2 modf2(vec2 x, vec2 y) {
51     return x - y * floor(x/y);
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     ImageBrushData image_data = fetch_image_data(prim_address);
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
76     ImageSource res = fetch_image_source(specific_resource_address);
77     vec2 uv0 = res.uv_rect.p0;
78     vec2 uv1 = res.uv_rect.p1;
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     }
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);
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         }
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;
121                     float epsilon = 0.001;
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                     }
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                 }
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                 }
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     }
170     float perspective_interpolate = (brush_flags & BRUSH_FLAG_PERSPECTIVE_INTERPOLATION) != 0 ? 1.0 : 0.0;
171     v_perspective.x = perspective_interpolate;
173     // Handle case where the UV coords are inverted (e.g. from an
174     // external image).
175     vec2 min_uv = min(uv0, uv1);
176     vec2 max_uv = max(uv0, uv1);
178     v_uv_sample_bounds = vec4(
179         min_uv + vec2(0.5),
180         max_uv - vec2(0.5)
181     ) / texture_size.xyxy;
183     vec2 f = (vi.local_pos - local_rect.p0) / rect_size(local_rect);
185 #ifdef WR_FEATURE_ALPHA_PASS
186     int color_mode = prim_user_data.x & 0xffff;
187     int blend_mode = prim_user_data.x >> 16;
189 #endif
191     // Derive the texture coordinates for this image, based on
192     // whether the source image is a local-space or screen-space
193     // image.
194     int raster_space = prim_user_data.y;
195     if (raster_space == RASTER_SCREEN) {
196         // Since the screen space UVs specify an arbitrary quad, do
197         // a bilinear interpolation to get the correct UV for this
198         // local position.
199         f = get_image_quad_uv(specific_resource_address, f);
200     }
202     // Offset and scale v_uv here to avoid doing it in the fragment shader.
203     vec2 repeat = rect_size(local_rect) / stretch_size;
204     v_uv = mix(uv0, uv1, f) - min_uv;
205     v_uv *= repeat.xy;
207     vec2 normalized_offset = vec2(0.0);
208 #ifdef WR_FEATURE_REPETITION
209     // In the case of border-image-repeat: repeat, we must apply an offset so that
210     // the first tile is centered.
211     //
212     // This is derived from:
213     //   uv_size = max_uv - min_uv
214     //   repeat = local_rect.size / stetch_size
215     //   layout_offset = local_rect.size / 2 - strecth_size / 2
216     //   texel_offset = layout_offset * uv_size / stretch_size
217     //   texel_offset = uv_size / 2 * (local_rect.size / stretch_size - stretch_size / stretch_size)
218     //   texel_offset = uv_size / 2 * (repeat - 1)
219     //
220     // The offset is then adjusted so that it loops in the [0, uv_size] range.
221     // In principle this is simply a modulo:
222     //
223     //   adjusted_offset = fact((repeat - 1)/2) * uv_size
224     //
225     // However we don't want fract's behavior with negative numbers which happens when the pattern
226     // is larger than the local rect (repeat is between 0 and 1), so we shift the content by 1 to
227     // remain positive.
228     //
229     //   adjusted_offset = fract(repeat/2 - 1/2 + 1) * uv_size
230     //
231     // `uv - offset` will go through another modulo in the fragment shader for which we again don't
232     // want the behavior for nagative numbers. We rearrange this here in the form
233     // `uv + (uv_size - offset)` to prevent that.
234     //
235     //   adjusted_offset = (1 - fract(repeat/2 - 1/2 + 1)) * uv_size
236     //
237     // We then separate the normalized part of the offset which we also need elsewhere.
238     bvec2 centered = bvec2(brush_flags & BRUSH_FLAG_SEGMENT_REPEAT_X_CENTERED,
239                            brush_flags & BRUSH_FLAG_SEGMENT_REPEAT_Y_CENTERED);
240     // Use mix() rather than if statements due to a miscompilation on Adreno 3xx. See bug 1853573.
241     normalized_offset = mix(vec2(0.0), 1.0 - fract(repeat * 0.5 + 0.5), centered);
242     v_uv += normalized_offset * (max_uv - min_uv);
243 #endif
244     v_uv /= texture_size;
245     if (perspective_interpolate == 0.0) {
246         v_uv *= vi.world_pos.w;
247     }
249 #ifdef WR_FEATURE_TEXTURE_RECT
250     v_uv_bounds = vec4(0.0, 0.0, vec2(textureSize(sColor0)));
251 #else
252     v_uv_bounds = vec4(min_uv, max_uv) / texture_size.xyxy;
253 #endif
255 #ifdef WR_FEATURE_REPETITION
256     // Normalize UV to 0..1 scale only if using repetition. Otherwise, leave
257     // UVs unnormalized since we won't compute a modulus without repetition
258     // enabled.
259     v_uv /= (v_uv_bounds.zw - v_uv_bounds.xy);
260 #endif
262 #ifdef WR_FEATURE_ALPHA_PASS
263     v_tile_repeat_bounds = repeat.xy + normalized_offset;
265     float opacity = float(prim_user_data.z) / 65535.0;
266     switch (blend_mode) {
267         case BLEND_MODE_ALPHA:
268             image_data.color.a *= opacity;
269             break;
270         case BLEND_MODE_PREMUL_ALPHA:
271         default:
272             image_data.color *= opacity;
273             break;
274     }
276     switch (color_mode) {
277         case COLOR_MODE_ALPHA:
278         case COLOR_MODE_BITMAP_SHADOW:
279             #ifdef SWGL_BLEND
280                 swgl_blendDropShadow(image_data.color);
281                 v_mask_swizzle = vec2(1.0, 0.0);
282                 v_color = vec4(1.0);
283             #else
284                 v_mask_swizzle = vec2(0.0, 1.0);
285                 v_color = image_data.color;
286             #endif
287             break;
288         case COLOR_MODE_IMAGE:
289             v_mask_swizzle = vec2(1.0, 0.0);
290             v_color = image_data.color;
291             break;
292         case COLOR_MODE_COLOR_BITMAP:
293             v_mask_swizzle = vec2(1.0, 0.0);
294             v_color = vec4(image_data.color.a);
295             break;
296         case COLOR_MODE_SUBPX_DUAL_SOURCE:
297             v_mask_swizzle = vec2(image_data.color.a, 0.0);
298             v_color = image_data.color;
299             break;
300         case COLOR_MODE_MULTIPLY_DUAL_SOURCE:
301             v_mask_swizzle = vec2(-image_data.color.a, image_data.color.a);
302             v_color = image_data.color;
303             break;
304         default:
305             v_mask_swizzle = vec2(0.0);
306             v_color = vec4(1.0);
307     }
308 #endif
310 #endif
312 #ifdef WR_FRAGMENT_SHADER
314 vec2 compute_repeated_uvs(float perspective_divisor) {
315 #ifdef WR_FEATURE_REPETITION
316     vec2 uv_size = v_uv_bounds.zw - v_uv_bounds.xy;
318     #ifdef WR_FEATURE_ALPHA_PASS
319     vec2 local_uv = v_uv * perspective_divisor;
320     // This prevents the uv on the top and left parts of the primitive that was inflated
321     // for anti-aliasing purposes from going beyound the range covered by the regular
322     // (non-inflated) primitive.
323     local_uv = max(local_uv, vec2(0.0));
325     // Handle horizontal and vertical repetitions.
326     vec2 repeated_uv = fract(local_uv) * uv_size + v_uv_bounds.xy;
328     // This takes care of the bottom and right inflated parts.
329     // We do it after the modulo because the latter wraps around the values exactly on
330     // the right and bottom edges, which we do not want.
331     if (local_uv.x >= v_tile_repeat_bounds.x) {
332         repeated_uv.x = v_uv_bounds.z;
333     }
334     if (local_uv.y >= v_tile_repeat_bounds.y) {
335         repeated_uv.y = v_uv_bounds.w;
336     }
337     #else
338     vec2 repeated_uv = fract(v_uv * perspective_divisor) * uv_size + v_uv_bounds.xy;
339     #endif
341     return repeated_uv;
342 #else
343     return v_uv * perspective_divisor + v_uv_bounds.xy;
344 #endif
347 Fragment brush_fs() {
348     float perspective_divisor = mix(gl_FragCoord.w, 1.0, v_perspective.x);
349     vec2 repeated_uv = compute_repeated_uvs(perspective_divisor);
351     // Clamp the uvs to avoid sampling artifacts.
352     vec2 uv = clamp(repeated_uv, v_uv_sample_bounds.xy, v_uv_sample_bounds.zw);
354     vec4 texel = TEX_SAMPLE(sColor0, uv);
356     Fragment frag;
358 #ifdef WR_FEATURE_ALPHA_PASS
359     #ifdef WR_FEATURE_ANTIALIASING
360         float alpha = antialias_brush();
361     #else
362         float alpha = 1.0;
363     #endif
364     #ifndef WR_FEATURE_DUAL_SOURCE_BLENDING
365         texel.rgb = texel.rgb * v_mask_swizzle.x + texel.aaa * v_mask_swizzle.y;
366     #endif
368     vec4 alpha_mask = texel * alpha;
369     frag.color = v_color * alpha_mask;
371     #ifdef WR_FEATURE_DUAL_SOURCE_BLENDING
372         frag.blend = alpha_mask * v_mask_swizzle.x + alpha_mask.aaaa * v_mask_swizzle.y;
373     #endif
374 #else
375     frag.color = texel;
376 #endif
378     return frag;
381 #if defined(SWGL_DRAW_SPAN) && (!defined(WR_FEATURE_ALPHA_PASS) || !defined(WR_FEATURE_DUAL_SOURCE_BLENDING))
382 void swgl_drawSpanRGBA8() {
383     if (!swgl_isTextureRGBA8(sColor0)) {
384         return;
385     }
387     #ifdef WR_FEATURE_ALPHA_PASS
388         if (v_mask_swizzle != vec2(1.0, 0.0)) {
389             return;
390         }
391     #endif
393     float perspective_divisor = mix(swgl_forceScalar(gl_FragCoord.w), 1.0, v_perspective.x);
395     #ifdef WR_FEATURE_REPETITION
396         // Get the UVs before any repetition, scaling, or offsetting has occurred...
397         vec2 uv = v_uv * perspective_divisor;
398     #else
399         vec2 uv = compute_repeated_uvs(perspective_divisor);
400     #endif
402     #ifdef WR_FEATURE_ALPHA_PASS
403     if (v_color != vec4(1.0)) {
404         #ifdef WR_FEATURE_REPETITION
405             swgl_commitTextureRepeatColorRGBA8(sColor0, uv, v_tile_repeat_bounds, v_uv_bounds, v_uv_sample_bounds, v_color);
406         #else
407             swgl_commitTextureColorRGBA8(sColor0, uv, v_uv_sample_bounds, v_color);
408         #endif
409         return;
410     }
411     // No color scaling required, so just fall through to a normal textured span...
412     #endif
414     #ifdef WR_FEATURE_REPETITION
415         #ifdef WR_FEATURE_ALPHA_PASS
416             swgl_commitTextureRepeatRGBA8(sColor0, uv, v_tile_repeat_bounds, v_uv_bounds, v_uv_sample_bounds);
417         #else
418             swgl_commitTextureRepeatRGBA8(sColor0, uv, vec2(0.0), v_uv_bounds, v_uv_sample_bounds);
419         #endif
420     #else
421         swgl_commitTextureRGBA8(sColor0, uv, v_uv_sample_bounds);
422     #endif
424 #endif
426 #endif