1use super::{Hsla, Hsva, PreMulRgba, Rgba, clamp_normal};
2use zng_layout::unit::Factor;
3
4use pastey::*;
5
6pub use zng_view_api::MixBlendMode;
7
8macro_rules! impl_mix {
9 (
10 separable {$(
11 $Mode:ident => |$fg:ident, $bg:ident, $ca0:ident, $ca1:ident| $mix:expr,
12 )+}
13
14 non_separable {$(
15 $NsMode:ident => |[$fgh:ident, $fgs:ident, $fgl:ident], [$bgh:ident, $bgs:ident, $bgl:ident]| $ns_mix:expr,
16 )+}
17 ) => {
18 pub trait MixAdjust {
20 fn mix(self, mode: MixBlendMode, background: Self) -> Self where Self:Sized {
22 match mode {
23 $(MixBlendMode::$Mode => paste!(self.[<mix_ $Mode:lower>](background)),)+
24 $(MixBlendMode::$NsMode => paste!(self.[<mix_ $NsMode:lower>](background)),)+
25 _ => unreachable!()
26 }
27 }
28
29 $(
30 paste! {
31 #[doc = "MixAdjust `self` over `background` using the [`MixBlendMode::" $Mode "`]."]
32 fn [<mix_ $Mode:lower>](self, background: Self) -> Self;
34 }
35 )+
36
37 $(
38 paste! {
39 #[doc = "MixAdjust `self` over `background` using the [`MixBlendMode::`" $NsMode "`]."]
40 fn [<mix_ $NsMode:lower>](self, background: Self) -> Self;
43 }
44 )+
45
46 fn lighten<A: Into<Factor>>(self, amount: A) -> Self;
59
60 fn darken<A: Into<Factor>>(self, amount: A) -> Self;
72
73 fn desaturate<A: Into<Factor>>(self, amount: A) -> Self;
85
86 fn with_lightness<L: Into<Factor>>(self, lightness: L) -> Self;
88 }
89
90 impl PreMulRgba {
91 $(
92 paste! {
93 fn [<mix_ $Mode:lower _impl>](self, background: PreMulRgba) -> PreMulRgba {
94 PreMulRgba {
95 red: {
96 let $fg = self.red;
97 let $bg = background.red;
98 let $ca0 = self.alpha;
99 let $ca1 = background.alpha;
100 $mix
101 },
102 green: {
103 let $fg = self.green;
104 let $bg = background.green;
105 let $ca0 = self.alpha;
106 let $ca1 = background.alpha;
107 $mix
108 },
109 blue: {
110 let $fg = self.blue;
111 let $bg = background.blue;
112 let $ca0 = self.alpha;
113 let $ca1 = background.alpha;
114 $mix
115 },
116 alpha: {
117 let fga = self.alpha;
118 let bga = background.alpha;
119 (fga + bga - fga * bga).max(0.0).min(1.0)
120 },
121 }
122 }
123 }
124 )+
125 }
126
127 impl Hsla {
128 $(
129 paste! {
130 fn [<mix_ $NsMode:lower _impl>](self, background: Hsla) -> Hsla {
131 let $fgh = self.hue;
132 let $fgs = self.saturation;
133 let $fgl = self.lightness;
134
135 let $bgh = background.hue;
136 let $bgs = background.saturation;
137 let $bgl = background.lightness;
138
139 let [h, s, l] = { $ns_mix };
140
141 Hsla {
142 hue: h,
143 saturation: s,
144 lightness: l,
145 alpha: {
146 let fga = self.alpha;
147 let bga = background.alpha;
148 (fga + bga - fga * bga).clamp(0.0, 1.0)
149 }
150 }
151 }
152 }
153 )+
154 }
155
156 impl MixAdjust for PreMulRgba {
157 $(
158 paste! {
159 fn [<mix_ $Mode:lower>](self, background: Self) -> Self {
160 self.[<mix_ $Mode:lower _impl>](background)
161 }
162 }
163 )+
164 $(
165 paste! {
166 fn [<mix_ $NsMode:lower>](self, background: Self) -> Self {
167 Hsla::from(self).[<mix_ $NsMode:lower>](Hsla::from(background)).into()
168 }
169 }
170 )+
171
172 fn lighten<A: Into<Factor>>(self, amount: A) -> Self {
173 Hsla::from(self).lighten(amount.into()).into()
174 }
175
176 fn darken<A: Into<Factor>>(self, amount: A) -> Self {
177 Hsla::from(self).darken(amount.into()).into()
178 }
179
180 fn desaturate<A: Into<Factor>>(self, amount: A) -> Self {
181 Hsla::from(self).desaturate(amount).into()
182 }
183
184 fn with_lightness<L: Into<Factor>>(self, lightness: L) -> Self {
185 Hsla::from(self).with_lightness(lightness).into()
186 }
187 }
188
189 impl MixAdjust for Hsla {
190 $(
191 paste! {
192 fn [<mix_ $Mode:lower>](self, background: Self) -> Self {
193 PreMulRgba::from(self).[<mix_ $Mode:lower>](PreMulRgba::from(background)).into()
194 }
195 }
196 )+
197
198 $(
199 paste! {
200 fn [<mix_ $NsMode:lower>](self, background: Self) -> Self {
201 self.[<mix_ $NsMode:lower _impl>](background)
202 }
203 }
204 )+
205
206 fn lighten<A: Into<Factor>>(self, amount: A) -> Self {
207 let mut lighter = self;
208 lighter.lightness = clamp_normal(lighter.lightness + (lighter.lightness * amount.into().0));
209 lighter
210 }
211
212 fn darken<A: Into<Factor>>(self, amount: A) -> Self {
213 let mut darker = self;
214 darker.lightness = clamp_normal(darker.lightness - (darker.lightness * amount.into().0));
215 darker
216 }
217
218 fn desaturate<A: Into<Factor>>(self, amount: A) -> Self {
219 let mut d = self;
220 d.saturation = clamp_normal(d.saturation - (d.saturation * amount.into().0));
221 d
222 }
223
224 fn with_lightness<L: Into<Factor>>(mut self, lightness: L) -> Self {
225 self.set_lightness(lightness);
226 self
227 }
228 }
229
230 impl MixAdjust for Rgba {
231 $(
232 paste! {
233 fn [<mix_ $Mode:lower>](self, background: Self) -> Self {
234 PreMulRgba::from(self).[<mix_ $Mode:lower>](PreMulRgba::from(background)).into()
235 }
236 }
237 )+
238 $(
239 paste! {
240 fn [<mix_ $NsMode:lower>](self, background: Self) -> Self {
241 Hsla::from(self).[<mix_ $NsMode:lower>](Hsla::from(background)).into()
242 }
243 }
244 )+
245
246 fn lighten<A: Into<Factor>>(self, amount: A) -> Self {
247 Hsla::from(self).lighten(amount.into()).into()
248 }
249
250 fn darken<A: Into<Factor>>(self, amount: A) -> Self {
251 Hsla::from(self).darken(amount.into()).into()
252 }
253
254 fn desaturate<A: Into<Factor>>(self, amount: A) -> Self {
255 Hsla::from(self).desaturate(amount).into()
256 }
257
258 fn with_lightness<L: Into<Factor>>(self, lightness: L) -> Self {
259 Hsla::from(self).with_lightness(lightness).into()
260 }
261 }
262
263 impl MixAdjust for Hsva {
264 $(
265 paste! {
266 fn [<mix_ $Mode:lower>](self, background: Self) -> Self {
267 PreMulRgba::from(self).[<mix_ $Mode:lower>](PreMulRgba::from(background)).into()
268 }
269 }
270 )+
271 $(
272 paste! {
273 fn [<mix_ $NsMode:lower>](self, background: Self) -> Self {
274 Hsla::from(self).[<mix_ $NsMode:lower>](Hsla::from(background)).into()
275 }
276 }
277 )+
278
279 fn lighten<A: Into<Factor>>(self, amount: A) -> Self {
280 Hsla::from(self).lighten(amount.into()).into()
281 }
282
283 fn darken<A: Into<Factor>>(self, amount: A) -> Self {
284 Hsla::from(self).darken(amount.into()).into()
285 }
286
287 fn desaturate<A: Into<Factor>>(self, amount: A) -> Self {
288 Hsla::from(self).desaturate(amount).into()
289 }
290
291 fn with_lightness<L: Into<Factor>>(self, lightness: L) -> Self {
292 Hsla::from(self).with_lightness(lightness).into()
293 }
294 }
295 };
296}
297
298#[rustfmt::skip]impl_mix! {
300 separable {
301 Normal => |fg, bg, fga, _bga| fg + bg * (1.0 - fga),
302
303 Multiply => |fg, bg, fga, bga| fg * bg + fg * (1.0 - bga) + bg * (1.0 - fga),
304
305 Screen => |fg, bg, _fga, _bga| fg + bg - fg * bg,
306
307 Overlay => |fg, bg, fga, bga| if bg * 2.0 <= bga {
308 2.0 * fg * bg + fg * (1.0 - bga) + bg * (1.0 - fga)
309 } else {
310 fg * (1.0 + bga) + bg * (1.0 + fga) - 2.0 * fg * bg - fga * bga
311 },
312
313 Darken => |fg, bg, fga, bga| (fg * bga).min(bg * fga) + fg * (1.0 - bga) + bg * (1.0 - fga),
314
315 Lighten => |fg, bg, fga, bga| (fg * bga).max(bg * fga) + fg * (1.0 - bga) + bg * (1.0 - fga),
316
317 ColorDodge => |fg, bg, fga, bga| if fg == fga {
318 fga * bga + fg * (1.0 - bga) + bg * (1.0 - fga)
319 } else {
320 fga * bga * 1.0_f32.min((bg / bga) * fga / (fga - fg)) + fg * (1.0 - bga) + bg * (1.0 - fga)
321 },
322
323 ColorBurn => |fg, bg, fga, bga| fga * bga * (1.0 - 1.0_f32.min((1.0 - bg / bga) * fga / fg)) + fg * (1.0 - bga) + bg * (1.0 - fga),
324
325 HardLight => |fg, bg, fga, bga| if fg * 2.0 <= fga {
326 2.0 * fg * bg + fg * (1.0 - bga) + bg * (1.0 - fga)
327 } else {
328 fg * (1.0 + bga) + bg * (1.0 + fga) - 2.0 * fg * bg - fga * bga
329 },
330
331 SoftLight => |fg, bg, fga, bga| {
332 let m = bg / bga;
333
334 if fg * 2.0 <= fga {
335 bg * (fga + (2.0 * fg - fga) * (1.0 - m)) + fg * (1.0 - bga) + bg * (1.0 - fga)
336 } else if bg * 4.0 <= bga {
337 let m2 = m * m;
338 let m3 = m2 * m;
339 bga * (2.0 * fg - fga) * (m3 * 16.0 - m2 * 12.0 - m * 3.0) + fg - fg * bga + bg
340 } else {
341 bga * (2.0 * fg - fga) * (m.sqrt() - m) + fg - fg * bga + bg
342 }
343 },
344
345 Difference => |fg, bg, fga, bga| fg + bg - 2.0 * (fg * bga).min(bg * fga),
346
347 Exclusion => |fg, bg, _fga, _bga| fg + bg - 2.0 * fg * bg,
348
349 PlusLighter => |fg, bg, fga, bga| (bg * bga + fg * fga).clamp(0.0, 1.0),
350 }
351
352 non_separable {
353 Hue => |[fgh, _fgs, _fgl], [_bgh, bgs, bgl]| [fgh, bgs, bgl],
354
355 Saturation => |[_fgh, fgs, _fgl], [bgh, _bgs, bgl]| [bgh, fgs, bgl],
356
357 Color => |[fgh, fgs, _fgl], [_bgh, _bgs, bgl]| [fgh, fgs, bgl],
358
359 Luminosity => |[fgh, _fgs, _fbl], [bgh, bgs, _bgl]| [bgh, bgs, fgh],
360 }
361}