diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 15e1f6d..a1896de 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,7 +8,7 @@ env: # If the compilation fails, then the version specified here needs to be bumped up to reality. # Be sure to also update the rust-version property in the workspace Cargo.toml file, # plus all the README.md files of the affected packages. - RUST_MIN_VER: "1.70" + RUST_MIN_VER: "1.82" # List of packages that will be checked with the minimum supported Rust version. # This should be limited to packages that are intended for publishing. RUST_MIN_VER_PKGS: "-p peniko" diff --git a/CHANGELOG.md b/CHANGELOG.md index a155570..99928d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,11 +13,30 @@ You can find its changes [documented below](#020-2024-09-19). ## [Unreleased] -This release has an [MSRV] of 1.70. +This release has an [MSRV] of 1.82. ### Changed - `Image` now stores the alpha as an `f32` ([#65][] by [@waywardmonkeys][]) +- Use `color` crate. See below for details ([#63][] by [@waywardmonkeys][]) + +### Color Changes + +The old code behind `peniko::Color` has been removed and color functionality is now provided by the [`color`] crate. + +This leads to a number of breaking changes: + +- `peniko::Color` is now a type alias for `AlphaColor` from the `color` crate. +- `AlphaColor` does not, at this time, impl `Default`, `PartialEq`, `PartialOrd`, or `Hash`. +- `Brush` and `BrushRef` no longer impl `PartialEq`. +- `ColorStop` no longer impls `Default` or `PartialOrd`. +- `Brush`, `BrushRef`, and `ColorStop` can be constructed from a variety of color types, although, for now, `Brush` and `BrushRef` convert this internally into an unclipped `AlphaColor`. +- The `color` crate is re-exported as `peniko::color`, so access to functionality from there is easy. +- The various pre-defined color constants like `peniko::Color::YELLOW` are no longer available. + Instead, use the CSS palette provided within `color`: `peniko::color::palette::css::YELLOW`. +- Similarly, parsing a color string is now provided by the `color` crate. + +This is the first step towards providing better support for richer color functionality throughout the Linebender stack. ## [0.2.0][] (2024-09-19) @@ -48,12 +67,14 @@ This release has an [MSRV] of 1.70. - Initial release [MSRV]: README.md#minimum-supported-rust-version-msrv +[`color`]: https://docs.rs/color/ [#26]: https://github.com/linebender/peniko/pull/26 [#40]: https://github.com/linebender/peniko/pull/40 [#46]: https://github.com/linebender/peniko/pull/46 [#47]: https://github.com/linebender/peniko/pull/47 [#52]: https://github.com/linebender/peniko/pull/52 +[#63]: https://github.com/linebender/peniko/pull/63 [#65]: https://github.com/linebender/peniko/pull/65 [@DJMcNab]: https://github.com/DJMcNab diff --git a/Cargo.lock b/Cargo.lock index c02a93b..207ca52 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,15 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +[[package]] +name = "color" +version = "0.1.0" +source = "git+https://github.com/linebender/color.git?rev=bccd4050607eba830cfba3b2f39616a27500288a#bccd4050607eba830cfba3b2f39616a27500288a" +dependencies = [ + "libm", + "serde", +] + [[package]] name = "kurbo" version = "0.11.1" @@ -23,9 +32,9 @@ dependencies = [ [[package]] name = "libm" -version = "0.2.8" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" +checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" [[package]] name = "mint" @@ -37,6 +46,7 @@ checksum = "e53debba6bda7a793e5f99b8dacf19e626084f525f7829104ba9898f367d85ff" name = "peniko" version = "0.2.0" dependencies = [ + "color", "kurbo", "serde", "serde_bytes", @@ -45,9 +55,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] @@ -63,9 +73,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.210" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" dependencies = [ "serde_derive", ] @@ -81,9 +91,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.210" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" dependencies = [ "proc-macro2", "quote", @@ -101,9 +111,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.77" +version = "2.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index e9cbc65..b7a398e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,10 +8,9 @@ keywords = ["graphics", "vector", "style"] categories = ["graphics"] repository = "https://github.com/linebender/peniko" readme = "README.md" -# We support from Rust 1.70 so that CI uses the sparse protocol. # Keep in sync with RUST_MIN_VER in .github/workflows/ci.yml and with the relevant README.md files. # and with the MSRV in the `Unreleased` section of CHANGELOG.md. -rust-version = "1.70" +rust-version = "1.82" [package.metadata.docs.rs] all-features = true @@ -21,16 +20,22 @@ targets = [] [features] default = ["std"] -std = ["kurbo/std"] -libm = ["kurbo/libm"] +std = ["color/std", "kurbo/std"] +libm = ["color/libm", "kurbo/libm"] mint = ["kurbo/mint"] -serde = ["smallvec/serde", "kurbo/serde", "dep:serde_bytes", "dep:serde"] +serde = ["color/serde", "smallvec/serde", "kurbo/serde", "dep:serde_bytes", "dep:serde"] [dependencies] # NOTE: Make sure to keep this in sync with the version badge in README.md kurbo = { version = "0.11.1", default-features = false } smallvec = "1.13.2" +[dependencies.color] +git = "https://github.com/linebender/color.git" +rev = "bccd4050607eba830cfba3b2f39616a27500288a" +version = "0.1.0" +default-features = false + [dependencies.serde] version = "1.0.210" optional = true diff --git a/README.md b/README.md index cc6231b..6d62add 100644 --- a/README.md +++ b/README.md @@ -14,15 +14,14 @@ -The Peniko library builds on top of [kurbo] and provides a set of generic types that define -styles for rendering and composition. +The Peniko library builds on top of [kurbo] and [color] and provides a set of generic types that define styles for rendering and composition. The name "Peniko" is Esperanto for "brush" which is one family of types that the library contains. ## Minimum supported Rust Version (MSRV) -This version of Peniko has been verified to compile with **Rust 1.70** and later. +This version of Peniko has been verified to compile with **Rust 1.82** and later. Future versions of Peniko might increase the Rust version requirement. It will not be treated as a breaking change and as such can even happen with small patch releases. @@ -66,6 +65,7 @@ Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be licensed as above, without any additional terms or conditions. +[color]: https://crates.io/crates/color [kurbo]: https://crates.io/crates/kurbo [Rust Code of Conduct]: https://www.rust-lang.org/policies/code-of-conduct [AUTHORS]: ./AUTHORS diff --git a/src/brush.rs b/src/brush.rs index 7cbb730..b76678a 100644 --- a/src/brush.rs +++ b/src/brush.rs @@ -3,10 +3,12 @@ use super::{Color, Gradient, Image}; +use color::{AlphaColor, ColorSpace, DynamicColor, OpaqueColor, Srgb}; + /// Describes the color content of a filled or stroked shape. /// /// See also [`BrushRef`] which can be used to avoid allocations. -#[derive(Clone, PartialEq, Debug)] +#[derive(Clone, Debug)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum Brush { /// Solid color brush. @@ -17,9 +19,21 @@ pub enum Brush { Image(Image), } -impl From for Brush { - fn from(c: Color) -> Self { - Self::Solid(c) +impl From> for Brush { + fn from(c: AlphaColor) -> Self { + Self::Solid(c.convert()) + } +} + +impl From for Brush { + fn from(c: DynamicColor) -> Self { + Self::Solid(c.to_alpha_color::()) + } +} + +impl From> for Brush { + fn from(c: OpaqueColor) -> Self { + Self::Solid(c.with_alpha(1.).convert()) } } @@ -37,7 +51,7 @@ impl From for Brush { impl Default for Brush { fn default() -> Self { - Self::Solid(Color::default()) + Self::Solid(Color::TRANSPARENT) } } @@ -77,7 +91,11 @@ impl Brush { /// This is useful for methods that would like to accept brushes by reference. Defining /// the type as `impl>` allows accepting types like `&LinearGradient` /// directly without cloning or allocating. -#[derive(Copy, Clone, PartialEq, Debug)] +#[cfg_attr( + target_pointer_width = "32", + expect(variant_size_differences, reason = "We're okay with this.") +)] +#[derive(Copy, Clone, Debug)] pub enum BrushRef<'a> { /// Solid color brush. Solid(Color), @@ -99,15 +117,39 @@ impl BrushRef<'_> { } } -impl From for BrushRef<'_> { - fn from(color: Color) -> Self { - Self::Solid(color) +impl From> for BrushRef<'_> { + fn from(color: AlphaColor) -> Self { + Self::Solid(color.convert()) + } +} + +impl From for BrushRef<'_> { + fn from(color: DynamicColor) -> Self { + Self::Solid(color.to_alpha_color::()) + } +} + +impl From> for BrushRef<'_> { + fn from(color: OpaqueColor) -> Self { + Self::Solid(color.with_alpha(1.).convert()) + } +} + +impl<'a, CS: ColorSpace> From<&'a AlphaColor> for BrushRef<'_> { + fn from(color: &'a AlphaColor) -> Self { + Self::Solid((*color).convert()) + } +} + +impl<'a> From<&'a DynamicColor> for BrushRef<'_> { + fn from(color: &'a DynamicColor) -> Self { + Self::Solid((*color).to_alpha_color::()) } } -impl<'a> From<&'a Color> for BrushRef<'_> { - fn from(color: &'a Color) -> Self { - Self::Solid(*color) +impl<'a, CS: ColorSpace> From<&'a OpaqueColor> for BrushRef<'_> { + fn from(color: &'a OpaqueColor) -> Self { + Self::Solid((*color).with_alpha(1.).convert()) } } diff --git a/src/color.rs b/src/color.rs deleted file mode 100644 index c1a1ed8..0000000 --- a/src/color.rs +++ /dev/null @@ -1,677 +0,0 @@ -// Copyright 2022 the Peniko Authors and the Piet Authors -// SPDX-License-Identifier: Apache-2.0 OR MIT - -// Borrows code heavily from the piet (https://github.com/linebender/piet/) Color -// type. -#[cfg(all(not(feature = "std"), feature = "libm"))] -#[allow(unused_imports)] -use kurbo::common::FloatFuncs as _; - -/// 32-bit RGBA color. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct Color { - /// Red component. - pub r: u8, - /// Green component. - pub g: u8, - /// Blue component. - pub b: u8, - /// Alpha component. - // If changing type, also change the docs on Self::multiply_alpha - pub a: u8, -} - -impl Color { - /// Creates a new RGB color with 255 alpha. - #[must_use] - pub const fn rgb8(r: u8, g: u8, b: u8) -> Self { - Self { r, g, b, a: 255 } - } - - /// Creates a new RGBA color. - #[must_use] - pub const fn rgba8(r: u8, g: u8, b: u8, a: u8) -> Self { - Self { r, g, b, a } - } - - /// Create a color from three floating point values, each in the range `0.0` to `1.0`. - /// - /// The interpretation is the same as rgb8, and no greater precision is - /// (currently) assumed. - #[must_use] - pub fn rgb(r: f64, g: f64, b: f64) -> Self { - Self::rgba(r, g, b, 1.0) - } - - /// Create a color from four floating point values, each in the range `0.0` to `1.0`. - /// - /// The interpretation is the same as rgba32, and no greater precision is - /// (currently) assumed. - #[must_use] - pub fn rgba(r: f64, g: f64, b: f64, a: f64) -> Self { - let r = (r.clamp(0.0, 1.0) * 255.0).round() as u8; - let g = (g.clamp(0.0, 1.0) * 255.0).round() as u8; - let b = (b.clamp(0.0, 1.0) * 255.0).round() as u8; - let a = (a.clamp(0.0, 1.0) * 255.0).round() as u8; - Self { r, g, b, a } - } - - /// Create a color from a CIEL\*a\*b\* polar (also known as CIE HCL) - /// specification. - /// - /// The `h` parameter is an angle in degrees, with 0 roughly magenta, 90 - /// roughly yellow, 180 roughly cyan, and 270 roughly blue. The `l` - /// parameter is perceptual luminance, with 0 black and 100 white. - /// The `c` parameter is a chrominance concentration, with 0 grayscale - /// and a nominal maximum of 127 (in the future, higher values might - /// be useful, for high gamut contexts). - /// - /// Currently this is just converted into sRGB, but in the future as we - /// support high-gamut colorspaces, it can be used to specify more colors - /// or existing colors with a higher accuracy. - /// - /// Currently out-of-gamut values are clipped to the nearest sRGB color, - /// which is perhaps not ideal (the clipping might change the hue). See - /// for discussion. - #[must_use] - pub fn hlc(h: f64, l: f64, c: f64) -> Self { - Self::hlca(h, l, c, 1.0) - } - - /// Create a color from a CIEL\*a\*b\* polar specification and alpha. - /// - /// The `a` value represents alpha in the range `0.0` to `1.0`. - #[allow(non_snake_case)] - #[allow(clippy::many_single_char_names)] - #[allow(clippy::unreadable_literal)] - #[must_use] - pub fn hlca(h: f64, l: f64, c: f64, alpha: f64) -> Self { - // The reverse transformation from Lab to XYZ, see - // https://en.wikipedia.org/wiki/CIELAB_color_space - fn f_inv(t: f64) -> f64 { - let d = 6. / 29.; - if t > d { - t.powi(3) - } else { - 3. * d * d * (t - 4. / 29.) - } - } - let th = h * (core::f64::consts::PI / 180.); - let a = c * th.cos(); - let b = c * th.sin(); - let ll = (l + 16.) * (1. / 116.); - // Produce raw XYZ values - let X = f_inv(ll + a * (1. / 500.)); - let Y = f_inv(ll); - let Z = f_inv(ll - b * (1. / 200.)); - // This matrix is the concatenation of three sources. - // First, the white point is taken to be ICC standard D50, so - // the diagonal matrix of [0.9642, 1, 0.8249]. Note that there - // is some controversy around this value. However, it matches - // the other matrices, thus minimizing chroma error. - // - // Second, an adaption matrix from D50 to D65. This is the - // inverse of the recommended D50 to D65 adaptation matrix - // from the W3C sRGB spec: - // https://www.w3.org/Graphics/Color/srgb - // - // Finally, the conversion from XYZ to linear sRGB values, - // also taken from the W3C sRGB spec. - let r_lin = 3.02172918 * X - 1.61692294 * Y - 0.40480625 * Z; - let g_lin = -0.94339358 * X + 1.91584267 * Y + 0.02755094 * Z; - let b_lin = 0.06945666 * X - 0.22903204 * Y + 1.15957526 * Z; - fn gamma(u: f64) -> f64 { - if u <= 0.0031308 { - 12.92 * u - } else { - 1.055 * u.powf(1. / 2.4) - 0.055 - } - } - Self::rgba(gamma(r_lin), gamma(g_lin), gamma(b_lin), alpha) - } - - /// Parses a color from a string. - /// - /// Currently accepts CSS style hexadecimal colors of the forms #RGB, #RGBA, - /// #RRGGBB, #RRGGBBAA or the name of an SVG color such as "aliceblue". - #[must_use] - pub fn parse(s: &str) -> Option { - parse_color(s) - } - - /// Returns the color with the alpha component multiplied by the specified - /// factor. - #[must_use] - #[deprecated( - since = "0.2.0", - note = "This method has been renamed to `multiply_alpha`." - )] - pub fn with_alpha_factor(self, alpha: f32) -> Self { - self.multiply_alpha(alpha) - } - - /// Returns the color with the alpha component multiplied by `alpha`. - /// The behaviour of this transformation is undefined if `alpha` is negative. - /// - /// If the resulting alpha would overflow, this currently saturates (to opaque). - #[must_use] - #[track_caller] - pub fn multiply_alpha(self, alpha: f32) -> Self { - debug_assert!( - alpha.is_finite() && alpha >= 0.0, - "A non-finite or negative alpha ({alpha}) is meaningless." - ); - let mut result = self; - result.a = ((result.a as f32) * alpha).round() as u8; - result - } - - /// Returns the color as a packed premultiplied value. - #[must_use] - pub fn to_premul_u32(self) -> u32 { - let a = self.a as f64 * (1.0 / 255.0); - let r = (self.r as f64 * a).round() as u32; - let g = (self.g as f64 * a).round() as u32; - let b = (self.b as f64 * a).round() as u32; - (r << 24) | (g << 16) | (b << 8) | self.a as u32 - } -} - -/// Named SVG colors. -#[allow(clippy::use_self)] -impl Color { - /// Alice blue (240, 248, 255, 255) - pub const ALICE_BLUE: Color = Color::rgba8(240, 248, 255, 255); - /// Antique white (250, 235, 215, 255) - pub const ANTIQUE_WHITE: Color = Color::rgba8(250, 235, 215, 255); - /// Aqua (0, 255, 255, 255) - pub const AQUA: Color = Color::rgba8(0, 255, 255, 255); - /// Aquamarine (127, 255, 212, 255) - pub const AQUAMARINE: Color = Color::rgba8(127, 255, 212, 255); - /// Azure (240, 255, 255, 255) - pub const AZURE: Color = Color::rgba8(240, 255, 255, 255); - /// Beige (245, 245, 220, 255) - pub const BEIGE: Color = Color::rgba8(245, 245, 220, 255); - /// Bisque (255, 228, 196, 255) - pub const BISQUE: Color = Color::rgba8(255, 228, 196, 255); - /// Black (0, 0, 0, 255) - pub const BLACK: Color = Color::rgba8(0, 0, 0, 255); - /// Blanched almond (255, 235, 205, 255) - pub const BLANCHED_ALMOND: Color = Color::rgba8(255, 235, 205, 255); - /// Blue (0, 0, 255, 255) - pub const BLUE: Color = Color::rgba8(0, 0, 255, 255); - /// Blue violet (138, 43, 226, 255) - pub const BLUE_VIOLET: Color = Color::rgba8(138, 43, 226, 255); - /// Brown (165, 42, 42, 255) - pub const BROWN: Color = Color::rgba8(165, 42, 42, 255); - /// Burlywood (222, 184, 135, 255) - pub const BURLYWOOD: Color = Color::rgba8(222, 184, 135, 255); - /// Cadet blue (95, 158, 160, 255) - pub const CADET_BLUE: Color = Color::rgba8(95, 158, 160, 255); - /// Chartreuse (127, 255, 0, 255) - pub const CHARTREUSE: Color = Color::rgba8(127, 255, 0, 255); - /// Chocolate (210, 105, 30, 255) - pub const CHOCOLATE: Color = Color::rgba8(210, 105, 30, 255); - /// Coral (255, 127, 80, 255) - pub const CORAL: Color = Color::rgba8(255, 127, 80, 255); - /// Cornflower blue (100, 149, 237, 255) - pub const CORNFLOWER_BLUE: Color = Color::rgba8(100, 149, 237, 255); - /// Cornsilk (255, 248, 220, 255) - pub const CORNSILK: Color = Color::rgba8(255, 248, 220, 255); - /// Crimson (220, 20, 60, 255) - pub const CRIMSON: Color = Color::rgba8(220, 20, 60, 255); - /// Cyan (0, 255, 255, 255) - pub const CYAN: Color = Color::rgba8(0, 255, 255, 255); - /// Dark blue (0, 0, 139, 255) - pub const DARK_BLUE: Color = Color::rgba8(0, 0, 139, 255); - /// Dark cyan (0, 139, 139, 255) - pub const DARK_CYAN: Color = Color::rgba8(0, 139, 139, 255); - /// Dark goldenrod (184, 134, 11, 255) - pub const DARK_GOLDENROD: Color = Color::rgba8(184, 134, 11, 255); - /// Dark gray (169, 169, 169, 255) - pub const DARK_GRAY: Color = Color::rgba8(169, 169, 169, 255); - /// Dark green (0, 100, 0, 255) - pub const DARK_GREEN: Color = Color::rgba8(0, 100, 0, 255); - /// Dark khaki (189, 183, 107, 255) - pub const DARK_KHAKI: Color = Color::rgba8(189, 183, 107, 255); - /// Dark magenta (139, 0, 139, 255) - pub const DARK_MAGENTA: Color = Color::rgba8(139, 0, 139, 255); - /// Dark olive green (85, 107, 47, 255) - pub const DARK_OLIVE_GREEN: Color = Color::rgba8(85, 107, 47, 255); - /// Dark orange (255, 140, 0, 255) - pub const DARK_ORANGE: Color = Color::rgba8(255, 140, 0, 255); - /// Dark orchid (153, 50, 204, 255) - pub const DARK_ORCHID: Color = Color::rgba8(153, 50, 204, 255); - /// Dark red (139, 0, 0, 255) - pub const DARK_RED: Color = Color::rgba8(139, 0, 0, 255); - /// Dark salmon (233, 150, 122, 255) - pub const DARK_SALMON: Color = Color::rgba8(233, 150, 122, 255); - /// Dark sea green (143, 188, 143, 255) - pub const DARK_SEA_GREEN: Color = Color::rgba8(143, 188, 143, 255); - /// Dark slate blue (72, 61, 139, 255) - pub const DARK_SLATE_BLUE: Color = Color::rgba8(72, 61, 139, 255); - /// Dark slate gray (47, 79, 79, 255) - pub const DARK_SLATE_GRAY: Color = Color::rgba8(47, 79, 79, 255); - /// Dark turquoise (0, 206, 209, 255) - pub const DARK_TURQUOISE: Color = Color::rgba8(0, 206, 209, 255); - /// Dark violet (148, 0, 211, 255) - pub const DARK_VIOLET: Color = Color::rgba8(148, 0, 211, 255); - /// Deep pink (255, 20, 147, 255) - pub const DEEP_PINK: Color = Color::rgba8(255, 20, 147, 255); - /// Deep sky blue (0, 191, 255, 255) - pub const DEEP_SKY_BLUE: Color = Color::rgba8(0, 191, 255, 255); - /// Dim gray (105, 105, 105, 255) - pub const DIM_GRAY: Color = Color::rgba8(105, 105, 105, 255); - /// Dodger blue (30, 144, 255, 255) - pub const DODGER_BLUE: Color = Color::rgba8(30, 144, 255, 255); - /// Firebrick (178, 34, 34, 255) - pub const FIREBRICK: Color = Color::rgba8(178, 34, 34, 255); - /// Floral white (255, 250, 240, 255) - pub const FLORAL_WHITE: Color = Color::rgba8(255, 250, 240, 255); - /// Forest green (34, 139, 34, 255) - pub const FOREST_GREEN: Color = Color::rgba8(34, 139, 34, 255); - /// Fuchsia (255, 0, 255, 255) - pub const FUCHSIA: Color = Color::rgba8(255, 0, 255, 255); - /// Gainsboro (220, 220, 220, 255) - pub const GAINSBORO: Color = Color::rgba8(220, 220, 220, 255); - /// Ghost white (248, 248, 255, 255) - pub const GHOST_WHITE: Color = Color::rgba8(248, 248, 255, 255); - /// Gold (255, 215, 0, 255) - pub const GOLD: Color = Color::rgba8(255, 215, 0, 255); - /// Goldenrod (218, 165, 32, 255) - pub const GOLDENROD: Color = Color::rgba8(218, 165, 32, 255); - /// Gray (128, 128, 128, 255) - pub const GRAY: Color = Color::rgba8(128, 128, 128, 255); - /// Green (0, 128, 0, 255) - pub const GREEN: Color = Color::rgba8(0, 128, 0, 255); - /// Green yellow (173, 255, 47, 255) - pub const GREEN_YELLOW: Color = Color::rgba8(173, 255, 47, 255); - /// Honeydew (240, 255, 240, 255) - pub const HONEYDEW: Color = Color::rgba8(240, 255, 240, 255); - /// Hot pink (255, 105, 180, 255) - pub const HOT_PINK: Color = Color::rgba8(255, 105, 180, 255); - /// Indian red (205, 92, 92, 255) - pub const INDIAN_RED: Color = Color::rgba8(205, 92, 92, 255); - /// Indigo (75, 0, 130, 255) - pub const INDIGO: Color = Color::rgba8(75, 0, 130, 255); - /// Ivory (255, 255, 240, 255) - pub const IVORY: Color = Color::rgba8(255, 255, 240, 255); - /// Khaki (240, 230, 140, 255) - pub const KHAKI: Color = Color::rgba8(240, 230, 140, 255); - /// Lavender (230, 230, 250, 255) - pub const LAVENDER: Color = Color::rgba8(230, 230, 250, 255); - /// Lavender blush (255, 240, 245, 255) - pub const LAVENDER_BLUSH: Color = Color::rgba8(255, 240, 245, 255); - /// Lawn green (124, 252, 0, 255) - pub const LAWN_GREEN: Color = Color::rgba8(124, 252, 0, 255); - /// Lemon chiffon (255, 250, 205, 255) - pub const LEMON_CHIFFON: Color = Color::rgba8(255, 250, 205, 255); - /// Light blue (173, 216, 230, 255) - pub const LIGHT_BLUE: Color = Color::rgba8(173, 216, 230, 255); - /// Light coral (240, 128, 128, 255) - pub const LIGHT_CORAL: Color = Color::rgba8(240, 128, 128, 255); - /// Light cyan (224, 255, 255, 255) - pub const LIGHT_CYAN: Color = Color::rgba8(224, 255, 255, 255); - /// Light goldenrod yellow (250, 250, 210, 255) - pub const LIGHT_GOLDENROD_YELLOW: Color = Color::rgba8(250, 250, 210, 255); - /// Light gray (211, 211, 211, 255) - pub const LIGHT_GRAY: Color = Color::rgba8(211, 211, 211, 255); - /// Light green (144, 238, 144, 255) - pub const LIGHT_GREEN: Color = Color::rgba8(144, 238, 144, 255); - /// Light pink (255, 182, 193, 255) - pub const LIGHT_PINK: Color = Color::rgba8(255, 182, 193, 255); - /// Light salmon (255, 160, 122, 255) - pub const LIGHT_SALMON: Color = Color::rgba8(255, 160, 122, 255); - /// Light sea green (32, 178, 170, 255) - pub const LIGHT_SEA_GREEN: Color = Color::rgba8(32, 178, 170, 255); - /// Light sky blue (135, 206, 250, 255) - pub const LIGHT_SKY_BLUE: Color = Color::rgba8(135, 206, 250, 255); - /// Light slate gray (119, 136, 153, 255) - pub const LIGHT_SLATE_GRAY: Color = Color::rgba8(119, 136, 153, 255); - /// Light steel blue (176, 196, 222, 255) - pub const LIGHT_STEEL_BLUE: Color = Color::rgba8(176, 196, 222, 255); - /// Light yellow (255, 255, 224, 255) - pub const LIGHT_YELLOW: Color = Color::rgba8(255, 255, 224, 255); - /// Lime (0, 255, 0, 255) - pub const LIME: Color = Color::rgba8(0, 255, 0, 255); - /// Lime green (50, 205, 50, 255) - pub const LIME_GREEN: Color = Color::rgba8(50, 205, 50, 255); - /// Linen (250, 240, 230, 255) - pub const LINEN: Color = Color::rgba8(250, 240, 230, 255); - /// Magenta (255, 0, 255, 255) - pub const MAGENTA: Color = Color::rgba8(255, 0, 255, 255); - /// Maroon (128, 0, 0, 255) - pub const MAROON: Color = Color::rgba8(128, 0, 0, 255); - /// Medium aquamarine (102, 205, 170, 255) - pub const MEDIUM_AQUAMARINE: Color = Color::rgba8(102, 205, 170, 255); - /// Medium blue (0, 0, 205, 255) - pub const MEDIUM_BLUE: Color = Color::rgba8(0, 0, 205, 255); - /// Medium orchid (186, 85, 211, 255) - pub const MEDIUM_ORCHID: Color = Color::rgba8(186, 85, 211, 255); - /// Medium purple (147, 112, 219, 255) - pub const MEDIUM_PURPLE: Color = Color::rgba8(147, 112, 219, 255); - /// Medium sea green (60, 179, 113, 255) - pub const MEDIUM_SEA_GREEN: Color = Color::rgba8(60, 179, 113, 255); - /// Medium slate blue (123, 104, 238, 255) - pub const MEDIUM_SLATE_BLUE: Color = Color::rgba8(123, 104, 238, 255); - /// Medium spring green (0, 250, 154, 255) - pub const MEDIUM_SPRING_GREEN: Color = Color::rgba8(0, 250, 154, 255); - /// Medium turquoise (72, 209, 204, 255) - pub const MEDIUM_TURQUOISE: Color = Color::rgba8(72, 209, 204, 255); - /// Medium violet red (199, 21, 133, 255) - pub const MEDIUM_VIOLET_RED: Color = Color::rgba8(199, 21, 133, 255); - /// Midnight blue (25, 25, 112, 255) - pub const MIDNIGHT_BLUE: Color = Color::rgba8(25, 25, 112, 255); - /// Mint cream (245, 255, 250, 255) - pub const MINT_CREAM: Color = Color::rgba8(245, 255, 250, 255); - /// Misty rose (255, 228, 225, 255) - pub const MISTY_ROSE: Color = Color::rgba8(255, 228, 225, 255); - /// Moccasin (255, 228, 181, 255) - pub const MOCCASIN: Color = Color::rgba8(255, 228, 181, 255); - /// Navajo white (255, 222, 173, 255) - pub const NAVAJO_WHITE: Color = Color::rgba8(255, 222, 173, 255); - /// Navy (0, 0, 128, 255) - pub const NAVY: Color = Color::rgba8(0, 0, 128, 255); - /// Old lace (253, 245, 230, 255) - pub const OLD_LACE: Color = Color::rgba8(253, 245, 230, 255); - /// Olive (128, 128, 0, 255) - pub const OLIVE: Color = Color::rgba8(128, 128, 0, 255); - /// Olive drab (107, 142, 35, 255) - pub const OLIVE_DRAB: Color = Color::rgba8(107, 142, 35, 255); - /// Orange (255, 165, 0, 255) - pub const ORANGE: Color = Color::rgba8(255, 165, 0, 255); - /// Orange red (255, 69, 0, 255) - pub const ORANGE_RED: Color = Color::rgba8(255, 69, 0, 255); - /// Orchid (218, 112, 214, 255) - pub const ORCHID: Color = Color::rgba8(218, 112, 214, 255); - /// Pale goldenrod (238, 232, 170, 255) - pub const PALE_GOLDENROD: Color = Color::rgba8(238, 232, 170, 255); - /// Pale green (152, 251, 152, 255) - pub const PALE_GREEN: Color = Color::rgba8(152, 251, 152, 255); - /// Pale turquoise (175, 238, 238, 255) - pub const PALE_TURQUOISE: Color = Color::rgba8(175, 238, 238, 255); - /// Pale violet red (219, 112, 147, 255) - pub const PALE_VIOLET_RED: Color = Color::rgba8(219, 112, 147, 255); - /// Papaya whip (255, 239, 213, 255) - pub const PAPAYA_WHIP: Color = Color::rgba8(255, 239, 213, 255); - /// Peach puff (255, 218, 185, 255) - pub const PEACH_PUFF: Color = Color::rgba8(255, 218, 185, 255); - /// Peru (205, 133, 63, 255) - pub const PERU: Color = Color::rgba8(205, 133, 63, 255); - /// Pink (255, 192, 203, 255) - pub const PINK: Color = Color::rgba8(255, 192, 203, 255); - /// Plum (221, 160, 221, 255) - pub const PLUM: Color = Color::rgba8(221, 160, 221, 255); - /// Powder blue (176, 224, 230, 255) - pub const POWDER_BLUE: Color = Color::rgba8(176, 224, 230, 255); - /// Purple (128, 0, 128, 255) - pub const PURPLE: Color = Color::rgba8(128, 0, 128, 255); - /// Rebecca purple (102, 51, 153, 255) - pub const REBECCA_PURPLE: Color = Color::rgba8(102, 51, 153, 255); - /// Red (255, 0, 0, 255) - pub const RED: Color = Color::rgba8(255, 0, 0, 255); - /// Rosy brown (188, 143, 143, 255) - pub const ROSY_BROWN: Color = Color::rgba8(188, 143, 143, 255); - /// Royal blue (65, 105, 225, 255) - pub const ROYAL_BLUE: Color = Color::rgba8(65, 105, 225, 255); - /// Saddle brown (139, 69, 19, 255) - pub const SADDLE_BROWN: Color = Color::rgba8(139, 69, 19, 255); - /// Salmon (250, 128, 114, 255) - pub const SALMON: Color = Color::rgba8(250, 128, 114, 255); - /// Sandy brown (244, 164, 96, 255) - pub const SANDY_BROWN: Color = Color::rgba8(244, 164, 96, 255); - /// Sea green (46, 139, 87, 255) - pub const SEA_GREEN: Color = Color::rgba8(46, 139, 87, 255); - /// Seashell (255, 245, 238, 255) - pub const SEASHELL: Color = Color::rgba8(255, 245, 238, 255); - /// Sienna (160, 82, 45, 255) - pub const SIENNA: Color = Color::rgba8(160, 82, 45, 255); - /// Silver (192, 192, 192, 255) - pub const SILVER: Color = Color::rgba8(192, 192, 192, 255); - /// Sky blue (135, 206, 235, 255) - pub const SKY_BLUE: Color = Color::rgba8(135, 206, 235, 255); - /// Slate blue (106, 90, 205, 255) - pub const SLATE_BLUE: Color = Color::rgba8(106, 90, 205, 255); - /// Slate gray (112, 128, 144, 255) - pub const SLATE_GRAY: Color = Color::rgba8(112, 128, 144, 255); - /// Snow (255, 250, 250, 255) - pub const SNOW: Color = Color::rgba8(255, 250, 250, 255); - /// Spring green (0, 255, 127, 255) - pub const SPRING_GREEN: Color = Color::rgba8(0, 255, 127, 255); - /// Steel blue (70, 130, 180, 255) - pub const STEEL_BLUE: Color = Color::rgba8(70, 130, 180, 255); - /// Tan (210, 180, 140, 255) - pub const TAN: Color = Color::rgba8(210, 180, 140, 255); - /// Teal (0, 128, 128, 255) - pub const TEAL: Color = Color::rgba8(0, 128, 128, 255); - /// Thistle (216, 191, 216, 255) - pub const THISTLE: Color = Color::rgba8(216, 191, 216, 255); - /// Tomato (255, 99, 71, 255) - pub const TOMATO: Color = Color::rgba8(255, 99, 71, 255); - /// Transparent (0, 0, 0, 0) - pub const TRANSPARENT: Color = Color::rgba8(0, 0, 0, 0); - /// Turquoise (64, 224, 208, 255) - pub const TURQUOISE: Color = Color::rgba8(64, 224, 208, 255); - /// Violet (238, 130, 238, 255) - pub const VIOLET: Color = Color::rgba8(238, 130, 238, 255); - /// Wheat (245, 222, 179, 255) - pub const WHEAT: Color = Color::rgba8(245, 222, 179, 255); - /// White (255, 255, 255, 255) - pub const WHITE: Color = Color::rgba8(255, 255, 255, 255); - /// White smoke (245, 245, 245, 255) - pub const WHITE_SMOKE: Color = Color::rgba8(245, 245, 245, 255); - /// Yellow (255, 255, 0, 255) - pub const YELLOW: Color = Color::rgba8(255, 255, 0, 255); - /// Yellow green (154, 205, 50, 255) - pub const YELLOW_GREEN: Color = Color::rgba8(154, 205, 50, 255); -} - -impl From<[u8; 3]> for Color { - fn from(rgb: [u8; 3]) -> Self { - Self::rgb8(rgb[0], rgb[1], rgb[2]) - } -} - -impl From<[u8; 4]> for Color { - fn from(rgba: [u8; 4]) -> Self { - Self::rgba8(rgba[0], rgba[1], rgba[2], rgba[3]) - } -} - -fn parse_color(s: &str) -> Option { - let s = s.trim(); - if let Some(stripped) = s.strip_prefix('#') { - Some(color_from_4bit_hex(get_4bit_hex_channels(stripped)?)) - } else { - Some(match s { - "aliceblue" => Color::ALICE_BLUE, - "antiquewhite" => Color::ANTIQUE_WHITE, - "aqua" => Color::AQUA, - "aquamarine" => Color::AQUAMARINE, - "azure" => Color::AZURE, - "beige" => Color::BEIGE, - "bisque" => Color::BISQUE, - "black" => Color::BLACK, - "blanchedalmond" => Color::BLANCHED_ALMOND, - "blue" => Color::BLUE, - "blueviolet" => Color::BLUE_VIOLET, - "brown" => Color::BROWN, - "burlywood" => Color::BURLYWOOD, - "cadetblue" => Color::CADET_BLUE, - "chartreuse" => Color::CHARTREUSE, - "chocolate" => Color::CHOCOLATE, - "coral" => Color::CORAL, - "cornflowerblue" => Color::CORNFLOWER_BLUE, - "cornsilk" => Color::CORNSILK, - "crimson" => Color::CRIMSON, - "cyan" => Color::CYAN, - "darkblue" => Color::DARK_BLUE, - "darkcyan" => Color::DARK_CYAN, - "darkgoldenrod" => Color::DARK_GOLDENROD, - "darkgray" => Color::DARK_GRAY, - "darkgreen" => Color::DARK_GREEN, - "darkkhaki" => Color::DARK_KHAKI, - "darkmagenta" => Color::DARK_MAGENTA, - "darkolivegreen" => Color::DARK_OLIVE_GREEN, - "darkorange" => Color::DARK_ORANGE, - "darkorchid" => Color::DARK_ORCHID, - "darkred" => Color::DARK_RED, - "darksalmon" => Color::DARK_SALMON, - "darkseagreen" => Color::DARK_SEA_GREEN, - "darkslateblue" => Color::DARK_SLATE_BLUE, - "darkslategray" => Color::DARK_SLATE_GRAY, - "darkturquoise" => Color::DARK_TURQUOISE, - "darkviolet" => Color::DARK_VIOLET, - "deeppink" => Color::DEEP_PINK, - "deepskyblue" => Color::DEEP_SKY_BLUE, - "dimgray" => Color::DIM_GRAY, - "dodgerblue" => Color::DODGER_BLUE, - "firebrick" => Color::FIREBRICK, - "floralwhite" => Color::FLORAL_WHITE, - "forestgreen" => Color::FOREST_GREEN, - "fuchsia" => Color::FUCHSIA, - "gainsboro" => Color::GAINSBORO, - "ghostwhite" => Color::GHOST_WHITE, - "gold" => Color::GOLD, - "goldenrod" => Color::GOLDENROD, - "gray" => Color::GRAY, - "green" => Color::GREEN, - "greenyellow" => Color::GREEN_YELLOW, - "honeydew" => Color::HONEYDEW, - "hotpink" => Color::HOT_PINK, - "indianred" => Color::INDIAN_RED, - "indigo" => Color::INDIGO, - "ivory" => Color::IVORY, - "khaki" => Color::KHAKI, - "lavender" => Color::LAVENDER, - "lavenderblush" => Color::LAVENDER_BLUSH, - "lawngreen" => Color::LAWN_GREEN, - "lemonchiffon" => Color::LEMON_CHIFFON, - "lightblue" => Color::LIGHT_BLUE, - "lightcoral" => Color::LIGHT_CORAL, - "lightcyan" => Color::LIGHT_CYAN, - "lightgoldenrodyellow" => Color::LIGHT_GOLDENROD_YELLOW, - "lightgray" => Color::LIGHT_GRAY, - "lightgreen" => Color::LIGHT_GREEN, - "lightpink" => Color::LIGHT_PINK, - "lightsalmon" => Color::LIGHT_SALMON, - "lightseagreen" => Color::LIGHT_SEA_GREEN, - "lightskyblue" => Color::LIGHT_SKY_BLUE, - "lightslategray" => Color::LIGHT_SLATE_GRAY, - "lightsteelblue" => Color::LIGHT_STEEL_BLUE, - "lightyellow" => Color::LIGHT_YELLOW, - "lime" => Color::LIME, - "limegreen" => Color::LIME_GREEN, - "linen" => Color::LINEN, - "magenta" => Color::MAGENTA, - "maroon" => Color::MAROON, - "mediumaquamarine" => Color::MEDIUM_AQUAMARINE, - "mediumblue" => Color::MEDIUM_BLUE, - "mediumorchid" => Color::MEDIUM_ORCHID, - "mediumpurple" => Color::MEDIUM_PURPLE, - "mediumseagreen" => Color::MEDIUM_SEA_GREEN, - "mediumslateblue" => Color::MEDIUM_SLATE_BLUE, - "mediumspringgreen" => Color::MEDIUM_SPRING_GREEN, - "mediumturquoise" => Color::MEDIUM_TURQUOISE, - "mediumvioletred" => Color::MEDIUM_VIOLET_RED, - "midnightblue" => Color::MIDNIGHT_BLUE, - "mintcream" => Color::MINT_CREAM, - "mistyrose" => Color::MISTY_ROSE, - "moccasin" => Color::MOCCASIN, - "navajowhite" => Color::NAVAJO_WHITE, - "navy" => Color::NAVY, - "oldlace" => Color::OLD_LACE, - "olive" => Color::OLIVE, - "olivedrab" => Color::OLIVE_DRAB, - "orange" => Color::ORANGE, - "orangered" => Color::ORANGE_RED, - "orchid" => Color::ORCHID, - "palegoldenrod" => Color::PALE_GOLDENROD, - "palegreen" => Color::PALE_GREEN, - "paleturquoise" => Color::PALE_TURQUOISE, - "palevioletred" => Color::PALE_VIOLET_RED, - "papayawhip" => Color::PAPAYA_WHIP, - "peachpuff" => Color::PEACH_PUFF, - "peru" => Color::PERU, - "pink" => Color::PINK, - "plum" => Color::PLUM, - "powderblue" => Color::POWDER_BLUE, - "purple" => Color::PURPLE, - "rebeccapurple" => Color::REBECCA_PURPLE, - "red" => Color::RED, - "rosybrown" => Color::ROSY_BROWN, - "royalblue" => Color::ROYAL_BLUE, - "saddlebrown" => Color::SADDLE_BROWN, - "salmon" => Color::SALMON, - "sandybrown" => Color::SANDY_BROWN, - "seagreen" => Color::SEA_GREEN, - "seashell" => Color::SEASHELL, - "sienna" => Color::SIENNA, - "silver" => Color::SILVER, - "skyblue" => Color::SKY_BLUE, - "slateblue" => Color::SLATE_BLUE, - "slategray" => Color::SLATE_GRAY, - "snow" => Color::SNOW, - "springgreen" => Color::SPRING_GREEN, - "steelblue" => Color::STEEL_BLUE, - "tan" => Color::TAN, - "teal" => Color::TEAL, - "thistle" => Color::THISTLE, - "tomato" => Color::TOMATO, - "transparent" => Color::TRANSPARENT, - "turquoise" => Color::TURQUOISE, - "violet" => Color::VIOLET, - "wheat" => Color::WHEAT, - "white" => Color::WHITE, - "whitesmoke" => Color::WHITE_SMOKE, - "yellow" => Color::YELLOW, - "yellowgreen" => Color::YELLOW_GREEN, - _ => return None, - }) - } -} - -// The following hex color parsing code taken from piet: - -const fn get_4bit_hex_channels(hex_str: &str) -> Option<[u8; 8]> { - let mut four_bit_channels = match hex_str.as_bytes() { - &[b'#', r, g, b] | &[r, g, b] => [r, r, g, g, b, b, b'f', b'f'], - &[b'#', r, g, b, a] | &[r, g, b, a] => [r, r, g, g, b, b, a, a], - &[b'#', r0, r1, g0, g1, b0, b1] | &[r0, r1, g0, g1, b0, b1] => { - [r0, r1, g0, g1, b0, b1, b'f', b'f'] - } - &[b'#', r0, r1, g0, g1, b0, b1, a0, a1] | &[r0, r1, g0, g1, b0, b1, a0, a1] => { - [r0, r1, g0, g1, b0, b1, a0, a1] - } - _ => return None, - }; - - // convert to hex in-place - // this is written without a for loop to satisfy `const` - let mut i = 0; - while i < four_bit_channels.len() { - let ascii = four_bit_channels[i]; - let as_hex = match hex_from_ascii_byte(ascii) { - Ok(hex) => hex, - Err(_) => return None, - }; - four_bit_channels[i] = as_hex; - i += 1; - } - Some(four_bit_channels) -} - -const fn color_from_4bit_hex(components: [u8; 8]) -> Color { - let [r0, r1, g0, g1, b0, b1, a0, a1] = components; - Color::rgba8(r0 << 4 | r1, g0 << 4 | g1, b0 << 4 | b1, a0 << 4 | a1) -} - -const fn hex_from_ascii_byte(b: u8) -> Result { - match b { - b'0'..=b'9' => Ok(b - b'0'), - b'A'..=b'F' => Ok(b - b'A' + 10), - b'a'..=b'f' => Ok(b - b'a' + 10), - _ => Err(b), - } -} diff --git a/src/gradient.rs b/src/gradient.rs index c3b2e08..490d885 100644 --- a/src/gradient.rs +++ b/src/gradient.rs @@ -1,21 +1,22 @@ // Copyright 2022 the Peniko Authors // SPDX-License-Identifier: Apache-2.0 OR MIT -use super::{Color, Extend}; +use super::Extend; +use color::{AlphaColor, ColorSpace, DynamicColor, OpaqueColor}; use kurbo::Point; use smallvec::SmallVec; use core::hash::{Hash, Hasher}; /// Offset and color of a transition point in a [gradient](Gradient). -#[derive(Copy, Clone, PartialOrd, Default, Debug)] +#[derive(Copy, Clone, Debug)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct ColorStop { /// Normalized offset of the stop. pub offset: f32, /// Color at the specified offset. - pub color: Color, + pub color: DynamicColor, } impl Hash for ColorStop { @@ -60,8 +61,17 @@ impl ColorStop { } } -impl From<(f32, Color)> for ColorStop { - fn from(pair: (f32, Color)) -> Self { +impl From<(f32, AlphaColor)> for ColorStop { + fn from(pair: (f32, AlphaColor)) -> Self { + Self { + offset: pair.0, + color: DynamicColor::from_alpha_color(pair.1), + } + } +} + +impl From<(f32, DynamicColor)> for ColorStop { + fn from(pair: (f32, DynamicColor)) -> Self { Self { offset: pair.0, color: pair.1, @@ -69,6 +79,15 @@ impl From<(f32, Color)> for ColorStop { } } +impl From<(f32, OpaqueColor)> for ColorStop { + fn from(pair: (f32, OpaqueColor)) -> Self { + Self { + offset: pair.0, + color: DynamicColor::from_alpha_color(pair.1.with_alpha(1.)), + } + } +} + /// Collection of color stops. pub type ColorStops = SmallVec<[ColorStop; 4]>; @@ -236,19 +255,53 @@ where } } -impl ColorStopsSource for &'_ [Color] { +impl ColorStopsSource for &'_ [AlphaColor] { + fn collect_stops(&self, vec: &mut SmallVec<[ColorStop; 4]>) { + if !self.is_empty() { + let denom = (self.len() - 1).max(1) as f32; + vec.extend(self.iter().enumerate().map(|(i, c)| ColorStop { + offset: (i as f32) / denom, + color: DynamicColor::from_alpha_color(*c), + })); + } + } +} + +impl ColorStopsSource for &'_ [DynamicColor] { + fn collect_stops(&self, vec: &mut SmallVec<[ColorStop; 4]>) { + if !self.is_empty() { + let denom = (self.len() - 1).max(1) as f32; + vec.extend(self.iter().enumerate().map(|(i, c)| ColorStop { + offset: (i as f32) / denom, + color: (*c), + })); + } + } +} + +impl ColorStopsSource for &'_ [OpaqueColor] { fn collect_stops(&self, vec: &mut SmallVec<[ColorStop; 4]>) { if !self.is_empty() { let denom = (self.len() - 1).max(1) as f32; vec.extend(self.iter().enumerate().map(|(i, c)| ColorStop { offset: (i as f32) / denom, - color: *c, + color: DynamicColor::from_alpha_color((*c).with_alpha(1.)), })); } } } -impl ColorStopsSource for [Color; N] { +impl ColorStopsSource for [AlphaColor; N] { + fn collect_stops(&self, vec: &mut SmallVec<[ColorStop; 4]>) { + (&self[..]).collect_stops(vec); + } +} +impl ColorStopsSource for [DynamicColor; N] { + fn collect_stops(&self, vec: &mut SmallVec<[ColorStop; 4]>) { + (&self[..]).collect_stops(vec); + } +} +impl ColorStopsSource for [OpaqueColor; N] { fn collect_stops(&self, vec: &mut SmallVec<[ColorStop; 4]>) { (&self[..]).collect_stops(vec); } diff --git a/src/lib.rs b/src/lib.rs index 037a230..9bf28da 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,13 +3,14 @@ //! A Rust 2D graphics type library //! -//! The `peniko` library builds on top of [`kurbo`] and provides a set of generic types that define -//! styles for rendering and composition. +//! The `peniko` library builds on top of [`kurbo`] and [`color`] and provides a set of +//! generic types that define styles for rendering and composition. //! //! The name "peniko" is Esperanto for "brush" which is one family of types that the library //! contains. //! //! [`kurbo`]: https://crates.io/crates/kurbo +//! [`color`]: https://crates.io/crates/color // LINEBENDER LINT SET - lib.rs - v1 // See https://linebender.org/wiki/canonical-lints/ @@ -20,34 +21,36 @@ // END LINEBENDER LINT SET #![cfg_attr(docsrs, feature(doc_auto_cfg))] #![cfg_attr(all(not(feature = "std"), not(test)), no_std)] -// There are lots of conversion to u8 color field, which in degenerate cases might not work -// properly, but generally are fine. -// E.g. `multiply_alpha` sets the alpha to `0` for a negative provided `alpha`. -#![allow(clippy::cast_possible_truncation)] -// Most enums are correctly exhaustive, as this is a vocabulary crate. -#![allow(clippy::exhaustive_enums)] +#![expect( + clippy::exhaustive_enums, + reason = "Most of the enums are correctly exhaustive as this is a vocabulary crate." +)] mod blend; mod blob; mod brush; -mod color; mod font; mod gradient; mod image; mod style; +/// Re-export of the color library. +pub use color; + /// Re-export of the kurbo 2D curve library. pub use kurbo; pub use blend::{BlendMode, Compose, Mix}; pub use blob::{Blob, WeakBlob}; pub use brush::{Brush, BrushRef, Extend}; -pub use color::Color; pub use font::Font; pub use gradient::{ColorStop, ColorStops, ColorStopsSource, Gradient, GradientKind}; pub use image::{Format, Image}; pub use style::{Fill, Style, StyleRef}; +/// A convenient alias for the color type used for [`Brush`]. +pub type Color = color::AlphaColor; + #[cfg(test)] mod tests { // CI will fail unless cargo nextest can execute at least one test per workspace. diff --git a/src/style.rs b/src/style.rs index 52b5b30..416e5cd 100644 --- a/src/style.rs +++ b/src/style.rs @@ -42,7 +42,10 @@ impl From for Style { /// This is useful for methods that would like to accept draw styles by reference. Defining /// the type as `impl>` allows accepting types like `&Stroke` or `Fill` /// directly without cloning or allocating. -#[allow(variant_size_differences)] // We don't expect this enum to be operated on in bulk. +#[expect( + variant_size_differences, + reason = "We don't expect this enum to be operated on in bulk." +)] #[derive(Debug, Copy, Clone)] pub enum StyleRef<'a> { /// Filled draw operation.