Merge branch 'master' into feature/canvas-interaction
This commit is contained in:
commit
67b2ccb4d5
@ -24,6 +24,8 @@ debug = ["iced_winit/debug"]
|
||||
tokio = ["iced_futures/tokio"]
|
||||
# Enables `async-std` as the `executor::Default` on native platforms
|
||||
async-std = ["iced_futures/async-std"]
|
||||
# Enables advanced color conversion via `palette`
|
||||
palette = ["iced_core/palette"]
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "actively-developed" }
|
||||
@ -39,6 +41,7 @@ members = [
|
||||
"winit",
|
||||
"examples/bezier_tool",
|
||||
"examples/clock",
|
||||
"examples/color_palette",
|
||||
"examples/counter",
|
||||
"examples/custom_widget",
|
||||
"examples/download_progress",
|
||||
@ -58,6 +61,7 @@ members = [
|
||||
]
|
||||
|
||||
[dependencies]
|
||||
iced_core = { version = "0.2", path = "core" }
|
||||
iced_futures = { version = "0.1", path = "futures" }
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||
|
@ -8,3 +8,7 @@ license = "MIT"
|
||||
repository = "https://github.com/hecrj/iced"
|
||||
|
||||
[dependencies]
|
||||
|
||||
[dependencies.palette]
|
||||
version = "0.5.0"
|
||||
optional = true
|
||||
|
@ -1,10 +1,16 @@
|
||||
#[cfg(feature = "palette")]
|
||||
use palette::rgb::{Srgb, Srgba};
|
||||
|
||||
/// A color in the sRGB color space.
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Default)]
|
||||
pub struct Color {
|
||||
/// Red component, 0.0 - 1.0
|
||||
pub r: f32,
|
||||
/// Green component, 0.0 - 1.0
|
||||
pub g: f32,
|
||||
/// Blue component, 0.0 - 1.0
|
||||
pub b: f32,
|
||||
/// Transparency, 0.0 - 1.0
|
||||
pub a: f32,
|
||||
}
|
||||
|
||||
@ -33,11 +39,45 @@ impl Color {
|
||||
a: 0.0,
|
||||
};
|
||||
|
||||
/// Creates a new [`Color`].
|
||||
///
|
||||
/// In debug mode, it will panic if the values are not in the correct
|
||||
/// range: 0.0 - 1.0
|
||||
///
|
||||
/// [`Color`]: struct.Color.html
|
||||
pub fn new(r: f32, g: f32, b: f32, a: f32) -> Color {
|
||||
debug_assert!(
|
||||
(0.0..=1.0).contains(&r),
|
||||
"Red component must be on [0, 1]"
|
||||
);
|
||||
debug_assert!(
|
||||
(0.0..=1.0).contains(&g),
|
||||
"Green component must be on [0, 1]"
|
||||
);
|
||||
debug_assert!(
|
||||
(0.0..=1.0).contains(&b),
|
||||
"Blue component must be on [0, 1]"
|
||||
);
|
||||
debug_assert!(
|
||||
(0.0..=1.0).contains(&a),
|
||||
"Alpha component must be on [0, 1]"
|
||||
);
|
||||
|
||||
Color { r, g, b, a }
|
||||
}
|
||||
|
||||
/// Creates a [`Color`] from its RGB components.
|
||||
///
|
||||
/// [`Color`]: struct.Color.html
|
||||
pub const fn from_rgb(r: f32, g: f32, b: f32) -> Color {
|
||||
Color { r, g, b, a: 1.0 }
|
||||
Color::from_rgba(r, g, b, 1.0f32)
|
||||
}
|
||||
|
||||
/// Creates a [`Color`] from its RGBA components.
|
||||
///
|
||||
/// [`Color`]: struct.Color.html
|
||||
pub const fn from_rgba(r: f32, g: f32, b: f32, a: f32) -> Color {
|
||||
Color { r, g, b, a }
|
||||
}
|
||||
|
||||
/// Creates a [`Color`] from its RGB8 components.
|
||||
@ -80,16 +120,114 @@ impl Color {
|
||||
self.a,
|
||||
]
|
||||
}
|
||||
|
||||
/// Inverts the [`Color`] in-place.
|
||||
///
|
||||
/// [`Color`]: struct.Color.html
|
||||
pub fn invert(&mut self) {
|
||||
self.r = 1.0f32 - self.r;
|
||||
self.b = 1.0f32 - self.g;
|
||||
self.g = 1.0f32 - self.b;
|
||||
}
|
||||
|
||||
/// Returns the inverted [`Color`].
|
||||
///
|
||||
/// [`Color`]: struct.Color.html
|
||||
pub fn inverse(self) -> Color {
|
||||
Color::new(1.0f32 - self.r, 1.0f32 - self.g, 1.0f32 - self.b, self.a)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<[f32; 3]> for Color {
|
||||
fn from([r, g, b]: [f32; 3]) -> Self {
|
||||
Color { r, g, b, a: 1.0 }
|
||||
Color::new(r, g, b, 1.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<[f32; 4]> for Color {
|
||||
fn from([r, g, b, a]: [f32; 4]) -> Self {
|
||||
Color { r, g, b, a }
|
||||
Color::new(r, g, b, a)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "palette")]
|
||||
/// Converts from palette's `Srgba` type to a [`Color`].
|
||||
///
|
||||
/// [`Color`]: struct.Color.html
|
||||
impl From<Srgba> for Color {
|
||||
fn from(srgba: Srgba) -> Self {
|
||||
Color::new(srgba.red, srgba.green, srgba.blue, srgba.alpha)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "palette")]
|
||||
/// Converts from [`Color`] to palette's `Srgba` type.
|
||||
///
|
||||
/// [`Color`]: struct.Color.html
|
||||
impl From<Color> for Srgba {
|
||||
fn from(c: Color) -> Self {
|
||||
Srgba::new(c.r, c.g, c.b, c.a)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "palette")]
|
||||
/// Converts from palette's `Srgb` type to a [`Color`].
|
||||
///
|
||||
/// [`Color`]: struct.Color.html
|
||||
impl From<Srgb> for Color {
|
||||
fn from(srgb: Srgb) -> Self {
|
||||
Color::new(srgb.red, srgb.green, srgb.blue, 1.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "palette")]
|
||||
/// Converts from [`Color`] to palette's `Srgb` type.
|
||||
///
|
||||
/// [`Color`]: struct.Color.html
|
||||
/// [`Srgb`]: ../palette/rgb/type.Srgb.html
|
||||
impl From<Color> for Srgb {
|
||||
fn from(c: Color) -> Self {
|
||||
Srgb::new(c.r, c.g, c.b)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "palette")]
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use palette::Blend;
|
||||
|
||||
#[test]
|
||||
fn srgba_traits() {
|
||||
let c = Color::from_rgb(0.5, 0.4, 0.3);
|
||||
// Round-trip conversion to the palette:Srgba type
|
||||
let s: Srgba = c.into();
|
||||
let r: Color = s.into();
|
||||
assert_eq!(c, r);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn color_manipulation() {
|
||||
let c1 = Color::from_rgb(0.5, 0.4, 0.3);
|
||||
let c2 = Color::from_rgb(0.2, 0.5, 0.3);
|
||||
|
||||
// Convert to linear color for manipulation
|
||||
let l1 = Srgba::from(c1).into_linear();
|
||||
let l2 = Srgba::from(c2).into_linear();
|
||||
|
||||
// Take the lighter of each of the RGB components
|
||||
let lighter = l1.lighten(l2);
|
||||
|
||||
// Convert back to our Color
|
||||
let r: Color = Srgba::from_linear(lighter).into();
|
||||
assert_eq!(
|
||||
r,
|
||||
Color {
|
||||
r: 0.5,
|
||||
g: 0.5,
|
||||
b: 0.3,
|
||||
a: 1.0
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -71,6 +71,7 @@ A bunch of simpler examples exist:
|
||||
|
||||
- [`bezier_tool`](bezier_tool), a Paint-like tool for drawing Bézier curves using the `Canvas` widget.
|
||||
- [`clock`](clock), an application that uses the `Canvas` widget to draw a clock and its hands to display the current time.
|
||||
- [`color_palette`](color_palette), a color palette generator based on a user-defined root color.
|
||||
- [`counter`](counter), the classic counter example explained in the [`README`](../README.md).
|
||||
- [`custom_widget`](custom_widget), a demonstration of how to build a custom widget that draws a circle.
|
||||
- [`download_progress`](download_progress), a basic application that asynchronously downloads a dummy file of 100 MB and tracks the download progress.
|
||||
|
10
examples/color_palette/Cargo.toml
Normal file
10
examples/color_palette/Cargo.toml
Normal file
@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "color_palette"
|
||||
version = "0.1.0"
|
||||
authors = ["Clark Moody <clark@clarkmoody.com>"]
|
||||
edition = "2018"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
iced = { path = "../..", features = ["canvas", "palette"] }
|
||||
palette = "0.5.0"
|
15
examples/color_palette/README.md
Normal file
15
examples/color_palette/README.md
Normal file
@ -0,0 +1,15 @@
|
||||
## Color palette
|
||||
|
||||
A color palette generator, based on a user-defined root color.
|
||||
|
||||
<div align="center">
|
||||
<a href="https://gfycat.com/dirtylonebighornsheep">
|
||||
<img src="https://github.com/hecrj/iced/raw/1a8d253611d3796b0a32b2f096bb54565a5292e0/examples/color_palette/screenshot.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
You can run it with `cargo run`:
|
||||
|
||||
```
|
||||
cargo run --package color_palette
|
||||
```
|
BIN
examples/color_palette/screenshot.png
Normal file
BIN
examples/color_palette/screenshot.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 103 KiB |
444
examples/color_palette/src/main.rs
Normal file
444
examples/color_palette/src/main.rs
Normal file
@ -0,0 +1,444 @@
|
||||
use iced::{
|
||||
canvas, slider, Align, Canvas, Color, Column, Element, HorizontalAlignment,
|
||||
Length, Point, Row, Sandbox, Settings, Size, Slider, Text, Vector,
|
||||
VerticalAlignment,
|
||||
};
|
||||
use palette::{self, Limited};
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::RangeInclusive;
|
||||
|
||||
pub fn main() {
|
||||
ColorPalette::run(Settings {
|
||||
antialiasing: true,
|
||||
..Settings::default()
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ColorPalette {
|
||||
theme: Theme,
|
||||
rgb: ColorPicker<Color>,
|
||||
hsl: ColorPicker<palette::Hsl>,
|
||||
hsv: ColorPicker<palette::Hsv>,
|
||||
hwb: ColorPicker<palette::Hwb>,
|
||||
lab: ColorPicker<palette::Lab>,
|
||||
lch: ColorPicker<palette::Lch>,
|
||||
canvas_layer: canvas::layer::Cache<Theme>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum Message {
|
||||
RgbColorChanged(Color),
|
||||
HslColorChanged(palette::Hsl),
|
||||
HsvColorChanged(palette::Hsv),
|
||||
HwbColorChanged(palette::Hwb),
|
||||
LabColorChanged(palette::Lab),
|
||||
LchColorChanged(palette::Lch),
|
||||
}
|
||||
|
||||
impl Sandbox for ColorPalette {
|
||||
type Message = Message;
|
||||
|
||||
fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
fn title(&self) -> String {
|
||||
String::from("Color palette - Iced")
|
||||
}
|
||||
|
||||
fn update(&mut self, message: Message) {
|
||||
let srgb = match message {
|
||||
Message::RgbColorChanged(rgb) => palette::Srgb::from(rgb),
|
||||
Message::HslColorChanged(hsl) => palette::Srgb::from(hsl),
|
||||
Message::HsvColorChanged(hsv) => palette::Srgb::from(hsv),
|
||||
Message::HwbColorChanged(hwb) => palette::Srgb::from(hwb),
|
||||
Message::LabColorChanged(lab) => palette::Srgb::from(lab),
|
||||
Message::LchColorChanged(lch) => palette::Srgb::from(lch),
|
||||
};
|
||||
|
||||
self.theme = Theme::new(srgb.clamp());
|
||||
self.canvas_layer.clear();
|
||||
}
|
||||
|
||||
fn view(&mut self) -> Element<Message> {
|
||||
let base = self.theme.base;
|
||||
|
||||
let srgb = palette::Srgb::from(base);
|
||||
let hsl = palette::Hsl::from(srgb);
|
||||
let hsv = palette::Hsv::from(srgb);
|
||||
let hwb = palette::Hwb::from(srgb);
|
||||
let lab = palette::Lab::from(srgb);
|
||||
let lch = palette::Lch::from(srgb);
|
||||
|
||||
Column::new()
|
||||
.padding(10)
|
||||
.spacing(10)
|
||||
.push(self.rgb.view(base).map(Message::RgbColorChanged))
|
||||
.push(self.hsl.view(hsl).map(Message::HslColorChanged))
|
||||
.push(self.hsv.view(hsv).map(Message::HsvColorChanged))
|
||||
.push(self.hwb.view(hwb).map(Message::HwbColorChanged))
|
||||
.push(self.lab.view(lab).map(Message::LabColorChanged))
|
||||
.push(self.lch.view(lch).map(Message::LchColorChanged))
|
||||
.push(
|
||||
Canvas::new()
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.push(self.canvas_layer.with(&self.theme)),
|
||||
)
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Theme {
|
||||
lower: Vec<Color>,
|
||||
base: Color,
|
||||
higher: Vec<Color>,
|
||||
}
|
||||
|
||||
impl Theme {
|
||||
pub fn new(base: impl Into<Color>) -> Theme {
|
||||
use palette::{Hsl, Hue, Shade, Srgb};
|
||||
|
||||
let base = base.into();
|
||||
|
||||
// Convert to HSL color for manipulation
|
||||
let hsl = Hsl::from(Srgb::from(base));
|
||||
|
||||
let lower = [
|
||||
hsl.shift_hue(-135.0).lighten(0.075),
|
||||
hsl.shift_hue(-120.0),
|
||||
hsl.shift_hue(-105.0).darken(0.075),
|
||||
hsl.darken(0.075),
|
||||
];
|
||||
|
||||
let higher = [
|
||||
hsl.lighten(0.075),
|
||||
hsl.shift_hue(105.0).darken(0.075),
|
||||
hsl.shift_hue(120.0),
|
||||
hsl.shift_hue(135.0).lighten(0.075),
|
||||
];
|
||||
|
||||
Theme {
|
||||
lower: lower
|
||||
.iter()
|
||||
.map(|&color| Srgb::from(color).clamp().into())
|
||||
.collect(),
|
||||
base,
|
||||
higher: higher
|
||||
.iter()
|
||||
.map(|&color| Srgb::from(color).clamp().into())
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.lower.len() + self.higher.len() + 1
|
||||
}
|
||||
|
||||
pub fn colors(&self) -> impl Iterator<Item = &Color> {
|
||||
self.lower
|
||||
.iter()
|
||||
.chain(std::iter::once(&self.base))
|
||||
.chain(self.higher.iter())
|
||||
}
|
||||
}
|
||||
|
||||
impl canvas::Drawable for Theme {
|
||||
fn draw(&self, frame: &mut canvas::Frame) {
|
||||
use canvas::Path;
|
||||
use palette::{Hsl, Srgb};
|
||||
|
||||
let pad = 20.0;
|
||||
|
||||
let box_size = Size {
|
||||
width: frame.width() / self.len() as f32,
|
||||
height: frame.height() / 2.0 - pad,
|
||||
};
|
||||
|
||||
let triangle = Path::new(|path| {
|
||||
path.move_to(Point { x: 0.0, y: -0.5 });
|
||||
path.line_to(Point { x: -0.5, y: 0.0 });
|
||||
path.line_to(Point { x: 0.5, y: 0.0 });
|
||||
path.close();
|
||||
});
|
||||
|
||||
let mut text = canvas::Text {
|
||||
horizontal_alignment: HorizontalAlignment::Center,
|
||||
vertical_alignment: VerticalAlignment::Top,
|
||||
size: 15.0,
|
||||
..canvas::Text::default()
|
||||
};
|
||||
|
||||
for (i, &color) in self.colors().enumerate() {
|
||||
let anchor = Point {
|
||||
x: (i as f32) * box_size.width,
|
||||
y: 0.0,
|
||||
};
|
||||
let rect = Path::rectangle(anchor, box_size);
|
||||
frame.fill(&rect, color);
|
||||
|
||||
// We show a little indicator for the base color
|
||||
if color == self.base {
|
||||
let triangle_x = anchor.x + box_size.width / 2.0;
|
||||
|
||||
frame.with_save(|frame| {
|
||||
frame.translate(Vector::new(triangle_x, 0.0));
|
||||
frame.scale(10.0);
|
||||
frame.rotate(std::f32::consts::PI);
|
||||
|
||||
frame.fill(&triangle, Color::WHITE);
|
||||
});
|
||||
|
||||
frame.with_save(|frame| {
|
||||
frame.translate(Vector::new(triangle_x, box_size.height));
|
||||
frame.scale(10.0);
|
||||
|
||||
frame.fill(&triangle, Color::WHITE);
|
||||
});
|
||||
}
|
||||
|
||||
frame.fill_text(canvas::Text {
|
||||
content: color_hex_string(&color),
|
||||
position: Point {
|
||||
x: anchor.x + box_size.width / 2.0,
|
||||
y: box_size.height,
|
||||
},
|
||||
..text
|
||||
});
|
||||
}
|
||||
|
||||
text.vertical_alignment = VerticalAlignment::Bottom;
|
||||
|
||||
let hsl = Hsl::from(Srgb::from(self.base));
|
||||
for i in 0..self.len() {
|
||||
let pct = (i as f32 + 1.0) / (self.len() as f32 + 1.0);
|
||||
let graded = Hsl {
|
||||
lightness: 1.0 - pct,
|
||||
..hsl
|
||||
};
|
||||
let color: Color = Srgb::from(graded.clamp()).into();
|
||||
|
||||
let anchor = Point {
|
||||
x: (i as f32) * box_size.width,
|
||||
y: box_size.height + 2.0 * pad,
|
||||
};
|
||||
|
||||
let rect = Path::rectangle(anchor, box_size);
|
||||
frame.fill(&rect, color);
|
||||
|
||||
frame.fill_text(canvas::Text {
|
||||
content: color_hex_string(&color),
|
||||
position: Point {
|
||||
x: anchor.x + box_size.width / 2.0,
|
||||
y: box_size.height + 2.0 * pad,
|
||||
},
|
||||
..text
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Theme {
|
||||
fn default() -> Self {
|
||||
Theme::new(Color::from_rgb8(75, 128, 190))
|
||||
}
|
||||
}
|
||||
|
||||
fn color_hex_string(color: &Color) -> String {
|
||||
format!(
|
||||
"#{:x}{:x}{:x}",
|
||||
(255.0 * color.r).round() as u8,
|
||||
(255.0 * color.g).round() as u8,
|
||||
(255.0 * color.b).round() as u8
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct ColorPicker<C: ColorSpace> {
|
||||
sliders: [slider::State; 3],
|
||||
color_space: PhantomData<C>,
|
||||
}
|
||||
|
||||
trait ColorSpace: Sized {
|
||||
const LABEL: &'static str;
|
||||
const COMPONENT_RANGES: [RangeInclusive<f32>; 3];
|
||||
|
||||
fn new(a: f32, b: f32, c: f32) -> Self;
|
||||
|
||||
fn components(&self) -> [f32; 3];
|
||||
|
||||
fn to_string(&self) -> String;
|
||||
}
|
||||
|
||||
impl<C: 'static + ColorSpace + Copy> ColorPicker<C> {
|
||||
fn view(&mut self, color: C) -> Element<C> {
|
||||
let [c1, c2, c3] = color.components();
|
||||
let [s1, s2, s3] = &mut self.sliders;
|
||||
let [cr1, cr2, cr3] = C::COMPONENT_RANGES;
|
||||
|
||||
Row::new()
|
||||
.spacing(10)
|
||||
.align_items(Align::Center)
|
||||
.push(Text::new(C::LABEL).width(Length::Units(50)))
|
||||
.push(Slider::new(s1, cr1, c1, move |v| C::new(v, c2, c3)))
|
||||
.push(Slider::new(s2, cr2, c2, move |v| C::new(c1, v, c3)))
|
||||
.push(Slider::new(s3, cr3, c3, move |v| C::new(c1, c2, v)))
|
||||
.push(
|
||||
Text::new(color.to_string())
|
||||
.width(Length::Units(185))
|
||||
.size(14),
|
||||
)
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl ColorSpace for Color {
|
||||
const LABEL: &'static str = "RGB";
|
||||
const COMPONENT_RANGES: [RangeInclusive<f32>; 3] =
|
||||
[0.0..=1.0, 0.0..=1.0, 0.0..=1.0];
|
||||
|
||||
fn new(r: f32, g: f32, b: f32) -> Self {
|
||||
Color::from_rgb(r, g, b)
|
||||
}
|
||||
|
||||
fn components(&self) -> [f32; 3] {
|
||||
[self.r, self.g, self.b]
|
||||
}
|
||||
|
||||
fn to_string(&self) -> String {
|
||||
format!(
|
||||
"rgb({:.0}, {:.0}, {:.0})",
|
||||
255.0 * self.r,
|
||||
255.0 * self.g,
|
||||
255.0 * self.b
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl ColorSpace for palette::Hsl {
|
||||
const LABEL: &'static str = "HSL";
|
||||
const COMPONENT_RANGES: [RangeInclusive<f32>; 3] =
|
||||
[0.0..=360.0, 0.0..=1.0, 0.0..=1.0];
|
||||
|
||||
fn new(hue: f32, saturation: f32, lightness: f32) -> Self {
|
||||
palette::Hsl::new(
|
||||
palette::RgbHue::from_degrees(hue),
|
||||
saturation,
|
||||
lightness,
|
||||
)
|
||||
}
|
||||
|
||||
fn components(&self) -> [f32; 3] {
|
||||
[
|
||||
self.hue.to_positive_degrees(),
|
||||
self.saturation,
|
||||
self.lightness,
|
||||
]
|
||||
}
|
||||
|
||||
fn to_string(&self) -> String {
|
||||
format!(
|
||||
"hsl({:.1}, {:.1}%, {:.1}%)",
|
||||
self.hue.to_positive_degrees(),
|
||||
100.0 * self.saturation,
|
||||
100.0 * self.lightness
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl ColorSpace for palette::Hsv {
|
||||
const LABEL: &'static str = "HSV";
|
||||
const COMPONENT_RANGES: [RangeInclusive<f32>; 3] =
|
||||
[0.0..=360.0, 0.0..=1.0, 0.0..=1.0];
|
||||
|
||||
fn new(hue: f32, saturation: f32, value: f32) -> Self {
|
||||
palette::Hsv::new(palette::RgbHue::from_degrees(hue), saturation, value)
|
||||
}
|
||||
|
||||
fn components(&self) -> [f32; 3] {
|
||||
[self.hue.to_positive_degrees(), self.saturation, self.value]
|
||||
}
|
||||
|
||||
fn to_string(&self) -> String {
|
||||
format!(
|
||||
"hsv({:.1}, {:.1}%, {:.1}%)",
|
||||
self.hue.to_positive_degrees(),
|
||||
100.0 * self.saturation,
|
||||
100.0 * self.value
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl ColorSpace for palette::Hwb {
|
||||
const LABEL: &'static str = "HWB";
|
||||
const COMPONENT_RANGES: [RangeInclusive<f32>; 3] =
|
||||
[0.0..=360.0, 0.0..=1.0, 0.0..=1.0];
|
||||
|
||||
fn new(hue: f32, whiteness: f32, blackness: f32) -> Self {
|
||||
palette::Hwb::new(
|
||||
palette::RgbHue::from_degrees(hue),
|
||||
whiteness,
|
||||
blackness,
|
||||
)
|
||||
}
|
||||
|
||||
fn components(&self) -> [f32; 3] {
|
||||
[
|
||||
self.hue.to_positive_degrees(),
|
||||
self.whiteness,
|
||||
self.blackness,
|
||||
]
|
||||
}
|
||||
|
||||
fn to_string(&self) -> String {
|
||||
format!(
|
||||
"hwb({:.1}, {:.1}%, {:.1}%)",
|
||||
self.hue.to_positive_degrees(),
|
||||
100.0 * self.whiteness,
|
||||
100.0 * self.blackness
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl ColorSpace for palette::Lab {
|
||||
const LABEL: &'static str = "Lab";
|
||||
const COMPONENT_RANGES: [RangeInclusive<f32>; 3] =
|
||||
[0.0..=100.0, -128.0..=127.0, -128.0..=127.0];
|
||||
|
||||
fn new(l: f32, a: f32, b: f32) -> Self {
|
||||
palette::Lab::new(l, a, b)
|
||||
}
|
||||
|
||||
fn components(&self) -> [f32; 3] {
|
||||
[self.l, self.a, self.b]
|
||||
}
|
||||
|
||||
fn to_string(&self) -> String {
|
||||
format!("Lab({:.1}, {:.1}, {:.1})", self.l, self.a, self.b)
|
||||
}
|
||||
}
|
||||
|
||||
impl ColorSpace for palette::Lch {
|
||||
const LABEL: &'static str = "Lch";
|
||||
const COMPONENT_RANGES: [RangeInclusive<f32>; 3] =
|
||||
[0.0..=100.0, 0.0..=128.0, 0.0..=360.0];
|
||||
|
||||
fn new(l: f32, chroma: f32, hue: f32) -> Self {
|
||||
palette::Lch::new(l, chroma, palette::LabHue::from_degrees(hue))
|
||||
}
|
||||
|
||||
fn components(&self) -> [f32; 3] {
|
||||
[self.l, self.chroma, self.hue.to_positive_degrees()]
|
||||
}
|
||||
|
||||
fn to_string(&self) -> String {
|
||||
format!(
|
||||
"Lch({:.1}, {:.1}, {:.1})",
|
||||
self.l,
|
||||
self.chroma,
|
||||
self.hue.to_positive_degrees()
|
||||
)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user