Skip to main content

motion_core/drawing/
blend.rs

1//! Blending, fill and mask interpretation.
2
3/// Linear-light layer blending, with explicit compositing space.
4///
5/// Separable modes evaluate straight linear channels with source-over alpha.
6/// Extended RGB is not gamut-clipped. Plus instead adds premultiplied RGB and
7/// clamps the summed alpha to one. Dodge/Burn retain their formula's intrinsic
8/// endpoints and saturation; other modes extend their piecewise formulas without
9/// input clipping. Nonseparable modes use hue/saturation/luminosity replacement.
10#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
11pub enum BlendMode {
12    /// Premultiplied source-over; the default.
13    #[default]
14    SourceOver,
15    /// Unclipped additive RGB with saturated additive alpha.
16    Plus,
17    /// Product of straight source and backdrop channels.
18    Multiply,
19    /// Source plus backdrop minus their product.
20    Screen,
21    /// Minimum of straight source and backdrop channels.
22    Darken,
23    /// Maximum of straight source and backdrop channels.
24    Lighten,
25    /// Absolute difference of straight channels.
26    Difference,
27    /// Source plus backdrop minus twice their product.
28    Exclusion,
29    /// Multiply or screen, selected by the backdrop channel at one half.
30    Overlay,
31    /// Multiply or screen, selected by the source channel at one half.
32    HardLight,
33    /// Piecewise soft lighting, with the low-backdrop cubic and upper square root.
34    SoftLight,
35    /// Backdrop divided by one minus source, saturated above at one.
36    ///
37    /// Nonpositive backdrop returns zero before source at or above one returns one.
38    ColorDodge,
39    /// One minus the saturated ratio of one minus backdrop to source.
40    ///
41    /// Backdrop at or above one returns one before nonpositive source returns zero.
42    ColorBurn,
43    /// Source hue with backdrop saturation and luminosity.
44    Hue,
45    /// Source saturation with backdrop hue and luminosity.
46    Saturation,
47    /// Source hue/saturation with backdrop luminosity.
48    Color,
49    /// Source luminosity with backdrop hue/saturation.
50    Luminosity,
51    /// Internal linear-only additive capture used by sample accumulation.
52    #[doc(hidden)]
53    LinearPlus,
54}
55
56/// Color space used for compositing, independent of stored linear pixels.
57#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
58pub enum BlendSpace {
59    /// Composite straight linear RGB, retaining the default.
60    #[default]
61    LinearRgb,
62    /// Composite encoded sRGB, then return to linear-premultiplied storage.
63    Srgb,
64}
65
66/// How closed contours determine the filled interior.
67///
68/// Recording retains both rules without silently substituting one for
69/// the default NonZero rule.
70///
71/// ```
72/// use motion_core::prelude::*;
73/// use motion_core::{
74///     DisplayList, RasterLimits, ResourcePool, ValidatedDisplayList, MAX_PATH_VERBS,
75///     MAX_STROKE_VERBS, MAX_SVG_PATH_BYTES, sample_offsets,
76/// };
77/// let path = Path2D::from_rect(Rect::new(0.0, 0.0, 10.0, 20.0))?;
78/// let fill = path.fill(Color::WHITE);
79/// let native = DrawContext::default().record(&fill, ValidationLimits::default())?;
80/// assert!(matches!(
81///     native[0],
82///     DrawCommand::Fill {
83///         rule: FillRule::NonZero,
84///         ..
85///     }
86/// ));
87/// let even = DrawContext::default().record(
88///     &fill.fill_rule(FillRule::EvenOdd),
89///     ValidationLimits::default(),
90/// )?;
91/// assert!(matches!(
92///     even[0],
93///     DrawCommand::Fill {
94///         rule: FillRule::EvenOdd,
95///         ..
96///     }
97/// ));
98/// # Ok::<(), motion_core::ValidationError>(())
99/// ```
100#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
101pub enum FillRule {
102    /// Nonzero signed winding count; the default.
103    #[default]
104    NonZero,
105    /// Odd crossing count.
106    EvenOdd,
107}
108
109/// How linear-premultiplied content becomes a layer's mask coverage.
110///
111/// Coverage is computed in the compositing color space and clamped
112/// to 0..=1 before optional inversion, without altering working colors.
113/// Alpha-zero pixels have zero ordinary coverage, even with hidden extended
114/// RGB; inverted modes therefore retain source content at those pixels.
115#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
116pub enum MaskMode {
117    /// Use only the completed mask's alpha.
118    #[default]
119    Alpha,
120    /// One minus the completed mask's alpha, including transparent artwork.
121    InvertedAlpha,
122    /// Use Rec.709 luminance of linear premultiplied RGB, already alpha-weighted.
123    Luminance,
124    /// One minus clamped linear-premultiplied luminance; alpha is not inverted first.
125    InvertedLuminance,
126}