diff --git a/CHANGELOG.md b/CHANGELOG.md index 938335202..1958a3a7b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,9 +15,15 @@ You can find its changes [documented below](#030---2024-10-04). This release has an [MSRV][] of 1.82. +### Highlights + +As part of an initiative to improve color handling across the ecosystem (and especially within Linebender crates), Vello is now using the new [`color`] crate. +This is the first step towards providing richer color functionality, better handling of color interpolation, and more. + ### Changed - Breaking: Updated `wgpu` to 23.0.1 ([#735][], [#743][] by [@waywardmonkeys]) +- Breaking: Updated to new `peniko` and `color` is now used for all colors ([#742][] by [@waywardmonkeys]) ### Fixed @@ -202,6 +208,7 @@ This release has an [MSRV][] of 1.75. [#733]: https://github.com/linebender/vello/pull/733 [#735]: https://github.com/linebender/vello/pull/735 [#740]: https://github.com/linebender/vello/pull/740 +[#742]: https://github.com/linebender/vello/pull/742 [#743]: https://github.com/linebender/vello/pull/743 [Unreleased]: https://github.com/linebender/vello/compare/v0.3.0...HEAD @@ -214,3 +221,4 @@ This release has an [MSRV][] of 1.75. [MSRV]: README.md#minimum-supported-rust-version-msrv [`run_app`]: https://docs.rs/winit/latest/winit/event_loop/struct.EventLoop.html#method.run_app [stroke-expansion]: https://linebender.org/gpu-stroke-expansion-paper/ +[`color`]: https://docs.rs/color/ diff --git a/Cargo.lock b/Cargo.lock index a3ec299c5..936d44795 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -432,6 +432,14 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "color" +version = "0.1.0" +source = "git+https://github.com/linebender/color.git?rev=bccd4050607eba830cfba3b2f39616a27500288a#bccd4050607eba830cfba3b2f39616a27500288a" +dependencies = [ + "serde", +] + [[package]] name = "colorchoice" version = "1.0.3" @@ -1631,9 +1639,9 @@ checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "peniko" version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a648c93f502a0bef0a9cb47fa1335994958a2744667d3f82defe513f276741a" +source = "git+https://github.com/linebender/peniko.git?rev=cb75a00#cb75a00f1915b46e65a47cbef69f693934b27629" dependencies = [ + "color", "kurbo", "smallvec", ] @@ -2042,18 +2050,18 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.214" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" +checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.214" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" +checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 50f1f07aa..646eb36e3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -101,7 +101,8 @@ bytemuck = { version = "1.18.0", features = ["derive"] } skrifa = "0.22.3" # The version of kurbo used below should be kept in sync # with the version of kurbo used by peniko. -peniko = "0.2.0" +# peniko = "0.2.0" +peniko = { version = "0.2.0", git = "https://github.com/linebender/peniko.git", rev = "cb75a00" } # FIXME: This can be removed once peniko supports the schemars feature. kurbo = "0.11.1" futures-intrusive = "0.5.0" diff --git a/README.md b/README.md index 44f9a2efe..a60598284 100644 --- a/README.md +++ b/README.md @@ -93,7 +93,7 @@ let mut scene = Scene::new(); scene.fill( Fill::NonZero, Affine::IDENTITY, - Color::rgb8(242, 140, 168), + Color::from_rgba8(242, 140, 168, 255), None, &Circle::new((420.0, 200.0), 120.0), ); @@ -114,7 +114,7 @@ renderer &scene, &surface_texture, &RenderParams { - base_color: Color::BLACK, // Background color + base_color: palette::css::BLACK, // Background color width, height, antialiasing_method: AaConfig::Msaa16, diff --git a/examples/headless/src/main.rs b/examples/headless/src/main.rs index d8045cd1b..d6d791e22 100644 --- a/examples/headless/src/main.rs +++ b/examples/headless/src/main.rs @@ -19,6 +19,7 @@ use anyhow::{anyhow, bail, Context, Result}; use clap::Parser; use scenes::{ImageCache, SceneParams, SceneSet, SimpleText}; use vello::kurbo::{Affine, Vec2}; +use vello::peniko::color::palette; use vello::util::RenderContext; use vello::wgpu::{ self, BufferDescriptor, BufferUsages, CommandEncoderDescriptor, Extent3d, ImageCopyBuffer, @@ -145,7 +146,7 @@ async fn render(mut scenes: SceneSet, index: usize, args: &Args) -> Result<()> { .args .base_color .or(scene_params.base_color) - .unwrap_or(vello::peniko::Color::BLACK), + .unwrap_or(palette::css::BLACK), width, height, antialiasing_method: vello::AaConfig::Area, diff --git a/examples/scenes/src/lib.rs b/examples/scenes/src/lib.rs index 3248a1bc8..b6870fe2a 100644 --- a/examples/scenes/src/lib.rs +++ b/examples/scenes/src/lib.rs @@ -32,7 +32,6 @@ mod simple_text; mod svg; pub mod test_scenes; -use anyhow::{anyhow, Result}; use clap::Args; pub use images::ImageCache; pub use simple_text::SimpleText; @@ -41,7 +40,7 @@ pub use svg::{default_scene, scene_from_files}; use test_scenes::test_scenes; use vello::kurbo::Vec2; -use vello::peniko::Color; +use vello::peniko::{color, Color}; use vello::Scene; pub struct SceneParams<'a> { @@ -93,7 +92,7 @@ pub struct Arguments { /// The svg files paths to render svgs: Option>, #[arg(help_heading = "Render Parameters")] - #[arg(long, global(false), value_parser = parse_color)] + #[arg(long, global(false), value_parser = parse_color_arg)] /// The base color applied as the blend background to the rasterizer. /// Format is CSS style hexadecimal (#RGB, #RGBA, #RRGGBB, #RRGGBBAA) or /// an SVG color name such as "aliceblue" @@ -101,7 +100,7 @@ pub struct Arguments { } impl Arguments { - pub fn select_scene_set(&self) -> Result> { + pub fn select_scene_set(&self) -> anyhow::Result> { // There is no file access on WASM, and on Android we haven't set up the assets // directory. // TODO: Upload the assets directory on Android @@ -120,6 +119,6 @@ impl Arguments { } } -fn parse_color(s: &str) -> Result { - Color::parse(s).ok_or(anyhow!("'{s}' is not a valid color")) +fn parse_color_arg(s: &str) -> Result { + color::parse_color(s).map(|c| c.to_alpha_color()) } diff --git a/examples/scenes/src/mmark.rs b/examples/scenes/src/mmark.rs index bd901155e..45b419ac3 100644 --- a/examples/scenes/src/mmark.rs +++ b/examples/scenes/src/mmark.rs @@ -118,13 +118,13 @@ impl TestScene for MMark { } const COLORS: &[Color] = &[ - Color::rgb8(0x10, 0x10, 0x10), - Color::rgb8(0x80, 0x80, 0x80), - Color::rgb8(0xc0, 0xc0, 0xc0), - Color::rgb8(0x10, 0x10, 0x10), - Color::rgb8(0x80, 0x80, 0x80), - Color::rgb8(0xc0, 0xc0, 0xc0), - Color::rgb8(0xe0, 0x10, 0x40), + Color::from_rgba8(0x10, 0x10, 0x10, 0xff), + Color::from_rgba8(0x80, 0x80, 0x80, 0xff), + Color::from_rgba8(0xc0, 0xc0, 0xc0, 0xff), + Color::from_rgba8(0x10, 0x10, 0x10, 0xff), + Color::from_rgba8(0x80, 0x80, 0x80, 0xff), + Color::from_rgba8(0xc0, 0xc0, 0xc0, 0xff), + Color::from_rgba8(0xe0, 0x10, 0x40, 0xff), ]; impl Element { diff --git a/examples/scenes/src/pico_svg.rs b/examples/scenes/src/pico_svg.rs index bf8a2707f..f0ef45a8c 100644 --- a/examples/scenes/src/pico_svg.rs +++ b/examples/scenes/src/pico_svg.rs @@ -8,6 +8,7 @@ use std::str::FromStr; use roxmltree::{Document, Node}; use vello::{ kurbo::{Affine, BezPath, Point, Size, Vec2}, + peniko::color::{self, palette}, peniko::Color, }; @@ -103,7 +104,7 @@ impl PicoSvg { Affine::new([-scale, 0.0, 0.0, scale, 0.0, 0.0]) }; let props = RecursiveProperties { - fill: Some(Color::BLACK), + fill: Some(palette::css::BLACK), }; // The root element is the svg document element, which we don't care about let mut items = Vec::new(); @@ -260,30 +261,29 @@ fn parse_transform(transform: &str) -> Affine { fn parse_color(color: &str) -> Color { let color = color.trim(); - if let Some(c) = Color::parse(color) { - c + if let Ok(c) = color::parse_color(color) { + c.to_alpha_color() } else if let Some(s) = color.strip_prefix("rgb(").and_then(|s| s.strip_suffix(')')) { let mut iter = s.split([',', ' ']).map(str::trim).map(u8::from_str); let r = iter.next().unwrap().unwrap(); let g = iter.next().unwrap().unwrap(); let b = iter.next().unwrap().unwrap(); - Color::rgb8(r, g, b) + Color::from_rgba8(r, g, b, 255) } else { - Color::rgba8(255, 0, 255, 0x80) + Color::from_rgba8(255, 0, 255, 0x80) } } -fn modify_opacity(mut color: Color, attr_name: &str, node: Node<'_, '_>) -> Color { +fn modify_opacity(color: Color, attr_name: &str, node: Node<'_, '_>) -> Color { if let Some(opacity) = node.attribute(attr_name) { - let alpha: f64 = if let Some(o) = opacity.strip_suffix('%') { + let alpha: f32 = if let Some(o) = opacity.strip_suffix('%') { let pctg = o.parse().unwrap_or(100.0); pctg * 0.01 } else { opacity.parse().unwrap_or(1.0) }; - color.a = (alpha.clamp(0.0, 1.0) * 255.0).round() as u8; - color + color.with_alpha(alpha.clamp(0., 1.)) } else { color } diff --git a/examples/scenes/src/simple_text.rs b/examples/scenes/src/simple_text.rs index 1f3c4eae7..bff6258e5 100644 --- a/examples/scenes/src/simple_text.rs +++ b/examples/scenes/src/simple_text.rs @@ -4,7 +4,7 @@ use std::sync::Arc; use vello::kurbo::Affine; -use vello::peniko::{Blob, Brush, BrushRef, Color, Font, StyleRef}; +use vello::peniko::{color::palette, Blob, Brush, BrushRef, Font, StyleRef}; use vello::skrifa::{raw::FontRef, MetadataProvider}; use vello::{Glyph, Scene}; @@ -59,7 +59,7 @@ impl SimpleText { size, &[], // This should be unused - &Brush::Solid(Color::WHITE), + &Brush::Solid(palette::css::WHITE), transform, glyph_transform, style, @@ -90,7 +90,7 @@ impl SimpleText { size, &[], // This should be unused - &Brush::Solid(Color::WHITE), + &Brush::Solid(palette::css::WHITE), transform, glyph_transform, style, @@ -191,7 +191,7 @@ impl SimpleText { text: &str, ) { use vello::peniko::Fill; - let brush = brush.unwrap_or(&Brush::Solid(Color::WHITE)); + let brush = brush.unwrap_or(&Brush::Solid(palette::css::WHITE)); self.add_run( scene, font, diff --git a/examples/scenes/src/svg.rs b/examples/scenes/src/svg.rs index 06027b545..370596845 100644 --- a/examples/scenes/src/svg.rs +++ b/examples/scenes/src/svg.rs @@ -12,7 +12,7 @@ use web_time::Instant; use anyhow::Result; use vello::{ kurbo::{Affine, Rect, Stroke, Vec2}, - peniko::{Color, Fill}, + peniko::{color::palette, Fill}, Scene, }; @@ -124,7 +124,7 @@ pub fn svg_function_of>( error_scene.fill( Fill::NonZero, Affine::IDENTITY, - Color::FUCHSIA, + palette::css::FUCHSIA, None, &Rect::new(0.0, 0.0, 1.0, 1.0), ); diff --git a/examples/scenes/src/test_scenes.rs b/examples/scenes/src/test_scenes.rs index 5cf19163b..90965f451 100644 --- a/examples/scenes/src/test_scenes.rs +++ b/examples/scenes/src/test_scenes.rs @@ -96,6 +96,7 @@ mod impls { use vello::kurbo::{ Affine, BezPath, Cap, Circle, Ellipse, Join, PathEl, Point, Rect, Shape, Stroke, Vec2, }; + use vello::peniko::color::{palette, AlphaColor, Lch}; use vello::peniko::*; use vello::*; @@ -137,28 +138,28 @@ mod impls { scene.fill( Fill::NonZero, Affine::translate((100.0, 100.0)), - Color::rgb8(0, 0, 255), + Color::from_rgba8(0, 0, 255, 255), None, &missing_movetos, ); scene.fill( Fill::NonZero, Affine::IDENTITY, - Color::rgb8(0, 0, 255), + Color::from_rgba8(0, 0, 255, 255), None, &empty, ); scene.fill( Fill::NonZero, Affine::IDENTITY, - Color::rgb8(0, 0, 255), + Color::from_rgba8(0, 0, 255, 255), None, &only_movetos, ); scene.stroke( &Stroke::new(8.0), Affine::translate((100.0, 100.0)), - Color::rgb8(0, 255, 255), + Color::from_rgba8(0, 255, 255, 255), None, &missing_movetos, ); @@ -168,10 +169,10 @@ mod impls { use PathEl::*; move |scene, params| { let colors = [ - Color::rgb8(140, 181, 236), - Color::rgb8(246, 236, 202), - Color::rgb8(201, 147, 206), - Color::rgb8(150, 195, 160), + Color::from_rgba8(140, 181, 236, 255), + Color::from_rgba8(246, 236, 202, 255), + Color::from_rgba8(201, 147, 206, 255), + Color::from_rgba8(150, 195, 160, 255), ]; let simple_stroke = [MoveTo((0., 0.).into()), LineTo((100., 0.).into())]; let join_stroke = [ @@ -345,10 +346,10 @@ mod impls { pub(super) fn tricky_strokes(scene: &mut Scene, params: &mut SceneParams) { use PathEl::*; let colors = [ - Color::rgb8(140, 181, 236), - Color::rgb8(246, 236, 202), - Color::rgb8(201, 147, 206), - Color::rgb8(150, 195, 160), + Color::from_rgba8(140, 181, 236, 255), + Color::from_rgba8(246, 236, 202, 255), + Color::from_rgba8(201, 147, 206, 255), + Color::from_rgba8(150, 195, 160, 255), ]; const CELL_SIZE: f64 = 200.; @@ -563,14 +564,14 @@ mod impls { scene.fill( Fill::NonZero, t, - &Brush::Solid(Color::rgb8(128, 128, 128)), + &Brush::Solid(Color::from_rgba8(128, 128, 128, 255)), None, &rect, ); scene.fill( rule.0, Affine::translate((0., 10.)) * t, - Color::YELLOW, + palette::css::YELLOW, None, &rule.2, ); @@ -585,28 +586,28 @@ mod impls { scene.fill( Fill::NonZero, t, - &Brush::Solid(Color::rgb8(128, 128, 128)), + &Brush::Solid(Color::from_rgba8(128, 128, 128, 255)), None, &rect, ); scene.fill( rule.0, Affine::translate((0., 10.)) * t, - Color::YELLOW, + palette::css::YELLOW, None, &rule.2, ); scene.fill( rule.0, Affine::translate((0., 10.)) * t * Affine::rotate(0.06), - Color::rgba(0., 1., 0.7, 0.6), + Color::new([0., 1., 0.7, 0.6]), None, &rule.2, ); scene.fill( rule.0, Affine::translate((0., 10.)) * t * Affine::rotate(-0.06), - Color::rgba(0.9, 0.7, 0.5, 0.6), + Color::new([0.9, 0.7, 0.5, 0.6]), None, &rule.2, ); @@ -657,7 +658,7 @@ mod impls { .with_join(Join::Bevel) .with_dashes(0.0, [1.0, 1.0]), Affine::translate((50.0, 50.0)), - Color::YELLOW, + palette::css::YELLOW, None, &path, ); @@ -685,7 +686,7 @@ mod impls { scene.fill( Fill::NonZero, Affine::IDENTITY, - &Brush::Solid(Color::rgb8(128, 128, 128)), + &Brush::Solid(Color::from_rgba8(128, 128, 128, 255)), None, &rect, ); @@ -703,7 +704,7 @@ mod impls { scene, None, text_size, - Color::WHITE, + palette::css::WHITE, Affine::translate((110.0, 700.0)), // Add a skew to simulate an oblique font. Some(Affine::skew(20f64.to_radians().tan(), 0.0)), @@ -718,7 +719,7 @@ mod impls { None, 72.0, &[("wght", weight), ("wdth", width)], - Color::WHITE, + palette::css::WHITE, Affine::translate((110.0, 800.0)), // Add a skew to simulate an oblique font. None, @@ -733,14 +734,14 @@ mod impls { scene.stroke( &Stroke::new(5.0), Affine::IDENTITY, - &Brush::Solid(Color::rgb8(128, 0, 0)), + &Brush::Solid(Color::from_rgba8(128, 0, 0, 255)), None, &[MoveTo(center), LineTo(p1)], ); scene.fill( Fill::NonZero, Affine::translate((150.0, 150.0)) * Affine::scale(0.2), - Color::RED, + palette::css::RED, None, &rect, ); @@ -749,14 +750,14 @@ mod impls { scene.fill( Fill::NonZero, Affine::translate((100.0, 100.0)) * Affine::scale(0.2), - Color::BLUE, + palette::css::BLUE, None, &rect, ); scene.fill( Fill::NonZero, Affine::translate((200.0, 200.0)) * Affine::scale(0.2), - Color::GREEN, + palette::css::GREEN, None, &rect, ); @@ -764,14 +765,14 @@ mod impls { scene.fill( Fill::NonZero, Affine::translate((400.0, 100.0)), - Color::PURPLE, + palette::css::PURPLE, None, &star, ); scene.fill( Fill::EvenOdd, Affine::translate((500.0, 100.0)), - Color::PURPLE, + palette::css::PURPLE, None, &star, ); @@ -784,17 +785,17 @@ mod impls { pub(super) fn brush_transform(scene: &mut Scene, params: &mut SceneParams) { let th = params.time; let linear = Gradient::new_linear((0.0, 0.0), (0.0, 200.0)).with_stops([ - Color::RED, - Color::GREEN, - Color::BLUE, + palette::css::RED, + palette::css::GREEN, + palette::css::BLUE, ]); scene.fill( Fill::NonZero, Affine::rotate(25f64.to_radians()) * Affine::scale_non_uniform(2.0, 1.0), &Gradient::new_radial((200.0, 200.0), 80.0).with_stops([ - Color::RED, - Color::GREEN, - Color::BLUE, + palette::css::RED, + palette::css::GREEN, + palette::css::BLUE, ]), None, &Rect::from_origin_size((100.0, 100.0), (200.0, 200.0)), @@ -822,7 +823,11 @@ mod impls { Sweep, } pub(super) fn square(scene: &mut Scene, kind: Kind, transform: Affine, extend: Extend) { - let colors = [Color::RED, Color::rgb8(0, 255, 0), Color::BLUE]; + let colors = [ + palette::css::RED, + Color::from_rgba8(0, 255, 0, 255), + palette::css::BLUE, + ]; let width = 300f64; let height = 300f64; let gradient: Brush = match kind { @@ -874,7 +879,7 @@ mod impls { scene, None, 32.0, - Some(&Color::WHITE.into()), + Some(&palette::css::WHITE.into()), Affine::translate((x, 70.0)), label, ); @@ -894,11 +899,15 @@ mod impls { transform: Affine, extend: Extend, ) { - let colors = [Color::RED, Color::YELLOW, Color::rgb8(6, 85, 186)]; + let colors = [ + palette::css::RED, + palette::css::YELLOW, + Color::from_rgba8(6, 85, 186, 255), + ]; let width = 400f64; let height = 200f64; let rect = Rect::new(0.0, 0.0, width, height); - scene.fill(Fill::NonZero, transform, Color::WHITE, None, &rect); + scene.fill(Fill::NonZero, transform, palette::css::WHITE, None, &rect); scene.fill( Fill::NonZero, transform, @@ -914,14 +923,14 @@ mod impls { scene.stroke( &Stroke::new(stroke_width), transform, - Color::BLACK, + palette::css::BLACK, None, &Ellipse::new((x0, y0), (r0, r0), 0.0), ); scene.stroke( &Stroke::new(stroke_width), transform, - Color::BLACK, + palette::css::BLACK, None, &Ellipse::new((x1, y1), (r1, r1), 0.0), ); @@ -1080,19 +1089,19 @@ mod impls { scene.fill( Fill::EvenOdd, Affine::IDENTITY, - Color::RED, + palette::css::RED, None, &main_rect, ); let options = [ - (800., Color::AQUA), - (700., Color::RED), - (600., Color::ALICE_BLUE), - (500., Color::YELLOW), - (400., Color::GREEN), - (300., Color::BLUE), - (200., Color::ORANGE), - (100., Color::WHITE), + (800., palette::css::AQUA), + (700., palette::css::RED), + (600., palette::css::ALICE_BLUE), + (500., palette::css::YELLOW), + (400., palette::css::GREEN), + (300., palette::css::BLUE), + (200., palette::css::ORANGE), + (100., palette::css::WHITE), ]; let mut depth = 0; for (width, color) in &options[..params.complexity.min(options.len() - 1)] { @@ -1127,7 +1136,7 @@ mod impls { scene.push_layer(Mix::Clip, 1.0, translate * rot, &base_tri); } let rot = Affine::rotate(rng.gen_range(0.0..PI)); - let color = Color::rgb(rng.r#gen(), rng.r#gen(), rng.r#gen()); + let color = Color::new([rng.r#gen(), rng.r#gen(), rng.r#gen(), 1.]); scene.fill(Fill::NonZero, translate * rot, color, None, &base_tri); for _ in 0..CLIPS_PER_FILL { scene.pop_layer(); @@ -1159,7 +1168,7 @@ mod impls { scene.stroke( &Stroke::new(2.0), Affine::IDENTITY, - Color::rgb8(0, 0, 255), + Color::from_rgba8(0, 0, 255, 255), None, &path, ); @@ -1190,7 +1199,7 @@ mod impls { scene.fill( Fill::NonZero, Affine::IDENTITY, - &Brush::Solid(Color::rgb8(0, 255, 0)), + &Brush::Solid(Color::from_rgba8(0, 255, 0, 255)), None, &rect, ); @@ -1204,14 +1213,14 @@ mod impls { scene.fill( Fill::NonZero, Affine::IDENTITY, - Color::rgb8(255, 0, 0), + Color::from_rgba8(255, 0, 0, 255), None, &make_diamond(1024.0, 100.0), ); scene.fill( Fill::NonZero, Affine::IDENTITY, - Color::rgba8(0, 255, 0, 0x80), + Color::from_rgba8(0, 255, 0, 0x80), None, &make_diamond(1024.0, 125.0), ); @@ -1224,7 +1233,7 @@ mod impls { scene.fill( Fill::NonZero, Affine::IDENTITY, - Color::rgba8(0, 0, 255, 0x80), + Color::from_rgba8(0, 0, 255, 0x80), None, &make_diamond(1024.0, 175.0), ); @@ -1234,29 +1243,28 @@ mod impls { pub(super) fn render_blend_square(scene: &mut Scene, blend: BlendMode, transform: Affine) { // Inspired by https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode let rect = Rect::from_origin_size(Point::new(0., 0.), (200., 200.)); - let linear = - Gradient::new_linear((0.0, 0.0), (200.0, 0.0)).with_stops([Color::BLACK, Color::WHITE]); + let linear = Gradient::new_linear((0.0, 0.0), (200.0, 0.0)) + .with_stops([palette::css::BLACK, palette::css::WHITE]); scene.fill(Fill::NonZero, transform, &linear, None, &rect); const GRADIENTS: &[(f64, f64, Color)] = &[ - (150., 0., Color::rgb8(255, 240, 64)), - (175., 100., Color::rgb8(255, 96, 240)), - (125., 200., Color::rgb8(64, 192, 255)), + (150., 0., Color::from_rgba8(255, 240, 64, 255)), + (175., 100., Color::from_rgba8(255, 96, 240, 255)), + (125., 200., Color::from_rgba8(64, 192, 255, 255)), ]; for (x, y, c) in GRADIENTS { - let mut color2 = *c; - color2.a = 0; + let color2 = c.with_alpha(0.); let radial = Gradient::new_radial((*x, *y), 100.0).with_stops([*c, color2]); scene.fill(Fill::NonZero, transform, &radial, None, &rect); } const COLORS: &[Color] = &[ - Color::rgb8(255, 0, 0), - Color::rgb8(0, 255, 0), - Color::rgb8(0, 0, 255), + Color::from_rgba8(255, 0, 0, 255), + Color::from_rgba8(0, 255, 0, 255), + Color::from_rgba8(0, 0, 255, 255), ]; scene.push_layer(Mix::Normal, 1.0, transform, &rect); for (i, c) in COLORS.iter().enumerate() { - let linear = - Gradient::new_linear((0.0, 0.0), (0.0, 200.0)).with_stops([Color::WHITE, *c]); + let linear = Gradient::new_linear((0.0, 0.0), (0.0, 200.0)) + .with_stops([palette::css::WHITE, *c]); scene.push_layer(blend, 1.0, transform, &rect); // squash the ellipse let a = transform @@ -1291,8 +1299,8 @@ mod impls { let x = N + 0.5; // Fractional pixel offset reveals the problem on axis-aligned edges. let mut y = N; - let bg_color = Color::rgb8(255, 194, 19); - let fg_color = Color::rgb8(12, 165, 255); + let bg_color = Color::from_rgba8(255, 194, 19, 255); + let fg_color = Color::from_rgba8(12, 165, 255, 255); // Two adjacent triangles touching at diagonal edge with opposing winding numbers scene.fill( @@ -1442,7 +1450,7 @@ mod impls { scene.fill( Fill::NonZero, Affine::translate((20.5, 20.5)) * Affine::scale(80.0), - Color::rgba8(0x70, 0x80, 0x80, 0xff), + Color::from_rgba8(0x70, 0x80, 0x80, 0xff), None, &path, ); @@ -1495,11 +1503,17 @@ mod impls { path.line_to((256.0, 24.5)); path.line_to((241.0, 24.5)); path.close_path(); - scene.fill(Fill::NonZero, Affine::IDENTITY, Color::YELLOW, None, &path); + scene.fill( + Fill::NonZero, + Affine::IDENTITY, + palette::css::YELLOW, + None, + &path, + ); scene.fill( Fill::EvenOdd, Affine::translate((300.0, 0.0)), - Color::LIME, + palette::css::LIME, None, &path, ); @@ -1512,14 +1526,14 @@ mod impls { scene.fill( Fill::NonZero, Affine::translate((0.0, 100.0)), - Color::YELLOW, + palette::css::YELLOW, None, &path, ); scene.fill( Fill::EvenOdd, Affine::translate((300.0, 100.0)), - Color::LIME, + palette::css::LIME, None, &path, ); @@ -1527,14 +1541,14 @@ mod impls { pub(super) fn base_color_test(scene: &mut Scene, params: &mut SceneParams) { // Cycle through the hue value every 5 seconds (t % 5) * 360/5 - let color = Color::hlc((params.time % 5.0) * 72.0, 80.0, 80.0); - params.base_color = Some(color); + let color = AlphaColor::::new([80., 80., (params.time % 5.) as f32 * 72., 1.]); + params.base_color = Some(color.convert()); // Blend a white square over it. scene.fill( Fill::NonZero, Affine::IDENTITY, - Color::rgba8(255, 255, 255, 128), + Color::from_rgba8(255, 255, 255, 128), None, &Rect::new(50.0, 50.0, 500.0, 500.0), ); @@ -1594,7 +1608,7 @@ mod impls { scene.fill( Fill::NonZero, Affine::new([scale, 0.0, 0.0, scale, 27.07470703125, 176.40660533027858]), - Color::rgb8(0, 0, 255), + Color::from_rgba8(0, 0, 255, 255), None, &large_background_rect, ); @@ -1608,7 +1622,7 @@ mod impls { 29.027636718750003, 182.9755506427786, ]), - Color::rgb8(0, 255, 0), + Color::from_rgba8(0, 255, 0, 255), None, &inside_clip_rect, ); @@ -1622,7 +1636,7 @@ mod impls { 29.027636718750003, scale * 559.3583631427786, ]), - Color::rgb8(255, 0, 0), + Color::from_rgba8(255, 0, 0, 255), None, &outside_clip_rect, ); @@ -1656,7 +1670,13 @@ mod impls { for i in 0..N_WIDE { let x = (i as f64 + 0.5) * (SCENE_WIDTH / N_WIDE as f64); let c = Circle::new((x, y), 3.0); - scene.fill(Fill::NonZero, Affine::IDENTITY, Color::YELLOW, None, &c); + scene.fill( + Fill::NonZero, + Affine::IDENTITY, + palette::css::YELLOW, + None, + &c, + ); } } } @@ -1701,14 +1721,14 @@ mod impls { pub(super) fn blurred_rounded_rect(scene: &mut Scene, params: &mut SceneParams) { params.resolution = Some(Vec2::new(1200., 1200.)); - params.base_color = Some(Color::WHITE); + params.base_color = Some(palette::css::WHITE); let rect = Rect::from_center_size((0.0, 0.0), (300.0, 240.0)); let radius = 50.0; scene.draw_blurred_rounded_rect( Affine::translate((300.0, 300.0)), rect, - Color::BLUE, + palette::css::BLUE, radius, params.time.sin() * 50.0 + 50.0, ); @@ -1717,7 +1737,7 @@ mod impls { scene.draw_blurred_rounded_rect( Affine::translate((900.0, 300.0)) * Affine::skew(20f64.to_radians().tan(), 0.0), rect, - Color::BLACK, + palette::css::BLACK, radius, params.time.sin() * 50.0 + 50.0, ); @@ -1726,7 +1746,7 @@ mod impls { scene.draw_blurred_rounded_rect( Affine::IDENTITY, Rect::new(100.0, 800.0, 400.0, 1100.0), - Color::BLACK, + palette::css::BLACK, 150.0, params.time.sin() * 50.0 + 50.0, ); @@ -1735,7 +1755,7 @@ mod impls { scene.draw_blurred_rounded_rect( Affine::IDENTITY, Rect::new(600.0, 800.0, 900.0, 900.0), - Color::BLACK, + palette::css::BLACK, 150.0, params.time.sin() * 50.0 + 50.0, ); @@ -1758,7 +1778,7 @@ mod impls { &shape, Affine::translate((600.0, 600.0)) * Affine::scale_non_uniform(2.2, 0.9), rect, - Color::BLACK, + palette::css::BLACK, radius, std_dev, ); @@ -1766,17 +1786,23 @@ mod impls { pub(super) fn image_sampling(scene: &mut Scene, params: &mut SceneParams) { params.resolution = Some(Vec2::new(1100., 1100.)); - params.base_color = Some(Color::WHITE); + params.base_color = Some(palette::css::WHITE); let mut blob: Vec = Vec::new(); - [Color::RED, Color::BLUE, Color::CYAN, Color::MAGENTA] - .iter() - .for_each(|c| { - let b = c.to_premul_u32().to_ne_bytes(); - blob.push(b[3]); - blob.push(b[2]); - blob.push(b[1]); - blob.push(b[0]); - }); + [ + palette::css::RED, + palette::css::BLUE, + palette::css::CYAN, + palette::css::MAGENTA, + ] + .iter() + .for_each(|c| { + // FIXME(color): Get rid of the to_u32 and just use the PremulRgba8 + let b = c.premultiply().to_rgba8().to_u32().to_ne_bytes(); + blob.push(b[3]); + blob.push(b[2]); + blob.push(b[1]); + blob.push(b[0]); + }); let data = Blob::new(Arc::new(blob)); let image = Image::new(data, Format::Rgba8, 2, 2); @@ -1808,17 +1834,23 @@ mod impls { pub(super) fn image_extend_modes(scene: &mut Scene, params: &mut SceneParams) { params.resolution = Some(Vec2::new(1500., 1500.)); - params.base_color = Some(Color::WHITE); + params.base_color = Some(palette::css::WHITE); let mut blob: Vec = Vec::new(); - [Color::RED, Color::BLUE, Color::CYAN, Color::MAGENTA] - .iter() - .for_each(|c| { - let b = c.to_premul_u32().to_ne_bytes(); - blob.push(b[3]); - blob.push(b[2]); - blob.push(b[1]); - blob.push(b[0]); - }); + [ + palette::css::RED, + palette::css::BLUE, + palette::css::CYAN, + palette::css::MAGENTA, + ] + .iter() + .for_each(|c| { + // FIXME(color): Get rid of the to_u32 and just use the PremulRgba8 + let b = c.premultiply().to_rgba8().to_u32().to_ne_bytes(); + blob.push(b[3]); + blob.push(b[2]); + blob.push(b[1]); + blob.push(b[0]); + }); let data = Blob::new(Arc::new(blob)); let image = Image::new(data, Format::Rgba8, 2, 2); let image = image.with_extend(Extend::Pad); diff --git a/examples/simple/src/main.rs b/examples/simple/src/main.rs index 044ea12a8..8a4a51592 100644 --- a/examples/simple/src/main.rs +++ b/examples/simple/src/main.rs @@ -7,6 +7,7 @@ use anyhow::Result; use std::num::NonZeroUsize; use std::sync::Arc; use vello::kurbo::{Affine, Circle, Ellipse, Line, RoundedRect, Stroke}; +use vello::peniko::color::palette; use vello::peniko::Color; use vello::util::{RenderContext, RenderSurface}; use vello::{AaConfig, Renderer, RendererOptions, Scene}; @@ -146,7 +147,7 @@ impl ApplicationHandler for SimpleVelloApp<'_> { &self.scene, &surface_texture, &vello::RenderParams { - base_color: Color::BLACK, // Background color + base_color: palette::css::BLACK, // Background color width, height, antialiasing_method: AaConfig::Msaa16, @@ -210,12 +211,12 @@ fn add_shapes_to_scene(scene: &mut Scene) { // Draw an outlined rectangle let stroke = Stroke::new(6.0); let rect = RoundedRect::new(10.0, 10.0, 240.0, 240.0, 20.0); - let rect_stroke_color = Color::rgb(0.9804, 0.702, 0.5294); + let rect_stroke_color = Color::new([0.9804, 0.702, 0.5294, 1.]); scene.stroke(&stroke, Affine::IDENTITY, rect_stroke_color, None, &rect); // Draw a filled circle let circle = Circle::new((420.0, 200.0), 120.0); - let circle_fill_color = Color::rgb(0.9529, 0.5451, 0.6588); + let circle_fill_color = Color::new([0.9529, 0.5451, 0.6588, 1.]); scene.fill( vello::peniko::Fill::NonZero, Affine::IDENTITY, @@ -226,7 +227,7 @@ fn add_shapes_to_scene(scene: &mut Scene) { // Draw a filled ellipse let ellipse = Ellipse::new((250.0, 420.0), (100.0, 160.0), -90.0); - let ellipse_fill_color = Color::rgb(0.7961, 0.651, 0.9686); + let ellipse_fill_color = Color::new([0.7961, 0.651, 0.9686, 1.]); scene.fill( vello::peniko::Fill::NonZero, Affine::IDENTITY, @@ -237,6 +238,6 @@ fn add_shapes_to_scene(scene: &mut Scene) { // Draw a straight line let line = Line::new((260.0, 20.0), (620.0, 100.0)); - let line_stroke_color = Color::rgb(0.5373, 0.7059, 0.9804); + let line_stroke_color = Color::new([0.5373, 0.7059, 0.9804, 1.]); scene.stroke(&stroke, Affine::IDENTITY, line_stroke_color, None, &line); } diff --git a/examples/simple_sdl2/src/main.rs b/examples/simple_sdl2/src/main.rs index 52ecd7fc3..125f0a7f2 100644 --- a/examples/simple_sdl2/src/main.rs +++ b/examples/simple_sdl2/src/main.rs @@ -14,6 +14,7 @@ use sdl2::keyboard::Keycode; use std::num::NonZeroUsize; use vello::kurbo::{Affine, Circle, Ellipse, Line, RoundedRect, Stroke}; +use vello::peniko::color::palette; use vello::peniko::Color; use vello::util::{RenderContext, RenderSurface}; use vello::{AaConfig, Renderer, RendererOptions, Scene}; @@ -80,7 +81,7 @@ fn main() { &scene, &surface_texture, &vello::RenderParams { - base_color: Color::BLACK, // Background color + base_color: palette::css::BLACK, // Background color width, height, antialiasing_method: AaConfig::Msaa16, @@ -120,12 +121,12 @@ fn add_shapes_to_scene(scene: &mut Scene) { // Draw an outlined rectangle let stroke = Stroke::new(6.0); let rect = RoundedRect::new(10.0, 10.0, 240.0, 240.0, 20.0); - let rect_stroke_color = Color::rgb(0.9804, 0.702, 0.5294); + let rect_stroke_color = Color::new([0.9804, 0.702, 0.5294, 1.]); scene.stroke(&stroke, Affine::IDENTITY, rect_stroke_color, None, &rect); // Draw a filled circle let circle = Circle::new((420.0, 200.0), 120.0); - let circle_fill_color = Color::rgb(0.9529, 0.5451, 0.6588); + let circle_fill_color = Color::new([0.9529, 0.5451, 0.6588, 1.]); scene.fill( vello::peniko::Fill::NonZero, Affine::IDENTITY, @@ -136,7 +137,7 @@ fn add_shapes_to_scene(scene: &mut Scene) { // Draw a filled ellipse let ellipse = Ellipse::new((250.0, 420.0), (100.0, 160.0), -90.0); - let ellipse_fill_color = Color::rgb(0.7961, 0.651, 0.9686); + let ellipse_fill_color = Color::new([0.7961, 0.651, 0.9686, 1.]); scene.fill( vello::peniko::Fill::NonZero, Affine::IDENTITY, @@ -147,6 +148,6 @@ fn add_shapes_to_scene(scene: &mut Scene) { // Draw a straight line let line = Line::new((260.0, 20.0), (620.0, 100.0)); - let line_stroke_color = Color::rgb(0.5373, 0.7059, 0.9804); + let line_stroke_color = Color::new([0.5373, 0.7059, 0.9804, 1.]); scene.stroke(&stroke, Affine::IDENTITY, line_stroke_color, None, &line); } diff --git a/examples/with_winit/src/lib.rs b/examples/with_winit/src/lib.rs index aa25a4e7e..05a443513 100644 --- a/examples/with_winit/src/lib.rs +++ b/examples/with_winit/src/lib.rs @@ -37,7 +37,7 @@ use web_time::Duration; use clap::Parser; use scenes::{ExampleScene, ImageCache, SceneParams, SceneSet, SimpleText}; use vello::kurbo::{Affine, Vec2}; -use vello::peniko::Color; +use vello::peniko::{color::palette, Color}; use vello::util::{RenderContext, RenderSurface}; use vello::{low_level::BumpAllocators, AaConfig, Renderer, RendererOptions, Scene}; @@ -492,7 +492,7 @@ impl ApplicationHandler for VelloApp<'_> { let base_color = self .base_color .or(scene_params.base_color) - .unwrap_or(Color::BLACK); + .unwrap_or(palette::css::BLACK); let antialiasing_method = AA_CONFIGS[self.aa_config_ix as usize]; let render_params = vello::RenderParams { base_color, diff --git a/examples/with_winit/src/stats.rs b/examples/with_winit/src/stats.rs index e9f9c6b4f..14ed0eb7a 100644 --- a/examples/with_winit/src/stats.rs +++ b/examples/with_winit/src/stats.rs @@ -6,7 +6,7 @@ use std::collections::VecDeque; #[cfg(feature = "wgpu-profiler")] use vello::kurbo::Line; use vello::kurbo::{Affine, PathEl, Rect, Stroke}; -use vello::peniko::{Brush, Color, Fill}; +use vello::peniko::{color::palette, Brush, Color, Fill}; use vello::{low_level::BumpAllocators, AaConfig, Scene}; #[cfg(all(feature = "wgpu-profiler", not(target_arch = "wasm32")))] @@ -48,7 +48,7 @@ impl Snapshot { scene.fill( Fill::NonZero, offset, - &Brush::Solid(Color::rgba8(0, 0, 0, 200)), + &Brush::Solid(Color::from_rgba8(0, 0, 0, 200)), None, &Rect::new(0., 0., width, height), ); @@ -88,7 +88,7 @@ impl Snapshot { scene, None, text_size, - Some(&Brush::Solid(Color::WHITE)), + Some(&Brush::Solid(palette::css::WHITE)), offset * Affine::translate((left_margin, (i + 1) as f64 * text_height)), label, ); @@ -97,7 +97,7 @@ impl Snapshot { scene, None, text_size, - Some(&Brush::Solid(Color::WHITE)), + Some(&Brush::Solid(palette::css::WHITE)), offset * Affine::translate((width * 0.67, text_height)), &format!("FPS: {:.2}", self.fps), ); @@ -135,9 +135,9 @@ impl Snapshot { let s = Affine::scale_non_uniform(1., -h); #[allow(clippy::match_overlapping_arm)] let color = match *sample { - ..=16_667 => Color::rgb8(100, 143, 255), - ..=33_334 => Color::rgb8(255, 176, 0), - _ => Color::rgb8(220, 38, 127), + ..=16_667 => Color::from_rgba8(100, 143, 255, 255), + ..=33_334 => Color::from_rgba8(255, 176, 0, 255), + _ => Color::from_rgba8(220, 38, 127, 255), }; scene.fill( Fill::NonZero, @@ -164,7 +164,7 @@ impl Snapshot { scene, None, thres_text_height as f32, - Some(&Brush::Solid(Color::WHITE)), + Some(&Brush::Solid(palette::css::WHITE)), offset * Affine::translate(( left_margin, @@ -175,7 +175,7 @@ impl Snapshot { scene.stroke( &Stroke::new(graph_max_height * 0.01), offset * Affine::translate((left_margin_padding, (1. - y) * graph_max_height)), - Color::WHITE, + palette::css::WHITE, None, &marker, ); @@ -289,14 +289,14 @@ pub fn draw_gpu_profiling( profiles: &[GpuTimerQueryResult], ) { const COLORS: &[Color] = &[ - Color::AQUA, - Color::RED, - Color::ALICE_BLUE, - Color::YELLOW, - Color::GREEN, - Color::BLUE, - Color::ORANGE, - Color::WHITE, + palette::css::AQUA, + palette::css::RED, + palette::css::ALICE_BLUE, + palette::css::YELLOW, + palette::css::GREEN, + palette::css::BLUE, + palette::css::ORANGE, + palette::css::WHITE, ]; if profiles_are_empty(profiles) { return; @@ -310,7 +310,7 @@ pub fn draw_gpu_profiling( scene.fill( Fill::NonZero, offset, - &Brush::Solid(Color::rgba8(0, 0, 0, 200)), + &Brush::Solid(Color::from_rgba8(0, 0, 0, 200)), None, &Rect::new(0., 0., width, height), ); @@ -351,7 +351,7 @@ pub fn draw_gpu_profiling( scene, None, text_size, - Some(&Brush::Solid(Color::WHITE)), + Some(&Brush::Solid(palette::css::WHITE)), offset * Affine::translate((left_margin, (i + 1) as f64 * text_height)), label, ); @@ -363,7 +363,7 @@ pub fn draw_gpu_profiling( scene, None, text_size, - Some(&Brush::Solid(Color::WHITE)), + Some(&Brush::Solid(palette::css::WHITE)), offset * Affine::translate((left_margin, (i + 1) as f64 * text_height)), label, ); @@ -415,9 +415,9 @@ pub fn draw_gpu_profiling( // Ensure that all remaining items can fit .min(timeline_range_end - (count - cur_index) as f64 * text_height); let (text_height, text_color) = if slow { - (text_height, Color::WHITE) + (text_height, palette::css::WHITE) } else { - (text_height * 0.6, Color::LIGHT_GRAY) + (text_height * 0.6, palette::css::LIGHT_GRAY) }; let text_size = (text_height * 0.9) as f32; // Text is specified by the baseline, but the y positions all refer to the top of the text diff --git a/vello/README.md b/vello/README.md index 2e47cd72a..9565d145f 100644 --- a/vello/README.md +++ b/vello/README.md @@ -79,7 +79,7 @@ let mut scene = vello::Scene::new(); scene.fill( vello::peniko::Fill::NonZero, vello::Affine::IDENTITY, - vello::Color::rgb8(242, 140, 168), + vello::Color::from_rgba8(242, 140, 168, 255), None, &vello::Circle::new((420.0, 200.0), 120.0), ); @@ -100,7 +100,7 @@ renderer &scene, &surface_texture, &vello::RenderParams { - base_color: Color::BLACK, // Background color + base_color: palette::css::BLACK, // Background color width, height, antialiasing_method: AaConfig::Msaa16, diff --git a/vello/src/debug/renderer.rs b/vello/src/debug/renderer.rs index 6de15991b..8b0325131 100644 --- a/vello/src/debug/renderer.rs +++ b/vello/src/debug/renderer.rs @@ -18,7 +18,7 @@ use crate::{ use { bytemuck::{offset_of, Pod, Zeroable}, - peniko::Color, + peniko::color::{palette, OpaqueColor, Srgb}, vello_encoding::{BumpAllocators, LineSoup, PathBbox}, }; pub(crate) struct DebugRenderer { @@ -255,8 +255,8 @@ impl DebugRenderer { ); let linepoints_uniforms = [ - LinepointsUniforms::new(Color::DARK_CYAN, 10.), - LinepointsUniforms::new(Color::RED, 80.), + LinepointsUniforms::new(palette::css::DARK_CYAN.discard_alpha(), 10.), + LinepointsUniforms::new(palette::css::RED.discard_alpha(), 80.), ]; let linepoints_uniforms_buf = recording.upload_uniform( "vello.debug.linepoints_uniforms", @@ -357,13 +357,9 @@ struct LinepointsUniforms { } impl LinepointsUniforms { - fn new(color: Color, point_size: f32) -> Self { + fn new(color: OpaqueColor, point_size: f32) -> Self { Self { - point_color: [ - color.r as f32 / 255., - color.g as f32 / 255., - color.b as f32 / 255., - ], + point_color: color.components, point_size, _pad0: [0; 30], _pad1: [0; 30], diff --git a/vello/src/lib.rs b/vello/src/lib.rs index cdfc7c90e..ce0190560 100644 --- a/vello/src/lib.rs +++ b/vello/src/lib.rs @@ -48,7 +48,7 @@ //! scene.fill( //! vello::peniko::Fill::NonZero, //! vello::Affine::IDENTITY, -//! vello::Color::rgb8(242, 140, 168), +//! vello::Color::from_rgba8(242, 140, 168, 255), //! None, //! &vello::Circle::new((420.0, 200.0), 120.0), //! ); @@ -69,7 +69,7 @@ //! &scene, //! &surface_texture, //! &vello::RenderParams { -//! base_color: Color::BLACK, // Background color +//! base_color: palette::css::BLACK, // Background color //! width, //! height, //! antialiasing_method: AaConfig::Msaa16, diff --git a/vello/src/scene.rs b/vello/src/scene.rs index 81d4136e9..555ca5f45 100644 --- a/vello/src/scene.rs +++ b/vello/src/scene.rs @@ -6,6 +6,7 @@ mod bitmap; use std::sync::Arc; use peniko::{ + color::{palette, DynamicColor, Srgb}, kurbo::{Affine, BezPath, Point, Rect, Shape, Stroke, Vec2}, BlendMode, Blob, Brush, BrushRef, Color, ColorStop, ColorStops, ColorStopsSource, Compose, Extend, Fill, Font, Gradient, Image, Mix, StyleRef, @@ -359,7 +360,7 @@ impl<'a> DrawGlyphs<'a> { glyphs: glyphs_start..glyphs_start, stream_offsets, }, - brush: Color::BLACK.into(), + brush: palette::css::BLACK.into(), brush_alpha: 1.0, } } @@ -987,7 +988,7 @@ fn color_index(cpal: &'_ Cpal<'_>, palette_index: u16) -> Option { let actual_colors = cpal.color_records_array().unwrap().unwrap(); // TODO: Error reporting in the `None` case let color = actual_colors.get(usize::from(palette_index))?; - Some(Color::rgba8( + Some(Color::from_rgba8( color.red, color.green, color.blue, @@ -1027,14 +1028,14 @@ impl ColorStopsSource for ColorStopsConverter<'_> { BrushRef::Gradient(grad) => grad .stops .first() - .map(|it| it.color) - .unwrap_or(Color::TRANSPARENT), - BrushRef::Image(_) => Color::BLACK, + .map(|it| it.color.to_alpha_color::()) + .unwrap_or(palette::css::TRANSPARENT), + BrushRef::Image(_) => palette::css::BLACK, }, }; let color = color.multiply_alpha(item.alpha); vec.push(ColorStop { - color, + color: DynamicColor::from_alpha_color(color), offset: item.offset, }); } diff --git a/vello_encoding/src/config.rs b/vello_encoding/src/config.rs index 8f155498f..b2515c387 100644 --- a/vello_encoding/src/config.rs +++ b/vello_encoding/src/config.rs @@ -180,7 +180,7 @@ impl RenderConfig { height_in_tiles, target_width: width, target_height: height, - base_color: base_color.to_premul_u32(), + base_color: base_color.premultiply().to_rgba8().to_u32(), lines_size: buffer_sizes.lines.len(), binning_size: buffer_sizes.bin_data.len() - layout.bin_data_start, tiles_size: buffer_sizes.tiles.len(), diff --git a/vello_encoding/src/draw.rs b/vello_encoding/src/draw.rs index 162384263..90304ff94 100644 --- a/vello_encoding/src/draw.rs +++ b/vello_encoding/src/draw.rs @@ -2,7 +2,10 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT use bytemuck::{Pod, Zeroable}; -use peniko::{BlendMode, Color}; +use peniko::{ + color::{AlphaColor, ColorSpace, DynamicColor, OpaqueColor, PremulColor, Srgb}, + BlendMode, +}; use super::Monoid; @@ -70,11 +73,43 @@ pub struct DrawColor { pub rgba: u32, } -impl DrawColor { - /// Creates new solid color draw data. - pub fn new(color: Color) -> Self { +impl From> for DrawColor { + fn from(color: AlphaColor) -> Self { Self { - rgba: color.to_premul_u32(), + rgba: color.convert::().premultiply().to_rgba8().to_u32(), + } + } +} + +impl From for DrawColor { + fn from(color: DynamicColor) -> Self { + Self { + rgba: color + .to_alpha_color::() + .premultiply() + .to_rgba8() + .to_u32(), + } + } +} + +impl From> for DrawColor { + fn from(color: OpaqueColor) -> Self { + Self { + rgba: color + .convert::() + .with_alpha(1.) + .premultiply() + .to_rgba8() + .to_u32(), + } + } +} + +impl From> for DrawColor { + fn from(color: PremulColor) -> Self { + Self { + rgba: color.convert::().to_rgba8().to_u32(), } } } diff --git a/vello_encoding/src/encoding.rs b/vello_encoding/src/encoding.rs index f3fa4b125..e47960d96 100644 --- a/vello_encoding/src/encoding.rs +++ b/vello_encoding/src/encoding.rs @@ -4,7 +4,7 @@ use super::{DrawBlurRoundedRect, DrawColor, DrawTag, PathEncoder, PathTag, Style, Transform}; use peniko::kurbo::{Shape, Stroke}; -use peniko::{BlendMode, BrushRef, Color, Fill}; +use peniko::{BlendMode, BrushRef, Fill}; #[cfg(feature = "full")] use { @@ -12,6 +12,7 @@ use { DrawImage, DrawLinearGradient, DrawRadialGradient, DrawSweepGradient, Glyph, GlyphRun, Patch, }, + peniko::color::{palette, DynamicColor}, peniko::{ColorStop, Extend, GradientKind, Image}, skrifa::instance::NormalizedCoord, }; @@ -274,7 +275,7 @@ impl Encoding { } else { color }; - self.encode_color(DrawColor::new(color)); + self.encode_color(color); } #[cfg(feature = "full")] BrushRef::Gradient(gradient) => match gradient.kind { @@ -339,7 +340,8 @@ impl Encoding { } /// Encodes a solid color brush. - pub fn encode_color(&mut self, color: DrawColor) { + pub fn encode_color(&mut self, color: impl Into) { + let color = color.into(); self.draw_tags.push(DrawTag::COLOR); self.draw_data.extend_from_slice(bytemuck::bytes_of(&color)); } @@ -354,8 +356,10 @@ impl Encoding { extend: Extend, ) { match self.add_ramp(color_stops, alpha, extend) { - RampStops::Empty => self.encode_color(DrawColor::new(Color::TRANSPARENT)), - RampStops::One(color) => self.encode_color(DrawColor::new(color)), + RampStops::Empty => self.encode_color(palette::css::TRANSPARENT), + RampStops::One(color) => { + self.encode_color(color); + } _ => { self.draw_tags.push(DrawTag::LINEAR_GRADIENT); self.draw_data @@ -376,12 +380,12 @@ impl Encoding { // Match Skia's epsilon for radii comparison const SKIA_EPSILON: f32 = 1.0 / (1 << 12) as f32; if gradient.p0 == gradient.p1 && (gradient.r0 - gradient.r1).abs() < SKIA_EPSILON { - self.encode_color(DrawColor::new(Color::TRANSPARENT)); + self.encode_color(palette::css::TRANSPARENT); return; } match self.add_ramp(color_stops, alpha, extend) { - RampStops::Empty => self.encode_color(DrawColor::new(Color::TRANSPARENT)), - RampStops::One(color) => self.encode_color(DrawColor::new(color)), + RampStops::Empty => self.encode_color(palette::css::TRANSPARENT), + RampStops::One(color) => self.encode_color(color), _ => { self.draw_tags.push(DrawTag::RADIAL_GRADIENT); self.draw_data @@ -401,12 +405,12 @@ impl Encoding { ) { const SKIA_DEGENERATE_THRESHOLD: f32 = 1.0 / (1 << 15) as f32; if (gradient.t0 - gradient.t1).abs() < SKIA_DEGENERATE_THRESHOLD { - self.encode_color(DrawColor::new(Color::TRANSPARENT)); + self.encode_color(palette::css::TRANSPARENT); return; } match self.add_ramp(color_stops, alpha, extend) { - RampStops::Empty => self.encode_color(DrawColor::new(Color::TRANSPARENT)), - RampStops::One(color) => self.encode_color(DrawColor::new(color)), + RampStops::Empty => self.encode_color(palette::css::TRANSPARENT), + RampStops::One(color) => self.encode_color(color), _ => { self.draw_tags.push(DrawTag::SWEEP_GRADIENT); self.draw_data @@ -418,7 +422,7 @@ impl Encoding { /// Encodes an image brush. #[cfg(feature = "full")] pub fn encode_image(&mut self, image: &Image, alpha: f32) { - let _alpha = alpha * f32::from(image.alpha); + let _alpha = alpha * image.alpha; // TODO: feed the alpha multiplier through the full pipeline for consistency // with other brushes? // Tracked in https://github.com/linebender/vello/issues/692 @@ -437,7 +441,7 @@ impl Encoding { // Encodes a blurred rounded rectangle brush. pub fn encode_blurred_rounded_rect( &mut self, - color: Color, + color: impl Into, width: f32, height: f32, radius: f32, @@ -446,7 +450,7 @@ impl Encoding { self.draw_tags.push(DrawTag::BLUR_RECT); self.draw_data .extend_from_slice(bytemuck::bytes_of(&DrawBlurRoundedRect { - color: DrawColor::new(color), + color: color.into(), width, height, radius, @@ -527,7 +531,7 @@ enum RampStops { /// Color stop sequence was empty. Empty, /// Contained a single color stop. - One(Color), + One(DynamicColor), /// More than one color stop. Many, } diff --git a/vello_encoding/src/ramp_cache.rs b/vello_encoding/src/ramp_cache.rs index ceee7bb6c..84ad5677d 100644 --- a/vello_encoding/src/ramp_cache.rs +++ b/vello_encoding/src/ramp_cache.rs @@ -3,6 +3,7 @@ use std::collections::HashMap; +use peniko::color::Srgb; use peniko::{Color, ColorStop, ColorStops}; const N_SAMPLES: usize = 512; @@ -81,7 +82,7 @@ impl RampCache { fn make_ramp(stops: &[ColorStop]) -> impl Iterator + '_ { let mut last_u = 0.0; - let mut last_c = ColorF64::from_color(stops[0].color); + let mut last_c = ColorF64::from_color(stops[0].color.to_alpha_color::()); let mut this_u = last_u; let mut this_c = last_c; let mut j = 0; @@ -92,7 +93,7 @@ fn make_ramp(stops: &[ColorStop]) -> impl Iterator + '_ { last_c = this_c; if let Some(s) = stops.get(j + 1) { this_u = s.offset as f64; - this_c = ColorF64::from_color(s.color); + this_c = ColorF64::from_color(s.color.to_alpha_color::()); j += 1; } else { break; @@ -113,12 +114,8 @@ struct ColorF64([f64; 4]); impl ColorF64 { fn from_color(color: Color) -> Self { - Self([ - color.r as f64 / 255.0, - color.g as f64 / 255.0, - color.b as f64 / 255.0, - color.a as f64 / 255.0, - ]) + let [r, g, b, a] = color.components; + Self([r as f64, g as f64, b as f64, a as f64]) } fn lerp(&self, other: &Self, a: f64) -> Self { diff --git a/vello_tests/snapshots/many_clips.png b/vello_tests/snapshots/many_clips.png index cf95a1cef..0b7ac99b1 100644 --- a/vello_tests/snapshots/many_clips.png +++ b/vello_tests/snapshots/many_clips.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3794d764eaab03964653644cb80e5ea6fcfb59a2b3496e95ed3ee7c90758644c -size 25610 +oid sha256:08ae2b90cea2d9f055d0b45cef370e980225f996ca81dceba171e0992dcb5596 +size 25513 diff --git a/vello_tests/src/lib.rs b/vello_tests/src/lib.rs index 76fb8e93a..30a1151fd 100644 --- a/vello_tests/src/lib.rs +++ b/vello_tests/src/lib.rs @@ -42,7 +42,7 @@ use std::sync::Arc; use anyhow::{anyhow, bail, Result}; use scenes::{ExampleScene, ImageCache, SceneParams, SimpleText}; use vello::kurbo::{Affine, Vec2}; -use vello::peniko::{Blob, Color, Format, Image}; +use vello::peniko::{color::palette, Blob, Color, Format, Image}; use vello::wgpu::{ self, BufferDescriptor, BufferUsages, CommandEncoderDescriptor, Extent3d, ImageCopyBuffer, TextureDescriptor, TextureFormat, TextureUsages, @@ -127,7 +127,7 @@ pub async fn get_scene_image(params: &TestParams, scene: &Scene) -> Result 1e-4 { panic!("Got {image_color:?}, expected clear color {color:?}"); } } diff --git a/vello_tests/tests/regression.rs b/vello_tests/tests/regression.rs index 9b80e6583..834f56847 100644 --- a/vello_tests/tests/regression.rs +++ b/vello_tests/tests/regression.rs @@ -3,7 +3,7 @@ use vello::{ kurbo::{Affine, RoundedRect, Stroke}, - peniko::Color, + peniko::color::palette, AaConfig, Scene, }; use vello_tests::{snapshot_test_sync, TestParams}; @@ -14,7 +14,7 @@ fn rounded_rectangle_watertight() { let mut scene = Scene::new(); let rect = RoundedRect::new(60.0, 10.0, 80.0, 30.0, 10.0); let stroke = Stroke::new(2.0); - scene.stroke(&stroke, Affine::IDENTITY, Color::WHITE, None, &rect); + scene.stroke(&stroke, Affine::IDENTITY, palette::css::WHITE, None, &rect); let mut params = TestParams::new("rounded_rectangle_watertight", 70, 30); params.anti_aliasing = AaConfig::Msaa16; snapshot_test_sync(scene, ¶ms) diff --git a/vello_tests/tests/smoke_snapshots.rs b/vello_tests/tests/smoke_snapshots.rs index b27d445e7..9e22fcc39 100644 --- a/vello_tests/tests/smoke_snapshots.rs +++ b/vello_tests/tests/smoke_snapshots.rs @@ -6,7 +6,7 @@ use scenes::SimpleText; use vello::{ kurbo::{Affine, Circle, Rect}, - peniko::{Brush, Color, Fill}, + peniko::{color::palette, Brush, Fill}, Scene, }; use vello_tests::{smoke_snapshot_test_sync, TestParams}; @@ -16,7 +16,7 @@ fn filled_square(use_cpu: bool) { scene.fill( Fill::NonZero, Affine::IDENTITY, - &Brush::Solid(Color::BLUE), + &Brush::Solid(palette::css::BLUE), None, &Rect::from_center_size((10., 10.), (6., 6.)), ); @@ -34,7 +34,7 @@ fn filled_circle(use_cpu: bool) { scene.fill( Fill::NonZero, Affine::IDENTITY, - &Brush::Solid(Color::BLUE), + &Brush::Solid(palette::css::BLUE), None, &Circle::new((10., 10.), 7.), );