зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1624468 - Add a fast path for more gradient types in WR r=gw
Add support for repeating gradients in the cached fast path. Documentation: https://bugzilla.mozilla.org/attachment.cgi?id=9138638 Differential Revision: https://phabricator.services.mozilla.com/D69476 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
7b5b3c8ae8
Коммит
03acaa106c
|
@ -19,6 +19,7 @@ use crate::prim_store::{PrimKeyCommonData, PrimTemplateCommonData, PrimitiveStor
|
|||
use crate::prim_store::{NinePatchDescriptor, PointKey, SizeKey, InternablePrimitive};
|
||||
use std::{hash, ops::{Deref, DerefMut}};
|
||||
use crate::util::pack_as_float;
|
||||
use crate::texture_cache::TEXTURE_REGION_DIMENSIONS;
|
||||
|
||||
/// The maximum number of stops a gradient may have to use the fast path.
|
||||
pub const GRADIENT_FP_STOPS: usize = 4;
|
||||
|
@ -151,9 +152,7 @@ impl From<LinearGradientKey> for LinearGradientTemplate {
|
|||
// gradient in a smaller task, and drawing as an image.
|
||||
// TODO(gw): Aim to reduce the constraints on fast path gradients in future,
|
||||
// although this catches the vast majority of gradients on real pages.
|
||||
let supports_caching =
|
||||
// No repeating support in fast path
|
||||
item.extend_mode == ExtendMode::Clamp &&
|
||||
let mut supports_caching =
|
||||
// Gradient must cover entire primitive
|
||||
item.tile_spacing.w + item.stretch_size.w >= common.prim_size.width &&
|
||||
item.tile_spacing.h + item.stretch_size.h >= common.prim_size.height &&
|
||||
|
@ -163,6 +162,31 @@ impl From<LinearGradientKey> for LinearGradientTemplate {
|
|||
// Fast path not supported on segmented (border-image) gradients.
|
||||
item.nine_patch.is_none();
|
||||
|
||||
// if we support caching and the gradient uses repeat, we might potentially
|
||||
// emit a lot of quads to cover the primitive. each quad will still cover
|
||||
// the entire gradient along the other axis, so the effect is linear in
|
||||
// display resolution, not quadratic (unlike say a tiny background image
|
||||
// tiling the display). in addition, excessive minification may lead to
|
||||
// texture trashing. so use the minification as a proxy heuristic for both
|
||||
// cases.
|
||||
//
|
||||
// note that the actual number of quads may be further increased due to
|
||||
// hard-stops and/or more than GRADIENT_FP_STOPS stops per gradient.
|
||||
if supports_caching && item.extend_mode == ExtendMode::Repeat {
|
||||
let single_repeat_size =
|
||||
if item.start_point.x.approx_eq(&item.end_point.x) {
|
||||
item.end_point.y - item.start_point.y
|
||||
} else {
|
||||
item.end_point.x - item.start_point.x
|
||||
};
|
||||
let downscaling = single_repeat_size as f32 / TEXTURE_REGION_DIMENSIONS as f32;
|
||||
if downscaling < 0.1 {
|
||||
// if a single copy of the gradient is this small relative to its baked
|
||||
// gradient cache, we have bad texture caching and/or too many quads.
|
||||
supports_caching = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert the stops to more convenient representation
|
||||
// for the current gradient builder.
|
||||
let stops: Vec<GradientStop> = item.stops.iter().map(|stop| {
|
||||
|
|
|
@ -6,7 +6,7 @@ use api::{BorderRadius, ClipMode, ColorF, ColorU};
|
|||
use api::{ImageRendering, RepeatMode, PrimitiveFlags};
|
||||
use api::{PremultipliedColorF, PropertyBinding, Shadow, GradientStop};
|
||||
use api::{BoxShadowClipMode, LineStyle, LineOrientation, BorderStyle};
|
||||
use api::{PrimitiveKeyKind};
|
||||
use api::{PrimitiveKeyKind, ExtendMode};
|
||||
use api::units::*;
|
||||
use crate::border::{get_max_scale_for_border, build_border_instances};
|
||||
use crate::border::BorderSegmentCacheKey;
|
||||
|
@ -3267,17 +3267,20 @@ impl PrimitiveStore {
|
|||
// size of the gradient task is the length of a texture cache
|
||||
// region, for maximum accuracy, and a minimal size on the
|
||||
// axis that doesn't matter.
|
||||
let (size, orientation, start_point, end_point) = if prim_data.start_point.x.approx_eq(&prim_data.end_point.x) {
|
||||
let start_point = -prim_data.start_point.y / gradient_size.height;
|
||||
let end_point = (prim_data.common.prim_size.height - prim_data.start_point.y) / gradient_size.height;
|
||||
let size = DeviceIntSize::new(16, TEXTURE_REGION_DIMENSIONS);
|
||||
(size, LineOrientation::Vertical, start_point, end_point)
|
||||
} else {
|
||||
let start_point = -prim_data.start_point.x / gradient_size.width;
|
||||
let end_point = (prim_data.common.prim_size.width - prim_data.start_point.x) / gradient_size.width;
|
||||
let size = DeviceIntSize::new(TEXTURE_REGION_DIMENSIONS, 16);
|
||||
(size, LineOrientation::Horizontal, start_point, end_point)
|
||||
};
|
||||
let (size, orientation, prim_start_offset, prim_end_offset) =
|
||||
if prim_data.start_point.x.approx_eq(&prim_data.end_point.x) {
|
||||
let prim_start_offset = -prim_data.start_point.y / gradient_size.height;
|
||||
let prim_end_offset = (prim_data.common.prim_size.height - prim_data.start_point.y)
|
||||
/ gradient_size.height;
|
||||
let size = DeviceIntSize::new(16, TEXTURE_REGION_DIMENSIONS);
|
||||
(size, LineOrientation::Vertical, prim_start_offset, prim_end_offset)
|
||||
} else {
|
||||
let prim_start_offset = -prim_data.start_point.x / gradient_size.width;
|
||||
let prim_end_offset = (prim_data.common.prim_size.width - prim_data.start_point.x)
|
||||
/ gradient_size.width;
|
||||
let size = DeviceIntSize::new(TEXTURE_REGION_DIMENSIONS, 16);
|
||||
(size, LineOrientation::Horizontal, prim_start_offset, prim_end_offset)
|
||||
};
|
||||
|
||||
// Build the cache key, including information about the stops.
|
||||
let mut stops = vec![GradientStopKey::empty(); prim_data.stops.len()];
|
||||
|
@ -3298,123 +3301,224 @@ impl PrimitiveStore {
|
|||
}
|
||||
}
|
||||
|
||||
// To support clamping, we need to make sure that quads are emitted for the
|
||||
// segments before and after the 0.0...1.0 range of offsets. The loop below
|
||||
// can handle that by duplicating the first and last point if necessary:
|
||||
if start_point < 0.0 {
|
||||
stops.insert(0, GradientStopKey {
|
||||
offset: start_point,
|
||||
color : stops[0].color
|
||||
});
|
||||
}
|
||||
|
||||
if end_point > 1.0 {
|
||||
stops.push( GradientStopKey {
|
||||
offset: end_point,
|
||||
color : stops[stops.len()-1].color
|
||||
});
|
||||
}
|
||||
|
||||
gradient.cache_segments.clear();
|
||||
|
||||
let mut first_stop = 0;
|
||||
// look for an inclusive range of stops [first_stop, last_stop].
|
||||
// once first_stop points at (or past) the last stop, we're done.
|
||||
while first_stop < stops.len()-1 {
|
||||
// emit render task caches and image rectangles to draw a gradient
|
||||
// with offsets from start_offset to end_offset.
|
||||
//
|
||||
// the primitive is covered by a gradient that ranges from
|
||||
// prim_start_offset to prim_end_offset.
|
||||
//
|
||||
// when clamping, these two pairs of offsets will always be the same.
|
||||
// when repeating, however, we march across the primitive, blitting
|
||||
// copies of the gradient along the way. each copy has a range from
|
||||
// 0.0 to 1.0 (assuming it's fully visible), but where it appears on
|
||||
// the primitive changes as we go. this position is also expressed
|
||||
// as an offset: gradient_offset_base. that is, in terms of stops,
|
||||
// we draw a gradient from start_offset to end_offset. its actual
|
||||
// location on the primitive is at start_offset + gradient_offset_base.
|
||||
//
|
||||
// either way, we need a while-loop to draw the gradient as well
|
||||
// because it might have more than 4 stops (the maximum of a cached
|
||||
// segment) and/or hard stops. so we have a walk-within-the-walk from
|
||||
// start_offset to end_offset caching up to GRADIENT_FP_STOPS stops at a
|
||||
// time.
|
||||
fn emit_segments(start_offset: f32, // start and end offset together are
|
||||
end_offset: f32, // always a subrange of 0..1
|
||||
gradient_offset_base: f32,
|
||||
prim_start_offset: f32, // the offsets of the entire gradient as it
|
||||
prim_end_offset: f32, // covers the entire primitive.
|
||||
prim_origin_in: LayoutPoint,
|
||||
prim_size_in: LayoutSize,
|
||||
task_size: DeviceIntSize,
|
||||
is_opaque: bool,
|
||||
stops: &[GradientStopKey],
|
||||
orientation: LineOrientation,
|
||||
frame_state: &mut FrameBuildingState,
|
||||
gradient: &mut LinearGradientPrimitive)
|
||||
{
|
||||
// these prints are used to generate documentation examples, so
|
||||
// leaving them in but commented out:
|
||||
//println!("emit_segments call:");
|
||||
//println!("\tstart_offset: {}, end_offset: {}", start_offset, end_offset);
|
||||
//println!("\tprim_start_offset: {}, prim_end_offset: {}", prim_start_offset, prim_end_offset);
|
||||
//println!("\tgradient_offset_base: {}", gradient_offset_base);
|
||||
let mut first_stop = 0;
|
||||
// look for an inclusive range of stops [first_stop, last_stop].
|
||||
// once first_stop points at (or past) the last stop, we're done.
|
||||
while first_stop < stops.len()-1 {
|
||||
|
||||
// if the entire segment starts at an offset that's past the primitive's
|
||||
// end_point, we're done.
|
||||
if stops[first_stop].offset > end_point {
|
||||
break;
|
||||
}
|
||||
|
||||
// accumulate stops until we have GRADIENT_FP_STOPS of them, or we hit
|
||||
// a hard stop:
|
||||
let mut last_stop = first_stop;
|
||||
let mut hard_stop = false; // did we stop on a hard stop?
|
||||
while last_stop < stops.len()-1 &&
|
||||
last_stop - first_stop + 1 < GRADIENT_FP_STOPS
|
||||
{
|
||||
if stops[last_stop+1].offset == stops[last_stop].offset {
|
||||
hard_stop = true;
|
||||
break;
|
||||
// if the entire sub-gradient starts at an offset that's past the
|
||||
// segment's end offset, we're done.
|
||||
if stops[first_stop].offset > end_offset {
|
||||
return;
|
||||
}
|
||||
|
||||
last_stop = last_stop + 1;
|
||||
}
|
||||
// accumulate stops until we have GRADIENT_FP_STOPS of them, or we hit
|
||||
// a hard stop:
|
||||
let mut last_stop = first_stop;
|
||||
let mut hard_stop = false; // did we stop on a hard stop?
|
||||
while last_stop < stops.len()-1 &&
|
||||
last_stop - first_stop + 1 < GRADIENT_FP_STOPS
|
||||
{
|
||||
if stops[last_stop+1].offset == stops[last_stop].offset {
|
||||
hard_stop = true;
|
||||
break;
|
||||
}
|
||||
|
||||
let num_stops = last_stop - first_stop + 1;
|
||||
|
||||
// repeated hard stops at the same offset, skip
|
||||
if num_stops == 0 {
|
||||
first_stop = last_stop + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
// if the last stop offset is before start_point, the segment's not visible:
|
||||
if stops[last_stop].offset < start_point {
|
||||
first_stop = if hard_stop { last_stop+1 } else { last_stop };
|
||||
continue;
|
||||
}
|
||||
|
||||
let segment_start_point = start_point.max(stops[first_stop].offset);
|
||||
let segment_end_point = end_point .min(stops[last_stop ].offset);
|
||||
|
||||
let mut segment_stops = [GradientStopKey::empty(); GRADIENT_FP_STOPS];
|
||||
for i in 0..num_stops {
|
||||
segment_stops[i] = stops[first_stop + i];
|
||||
}
|
||||
|
||||
let cache_key = GradientCacheKey {
|
||||
orientation,
|
||||
start_stop_point: VectorKey {
|
||||
x: segment_start_point,
|
||||
y: segment_end_point,
|
||||
},
|
||||
stops: segment_stops,
|
||||
};
|
||||
|
||||
let mut prim_origin = prim_instance.prim_origin;
|
||||
let mut prim_size = prim_data.common.prim_size;
|
||||
|
||||
let inv_length = 1.0 / ( end_point - start_point );
|
||||
if orientation == LineOrientation::Horizontal {
|
||||
prim_origin.x += ( segment_start_point - start_point ) * inv_length * prim_size.width;
|
||||
prim_size.width *= ( segment_end_point - segment_start_point ) * inv_length;
|
||||
} else {
|
||||
prim_origin.y += ( segment_start_point - start_point ) * inv_length * prim_size.height;
|
||||
prim_size.height *= ( segment_end_point - segment_start_point ) * inv_length;
|
||||
}
|
||||
|
||||
let local_rect = LayoutRect::new( prim_origin, prim_size );
|
||||
|
||||
// Request the render task each frame.
|
||||
gradient.cache_segments.push(
|
||||
CachedGradientSegment {
|
||||
handle: frame_state.resource_cache.request_render_task(
|
||||
RenderTaskCacheKey {
|
||||
size,
|
||||
kind: RenderTaskCacheKeyKind::Gradient(cache_key),
|
||||
},
|
||||
frame_state.gpu_cache,
|
||||
frame_state.render_tasks,
|
||||
None,
|
||||
prim_data.stops_opacity.is_opaque,
|
||||
|render_tasks| {
|
||||
render_tasks.add().init(RenderTask::new_gradient(
|
||||
size,
|
||||
segment_stops,
|
||||
orientation,
|
||||
segment_start_point,
|
||||
segment_end_point,
|
||||
))
|
||||
}),
|
||||
local_rect: local_rect,
|
||||
last_stop = last_stop + 1;
|
||||
}
|
||||
);
|
||||
|
||||
// if ending on a hardstop, skip past it for the start of the next run:
|
||||
first_stop = if hard_stop { last_stop + 1 } else { last_stop };
|
||||
let num_stops = last_stop - first_stop + 1;
|
||||
|
||||
// repeated hard stops at the same offset, skip
|
||||
if num_stops == 0 {
|
||||
first_stop = last_stop + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
// if the last_stop offset is before start_offset, the segment's not visible:
|
||||
if stops[last_stop].offset < start_offset {
|
||||
first_stop = if hard_stop { last_stop+1 } else { last_stop };
|
||||
continue;
|
||||
}
|
||||
|
||||
let segment_start_point = start_offset.max(stops[first_stop].offset);
|
||||
let segment_end_point = end_offset .min(stops[last_stop ].offset);
|
||||
|
||||
let mut segment_stops = [GradientStopKey::empty(); GRADIENT_FP_STOPS];
|
||||
for i in 0..num_stops {
|
||||
segment_stops[i] = stops[first_stop + i];
|
||||
}
|
||||
|
||||
let cache_key = GradientCacheKey {
|
||||
orientation,
|
||||
start_stop_point: VectorKey {
|
||||
x: segment_start_point,
|
||||
y: segment_end_point,
|
||||
},
|
||||
stops: segment_stops,
|
||||
};
|
||||
|
||||
let mut prim_origin = prim_origin_in;
|
||||
let mut prim_size = prim_size_in;
|
||||
|
||||
// the primitive is covered by a segment from overall_start to
|
||||
// overall_end; scale and shift based on the length of the actual
|
||||
// segment that we're drawing:
|
||||
let inv_length = 1.0 / ( prim_end_offset - prim_start_offset );
|
||||
if orientation == LineOrientation::Horizontal {
|
||||
prim_origin.x += ( segment_start_point + gradient_offset_base - prim_start_offset )
|
||||
* inv_length * prim_size.width;
|
||||
prim_size.width *= ( segment_end_point - segment_start_point )
|
||||
* inv_length; // 2 gradient_offset_bases cancel out
|
||||
} else {
|
||||
prim_origin.y += ( segment_start_point + gradient_offset_base - prim_start_offset )
|
||||
* inv_length * prim_size.height;
|
||||
prim_size.height *= ( segment_end_point - segment_start_point )
|
||||
* inv_length; // 2 gradient_offset_bases cancel out
|
||||
}
|
||||
|
||||
// <= 0 can happen if a hardstop lands exactly on an edge
|
||||
if prim_size.area() > 0.0 {
|
||||
let local_rect = LayoutRect::new( prim_origin, prim_size );
|
||||
|
||||
// documentation example traces:
|
||||
//println!("\t\tcaching from offset {} to {}", segment_start_point, segment_end_point);
|
||||
//println!("\t\tand blitting to {:?}", local_rect);
|
||||
|
||||
// Request the render task each frame.
|
||||
gradient.cache_segments.push(
|
||||
CachedGradientSegment {
|
||||
handle: frame_state.resource_cache.request_render_task(
|
||||
RenderTaskCacheKey {
|
||||
size: task_size,
|
||||
kind: RenderTaskCacheKeyKind::Gradient(cache_key),
|
||||
},
|
||||
frame_state.gpu_cache,
|
||||
frame_state.render_tasks,
|
||||
None,
|
||||
is_opaque,
|
||||
|render_tasks| {
|
||||
render_tasks.add().init(RenderTask::new_gradient(
|
||||
task_size,
|
||||
segment_stops,
|
||||
orientation,
|
||||
segment_start_point,
|
||||
segment_end_point,
|
||||
))
|
||||
}),
|
||||
local_rect: local_rect,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// if ending on a hardstop, skip past it for the start of the next run:
|
||||
first_stop = if hard_stop { last_stop + 1 } else { last_stop };
|
||||
}
|
||||
}
|
||||
|
||||
if prim_data.extend_mode == ExtendMode::Clamp ||
|
||||
( prim_start_offset >= 0.0 && prim_end_offset <= 1.0 ) // repeat doesn't matter
|
||||
{
|
||||
// To support clamping, we need to make sure that quads are emitted for the
|
||||
// segments before and after the 0.0...1.0 range of offsets. emit_segments
|
||||
// can handle that by duplicating the first and last point if necessary:
|
||||
if prim_start_offset < 0.0 {
|
||||
stops.insert(0, GradientStopKey {
|
||||
offset: prim_start_offset,
|
||||
color : stops[0].color
|
||||
});
|
||||
}
|
||||
|
||||
if prim_end_offset > 1.0 {
|
||||
stops.push( GradientStopKey {
|
||||
offset: prim_end_offset,
|
||||
color : stops[stops.len()-1].color
|
||||
});
|
||||
}
|
||||
|
||||
emit_segments(prim_start_offset, prim_end_offset,
|
||||
0.0,
|
||||
prim_start_offset, prim_end_offset,
|
||||
prim_instance.prim_origin,
|
||||
prim_data.common.prim_size,
|
||||
size,
|
||||
prim_data.stops_opacity.is_opaque,
|
||||
&stops,
|
||||
orientation,
|
||||
frame_state,
|
||||
gradient);
|
||||
}
|
||||
else
|
||||
{
|
||||
let mut segment_start_point = prim_start_offset;
|
||||
while segment_start_point < prim_end_offset {
|
||||
|
||||
// gradient stops are expressed in the range 0.0 ... 1.0, so to blit
|
||||
// a copy of the gradient, snap to the integer just before the offset
|
||||
// we want ...
|
||||
let gradient_offset_base = segment_start_point.floor();
|
||||
// .. and then draw from a start offset in range 0 to 1 ...
|
||||
let repeat_start = segment_start_point - gradient_offset_base;
|
||||
// .. up to the next integer, but clamped to the primitive's real
|
||||
// end offset:
|
||||
let repeat_end = (gradient_offset_base + 1.0).min(prim_end_offset) - gradient_offset_base;
|
||||
|
||||
emit_segments(repeat_start, repeat_end,
|
||||
gradient_offset_base,
|
||||
prim_start_offset, prim_end_offset,
|
||||
prim_instance.prim_origin,
|
||||
prim_data.common.prim_size,
|
||||
size,
|
||||
prim_data.stops_opacity.is_opaque,
|
||||
&stops,
|
||||
orientation,
|
||||
frame_state,
|
||||
gradient);
|
||||
|
||||
segment_start_point = repeat_end + gradient_offset_base;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,119 @@
|
|||
---
|
||||
root:
|
||||
items:
|
||||
# non-repeating
|
||||
- type: gradient
|
||||
bounds: 100 50 500 10
|
||||
start: 100 0
|
||||
end: 200 0
|
||||
repeat: false
|
||||
stops: [0.0, green,
|
||||
0.5, green,
|
||||
0.5, blue,
|
||||
1.0, blue ]
|
||||
|
||||
# repeat 4 times
|
||||
- type: gradient
|
||||
bounds: 100 100 500 10
|
||||
start: 100 0
|
||||
end: 200 0
|
||||
repeat: true
|
||||
stops: [0.0, green,
|
||||
0.5, green,
|
||||
0.5, blue,
|
||||
1.0, blue ]
|
||||
|
||||
# same but start doesn't line up with 0
|
||||
- type: gradient
|
||||
bounds: 100 150 500 10
|
||||
start: 125 0
|
||||
end: 225 0
|
||||
repeat: true
|
||||
stops: [0.0, green,
|
||||
0.5, green,
|
||||
0.5, blue,
|
||||
1.0, blue ]
|
||||
|
||||
# more hard stops, non-uniform distribution
|
||||
- type: gradient
|
||||
bounds: 100 250 500 10
|
||||
start: 200 0
|
||||
end: 300 0
|
||||
repeat: false
|
||||
stops: [0.0, green,
|
||||
0.25, green,
|
||||
0.25, red,
|
||||
0.75, red,
|
||||
0.75, blue,
|
||||
1.0, blue ]
|
||||
|
||||
# repeat the hard stops
|
||||
- type: gradient
|
||||
bounds: 100 300 500 10
|
||||
start: 200 0
|
||||
end: 300 0
|
||||
repeat: true
|
||||
stops: [0.0, green,
|
||||
0.25, green,
|
||||
0.25, red,
|
||||
0.75, red,
|
||||
0.75, blue,
|
||||
1.0, blue ]
|
||||
|
||||
# same but start doesn't line up with 0
|
||||
- type: gradient
|
||||
bounds: 100 350 500 10
|
||||
start: 175 0
|
||||
end: 275 0
|
||||
repeat: true
|
||||
stops: [0.0, green,
|
||||
0.25, green,
|
||||
0.25, red,
|
||||
0.75, red,
|
||||
0.75, blue,
|
||||
1.0, blue ]
|
||||
|
||||
# the entire gradient from 0 to 1 is
|
||||
# "offscreen", we're only seeing its
|
||||
# repeats. the gradient is 100 wide
|
||||
# and ends at -75, so the first
|
||||
# three-quarters of it would be hidden,
|
||||
# that is, it should start with blue.
|
||||
- type: gradient
|
||||
bounds: 100 400 500 10
|
||||
start: -175 0
|
||||
end: -75 0
|
||||
repeat: true
|
||||
stops: [0.0, green,
|
||||
0.25, green,
|
||||
0.25, red,
|
||||
0.75, red,
|
||||
0.75, blue,
|
||||
1.0, blue ]
|
||||
|
||||
# same but over on the right
|
||||
- type: gradient
|
||||
bounds: 100 450 500 10
|
||||
start: 575 0
|
||||
end: 675 0
|
||||
repeat: true
|
||||
stops: [0.0, green,
|
||||
0.25, green,
|
||||
0.25, red,
|
||||
0.75, red,
|
||||
0.75, blue,
|
||||
1.0, blue ]
|
||||
|
||||
# a repeat, but not really because only part
|
||||
# of the gradient is visible
|
||||
- type: gradient
|
||||
bounds: 100 500 500 10
|
||||
start: -50 0
|
||||
end: 550 0
|
||||
repeat: true
|
||||
stops: [0.0, green,
|
||||
0.25, green,
|
||||
0.25, red,
|
||||
0.75, red,
|
||||
0.75, blue,
|
||||
1.0, blue ]
|
|
@ -0,0 +1,119 @@
|
|||
---
|
||||
root:
|
||||
items:
|
||||
# non-repeating
|
||||
- type: gradient
|
||||
bounds: 100 50 500 10
|
||||
start: 100 0
|
||||
end: 200 0.001
|
||||
repeat: false
|
||||
stops: [0.0, green,
|
||||
0.5, green,
|
||||
0.5, blue,
|
||||
1.0, blue ]
|
||||
|
||||
# repeat 4 times
|
||||
- type: gradient
|
||||
bounds: 100 100 500 10
|
||||
start: 100 0
|
||||
end: 200 0.001
|
||||
repeat: true
|
||||
stops: [0.0, green,
|
||||
0.5, green,
|
||||
0.5, blue,
|
||||
1.0, blue ]
|
||||
|
||||
# same but start doesn't line up with 0
|
||||
- type: gradient
|
||||
bounds: 100 150 500 10
|
||||
start: 125 0
|
||||
end: 225 0.001
|
||||
repeat: true
|
||||
stops: [0.0, green,
|
||||
0.5, green,
|
||||
0.5, blue,
|
||||
1.0, blue ]
|
||||
|
||||
# more hard stops, non-uniform distribution
|
||||
- type: gradient
|
||||
bounds: 100 250 500 10
|
||||
start: 200 0
|
||||
end: 300 0.001
|
||||
repeat: false
|
||||
stops: [0.0, green,
|
||||
0.25, green,
|
||||
0.25, red,
|
||||
0.75, red,
|
||||
0.75, blue,
|
||||
1.0, blue ]
|
||||
|
||||
# repeat the hard stops
|
||||
- type: gradient
|
||||
bounds: 100 300 500 10
|
||||
start: 200 0
|
||||
end: 300 0.001
|
||||
repeat: true
|
||||
stops: [0.0, green,
|
||||
0.25, green,
|
||||
0.25, red,
|
||||
0.75, red,
|
||||
0.75, blue,
|
||||
1.0, blue ]
|
||||
|
||||
# same but start doesn't line up with 0
|
||||
- type: gradient
|
||||
bounds: 100 350 500 10
|
||||
start: 175 0
|
||||
end: 275 0.001
|
||||
repeat: true
|
||||
stops: [0.0, green,
|
||||
0.25, green,
|
||||
0.25, red,
|
||||
0.75, red,
|
||||
0.75, blue,
|
||||
1.0, blue ]
|
||||
|
||||
# the entire gradient from 0 to 1 is
|
||||
# "offscreen", we're only seeing its
|
||||
# repeats. the gradient is 100 wide
|
||||
# and ends at -75, so the first
|
||||
# three-quarters of it would be hidden,
|
||||
# that is, it should start with blue.
|
||||
- type: gradient
|
||||
bounds: 100 400 500 10
|
||||
start: -175 0
|
||||
end: -75 0.001
|
||||
repeat: true
|
||||
stops: [0.0, green,
|
||||
0.25, green,
|
||||
0.25, red,
|
||||
0.75, red,
|
||||
0.75, blue,
|
||||
1.0, blue ]
|
||||
|
||||
# same but over on the right
|
||||
- type: gradient
|
||||
bounds: 100 450 500 10
|
||||
start: 575 0
|
||||
end: 675 0.001
|
||||
repeat: true
|
||||
stops: [0.0, green,
|
||||
0.25, green,
|
||||
0.25, red,
|
||||
0.75, red,
|
||||
0.75, blue,
|
||||
1.0, blue ]
|
||||
|
||||
# a repeat, but not really because only part
|
||||
# of the gradient is visible
|
||||
- type: gradient
|
||||
bounds: 100 500 500 10
|
||||
start: -50 0
|
||||
end: 550 0.001
|
||||
repeat: true
|
||||
stops: [0.0, green,
|
||||
0.25, green,
|
||||
0.25, red,
|
||||
0.75, red,
|
||||
0.75, blue,
|
||||
1.0, blue ]
|
|
@ -76,7 +76,8 @@ fuzzy(1,3) == tiling-conic-3.yaml tiling-conic-3-ref.yaml
|
|||
== linear-adjust-tile-size.yaml linear-adjust-tile-size-ref.yaml
|
||||
|
||||
platform(linux,mac) == linear-aligned-border-radius.yaml linear-aligned-border-radius.png
|
||||
platform(linux,mac) == repeat-border-radius.yaml repeat-border-radius.png
|
||||
# interpolation fuzz from sampling texture-baked gradient ramps
|
||||
platform(linux,mac) fuzzy-range(<=1,*1404) == repeat-border-radius.yaml repeat-border-radius.png
|
||||
|
||||
== conic.yaml conic-ref.yaml
|
||||
fuzzy(1,56) == conic-simple.yaml conic-simple.png
|
||||
|
@ -94,3 +95,4 @@ fuzzy-range(<=1,*169000) == gradient_cache_5stops_vertical.yaml gradient_cache_5
|
|||
== gradient_cache_hardstop.yaml gradient_cache_hardstop_ref.yaml
|
||||
== gradient_cache_hardstop_clip.yaml gradient_cache_hardstop_clip_ref.yaml
|
||||
== gradient_cache_clamp.yaml gradient_cache_clamp_ref.yaml
|
||||
== gradient_cache_repeat.yaml gradient_cache_repeat_ref.yaml
|
||||
|
|
Загрузка…
Ссылка в новой задаче