1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
use crate::pipeline;

/// A blending mode.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)]
pub enum BlendMode {
    /// Replaces destination with zero: fully transparent.
    Clear,
    /// Replaces destination.
    Source,
    /// Preserves destination.
    Destination,
    /// Source over destination.
    SourceOver,
    /// Destination over source.
    DestinationOver,
    /// Source trimmed inside destination.
    SourceIn,
    /// Destination trimmed by source.
    DestinationIn,
    /// Source trimmed outside destination.
    SourceOut,
    /// Destination trimmed outside source.
    DestinationOut,
    /// Source inside destination blended with destination.
    SourceAtop,
    /// Destination inside source blended with source.
    DestinationAtop,
    /// Each of source and destination trimmed outside the other.
    Xor,
    /// Sum of colors.
    Plus,
    /// Product of premultiplied colors; darkens destination.
    Modulate,
    /// Multiply inverse of pixels, inverting result; brightens destination.
    Screen,
    /// Multiply or screen, depending on destination.
    Overlay,
    /// Darker of source and destination.
    Darken,
    /// Lighter of source and destination.
    Lighten,
    /// Brighten destination to reflect source.
    ColorDodge,
    /// Darken destination to reflect source.
    ColorBurn,
    /// Multiply or screen, depending on source.
    HardLight,
    /// Lighten or darken, depending on source.
    SoftLight,
    /// Subtract darker from lighter with higher contrast.
    Difference,
    /// Subtract darker from lighter with lower contrast.
    Exclusion,
    /// Multiply source with destination, darkening image.
    Multiply,
    /// Hue of source with saturation and luminosity of destination.
    Hue,
    /// Saturation of source with hue and luminosity of destination.
    Saturation,
    /// Hue and saturation of source with luminosity of destination.
    Color,
    /// Luminosity of source with hue and saturation of destination.
    Luminosity,
}

impl Default for BlendMode {
    fn default() -> Self {
        BlendMode::SourceOver
    }
}

impl BlendMode {
    pub(crate) fn should_pre_scale_coverage(self) -> bool {
        // The most important things we do here are:
        //   1) never pre-scale with rgb coverage if the blend mode involves a source-alpha term;
        //   2) always pre-scale Plus.
        //
        // When we pre-scale with rgb coverage, we scale each of source r,g,b, with a distinct value,
        // and source alpha with one of those three values. This process destructively updates the
        // source-alpha term, so we can't evaluate blend modes that need its original value.
        //
        // Plus always requires pre-scaling as a specific quirk of its implementation in
        // RasterPipeline. This lets us put the clamp inside the blend mode itself rather
        // than as a separate stage that'd come after the lerp.
        //
        // This function is a finer-grained breakdown of SkBlendMode_SupportsCoverageAsAlpha().
        matches!(
            self,
            BlendMode::Destination |        // d              --> no sa term, ok!
            BlendMode::DestinationOver |    // d + s*inv(da)  --> no sa term, ok!
            BlendMode::Plus |               // clamp(s+d)     --> no sa term, ok!
            BlendMode::DestinationOut |     // d * inv(sa)
            BlendMode::SourceAtop |         // s*da + d*inv(sa)
            BlendMode::SourceOver |         // s + d*inv(sa)
            BlendMode::Xor // s*inv(da) + d*inv(sa)
        )
    }

    pub(crate) fn to_stage(self) -> Option<pipeline::Stage> {
        match self {
            BlendMode::Clear => Some(pipeline::Stage::Clear),
            BlendMode::Source => None, // This stage is a no-op.
            BlendMode::Destination => Some(pipeline::Stage::MoveDestinationToSource),
            BlendMode::SourceOver => Some(pipeline::Stage::SourceOver),
            BlendMode::DestinationOver => Some(pipeline::Stage::DestinationOver),
            BlendMode::SourceIn => Some(pipeline::Stage::SourceIn),
            BlendMode::DestinationIn => Some(pipeline::Stage::DestinationIn),
            BlendMode::SourceOut => Some(pipeline::Stage::SourceOut),
            BlendMode::DestinationOut => Some(pipeline::Stage::DestinationOut),
            BlendMode::SourceAtop => Some(pipeline::Stage::SourceAtop),
            BlendMode::DestinationAtop => Some(pipeline::Stage::DestinationAtop),
            BlendMode::Xor => Some(pipeline::Stage::Xor),
            BlendMode::Plus => Some(pipeline::Stage::Plus),
            BlendMode::Modulate => Some(pipeline::Stage::Modulate),
            BlendMode::Screen => Some(pipeline::Stage::Screen),
            BlendMode::Overlay => Some(pipeline::Stage::Overlay),
            BlendMode::Darken => Some(pipeline::Stage::Darken),
            BlendMode::Lighten => Some(pipeline::Stage::Lighten),
            BlendMode::ColorDodge => Some(pipeline::Stage::ColorDodge),
            BlendMode::ColorBurn => Some(pipeline::Stage::ColorBurn),
            BlendMode::HardLight => Some(pipeline::Stage::HardLight),
            BlendMode::SoftLight => Some(pipeline::Stage::SoftLight),
            BlendMode::Difference => Some(pipeline::Stage::Difference),
            BlendMode::Exclusion => Some(pipeline::Stage::Exclusion),
            BlendMode::Multiply => Some(pipeline::Stage::Multiply),
            BlendMode::Hue => Some(pipeline::Stage::Hue),
            BlendMode::Saturation => Some(pipeline::Stage::Saturation),
            BlendMode::Color => Some(pipeline::Stage::Color),
            BlendMode::Luminosity => Some(pipeline::Stage::Luminosity),
        }
    }
}