use super::*;
impl Color {
pub fn rgb(red: u8, green: u8, blue: u8) -> Self {
Self {
red,
green,
blue,
alpha: 255,
}
}
pub fn rgba(red: u8, green: u8, blue: u8, alpha: u8) -> Self {
Self {
red,
green,
blue,
alpha,
}
}
pub fn from_u32(packed: u32) -> Self {
Self {
red: ((packed >> 24) & 0xFF) as u8,
green: ((packed >> 16) & 0xFF) as u8,
blue: ((packed >> 8) & 0xFF) as u8,
alpha: (packed & 0xFF) as u8,
}
}
pub fn to_u32(self) -> u32 {
((self.get_red() as u32) << 24)
| ((self.get_green() as u32) << 16)
| ((self.get_blue() as u32) << 8)
| (self.get_alpha() as u32)
}
pub fn to_css(self) -> String {
if self.get_alpha() == 255 {
format!(
"#{:02x}{:02x}{:02x}",
self.get_red(),
self.get_green(),
self.get_blue()
)
} else {
format!(
"rgba({},{},{},{})",
self.get_red(),
self.get_green(),
self.get_blue(),
format!("{:.3}", self.get_alpha() as f64 / 255.0)
.trim_end_matches('0')
.trim_end_matches('.')
)
}
}
pub fn lerp(self, other: Self, amount: f64) -> Self {
let amount: f64 = amount.clamp(0.0, 1.0);
let mix = move |from: u8, to: u8| {
(from as f64 + (to as f64 - from as f64) * amount)
.round()
.clamp(0.0, 255.0) as u8
};
Self {
red: mix(self.get_red(), other.get_red()),
green: mix(self.get_green(), other.get_green()),
blue: mix(self.get_blue(), other.get_blue()),
alpha: mix(self.get_alpha(), other.get_alpha()),
}
}
pub fn parse(input: &str) -> Option<Self> {
parse_css_color(input)
}
pub fn with_alpha(self, alpha: u8) -> Self {
Self { alpha, ..self }
}
pub fn mix(self, other: Self, ratio: f64) -> Self {
let ratio: f64 = ratio.clamp(0.0, 1.0);
let blend = move |a: u8, b: u8| (a as f64 * (1.0 - ratio) + b as f64 * ratio).round() as u8;
Self {
red: blend(self.get_red(), other.get_red()),
green: blend(self.get_green(), other.get_green()),
blue: blend(self.get_blue(), other.get_blue()),
alpha: blend(self.get_alpha(), other.get_alpha()),
}
}
pub fn lighten(self, ratio: f64) -> Self {
self.mix(Self::rgb(255, 255, 255), ratio)
}
pub fn darken(self, ratio: f64) -> Self {
self.mix(Self::rgb(0, 0, 0), ratio)
}
}
impl LinearGradient {
pub fn new(start: (f64, f64), end: (f64, f64)) -> Self {
Self {
start,
end,
stops: Vec::new(),
}
}
pub fn with_stop(mut self, offset: f64, color: Color) -> Self {
self.get_mut_stops().push(GradientStop { offset, color });
self.get_mut_stops()
.sort_by(|a: &GradientStop, b: &GradientStop| {
a.offset
.partial_cmp(&b.offset)
.unwrap_or(core::cmp::Ordering::Equal)
});
self
}
}
impl RadialGradient {
pub fn new(center: (f64, f64), radius: f64) -> Self {
Self {
center,
radius,
stops: Vec::new(),
}
}
pub fn with_stop(mut self, offset: f64, color: Color) -> Self {
self.get_mut_stops().push(GradientStop { offset, color });
self.get_mut_stops()
.sort_by(|a: &GradientStop, b: &GradientStop| {
a.offset
.partial_cmp(&b.offset)
.unwrap_or(core::cmp::Ordering::Equal)
});
self
}
}
impl TextStyle {
pub fn new() -> Self {
Self {
family: DEFAULT_FONT_FAMILY.to_string(),
size: 12.0,
weight: DEFAULT_FONT_WEIGHT.to_string(),
style: DEFAULT_FONT_STYLE.to_string(),
align: TextAlign::default(),
vertical_align: TextVerticalAlign::default(),
line_height: 1.2,
wrap: false,
overflow: TextOverflow::default(),
color: Color::rgb(51, 51, 51),
background: None,
padding: (0.0, 0.0, 0.0, 0.0),
border_width: 0.0,
border_color: None,
border_radius: 0.0,
rotate: 0.0,
stroke_width: 0.0,
stroke_color: None,
rich: Vec::new(),
}
}
pub fn font(&self) -> String {
format!(
"{} {} {} {}",
self.get_style(),
self.get_weight(),
self.get_size(),
self.get_family()
)
}
pub fn line_height_px(&self) -> f64 {
self.get_size() * self.get_line_height()
}
pub fn with_rich(mut self, name: &str, style: RichTextStyle) -> Self {
self.get_mut_rich().push((name.to_string(), style));
self
}
}
impl Default for TextStyle {
fn default() -> Self {
Self::new()
}
}
impl RichTextStyle {
pub fn new() -> Self {
Self {
family: None,
size: None,
weight: None,
style: None,
color: None,
background: None,
padding: None,
line_height: None,
}
}
}
impl Default for RichTextStyle {
fn default() -> Self {
Self::new()
}
}
impl LineStyle {
pub fn new() -> Self {
Self {
width: 1.0,
cap: LineCap::default(),
join: LineJoin::default(),
dash: DashPattern::solid(),
opacity: 1.0,
shadow: None,
color: Color::rgb(51, 51, 51),
}
}
pub fn geometry(&self) -> StrokeStyle {
StrokeStyle {
width: self.get_width() as f32,
cap: *self.get_cap(),
join: *self.get_join(),
dash: self.get_dash().clone(),
}
}
pub fn dash_for(dash_type: LineDashType, width: f64) -> DashPattern {
let width: f32 = width as f32;
match dash_type {
LineDashType::Solid => DashPattern::solid(),
LineDashType::Dashed => DashPattern::new(vec![width * 5.0, width * 5.0], 0.0),
LineDashType::Dotted => DashPattern::new(vec![width, width * 2.0], 0.0),
}
}
}
impl Default for LineStyle {
fn default() -> Self {
Self::new()
}
}
impl ShapeStyle {
pub fn new() -> Self {
Self {
fill: Fill::None,
stroke: Stroke::None,
line_style: LineStyle::new(),
shadow: None,
opacity: 1.0,
}
}
}
impl Default for ShapeStyle {
fn default() -> Self {
Self::new()
}
}
impl Fill {
pub fn solid_color(&self) -> Option<Color> {
match self {
Fill::Solid(color) => Some(*color),
_ => None,
}
}
}
impl Stroke {
pub fn solid_color(&self) -> Option<Color> {
match self {
Stroke::Solid(color) => Some(*color),
_ => None,
}
}
}