Skip to main content

vello_encoding/
draw.rs

1// Copyright 2022 the Vello Authors
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3
4use bytemuck::{Pod, Zeroable};
5use peniko::{
6    BlendMode,
7    color::{AlphaColor, ColorSpace, DynamicColor, OpaqueColor, PremulColor, Srgb},
8};
9
10use super::Monoid;
11
12/// Draw tag representation.
13#[derive(Copy, Clone, PartialEq, Eq, Pod, Zeroable)]
14#[repr(C)]
15pub struct DrawTag(pub u32);
16
17impl DrawTag {
18    /// No operation.
19    pub const NOP: Self = Self(0);
20
21    /// Color fill.
22    pub const COLOR: Self = Self(0x44);
23
24    /// Linear gradient fill.
25    pub const LINEAR_GRADIENT: Self = Self(0x114);
26
27    /// Radial gradient fill.
28    pub const RADIAL_GRADIENT: Self = Self(0x29c);
29
30    /// Sweep gradient fill.
31    pub const SWEEP_GRADIENT: Self = Self(0x254);
32
33    /// Image fill.
34    pub const IMAGE: Self = Self(0x28C); // info: 10, scene: 3
35
36    /// Blurred rounded rectangle.
37    pub const BLUR_RECT: Self = Self(0x2d4); // info: 11, scene: 5 (DrawBlurRoundedRect)
38
39    /// Begin layer/clip.
40    pub const BEGIN_CLIP: Self = Self(0x49);
41
42    /// End layer/clip.
43    pub const END_CLIP: Self = Self(0x21);
44}
45
46impl DrawTag {
47    /// Returns the size of the info buffer (in u32s) used by this tag.
48    pub const fn info_size(self) -> u32 {
49        (self.0 >> 6) & 0xf
50    }
51}
52
53/// The first word of each draw info stream entry contains the flags.
54///
55/// This is not part of the draw object stream but gets used after the draw
56/// objects get reduced on the GPU. `0` represents a non-zero fill.
57/// `1` represents an even-odd fill.
58pub const DRAW_INFO_FLAGS_FILL_RULE_BIT: u32 = 1;
59
60/// Draw object bounding box.
61#[derive(Copy, Clone, Pod, Zeroable, Debug, Default)]
62#[repr(C)]
63pub struct DrawBbox {
64    pub bbox: [f32; 4],
65}
66
67/// Draw data for a solid color.
68#[derive(Clone, Copy, Debug, Default, Zeroable, Pod)]
69#[repr(C)]
70pub struct DrawColor {
71    /// Packed little-endian RGBA premultiplied color with the red component in the low byte, i.e.,
72    /// with `r` the least significant byte and `a` the most significant.
73    pub rgba: u32,
74}
75
76impl<CS: ColorSpace> From<AlphaColor<CS>> for DrawColor {
77    fn from(color: AlphaColor<CS>) -> Self {
78        Self {
79            rgba: color.convert::<Srgb>().premultiply().to_rgba8().to_u32(),
80        }
81    }
82}
83
84impl From<DynamicColor> for DrawColor {
85    fn from(color: DynamicColor) -> Self {
86        Self {
87            rgba: color
88                .to_alpha_color::<Srgb>()
89                .premultiply()
90                .to_rgba8()
91                .to_u32(),
92        }
93    }
94}
95
96impl<CS: ColorSpace> From<OpaqueColor<CS>> for DrawColor {
97    fn from(color: OpaqueColor<CS>) -> Self {
98        Self {
99            rgba: color
100                .convert::<Srgb>()
101                .with_alpha(1.)
102                .premultiply()
103                .to_rgba8()
104                .to_u32(),
105        }
106    }
107}
108
109impl<CS: ColorSpace> From<PremulColor<CS>> for DrawColor {
110    fn from(color: PremulColor<CS>) -> Self {
111        Self {
112            rgba: color.convert::<Srgb>().to_rgba8().to_u32(),
113        }
114    }
115}
116
117/// Draw data for a linear gradient.
118#[derive(Clone, Copy, Debug, Default, Zeroable, Pod)]
119#[repr(C)]
120pub struct DrawLinearGradient {
121    /// Ramp index.
122    pub index: u32,
123    /// Start point.
124    pub p0: [f32; 2],
125    /// End point.
126    pub p1: [f32; 2],
127}
128
129/// Draw data for a radial gradient.
130#[derive(Clone, Copy, Debug, Default, Zeroable, Pod)]
131#[repr(C)]
132pub struct DrawRadialGradient {
133    /// Ramp index.
134    pub index: u32,
135    /// Start point.
136    pub p0: [f32; 2],
137    /// End point.
138    pub p1: [f32; 2],
139    /// Start radius.
140    pub r0: f32,
141    /// End radius.
142    pub r1: f32,
143}
144
145/// Draw data for a sweep gradient.
146#[derive(Clone, Copy, Debug, Default, Zeroable, Pod)]
147#[repr(C)]
148pub struct DrawSweepGradient {
149    /// Ramp index.
150    pub index: u32,
151    /// Center point.
152    pub p0: [f32; 2],
153    /// Normalized start angle.
154    pub t0: f32,
155    /// Normalized end angle.
156    pub t1: f32,
157}
158
159/// Draw data for an image.
160#[derive(Clone, Copy, Debug, Default, Zeroable, Pod)]
161#[repr(C)]
162pub struct DrawImage {
163    /// Packed atlas coordinates.
164    pub xy: u32,
165    /// Packed image dimensions.
166    pub width_height: u32,
167    /// Packed quality, extend mode and 8-bit alpha (bits `qqxxyyaaaaaaaa`,
168    /// 18 unused prefix bits).
169    pub sample_alpha: u32,
170}
171
172/// Draw data for a blurred rounded rectangle.
173#[derive(Clone, Copy, Debug, Default, Zeroable, Pod)]
174#[repr(C)]
175pub struct DrawBlurRoundedRect {
176    /// Solid color brush.
177    pub color: DrawColor,
178    /// Rectangle width.
179    pub width: f32,
180    /// Rectangle height.
181    pub height: f32,
182    /// Rectangle corner radius.
183    pub radius: f32,
184    /// Standard deviation of gaussian filter.
185    pub std_dev: f32,
186}
187
188/// Draw data for a clip or layer.
189#[derive(Clone, Copy, Debug, Default, Zeroable, Pod)]
190#[repr(C)]
191pub struct DrawBeginClip {
192    /// Blend mode.
193    pub blend_mode: u32,
194    /// Group alpha.
195    pub alpha: f32,
196}
197
198impl DrawBeginClip {
199    /// The `blend_mode` used to indicate that a layer should be
200    /// treated as a luminance mask.
201    ///
202    /// The least significant 16 bits are reserved for Mix + Compose
203    /// combinations.
204    pub const LUMINANCE_MASK_BLEND_MODE: u32 = 0x10000;
205    /// The `blend_mode` used to indicate that a layer should be
206    /// treated as a clip.
207    ///
208    /// This is equivalent to `Compose::SrcOver` with a `Mix` of 128,
209    /// for legacy reasons.
210    /// We expect this to change in the future.
211    pub const CLIP_BLEND_MODE: u32 = 0x8003;
212
213    /// Creates new clip draw data for a Porter-Duff blend mode.
214    pub fn new(blend_mode: BlendMode, alpha: f32) -> Self {
215        Self {
216            blend_mode: ((blend_mode.mix as u32) << 8) | blend_mode.compose as u32,
217            alpha,
218        }
219    }
220
221    /// Creates a new clip draw data for a luminance mask.
222    pub fn luminance_mask(alpha: f32) -> Self {
223        Self {
224            blend_mode: Self::LUMINANCE_MASK_BLEND_MODE,
225            alpha,
226        }
227    }
228
229    /// Creates the clip draw data for a clip-only layer.
230    pub fn clip() -> Self {
231        Self {
232            blend_mode: Self::CLIP_BLEND_MODE,
233            alpha: 1.0,
234        }
235    }
236}
237
238/// Monoid for the draw tag stream.
239#[derive(Copy, Clone, PartialEq, Eq, Pod, Zeroable, Default, Debug)]
240#[repr(C)]
241pub struct DrawMonoid {
242    // The number of paths preceding this draw object.
243    pub path_ix: u32,
244    // The number of clip operations preceding this draw object.
245    pub clip_ix: u32,
246    // The offset of the encoded draw object in the scene (u32s).
247    pub scene_offset: u32,
248    // The offset of the associated info.
249    pub info_offset: u32,
250}
251
252impl Monoid for DrawMonoid {
253    type SourceValue = DrawTag;
254
255    fn new(tag: DrawTag) -> Self {
256        Self {
257            path_ix: (tag != DrawTag::NOP) as u32,
258            clip_ix: tag.0 & 1,
259            scene_offset: (tag.0 >> 2) & 0x7,
260            info_offset: (tag.0 >> 6) & 0xf,
261        }
262    }
263
264    fn combine(&self, other: &Self) -> Self {
265        Self {
266            path_ix: self.path_ix + other.path_ix,
267            clip_ix: self.clip_ix + other.clip_ix,
268            scene_offset: self.scene_offset + other.scene_offset,
269            info_offset: self.info_offset + other.info_offset,
270        }
271    }
272}
273
274#[cfg(test)]
275mod tests {
276    use peniko::Color;
277
278    use super::DrawColor;
279
280    #[test]
281    fn draw_color_endianness() {
282        // `DrawColor` should be packed little-endian with red the least significant byte.
283        //
284        // If this changes intentionally, the `DrawColor` docs also need updating.
285        let c = Color::from_rgba8(0x00, 0xca, 0xfe, 0xff);
286        assert_eq!(
287            bytemuck::bytes_of(&DrawColor::from(c)),
288            [0x00, 0xca, 0xfe, 0xff]
289        );
290    }
291
292    #[test]
293    fn draw_color_premultiplied() {
294        // If this changes intentionally, the `DrawColor` docs also need updating.
295        let c = Color::from_rgba8(0x00, 0xca, 0xfe, 0x00);
296        assert_eq!(DrawColor::from(c).rgba, 0);
297    }
298}