Refactored GradientStops to support intermediary hint offsets. Also now normalized the offsets when converting to layout.
This commit is contained in:
parent
40259b4520
commit
1b364d654e
|
@ -0,0 +1 @@
|
|||
cargo build > dump.log 2>&1
|
|
@ -22,9 +22,9 @@ fn linear_angle() -> impl Widget {
|
|||
h_stack! {
|
||||
spacing: 5;
|
||||
items: (
|
||||
//sample("linear 90º", linear_gradient(90.deg(), (colors::RED, colors::BLUE))),
|
||||
//sample("linear 45º", linear_gradient(45.deg(), (colors::GREEN, colors::BLUE))),
|
||||
sample("linear 0º", linear_gradient(0.deg(), (colors::BLACK, colors::GREEN))),
|
||||
//sample("linear 90º", linear_gradient(90.deg(), [colors::RED, colors::BLUE])),
|
||||
//sample("linear 45º", linear_gradient(45.deg(), [colors::GREEN, colors::BLUE])),
|
||||
sample("linear 0º", linear_gradient(0.deg(), [colors::BLACK, colors::GREEN])),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -35,15 +35,15 @@ fn linear_points() -> impl Widget {
|
|||
items: (
|
||||
//sample(
|
||||
// "linear points - clamp",
|
||||
// linear_gradient_pt((30, 30), (90, 90), (colors::GREEN, colors::RED), ExtendMode::Clamp)
|
||||
// linear_gradient_pt((30, 30), (90, 90), [colors::GREEN, colors::RED], ExtendMode::Clamp)
|
||||
//),
|
||||
//sample(
|
||||
// "linear points - repeat",
|
||||
// linear_gradient_pt((30, 30), (90, 90), (colors::GREEN, colors::RED), ExtendMode::Repeat)
|
||||
// linear_gradient_pt((30, 30), (90, 90), [colors::GREEN, colors::RED], ExtendMode::Repeat)
|
||||
//),
|
||||
sample(
|
||||
"test",
|
||||
linear_gradient_pt((90, 180), (90, 0), (colors::BLACK, colors::GREEN), ExtendMode::Repeat)
|
||||
linear_gradient_pt((90, 180), (90, 0), [colors::BLACK, colors::GREEN], ExtendMode::Repeat)
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -56,11 +56,11 @@ fn linear_tile() -> impl Widget {
|
|||
items: (
|
||||
sample(
|
||||
"linear tiles",
|
||||
linear_gradient_tile(45.deg(), (colors::GREEN, colors::YELLOW), (w, w), (0, 0))
|
||||
linear_gradient_tile(45.deg(), [colors::GREEN, colors::YELLOW], (w, w), (0, 0))
|
||||
),
|
||||
sample(
|
||||
"linear tiles spaced",
|
||||
linear_gradient_tile(45.deg(), (colors::MAGENTA, colors::AQUA), (w + 5, w + 5), (5, 5))
|
||||
linear_gradient_tile(45.deg(), [colors::MAGENTA, colors::AQUA], (w + 5, w + 5), (5, 5))
|
||||
|
||||
),
|
||||
);
|
||||
|
|
|
@ -703,6 +703,19 @@ pub fn hue_rotate<A: Into<AngleDegree>>(angle: A) -> Filter {
|
|||
Filter::default().hue_rotate(angle)
|
||||
}
|
||||
|
||||
/// Linear interpolate between `a` and `b` by the normalized `amount`.
|
||||
pub fn lerp_render_color(a: RenderColor, b: RenderColor, amount: f32) -> RenderColor {
|
||||
fn lerp(a: f32, b: f32, s: f32) -> f32 {
|
||||
a + (b - a) * s
|
||||
}
|
||||
RenderColor {
|
||||
r: lerp(a.r, b.r, amount),
|
||||
g: lerp(a.g, b.g, amount),
|
||||
b: lerp(a.b, b.b, amount),
|
||||
a: lerp(a.a, b.a, amount),
|
||||
}
|
||||
}
|
||||
|
||||
/// Named web colors
|
||||
pub mod colors {
|
||||
use super::Rgba;
|
||||
|
@ -1417,6 +1430,16 @@ pub mod colors {
|
|||
///
|
||||
/// `rgb(0, 0, 0)`
|
||||
pub const BLACK: Rgba = rgb!(0, 0, 0);
|
||||
|
||||
/// Transparent (`#00000000`)
|
||||
///
|
||||
/// `rgba(0, 0, 0, 0)`
|
||||
pub const TRANSPARENT: Rgba = Rgba {
|
||||
red: 0.0,
|
||||
green: 0.0,
|
||||
blue: 0.0,
|
||||
alpha: 0.0,
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -617,7 +617,7 @@ impl FrameBuilder {
|
|||
rect: LayoutRect,
|
||||
start: LayoutPoint,
|
||||
end: LayoutPoint,
|
||||
stops: &[crate::widgets::LayoutGradientStop],
|
||||
stops: &[crate::widgets::LayoutColorStop],
|
||||
extend_mode: ExtendMode,
|
||||
) {
|
||||
if self.cancel_widget {
|
||||
|
@ -649,7 +649,7 @@ impl FrameBuilder {
|
|||
rect: LayoutRect,
|
||||
start: LayoutPoint,
|
||||
end: LayoutPoint,
|
||||
stops: &[crate::widgets::LayoutGradientStop],
|
||||
stops: &[crate::widgets::LayoutColorStop],
|
||||
tile_size: LayoutSize,
|
||||
tile_spacing: LayoutSize,
|
||||
) {
|
||||
|
|
|
@ -30,7 +30,7 @@ mod build_tests {
|
|||
fn _basic(child: impl UiNode) -> impl UiNode {
|
||||
button! {
|
||||
on_click: |_,_|{};
|
||||
background_gradient: 90.deg(), vec![rgb(0.0, 0.0, 0.0), rgb(1.0, 1.0, 1.0)];
|
||||
background_gradient: 90.deg(), [rgb(0.0, 0.0, 0.0), rgb(1.0, 1.0, 1.0)];
|
||||
content: child;
|
||||
}
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ mod build_tests {
|
|||
|
||||
background_gradient: {
|
||||
angle: 90.deg(),
|
||||
stops: vec![rgb(0.0, 0.0, 0.0), rgb(1.0, 1.0, 1.0)]
|
||||
stops: [rgb(0.0, 0.0, 0.0), rgb(1.0, 1.0, 1.0)]
|
||||
};
|
||||
|
||||
content: child;
|
||||
|
|
|
@ -1,34 +1,13 @@
|
|||
use std::f32::consts::PI;
|
||||
|
||||
use crate::prelude::new_widget::*;
|
||||
|
||||
pub use webrender::api::ExtendMode;
|
||||
|
||||
/// Computed [`GradientStop`].
|
||||
pub type LayoutGradientStop = webrender::api::GradientStop;
|
||||
|
||||
/// A color stop in a linear or radial gradient.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct GradientStop {
|
||||
pub offset: Length,
|
||||
pub color: Rgba,
|
||||
}
|
||||
impl GradientStop {
|
||||
#[inline]
|
||||
pub fn to_layout(self, available_length: LayoutLength, ctx: &LayoutContext) -> LayoutGradientStop {
|
||||
LayoutGradientStop {
|
||||
offset: self.offset.to_layout(available_length, ctx).get(),
|
||||
color: self.color.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct LinearGradientNode<A: VarLocal<AngleRadian>, S: VarLocal<GradientStops>> {
|
||||
angle: A,
|
||||
stops: S,
|
||||
render_start: LayoutPoint,
|
||||
render_end: LayoutPoint,
|
||||
render_stops: Vec<LayoutGradientStop>,
|
||||
render_stops: Vec<LayoutColorStop>,
|
||||
final_size: LayoutSize,
|
||||
}
|
||||
#[impl_ui_node(none)]
|
||||
|
@ -55,9 +34,7 @@ impl<A: VarLocal<AngleRadian>, S: VarLocal<GradientStops>> UiNode for LinearGrad
|
|||
|
||||
self.render_start = start;
|
||||
self.render_end = end;
|
||||
self.render_stops.clear();
|
||||
self.render_stops
|
||||
.extend(self.stops.get_local().iter().map(|&s| s.to_layout(length, ctx)));
|
||||
self.stops.get_local().layout(length, ctx, &mut self.render_stops);
|
||||
}
|
||||
|
||||
fn render(&self, frame: &mut FrameBuilder) {
|
||||
|
@ -78,7 +55,7 @@ struct LinearGradientPointsNode<A: VarLocal<Point>, B: VarLocal<Point>, S: VarLo
|
|||
extend_mode: E,
|
||||
render_start: LayoutPoint,
|
||||
render_end: LayoutPoint,
|
||||
render_stops: Vec<LayoutGradientStop>,
|
||||
render_stops: Vec<LayoutColorStop>,
|
||||
final_size: LayoutSize,
|
||||
}
|
||||
#[impl_ui_node(none)]
|
||||
|
@ -116,9 +93,7 @@ impl<A: VarLocal<Point>, B: VarLocal<Point>, S: VarLocal<GradientStops>, E: VarL
|
|||
|
||||
let length = LayoutLength::new(self.render_start.distance_to(self.render_end));
|
||||
|
||||
self.render_stops.clear();
|
||||
self.render_stops
|
||||
.extend(self.stops.get_local().iter().map(|&s| s.to_layout(length, ctx)));
|
||||
self.stops.get_local().layout(length, ctx, &mut self.render_stops);
|
||||
}
|
||||
|
||||
fn render(&self, frame: &mut FrameBuilder) {
|
||||
|
@ -140,7 +115,7 @@ struct LinearGradientTileNode<A: VarLocal<AngleRadian>, S: VarLocal<GradientStop
|
|||
|
||||
render_start: LayoutPoint,
|
||||
render_end: LayoutPoint,
|
||||
render_stops: Vec<LayoutGradientStop>,
|
||||
render_stops: Vec<LayoutColorStop>,
|
||||
|
||||
render_tile_size: LayoutSize,
|
||||
render_tile_spacing: LayoutSize,
|
||||
|
@ -184,9 +159,7 @@ impl<A: VarLocal<AngleRadian>, S: VarLocal<GradientStops>, T: VarLocal<Size>, TS
|
|||
|
||||
self.render_start = start;
|
||||
self.render_end = end;
|
||||
self.render_stops.clear();
|
||||
self.render_stops
|
||||
.extend(self.stops.get_local().iter().map(|s| s.to_layout(length, ctx)));
|
||||
self.stops.get_local().layout(length, ctx, &mut self.render_stops);
|
||||
}
|
||||
|
||||
fn render(&self, frame: &mut FrameBuilder) {
|
||||
|
@ -258,42 +231,42 @@ pub fn linear_gradient_tile(
|
|||
|
||||
/// Linear gradient from bottom to top.
|
||||
pub fn linear_gradient_to_top(stops: impl IntoVar<GradientStops>) -> impl UiNode {
|
||||
linear_gradient_pt((0, 100.pct()), (0, 0), stops)
|
||||
linear_gradient_pt((0, 100.pct()), (0, 0), stops, ExtendMode::Clamp)
|
||||
}
|
||||
|
||||
/// Linear gradient from top to bottom.
|
||||
pub fn linear_gradient_to_bottom(stops: impl IntoVar<GradientStops>) -> impl UiNode {
|
||||
linear_gradient_pt((0, 0), (0, 100.pct()), stops)
|
||||
linear_gradient_pt((0, 0), (0, 100.pct()), stops, ExtendMode::Clamp)
|
||||
}
|
||||
|
||||
/// Linear gradient from right to left.
|
||||
pub fn linear_gradient_to_left(stops: impl IntoVar<GradientStops>) -> impl UiNode {
|
||||
linear_gradient_pt((100.pct(), 0), (0, 0), stops)
|
||||
linear_gradient_pt((100.pct(), 0), (0, 0), stops, ExtendMode::Clamp)
|
||||
}
|
||||
|
||||
/// Linear gradient from left to right.
|
||||
pub fn linear_gradient_to_right(stops: impl IntoVar<GradientStops>) -> impl UiNode {
|
||||
linear_gradient_pt((0, 0), (100.pct(), 0), stops)
|
||||
linear_gradient_pt((0, 0), (100.pct(), 0), stops, ExtendMode::Clamp)
|
||||
}
|
||||
|
||||
/// Linear gradient from bottom-left to top-right.
|
||||
pub fn linear_gradient_to_top_right(stops: impl IntoVar<GradientStops>) -> impl UiNode {
|
||||
linear_gradient_pt((0, 100.pct()), (100.pct(), 0), stops)
|
||||
linear_gradient_pt((0, 100.pct()), (100.pct(), 0), stops, ExtendMode::Clamp)
|
||||
}
|
||||
|
||||
/// Linear gradient from top-left to bottom-right.
|
||||
pub fn linear_gradient_to_bottom_right(stops: impl IntoVar<GradientStops>) -> impl UiNode {
|
||||
linear_gradient_pt((0, 0), (100.pct(), 100.pct()), stops)
|
||||
linear_gradient_pt((0, 0), (100.pct(), 100.pct()), stops, ExtendMode::Clamp)
|
||||
}
|
||||
|
||||
/// Linear gradient from bottom-right to top-left.
|
||||
pub fn linear_gradient_to_top_left(stops: impl IntoVar<GradientStops>) -> impl UiNode {
|
||||
linear_gradient_pt((100.pct(), 100.pct()), (0, 0), stops)
|
||||
linear_gradient_pt((100.pct(), 100.pct()), (0, 0), stops, ExtendMode::Clamp)
|
||||
}
|
||||
|
||||
/// Linear gradient from top-right to bottom-left.
|
||||
pub fn linear_gradient_to_bottom_left(stops: impl IntoVar<GradientStops>) -> impl UiNode {
|
||||
linear_gradient_pt((100.pct(), 0), (0, 100.pct()), stops)
|
||||
linear_gradient_pt((100.pct(), 0), (0, 100.pct()), stops, ExtendMode::Clamp)
|
||||
}
|
||||
|
||||
struct FillColorNode<C: VarLocal<Rgba>> {
|
||||
|
@ -327,65 +300,332 @@ pub fn fill_color(color: impl IntoVar<Rgba>) -> impl UiNode {
|
|||
}
|
||||
}
|
||||
|
||||
/// Gradient stops for linear or radial gradients.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct GradientStops(pub Vec<GradientStop>);
|
||||
impl std::ops::Deref for GradientStops {
|
||||
type Target = [GradientStop];
|
||||
/// Computed [`GradientStop`].
|
||||
pub type LayoutColorStop = webrender::api::GradientStop;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
/// A color stop in a gradient.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct ColorStop {
|
||||
pub color: Rgba,
|
||||
pub offset: Length,
|
||||
}
|
||||
impl ColorStop {
|
||||
#[inline]
|
||||
pub fn to_layout(self, length: LayoutLength, ctx: &LayoutContext) -> LayoutColorStop {
|
||||
LayoutColorStop {
|
||||
offset: self.offset.to_layout(length, ctx).get(),
|
||||
color: self.color.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl_from_and_into_var! {
|
||||
fn from(stops: Vec<(Rgba, Length)>) -> GradientStops {
|
||||
GradientStops(stops.into_iter()
|
||||
.map(|(color, offset)| GradientStop {
|
||||
color,
|
||||
offset,
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
|
||||
/// Each item contains two color stops, with the same color.
|
||||
fn from(stops: Vec<(Rgba, Length, Length)>) -> GradientStops {{
|
||||
let mut r = Vec::with_capacity(stops.len() * 2);
|
||||
for (color, offset0, offset1) in stops {
|
||||
r.push(GradientStop {
|
||||
color,
|
||||
offset: offset0
|
||||
});
|
||||
r.push(GradientStop {
|
||||
color,
|
||||
offset: offset1
|
||||
});
|
||||
impl<C: Into<Rgba>, O: Into<Length>> From<(C, O)> for ColorStop {
|
||||
fn from((c, o): (C, O)) -> Self {
|
||||
ColorStop {
|
||||
color: c.into(),
|
||||
offset: o.into(),
|
||||
}
|
||||
GradientStops(r)
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
||||
/// Gradient stops that are all evenly spaced.
|
||||
fn from(stops: Vec<Rgba>) -> GradientStops {{
|
||||
let point = 1. / (stops.len() as f32 - 1.);
|
||||
GradientStops(stops.into_iter()
|
||||
.enumerate()
|
||||
.map(|(i, color)| GradientStop {
|
||||
offset: ((i as f32) * point).normal().into(),
|
||||
color,
|
||||
})
|
||||
.collect())
|
||||
}}
|
||||
/// A stop in a gradient.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum GradientStop {
|
||||
/// Color stop.
|
||||
Color(ColorStop),
|
||||
/// Midway point between two colors.
|
||||
Mid(Length),
|
||||
}
|
||||
|
||||
/// A single two color gradient stops. The first color is at offset `0.0`,
|
||||
/// the second color is at offset `1.0`.
|
||||
fn from((stop0, stop1): (Rgba, Rgba)) -> GradientStops {
|
||||
GradientStops(vec![
|
||||
GradientStop { offset: 0.0.normal().into(), color: stop0 },
|
||||
GradientStop { offset: 1.0.normal().into(), color: stop1 },
|
||||
])
|
||||
/// Stops in a gradient.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct GradientStops {
|
||||
/// First color stop.
|
||||
pub start: ColorStop,
|
||||
|
||||
/// Optional stops between start and end.
|
||||
pub middle: Vec<GradientStop>,
|
||||
|
||||
/// Last color stop.
|
||||
pub end: ColorStop,
|
||||
}
|
||||
#[allow(clippy::len_without_is_empty)] // cannot be empty
|
||||
impl GradientStops {
|
||||
/// Start a color gradient builder with the first color stop.
|
||||
pub fn start(color: impl Into<Rgba>, offset: impl Into<Length>) -> GradientStopsBuilder {
|
||||
GradientStopsBuilder {
|
||||
start: ColorStop {
|
||||
color: color.into(),
|
||||
offset: offset.into(),
|
||||
},
|
||||
middle: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
fn from(stops: Vec<GradientStop>) -> GradientStops {
|
||||
GradientStops(stops)
|
||||
/// Gradients stops with two colors from `start` to `end`.
|
||||
pub fn start_end(start: impl Into<Rgba>, end: impl Into<Rgba>) -> Self {
|
||||
GradientStops {
|
||||
start: ColorStop {
|
||||
color: start.into(),
|
||||
offset: Length::zero(),
|
||||
},
|
||||
middle: vec![],
|
||||
end: ColorStop {
|
||||
color: end.into(),
|
||||
offset: 100.pct().into(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Gradients stops with two colors from `start` to `end` and with custom midway point.
|
||||
pub fn start_mid_end(start: impl Into<Rgba>, mid: impl Into<Length>, end: impl Into<Rgba>) -> Self {
|
||||
GradientStops {
|
||||
start: ColorStop {
|
||||
color: start.into(),
|
||||
offset: Length::zero(),
|
||||
},
|
||||
middle: vec![GradientStop::Mid(mid.into())],
|
||||
end: ColorStop {
|
||||
color: end.into(),
|
||||
offset: 100.pct().into(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn start_missing() -> ColorStop {
|
||||
ColorStop {
|
||||
color: colors::TRANSPARENT,
|
||||
offset: Length::zero(),
|
||||
}
|
||||
}
|
||||
|
||||
fn end_missing() -> ColorStop {
|
||||
ColorStop {
|
||||
color: colors::TRANSPARENT,
|
||||
offset: 100.pct().into(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Gradient stops from colors spaced equally.
|
||||
pub fn from_colors<C: Into<Rgba> + Copy>(colors: &[C]) -> Self {
|
||||
if colors.is_empty() {
|
||||
GradientStops {
|
||||
start: Self::start_missing(),
|
||||
middle: vec![],
|
||||
end: Self::end_missing(),
|
||||
}
|
||||
} else if colors.len() == 1 {
|
||||
GradientStops {
|
||||
start: ColorStop {
|
||||
color: colors[0].into(),
|
||||
offset: Length::zero(),
|
||||
},
|
||||
middle: vec![],
|
||||
end: Self::end_missing(),
|
||||
}
|
||||
} else {
|
||||
let last = colors.len() - 1;
|
||||
let mut offset = 1.0 / colors.len() as f32;
|
||||
let offset_step = offset;
|
||||
GradientStops {
|
||||
start: ColorStop {
|
||||
color: colors[0].into(),
|
||||
offset: Length::zero(),
|
||||
},
|
||||
middle: colors[1..last]
|
||||
.iter()
|
||||
.map(|&c| {
|
||||
GradientStop::Color(ColorStop {
|
||||
color: c.into(),
|
||||
offset: {
|
||||
let r = offset;
|
||||
offset += offset_step;
|
||||
r.normal().into()
|
||||
},
|
||||
})
|
||||
})
|
||||
.collect(),
|
||||
end: ColorStop {
|
||||
color: colors[last].into(),
|
||||
offset: 100.pct().into(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Gradient stops from color stops.
|
||||
pub fn from_stops<C: Into<ColorStop> + Copy>(stops: &[C]) -> Self {
|
||||
if stops.is_empty() {
|
||||
GradientStops {
|
||||
start: Self::start_missing(),
|
||||
middle: vec![],
|
||||
end: Self::end_missing(),
|
||||
}
|
||||
} else if stops.len() == 1 {
|
||||
GradientStops {
|
||||
start: stops[0].into(),
|
||||
middle: vec![],
|
||||
end: Self::end_missing(),
|
||||
}
|
||||
} else {
|
||||
let last = stops.len() - 1;
|
||||
GradientStops {
|
||||
start: stops[0].into(),
|
||||
middle: stops[1..last].iter().map(|&c| GradientStop::Color(c.into())).collect(),
|
||||
end: stops[last].into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Computes the actual color stops.
|
||||
pub fn layout(&self, length: LayoutLength, ctx: &LayoutContext, render_stops: &mut Vec<LayoutColorStop>) {
|
||||
// In this method we need to:
|
||||
// 1 - Convert all Length values to LayoutLength.
|
||||
// 2 - Adjust offsets to they are always larger or equal to the previous offset.
|
||||
// 3 - Convert GradientStop::Mid to LayoutColorStop.
|
||||
|
||||
render_stops.clear();
|
||||
let mut prev_stop = self.start.to_layout(length, ctx); // 1
|
||||
let mut pending_mid = None;
|
||||
|
||||
render_stops.push(prev_stop);
|
||||
for gs in self.middle.iter() {
|
||||
match gs {
|
||||
GradientStop::Color(s) => {
|
||||
let mut stop = s.to_layout(length, ctx); // 1
|
||||
|
||||
if let Some(mid) = pending_mid.take() {
|
||||
if stop.offset < mid {
|
||||
stop.offset = mid; // 2
|
||||
}
|
||||
|
||||
render_stops.push(Self::mid_to_color_stop(prev_stop, mid, stop));
|
||||
// 3
|
||||
} else if stop.offset < prev_stop.offset {
|
||||
stop.offset = prev_stop.offset; // 2
|
||||
}
|
||||
|
||||
render_stops.push(stop);
|
||||
prev_stop = stop;
|
||||
}
|
||||
GradientStop::Mid(l) => {
|
||||
// TODO do we care if pending_mid is some here?
|
||||
|
||||
let mut l = l.to_layout(length, ctx).0; // 1
|
||||
if l > prev_stop.offset {
|
||||
l = prev_stop.offset; // 2
|
||||
}
|
||||
pending_mid = Some(l);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut stop = self.end.to_layout(length, ctx); // 1
|
||||
if let Some(mid) = pending_mid.take() {
|
||||
if stop.offset < mid {
|
||||
stop.offset = mid; // 2
|
||||
}
|
||||
|
||||
render_stops.push(Self::mid_to_color_stop(prev_stop, mid, stop)); // 3
|
||||
} else if stop.offset < prev_stop.offset {
|
||||
stop.offset = prev_stop.offset; // 2
|
||||
}
|
||||
|
||||
render_stops.push(stop);
|
||||
}
|
||||
|
||||
fn mid_to_color_stop(prev: LayoutColorStop, mid: f32, next: LayoutColorStop) -> LayoutColorStop {
|
||||
let lerp_mid = (next.offset - prev.offset) / (mid - prev.offset);
|
||||
LayoutColorStop {
|
||||
color: lerp_render_color(prev.color, next.color, lerp_mid),
|
||||
offset: mid,
|
||||
}
|
||||
}
|
||||
|
||||
/// Number of stops.
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
self.middle.len() + 2
|
||||
}
|
||||
}
|
||||
impl<C: Into<Rgba> + Copy + 'static> From<&[C]> for GradientStops {
|
||||
fn from(a: &[C]) -> Self {
|
||||
GradientStops::from_colors(a)
|
||||
}
|
||||
}
|
||||
impl<C: Into<Rgba> + Copy + 'static> IntoVar<GradientStops> for &[C] {
|
||||
type Var = OwnedVar<GradientStops>;
|
||||
|
||||
fn into_var(self) -> Self::Var {
|
||||
OwnedVar(self.into())
|
||||
}
|
||||
}
|
||||
macro_rules! impl_from_color_arrays {
|
||||
($($N:tt),+ $(,)?) => {$(
|
||||
impl<C: Into<Rgba> + Copy + 'static> From<[C; $N]> for GradientStops {
|
||||
fn from(a: [C; $N]) -> Self {
|
||||
GradientStops::from_colors(&a)
|
||||
}
|
||||
}
|
||||
impl<C: Into<Rgba> + Copy + 'static> IntoVar<GradientStops> for [C; $N] {
|
||||
type Var = OwnedVar<GradientStops>;
|
||||
|
||||
fn into_var(self) -> Self::Var {
|
||||
OwnedVar(self.into())
|
||||
}
|
||||
}
|
||||
)+};
|
||||
}
|
||||
impl_from_color_arrays!(2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32);
|
||||
|
||||
/// A [`GradientStops`] builder.
|
||||
pub struct GradientStopsBuilder {
|
||||
start: ColorStop,
|
||||
middle: Vec<GradientStop>,
|
||||
}
|
||||
impl GradientStopsBuilder {
|
||||
/// Add a color stop.
|
||||
pub fn color(mut self, color: impl Into<Rgba>, offset: impl Into<Length>) -> GradientStopsBuilderWithMid {
|
||||
self.middle.push(GradientStop::Color(ColorStop {
|
||||
color: color.into(),
|
||||
offset: offset.into(),
|
||||
}));
|
||||
GradientStopsBuilderWithMid(self)
|
||||
}
|
||||
|
||||
fn mid(mut self, offset: impl Into<Length>) -> Self {
|
||||
self.middle.push(GradientStop::Mid(offset.into()));
|
||||
self
|
||||
}
|
||||
|
||||
/// Finishes the gradient with the last color stop.
|
||||
pub fn end(self, color: impl Into<Rgba>, offset: impl Into<Length>) -> GradientStops {
|
||||
GradientStops {
|
||||
start: self.start,
|
||||
middle: self.middle,
|
||||
end: ColorStop {
|
||||
color: color.into(),
|
||||
offset: offset.into(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// [`GradientStopsBuilder`] in a state that allows adding a midway point.
|
||||
pub struct GradientStopsBuilderWithMid(GradientStopsBuilder);
|
||||
impl GradientStopsBuilderWithMid {
|
||||
/// Add a color stop.
|
||||
pub fn color(self, color: impl Into<Rgba>, offset: impl Into<Length>) -> GradientStopsBuilderWithMid {
|
||||
self.0.color(color, offset)
|
||||
}
|
||||
|
||||
/// Add the midway points between the previous color stop and the next.
|
||||
pub fn mid(self, offset: impl Into<Length>) -> GradientStopsBuilder {
|
||||
self.0.mid(offset)
|
||||
}
|
||||
|
||||
/// Finishes the gradient with the last color stop.
|
||||
pub fn end(self, color: impl Into<Rgba>, offset: impl Into<Length>) -> GradientStops {
|
||||
self.0.end(color, offset)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue