| // Copyright 2022 the Vello Authors | |
| // SPDX-License-Identifier: Apache-2.0 OR MIT | |
| use bytemuck::{Pod, Zeroable}; | |
| use peniko::{ | |
| BlendMode, | |
| color::{AlphaColor, ColorSpace, DynamicColor, OpaqueColor, PremulColor, Srgb}, | |
| }; | |
| use super::Monoid; | |
| /// Draw tag representation. | |
| #[derive(Copy, Clone, PartialEq, Eq, Pod, Zeroable)] | |
| #[repr(C)] | |
| pub struct DrawTag(pub u32); | |
| impl DrawTag { | |
| /// No operation. | |
| pub const NOP: Self = Self(0); | |
| /// Color fill. | |
| pub const COLOR: Self = Self(0x44); | |
| /// Linear gradient fill. | |
| pub const LINEAR_GRADIENT: Self = Self(0x114); | |
| /// Radial gradient fill. | |
| pub const RADIAL_GRADIENT: Self = Self(0x29c); | |
| /// Sweep gradient fill. | |
| pub const SWEEP_GRADIENT: Self = Self(0x254); | |
| /// Image fill. | |
| pub const IMAGE: Self = Self(0x28C); // info: 10, scene: 3 | |
| /// Blurred rounded rectangle. | |
| pub const BLUR_RECT: Self = Self(0x2d4); // info: 11, scene: 5 (DrawBlurRoundedRect) | |
| /// Begin layer/clip. | |
| pub const BEGIN_CLIP: Self = Self(0x49); | |
| /// End layer/clip. | |
| pub const END_CLIP: Self = Self(0x21); | |
| } | |
| impl DrawTag { | |
| /// Returns the size of the info buffer (in u32s) used by this tag. | |
| pub const fn info_size(self) -> u32 { | |
| (self.0 >> 6) & 0xf | |
| } | |
| } | |
| /// The first word of each draw info stream entry contains the flags. | |
| /// | |
| /// This is not part of the draw object stream but gets used after the draw | |
| /// objects get reduced on the GPU. `0` represents a non-zero fill. | |
| /// `1` represents an even-odd fill. | |
| pub const DRAW_INFO_FLAGS_FILL_RULE_BIT: u32 = 1; | |
| /// Draw object bounding box. | |
| #[derive(Copy, Clone, Pod, Zeroable, Debug, Default)] | |
| #[repr(C)] | |
| pub struct DrawBbox { | |
| pub bbox: [f32; 4], | |
| } | |
| /// Draw data for a solid color. | |
| #[derive(Clone, Copy, Debug, Default, Zeroable, Pod)] | |
| #[repr(C)] | |
| pub struct DrawColor { | |
| /// Packed little-endian RGBA premultiplied color with the red component in the low byte, i.e., | |
| /// with `r` the least significant byte and `a` the most significant. | |
| pub rgba: u32, | |
| } | |
| impl<CS: ColorSpace> From<AlphaColor<CS>> for DrawColor { | |
| fn from(color: AlphaColor<CS>) -> Self { | |
| Self { | |
| rgba: color.convert::<Srgb>().premultiply().to_rgba8().to_u32(), | |
| } | |
| } | |
| } | |
| impl From<DynamicColor> for DrawColor { | |
| fn from(color: DynamicColor) -> Self { | |
| Self { | |
| rgba: color | |
| .to_alpha_color::<Srgb>() | |
| .premultiply() | |
| .to_rgba8() | |
| .to_u32(), | |
| } | |
| } | |
| } | |
| impl<CS: ColorSpace> From<OpaqueColor<CS>> for DrawColor { | |
| fn from(color: OpaqueColor<CS>) -> Self { | |
| Self { | |
| rgba: color | |
| .convert::<Srgb>() | |
| .with_alpha(1.) | |
| .premultiply() | |
| .to_rgba8() | |
| .to_u32(), | |
| } | |
| } | |
| } | |
| impl<CS: ColorSpace> From<PremulColor<CS>> for DrawColor { | |
| fn from(color: PremulColor<CS>) -> Self { | |
| Self { | |
| rgba: color.convert::<Srgb>().to_rgba8().to_u32(), | |
| } | |
| } | |
| } | |
| /// Draw data for a linear gradient. | |
| #[derive(Clone, Copy, Debug, Default, Zeroable, Pod)] | |
| #[repr(C)] | |
| pub struct DrawLinearGradient { | |
| /// Ramp index. | |
| pub index: u32, | |
| /// Start point. | |
| pub p0: [f32; 2], | |
| /// End point. | |
| pub p1: [f32; 2], | |
| } | |
| /// Draw data for a radial gradient. | |
| #[derive(Clone, Copy, Debug, Default, Zeroable, Pod)] | |
| #[repr(C)] | |
| pub struct DrawRadialGradient { | |
| /// Ramp index. | |
| pub index: u32, | |
| /// Start point. | |
| pub p0: [f32; 2], | |
| /// End point. | |
| pub p1: [f32; 2], | |
| /// Start radius. | |
| pub r0: f32, | |
| /// End radius. | |
| pub r1: f32, | |
| } | |
| /// Draw data for a sweep gradient. | |
| #[derive(Clone, Copy, Debug, Default, Zeroable, Pod)] | |
| #[repr(C)] | |
| pub struct DrawSweepGradient { | |
| /// Ramp index. | |
| pub index: u32, | |
| /// Center point. | |
| pub p0: [f32; 2], | |
| /// Normalized start angle. | |
| pub t0: f32, | |
| /// Normalized end angle. | |
| pub t1: f32, | |
| } | |
| /// Draw data for an image. | |
| #[derive(Clone, Copy, Debug, Default, Zeroable, Pod)] | |
| #[repr(C)] | |
| pub struct DrawImage { | |
| /// Packed atlas coordinates. | |
| pub xy: u32, | |
| /// Packed image dimensions. | |
| pub width_height: u32, | |
| /// Packed quality, extend mode and 8-bit alpha (bits `qqxxyyaaaaaaaa`, | |
| /// 18 unused prefix bits). | |
| pub sample_alpha: u32, | |
| } | |
| /// Draw data for a blurred rounded rectangle. | |
| #[derive(Clone, Copy, Debug, Default, Zeroable, Pod)] | |
| #[repr(C)] | |
| pub struct DrawBlurRoundedRect { | |
| /// Solid color brush. | |
| pub color: DrawColor, | |
| /// Rectangle width. | |
| pub width: f32, | |
| /// Rectangle height. | |
| pub height: f32, | |
| /// Rectangle corner radius. | |
| pub radius: f32, | |
| /// Standard deviation of gaussian filter. | |
| pub std_dev: f32, | |
| } | |
| /// Draw data for a clip or layer. | |
| #[derive(Clone, Copy, Debug, Default, Zeroable, Pod)] | |
| #[repr(C)] | |
| pub struct DrawBeginClip { | |
| /// Blend mode. | |
| pub blend_mode: u32, | |
| /// Group alpha. | |
| pub alpha: f32, | |
| } | |
| impl DrawBeginClip { | |
| /// The `blend_mode` used to indicate that a layer should be | |
| /// treated as a luminance mask. | |
| /// | |
| /// The least significant 16 bits are reserved for Mix + Compose | |
| /// combinations. | |
| pub const LUMINANCE_MASK_BLEND_MODE: u32 = 0x10000; | |
| /// The `blend_mode` used to indicate that a layer should be | |
| /// treated as a clip. | |
| /// | |
| /// This is equivalent to `Compose::SrcOver` with a `Mix` of 128, | |
| /// for legacy reasons. | |
| /// We expect this to change in the future. | |
| pub const CLIP_BLEND_MODE: u32 = 0x8003; | |
| /// Creates new clip draw data for a Porter-Duff blend mode. | |
| pub fn new(blend_mode: BlendMode, alpha: f32) -> Self { | |
| Self { | |
| blend_mode: ((blend_mode.mix as u32) << 8) | blend_mode.compose as u32, | |
| alpha, | |
| } | |
| } | |
| /// Creates a new clip draw data for a luminance mask. | |
| pub fn luminance_mask(alpha: f32) -> Self { | |
| Self { | |
| blend_mode: Self::LUMINANCE_MASK_BLEND_MODE, | |
| alpha, | |
| } | |
| } | |
| /// Creates the clip draw data for a clip-only layer. | |
| pub fn clip() -> Self { | |
| Self { | |
| blend_mode: Self::CLIP_BLEND_MODE, | |
| alpha: 1.0, | |
| } | |
| } | |
| } | |
| /// Monoid for the draw tag stream. | |
| #[derive(Copy, Clone, PartialEq, Eq, Pod, Zeroable, Default, Debug)] | |
| #[repr(C)] | |
| pub struct DrawMonoid { | |
| // The number of paths preceding this draw object. | |
| pub path_ix: u32, | |
| // The number of clip operations preceding this draw object. | |
| pub clip_ix: u32, | |
| // The offset of the encoded draw object in the scene (u32s). | |
| pub scene_offset: u32, | |
| // The offset of the associated info. | |
| pub info_offset: u32, | |
| } | |
| impl Monoid for DrawMonoid { | |
| type SourceValue = DrawTag; | |
| fn new(tag: DrawTag) -> Self { | |
| Self { | |
| path_ix: (tag != DrawTag::NOP) as u32, | |
| clip_ix: tag.0 & 1, | |
| scene_offset: (tag.0 >> 2) & 0x7, | |
| info_offset: (tag.0 >> 6) & 0xf, | |
| } | |
| } | |
| fn combine(&self, other: &Self) -> Self { | |
| Self { | |
| path_ix: self.path_ix + other.path_ix, | |
| clip_ix: self.clip_ix + other.clip_ix, | |
| scene_offset: self.scene_offset + other.scene_offset, | |
| info_offset: self.info_offset + other.info_offset, | |
| } | |
| } | |
| } | |
| #[cfg(test)] | |
| mod tests { | |
| use peniko::Color; | |
| use super::DrawColor; | |
| #[test] | |
| fn draw_color_endianness() { | |
| // `DrawColor` should be packed little-endian with red the least significant byte. | |
| // | |
| // If this changes intentionally, the `DrawColor` docs also need updating. | |
| let c = Color::from_rgba8(0x00, 0xca, 0xfe, 0xff); | |
| assert_eq!( | |
| bytemuck::bytes_of(&DrawColor::from(c)), | |
| [0x00, 0xca, 0xfe, 0xff] | |
| ); | |
| } | |
| #[test] | |
| fn draw_color_premultiplied() { | |
| // If this changes intentionally, the `DrawColor` docs also need updating. | |
| let c = Color::from_rgba8(0x00, 0xca, 0xfe, 0x00); | |
| assert_eq!(DrawColor::from(c).rgba, 0); | |
| } | |
| } |