use crate::canvas::{
geometry::Transform2D, Align, Baseline, Color, FillRule, FontId, ImageId, LineCap, LineJoin,
};
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub(crate) struct GradientStop(pub f32, pub Color);
impl Eq for GradientStop {}
impl Ord for GradientStop {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
if other < self {
std::cmp::Ordering::Less
} else if self < other {
std::cmp::Ordering::Greater
} else {
std::cmp::Ordering::Equal
}
}
}
pub(crate) type MultiStopGradient = [GradientStop; 16];
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub(crate) enum GradientColors {
TwoStop {
start_color: Color,
end_color: Color,
},
MultiStop {
stops: MultiStopGradient,
},
}
impl GradientColors {
fn mul_alpha(&mut self, a: f32) {
match self {
GradientColors::TwoStop {
start_color,
end_color,
} => {
start_color.a *= a;
end_color.a *= a;
}
GradientColors::MultiStop { stops } => {
for stop in stops {
stop.1.a *= a;
}
}
}
}
fn from_stops(stops: &[(f32, Color)]) -> GradientColors {
if stops.is_empty() {
GradientColors::TwoStop {
start_color: Color::black(),
end_color: Color::black(),
}
} else if stops.len() == 1 {
GradientColors::TwoStop {
start_color: stops[0].1,
end_color: stops[0].1,
}
} else if stops.len() == 2 && stops[0].0 <= 0.0 && stops[1].0 >= 1.0 {
GradientColors::TwoStop {
start_color: stops[0].1,
end_color: stops[1].1,
}
} else {
let mut out_stops: [GradientStop; 16] = Default::default();
for i in 0..16 {
if i < stops.len() {
out_stops[i] = GradientStop(stops[i].0, stops[i].1);
} else {
out_stops[i] = GradientStop(2.0, Color::black());
}
}
GradientColors::MultiStop { stops: out_stops }
}
}
}
#[derive(Copy, Clone, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub(crate) enum PaintFlavor {
Color(Color),
#[cfg_attr(feature = "serde", serde(skip))]
Image {
id: ImageId,
cx: f32,
cy: f32,
width: f32,
height: f32,
angle: f32,
alpha: f32,
},
LinearGradient {
start_x: f32,
start_y: f32,
end_x: f32,
end_y: f32,
colors: GradientColors,
},
BoxGradient {
x: f32,
y: f32,
width: f32,
height: f32,
radius: f32,
feather: f32,
colors: GradientColors,
},
RadialGradient {
cx: f32,
cy: f32,
in_radius: f32,
out_radius: f32,
colors: GradientColors,
},
}
impl PaintFlavor {
pub(crate) fn gradient_colors(&self) -> Option<&GradientColors> {
match self {
PaintFlavor::LinearGradient { colors, .. } => Some(colors),
PaintFlavor::BoxGradient { colors, .. } => Some(colors),
PaintFlavor::RadialGradient { colors, .. } => Some(colors),
_ => None,
}
}
}
#[derive(Copy, Clone, Debug)]
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
pub struct Paint {
pub(crate) flavor: PaintFlavor,
pub(crate) transform: Transform2D,
#[cfg_attr(feature = "serialization", serde(skip))]
pub(crate) alpha_mask: Option<ImageId>,
pub(crate) shape_anti_alias: bool,
pub(crate) stencil_strokes: bool,
pub(crate) miter_limit: f32,
pub(crate) line_width: f32,
pub(crate) line_cap_start: LineCap,
pub(crate) line_cap_end: LineCap,
pub(crate) line_join: LineJoin,
#[cfg_attr(feature = "serialization", serde(skip))]
pub(crate) font_ids: [Option<FontId>; 8],
pub(crate) font_size: f32,
pub(crate) letter_spacing: f32,
pub(crate) text_baseline: Baseline,
pub(crate) text_align: Align,
pub(crate) fill_rule: FillRule,
}
impl Default for Paint {
fn default() -> Self {
Self {
flavor: PaintFlavor::Color(Color::white()),
transform: Default::default(),
alpha_mask: Default::default(),
shape_anti_alias: true,
stencil_strokes: true,
miter_limit: 10.0,
line_width: 1.0,
line_cap_start: Default::default(),
line_cap_end: Default::default(),
line_join: Default::default(),
font_ids: Default::default(),
font_size: 16.0,
letter_spacing: 0.0,
text_baseline: Default::default(),
text_align: Default::default(),
fill_rule: Default::default(),
}
}
}
impl Paint {
pub fn color(color: Color) -> Self {
let mut new = Self::default();
new.flavor = PaintFlavor::Color(color);
new
}
pub fn image(
id: ImageId,
cx: f32,
cy: f32,
width: f32,
height: f32,
angle: f32,
alpha: f32,
) -> Self {
let mut new = Self::default();
new.flavor = PaintFlavor::Image {
id,
cx,
cy,
width,
height,
angle,
alpha,
};
new
}
pub fn linear_gradient(
start_x: f32,
start_y: f32,
end_x: f32,
end_y: f32,
start_color: Color,
end_color: Color,
) -> Self {
let mut new = Self::default();
new.flavor = PaintFlavor::LinearGradient {
start_x,
start_y,
end_x,
end_y,
colors: GradientColors::TwoStop {
start_color,
end_color,
},
};
new
}
pub fn linear_gradient_stops(
start_x: f32,
start_y: f32,
end_x: f32,
end_y: f32,
stops: &[(f32, Color)],
) -> Self {
let mut new = Self::default();
new.flavor = PaintFlavor::LinearGradient {
start_x,
start_y,
end_x,
end_y,
colors: GradientColors::from_stops(stops),
};
new
}
pub fn box_gradient(
x: f32,
y: f32,
width: f32,
height: f32,
radius: f32,
feather: f32,
inner_color: Color,
outer_color: Color,
) -> Self {
let mut new = Self::default();
new.flavor = PaintFlavor::BoxGradient {
x,
y,
width,
height,
radius,
feather,
colors: GradientColors::TwoStop {
start_color: inner_color,
end_color: outer_color,
},
};
new
}
pub fn radial_gradient(
cx: f32,
cy: f32,
in_radius: f32,
out_radius: f32,
inner_color: Color,
outer_color: Color,
) -> Self {
let mut new = Self::default();
new.flavor = PaintFlavor::RadialGradient {
cx,
cy,
in_radius,
out_radius,
colors: GradientColors::TwoStop {
start_color: inner_color,
end_color: outer_color,
},
};
new
}
pub fn radial_gradient_stops(
cx: f32,
cy: f32,
in_radius: f32,
out_radius: f32,
stops: &[(f32, Color)],
) -> Self {
let mut new = Self::default();
new.flavor = PaintFlavor::RadialGradient {
cx,
cy,
in_radius,
out_radius,
colors: GradientColors::from_stops(stops),
};
new
}
pub fn set_color(&mut self, color: Color) {
self.flavor = PaintFlavor::Color(color);
}
pub(crate) fn alpha_mask(&self) -> Option<ImageId> {
self.alpha_mask
}
pub(crate) fn set_alpha_mask(&mut self, image_id: Option<ImageId>) {
self.alpha_mask = image_id;
}
pub fn anti_alias(&self) -> bool {
self.shape_anti_alias
}
pub fn set_anti_alias(&mut self, value: bool) {
self.shape_anti_alias = value;
}
pub fn stencil_strokes(&self) -> bool {
self.stencil_strokes
}
pub fn set_stencil_strokes(&mut self, value: bool) {
self.stencil_strokes = value;
}
pub fn line_width(&self) -> f32 {
self.line_width
}
pub fn set_line_width(&mut self, width: f32) {
self.line_width = width;
}
pub fn miter_limit(&self) -> f32 {
self.miter_limit
}
pub fn set_miter_limit(&mut self, limit: f32) {
self.miter_limit = limit;
}
pub fn line_cap_start(&self) -> LineCap {
self.line_cap_start
}
pub fn line_cap_end(&self) -> LineCap {
self.line_cap_end
}
pub fn set_line_cap(&mut self, cap: LineCap) {
self.line_cap_start = cap;
self.line_cap_end = cap;
}
pub fn set_line_cap_start(&mut self, cap: LineCap) {
self.line_cap_start = cap;
}
pub fn set_line_cap_end(&mut self, cap: LineCap) {
self.line_cap_end = cap;
}
pub fn line_join(&self) -> LineJoin {
self.line_join
}
pub fn set_line_join(&mut self, join: LineJoin) {
self.line_join = join;
}
pub fn set_font(&mut self, font_ids: &[FontId]) {
self.font_ids = Default::default();
for (i, id) in font_ids.iter().take(8).enumerate() {
self.font_ids[i] = Some(*id);
}
}
pub fn font_size(&self) -> f32 {
self.font_size
}
pub fn set_font_size(&mut self, size: f32) {
self.font_size = size;
}
pub fn letter_spacing(&self) -> f32 {
self.letter_spacing
}
pub fn set_letter_spacing(&mut self, spacing: f32) {
self.letter_spacing = spacing;
}
pub fn text_baseline(&self) -> Baseline {
self.text_baseline
}
pub fn set_text_baseline(&mut self, align: Baseline) {
self.text_baseline = align;
}
pub fn text_align(&self) -> Align {
self.text_align
}
pub fn set_text_align(&mut self, align: Align) {
self.text_align = align;
}
pub fn fill_rule(&self) -> FillRule {
self.fill_rule
}
pub fn set_fill_rule(&mut self, rule: FillRule) {
self.fill_rule = rule;
}
pub(crate) fn mul_alpha(&mut self, a: f32) {
match &mut self.flavor {
PaintFlavor::Color(color) => {
color.a *= a;
}
PaintFlavor::Image { alpha, .. } => {
*alpha *= a;
}
PaintFlavor::LinearGradient { colors, .. } => {
colors.mul_alpha(a);
}
PaintFlavor::BoxGradient { colors, .. } => {
colors.mul_alpha(a);
}
PaintFlavor::RadialGradient { colors, .. } => {
colors.mul_alpha(a);
}
}
}
}