diff --git a/webrender/res/prim_shared.glsl b/webrender/res/prim_shared.glsl index 4c65969f57..80d02e2632 100644 --- a/webrender/res/prim_shared.glsl +++ b/webrender/res/prim_shared.glsl @@ -850,11 +850,18 @@ vec4 dither(vec4 color) { #endif //WR_FEATURE_DITHERING vec4 sample_gradient(float offset, float gradient_repeat, float gradient_index, vec2 gradient_size) { - // Either saturate or modulo the offset depending on repeat mode - float x = mix(clamp(offset, 0.0, 1.0), fract(offset), gradient_repeat); - - // Scale to the number of gradient color entries (texture width / 2). - x = x * 0.5 * gradient_size.x; + // Modulo the offset if the gradient repeats. We don't need to clamp non-repeating + // gradients because the gradient data texture is bound with CLAMP_TO_EDGE, and the + // first and last color entries are filled with the first and last stop colors + float x = mix(offset, fract(offset), gradient_repeat); + + // Calculate the color entry index to use for this offset: + // offsets < 0 use the first color entry, 0 + // offsets from [0, 1) use the color entries in the range of [1, N-1) + // offsets >= 1 use the last color entry, N-1 + // so transform the range [0, 1) -> [1, N-1) + float gradient_entries = 0.5 * gradient_size.x; + x = x * (gradient_entries - 2.0) + 1.0; // Calculate the texel to index into the gradient color entries: // floor(x) is the gradient color entry index diff --git a/webrender/src/prim_store.rs b/webrender/src/prim_store.rs index 02710fee90..17188dcf02 100644 --- a/webrender/src/prim_store.rs +++ b/webrender/src/prim_store.rs @@ -299,8 +299,20 @@ pub struct RadialGradientPrimitiveCpu { pub cache_dirty: bool, } -// The number of entries in a gradient data table. -pub const GRADIENT_DATA_RESOLUTION: usize = 128; +// The gradient entry index for the first color stop +pub const GRADIENT_DATA_FIRST_STOP: usize = 0; +// The gradient entry index for the last color stop +pub const GRADIENT_DATA_LAST_STOP: usize = GRADIENT_DATA_SIZE - 1; + +// The start of the gradient data table +pub const GRADIENT_DATA_TABLE_BEGIN: usize = GRADIENT_DATA_FIRST_STOP + 1; +// The exclusive bound of the gradient data table +pub const GRADIENT_DATA_TABLE_END: usize = GRADIENT_DATA_LAST_STOP; +// The number of entries in the gradient data table. +pub const GRADIENT_DATA_TABLE_SIZE: usize = 128; + +// The number of entries in a gradient data: GRADIENT_DATA_TABLE_SIZE + first stop entry + last stop entry +pub const GRADIENT_DATA_SIZE: usize = GRADIENT_DATA_TABLE_SIZE + 2; #[derive(Debug, Clone, Copy)] #[repr(C)] @@ -317,10 +329,12 @@ pub struct GradientDataEntry { // the offset within that entry bucket is used to interpolate between the two colors in that entry. // This layout preserves hard stops, as the end color for a given entry can differ from the start // color for the following entry, despite them being adjacent. Colors are stored within in BGRA8 -// format for texture upload. +// format for texture upload. This table requires the gradient color stops to be normalized to the +// range [0, 1]. The first and last entries hold the first and last color stop colors respectively, +// while the entries in between hold the interpolated color stop values for the range [0, 1]. pub struct GradientData { - pub colors_high: [GradientDataEntry; GRADIENT_DATA_RESOLUTION], - pub colors_low: [GradientDataEntry; GRADIENT_DATA_RESOLUTION], + pub colors_high: [GradientDataEntry; GRADIENT_DATA_SIZE], + pub colors_low: [GradientDataEntry; GRADIENT_DATA_SIZE], } impl Default for GradientData { @@ -342,7 +356,8 @@ impl Clone for GradientData { } impl GradientData { - // Generate a color ramp between the start and end indexes from a start color to an end color. + /// Generate a color ramp filling the indices in [start_idx, end_idx) and interpolating + /// from start_color to end_color. fn fill_colors(&mut self, start_idx: usize, end_idx: usize, start_color: &ColorF, end_color: &ColorF) { // Calculate the color difference for individual steps in the ramp. let inv_steps = 1.0 / (end_idx - start_idx) as f32; @@ -373,18 +388,18 @@ impl GradientData { } } - // Compute an entry index based on a gradient stop offset. + /// Compute an index into the gradient entry table based on a gradient stop offset. This + /// function maps offsets from [0, 1] to indices in [GRADIENT_DATA_TABLE_BEGIN, GRADIENT_DATA_TABLE_END]. #[inline] fn get_index(offset: f32) -> usize { - (offset.max(0.0).min(1.0) * GRADIENT_DATA_RESOLUTION as f32).round() as usize + (offset.max(0.0).min(1.0) + * GRADIENT_DATA_TABLE_SIZE as f32 + + GRADIENT_DATA_TABLE_BEGIN as f32).round() as usize } // Build the gradient data from the supplied stops, reversing them if necessary. fn build(&mut self, src_stops: AuxIter, reverse_stops: bool) { - const MAX_IDX: usize = GRADIENT_DATA_RESOLUTION; - const MIN_IDX: usize = 0; - // Preconditions (should be ensured by DisplayListBuilder): // * we have at least two stops // * first stop has offset 0.0 @@ -396,8 +411,13 @@ impl GradientData { debug_assert_eq!(first.offset, 0.0); if reverse_stops { - // If the gradient is reversed, then we invert offsets and draw right-to-left - let mut cur_idx = MAX_IDX; + // Fill in the first entry (for reversed stops) with the first color stop + self.fill_colors(GRADIENT_DATA_LAST_STOP, GRADIENT_DATA_LAST_STOP + 1, &cur_color, &cur_color); + + // Fill in the center of the gradient table, generating a color ramp between each consecutive pair + // of gradient stops. Each iteration of a loop will fill the indices in [next_idx, cur_idx). The + // loop will then fill indices in [GRADIENT_DATA_TABLE_BEGIN, GRADIENT_DATA_TABLE_END). + let mut cur_idx = GRADIENT_DATA_TABLE_END; for next in src_stops { let next_idx = Self::get_index(1.0 - next.offset); if next_idx < cur_idx { @@ -407,9 +427,18 @@ impl GradientData { } cur_color = next.color; } - debug_assert_eq!(cur_idx, MIN_IDX); + debug_assert_eq!(cur_idx, GRADIENT_DATA_TABLE_BEGIN); + + // Fill in the last entry (for reversed stops) with the last color stop + self.fill_colors(GRADIENT_DATA_FIRST_STOP, GRADIENT_DATA_FIRST_STOP + 1, &cur_color, &cur_color); } else { - let mut cur_idx = MIN_IDX; + // Fill in the first entry with the first color stop + self.fill_colors(GRADIENT_DATA_FIRST_STOP, GRADIENT_DATA_FIRST_STOP + 1, &cur_color, &cur_color); + + // Fill in the center of the gradient table, generating a color ramp between each consecutive pair + // of gradient stops. Each iteration of a loop will fill the indices in [cur_idx, next_idx). The + // loop will then fill indices in [GRADIENT_DATA_TABLE_BEGIN, GRADIENT_DATA_TABLE_END). + let mut cur_idx = GRADIENT_DATA_TABLE_BEGIN; for next in src_stops { let next_idx = Self::get_index(next.offset); if next_idx > cur_idx { @@ -419,7 +448,10 @@ impl GradientData { } cur_color = next.color; } - debug_assert_eq!(cur_idx, MAX_IDX); + debug_assert_eq!(cur_idx, GRADIENT_DATA_TABLE_END); + + // Fill in the last entry with the last color stop + self.fill_colors(GRADIENT_DATA_LAST_STOP, GRADIENT_DATA_LAST_STOP + 1, &cur_color, &cur_color); } } } diff --git a/wrench/reftests/gradient/linear-clamp-1-ref.yaml b/wrench/reftests/gradient/linear-clamp-1-ref.yaml new file mode 100644 index 0000000000..81c366d858 --- /dev/null +++ b/wrench/reftests/gradient/linear-clamp-1-ref.yaml @@ -0,0 +1,8 @@ +--- +root: + items: + - type: gradient + bounds: 50 50 200 200 + start: 0 100 + end: 200 100 + stops: [0.0, blue, 0.5, blue, 0.5, red, 1.0, red] diff --git a/wrench/reftests/gradient/linear-clamp-1a.yaml b/wrench/reftests/gradient/linear-clamp-1a.yaml new file mode 100644 index 0000000000..b83963a37a --- /dev/null +++ b/wrench/reftests/gradient/linear-clamp-1a.yaml @@ -0,0 +1,8 @@ +--- +root: + items: + - type: gradient + bounds: 50 50 200 200 + start: 0 100 + end: 100 100 + stops: [0.0, blue, 1.0, blue, 1.0, red] diff --git a/wrench/reftests/gradient/linear-clamp-1b.yaml b/wrench/reftests/gradient/linear-clamp-1b.yaml new file mode 100644 index 0000000000..ffe3391999 --- /dev/null +++ b/wrench/reftests/gradient/linear-clamp-1b.yaml @@ -0,0 +1,8 @@ +--- +root: + items: + - type: gradient + bounds: 50 50 200 200 + start: 100 100 + end: 200 100 + stops: [0.0, blue, 0.0, red, 1.0, red] diff --git a/wrench/reftests/gradient/linear-clamp-2-ref.yaml b/wrench/reftests/gradient/linear-clamp-2-ref.yaml new file mode 100644 index 0000000000..8eb475d0a5 --- /dev/null +++ b/wrench/reftests/gradient/linear-clamp-2-ref.yaml @@ -0,0 +1,8 @@ +--- +root: + items: + - type: gradient + bounds: 50 50 200 200 + start: 0 100 + end: 200 100 + stops: [0.0, blue, 0.25, blue, 0.25, green, 0.75, green, 0.75, red, 1.0, red] diff --git a/wrench/reftests/gradient/linear-clamp-2.yaml b/wrench/reftests/gradient/linear-clamp-2.yaml new file mode 100644 index 0000000000..48428b974a --- /dev/null +++ b/wrench/reftests/gradient/linear-clamp-2.yaml @@ -0,0 +1,8 @@ +--- +root: + items: + - type: gradient + bounds: 50 50 200 200 + start: 50 100 + end: 150 100 + stops: [0.0, blue, 0.0, green, 1.0, green, 1.0, red] diff --git a/wrench/reftests/gradient/reftest.list b/wrench/reftests/gradient/reftest.list index 467b67c971..0b90112701 100644 --- a/wrench/reftests/gradient/reftest.list +++ b/wrench/reftests/gradient/reftest.list @@ -6,6 +6,10 @@ == linear-reverse.yaml linear-ref.png fuzzy(1,35000) == linear-stops.yaml linear-stops-ref.png +== linear-clamp-1a.yaml linear-clamp-1-ref.yaml +== linear-clamp-1b.yaml linear-clamp-1-ref.yaml +== linear-clamp-2.yaml linear-clamp-2-ref.yaml + # dithering requires us to fuzz here fuzzy(1,20000) == linear.yaml linear-ref.yaml fuzzy(1,20000) == linear-reverse.yaml linear-ref.yaml @@ -35,14 +39,15 @@ fuzzy(255,1200) == repeat-linear-reverse.yaml repeat-linear-ref.yaml fuzzy(255,2664) == repeat-radial.yaml repeat-radial-ref.yaml fuzzy(255,2664) == repeat-radial-negative.yaml repeat-radial-ref.yaml -== tiling-linear-1.yaml tiling-linear-1-ref.yaml -== tiling-linear-2.yaml tiling-linear-2-ref.yaml +# fuzzy because of thin spaced out column of pixels that are 1 off +fuzzy(1,50) == tiling-linear-1.yaml tiling-linear-1-ref.yaml +fuzzy(1,38) == tiling-linear-2.yaml tiling-linear-2-ref.yaml == tiling-linear-3.yaml tiling-linear-3-ref.yaml fuzzy(1,16) == tiling-radial-1.yaml tiling-radial-1-ref.yaml fuzzy(1,1) == tiling-radial-2.yaml tiling-radial-2-ref.yaml == tiling-radial-3.yaml tiling-radial-3-ref.yaml -fuzzy(1,16) == tiling-radial-4.yaml tiling-radial-4-ref.yaml +fuzzy(1,17) == tiling-radial-4.yaml tiling-radial-4-ref.yaml == radial-zero-size-1.yaml radial-zero-size-ref.yaml == radial-zero-size-2.yaml radial-zero-size-ref.yaml