1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.graphics;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 
22 public enum BlendMode {
23 
24     /**
25      * {@usesMathJax}
26      *
27      * <p>
28      *  <img src="{@docRoot}reference/android/images/graphics/blendmode_CLEAR.png" />
29      *  <figcaption>Destination pixels covered by the source are cleared to 0.</figcaption>
30      * </p>
31      * <p>\(\alpha_{out} = 0\)</p>
32      * <p>\(C_{out} = 0\)</p>
33      */
34     CLEAR(0),
35 
36     /**
37      * {@usesMathJax}
38      *
39      * <p>
40      *     <img src="{@docRoot}reference/android/images/graphics/blendmode_SRC.png" />
41      *     <figcaption>The source pixels replace the destination pixels.</figcaption>
42      * </p>
43      * <p>\(\alpha_{out} = \alpha_{src}\)</p>
44      * <p>\(C_{out} = C_{src}\)</p>
45      */
46     SRC(1),
47 
48     /**
49      * {@usesMathJax}
50      *
51      * <p>
52      *     <img src="{@docRoot}reference/android/images/graphics/blendmode_DST.png" />
53      *     <figcaption>The source pixels are discarded, leaving the destination intact.</figcaption>
54      * </p>
55      * <p>\(\alpha_{out} = \alpha_{dst}\)</p>
56      * <p>\(C_{out} = C_{dst}\)</p>
57      */
58     DST(2),
59 
60     /**
61      * {@usesMathJax}
62      *
63      * <p>
64      *     <img src="{@docRoot}reference/android/images/graphics/blendmode_SRC_OVER.png" />
65      *     <figcaption>The source pixels are drawn over the destination pixels.</figcaption>
66      * </p>
67      * <p>\(\alpha_{out} = \alpha_{src} + (1 - \alpha_{src}) * \alpha_{dst}\)</p>
68      * <p>\(C_{out} = C_{src} + (1 - \alpha_{src}) * C_{dst}\)</p>
69      */
70     SRC_OVER(3),
71 
72     /**
73      * {@usesMathJax}
74      *
75      * <p>
76      *     <img src="{@docRoot}reference/android/images/graphics/blendmode_DST_OVER.png" />
77      *     <figcaption>The source pixels are drawn behind the destination pixels.</figcaption>
78      * </p>
79      * <p>\(\alpha_{out} = \alpha_{dst} + (1 - \alpha_{dst}) * \alpha_{src}\)</p>
80      * <p>\(C_{out} = C_{dst} + (1 - \alpha_{dst}) * C_{src}\)</p>
81      */
82     DST_OVER(4),
83 
84     /**
85      * {@usesMathJax}
86      *
87      * <p>
88      *     <img src="{@docRoot}reference/android/images/graphics/blendmode_SRC_IN.png" />
89      *     <figcaption>Keeps the source pixels that cover the destination pixels,
90      *     discards the remaining source and destination pixels.</figcaption>
91      * </p>
92      * <p>\(\alpha_{out} = \alpha_{src} * \alpha_{dst}\)</p>
93      * <p>\(C_{out} = C_{src} * \alpha_{dst}\)</p>
94      */
95     SRC_IN(5),
96 
97     /**
98      * {@usesMathJax}
99      *
100      * <p>
101      *     <img src="{@docRoot}reference/android/images/graphics/blendmode_DST_IN.png" />
102      *     <figcaption>Keeps the destination pixels that cover source pixels,
103      *     discards the remaining source and destination pixels.</figcaption>
104      * </p>
105      * <p>\(\alpha_{out} = \alpha_{src} * \alpha_{dst}\)</p>
106      * <p>\(C_{out} = C_{dst} * \alpha_{src}\)</p>
107      */
108     DST_IN(6),
109 
110     /**
111      * {@usesMathJax}
112      *
113      * <p>
114      *     <img src="{@docRoot}reference/android/images/graphics/blendmode_SRC_OUT.png" />
115      *     <figcaption>Keeps the source pixels that do not cover destination pixels.
116      *     Discards source pixels that cover destination pixels. Discards all
117      *     destination pixels.</figcaption>
118      * </p>
119      * <p>\(\alpha_{out} = (1 - \alpha_{dst}) * \alpha_{src}\)</p>
120      * <p>\(C_{out} = (1 - \alpha_{dst}) * C_{src}\)</p>
121      */
122     SRC_OUT(7),
123 
124     /**
125      * {@usesMathJax}
126      *
127      * <p>
128      *     <img src="{@docRoot}reference/android/images/graphics/blendmode_DST_OUT.png" />
129      *     <figcaption>Keeps the destination pixels that are not covered by source pixels.
130      *     Discards destination pixels that are covered by source pixels. Discards all
131      *     source pixels.</figcaption>
132      * </p>
133      * <p>\(\alpha_{out} = (1 - \alpha_{src}) * \alpha_{dst}\)</p>
134      * <p>\(C_{out} = (1 - \alpha_{src}) * C_{dst}\)</p>
135      */
136     DST_OUT(8),
137 
138     /**
139      * {@usesMathJax}
140      *
141      * <p>
142      *     <img src="{@docRoot}reference/android/images/graphics/blendmode_SRC_ATOP.png" />
143      *     <figcaption>Discards the source pixels that do not cover destination pixels.
144      *     Draws remaining source pixels over destination pixels.</figcaption>
145      * </p>
146      * <p>\(\alpha_{out} = \alpha_{dst}\)</p>
147      * <p>\(C_{out} = \alpha_{dst} * C_{src} + (1 - \alpha_{src}) * C_{dst}\)</p>
148      */
149     SRC_ATOP(9),
150 
151     /**
152      * {@usesMathJax}
153      *
154      * <p>
155      *     <img src="{@docRoot}reference/android/images/graphics/blendmode_DST_ATOP.png" />
156      *     <figcaption>Discards the destination pixels that are not covered by source pixels.
157      *     Draws remaining destination pixels over source pixels.</figcaption>
158      * </p>
159      * <p>\(\alpha_{out} = \alpha_{src}\)</p>
160      * <p>\(C_{out} = \alpha_{src} * C_{dst} + (1 - \alpha_{dst}) * C_{src}\)</p>
161      */
162     DST_ATOP(10),
163 
164     /**
165      * {@usesMathJax}
166      *
167      * <p>
168      *     <img src="{@docRoot}reference/android/images/graphics/blendmode_XOR.png" />
169      *     <figcaption>Discards the source and destination pixels where source pixels
170      *     cover destination pixels. Draws remaining source pixels.</figcaption>
171      * </p>
172      * <p>
173      *     \(\alpha_{out} = (1 - \alpha_{dst}) * \alpha_{src} + (1 - \alpha_{src}) * \alpha_{dst}\)
174      * </p>
175      * <p>\(C_{out} = (1 - \alpha_{dst}) * C_{src} + (1 - \alpha_{src}) * C_{dst}\)</p>
176      */
177     XOR(11),
178 
179     /**
180      * {@usesMathJax}
181      *
182      * <p>
183      *     <img src="{@docRoot}reference/android/images/graphics/blendmode_PLUS.png" />
184      *     <figcaption>Adds the source pixels to the destination pixels and saturates
185      *     the result.</figcaption>
186      * </p>
187      * <p>\(\alpha_{out} = max(0, min(\alpha_{src} + \alpha_{dst}, 1))\)</p>
188      * <p>\(C_{out} = max(0, min(C_{src} + C_{dst}, 1))\)</p>
189      */
190     PLUS(12),
191 
192     /**
193      * {@usesMathJax}
194      *
195      * <p>
196      *     <img src="{@docRoot}reference/android/images/graphics/blendmode_MODULATE.png" />
197      *     <figcaption>Multiplies the source and destination pixels.</figcaption>
198      * </p>
199      * <p>\(\alpha_{out} = \alpha_{src} * \alpha_{dst}\)</p>
200      * <p>\(C_{out} = C_{src} * C_{dst}\)</p>
201      *
202      */
203     MODULATE(13),
204 
205     /**
206      * {@usesMathJax}
207      *
208      * <p>
209      *     <img src="{@docRoot}reference/android/images/graphics/blendmode_SCREEN.png" />
210      *     <figcaption>
211      *         Adds the source and destination pixels, then subtracts the
212      *         source pixels multiplied by the destination.
213      *     </figcaption>
214      * </p>
215      * <p>\(\alpha_{out} = \alpha_{src} + \alpha_{dst} - \alpha_{src} * \alpha_{dst}\)</p>
216      * <p>\(C_{out} = C_{src} + C_{dst} - C_{src} * C_{dst}\)</p>
217      */
218     SCREEN(14),
219 
220     /**
221      * {@usesMathJax}
222      *
223      * <p>
224      *     <img src="{@docRoot}reference/android/images/graphics/blendmode_OVERLAY.png" />
225      *     <figcaption>
226      *         Multiplies or screens the source and destination depending on the
227      *         destination color.
228      *     </figcaption>
229      * </p>
230      * <p>\(\alpha_{out} = \alpha_{src} + \alpha_{dst} - \alpha_{src} * \alpha_{dst}\)</p>
231      * <p>\(\begin{equation}
232      * C_{out} = \begin{cases} 2 * C_{src} * C_{dst} & 2 * C_{dst} \lt \alpha_{dst} \\
233      * \alpha_{src} * \alpha_{dst} - 2 (\alpha_{dst} - C_{src}) (\alpha_{src} - C_{dst}) &
234      * otherwise \end{cases}
235      * \end{equation}\)</p>
236      */
237     OVERLAY(15),
238 
239     /**
240      * {@usesMathJax}
241      *
242      * <p>
243      *     <img src="{@docRoot}reference/android/images/graphics/blendmode_DARKEN.png" />
244      *     <figcaption>
245      *         Retains the smallest component of the source and
246      *         destination pixels.
247      *     </figcaption>
248      * </p>
249      * <p>\(\alpha_{out} = \alpha_{src} + \alpha_{dst} - \alpha_{src} * \alpha_{dst}\)</p>
250      * <p>
251      *     \(C_{out} =
252      *     (1 - \alpha_{dst}) * C_{src} + (1 - \alpha_{src}) * C_{dst} + min(C_{src}, C_{dst})\)
253      * </p>
254      */
255     DARKEN(16),
256 
257     /**
258      * {@usesMathJax}
259      *
260      * <p>
261      *     <img src="{@docRoot}reference/android/images/graphics/blendmode_LIGHTEN.png" />
262      *     <figcaption>Retains the largest component of the source and
263      *     destination pixel.</figcaption>
264      * </p>
265      * <p>\(\alpha_{out} = \alpha_{src} + \alpha_{dst} - \alpha_{src} * \alpha_{dst}\)</p>
266      * <p>
267      *     \(C_{out} =
268      *      (1 - \alpha_{dst}) * C_{src} + (1 - \alpha_{src}) * C_{dst} + max(C_{src}, C_{dst})\)
269      * </p>
270      */
271     LIGHTEN(17),
272 
273     /**
274      * {@usesMathJax}
275      *
276      * <p>
277      *     <img src="{@docRoot}reference/android/images/graphics/blendmode_COLOR_DODGE.png" />
278      *     <figcaption>Makes destination brighter to reflect source.</figcaption>
279      * </p>
280      * <p>
281      *     \(\alpha_{out} = \alpha_{src} + \alpha_{dst} - \alpha_{src} * \alpha_{dst}\)
282      * </p>
283      * <p>
284      *      \begin{equation}
285      *      C_{out} =
286      *      \begin{cases}
287      *          C_{src} * (1 - \alpha_{dst}) & C_{dst} = 0 \\
288      *          C_{src} + \alpha_{dst}*(1 - \alpha_{src}) & C_{src} = \alpha_{src} \\
289      *          \alpha_{src} * min(\alpha_{dst}, C_{dst} * \alpha_{src}/(\alpha_{src} - C_{src}))
290      *              + C_{src} *(1 - \alpha_{dst} + \alpha_{dst}*(1 - \alpha_{src}) & otherwise
291      *      \end{cases}
292      *      \end{equation}
293      * </p>
294      */
295     COLOR_DODGE(18),
296 
297     /**
298      * {@usesMathJax}
299      *
300      * <p>
301      *     <img src="{@docRoot}reference/android/images/graphics/blendmode_COLOR_BURN.png" />
302      *     <figcaption>Makes destination darker to reflect source.</figcaption>
303      * </p>
304      * <p>
305      *     \(\alpha_{out} = \alpha_{src} + \alpha_{dst} - \alpha_{src} * \alpha_{dst}\)
306      * </p>
307      * <p>
308      *     \begin{equation}
309      *     C_{out} =
310      *     \begin{cases}
311      *         C_{dst} + C_{src}*(1 - \alpha_{dst}) & C_{dst} = \alpha_{dst} \\
312      *         \alpha_{dst}*(1 - \alpha_{src}) & C_{src} = 0 \\
313      *         \alpha_{src}*(\alpha_{dst} - min(\alpha_{dst}, (\alpha_{dst}
314      *         - C_{dst})*\alpha_{src}/C_{src}))
315      *         + C_{src} * (1 - \alpha_{dst}) + \alpha_{dst}*(1-\alpha_{src}) & otherwise
316      *     \end{cases}
317      *     \end{equation}
318      * </p>
319      */
320     COLOR_BURN(19),
321 
322     /**
323      * {@usesMathJax}
324      *
325      * <p>
326      *     <img src="{@docRoot}reference/android/images/graphics/blendmode_HARD_LIGHT.png" />
327      *     <figcaption>Makes destination lighter or darker, depending on source.</figcaption>
328      * </p>
329      * <p>
330      *     \(\alpha_{out} = \alpha_{src} + \alpha_{dst} - \alpha_{src} * \alpha_{dst}\)
331      * </p>
332      * <p>
333      *     \begin{equation}
334      *      C_{out} =
335      *      \begin{cases}
336      *           2*C_{src}*C_{dst} & C_{src}*(1-\alpha_{dst}) + C_{dst}*(1-\alpha_{src}) + 2*C_{src}
337      *              \leq \alpha_{src} \\
338      *           \alpha_{src}*\alpha_{dst}- 2*(\alpha_{dst} - C_{dst})*(\alpha_{src} - C_{src})
339      *              & otherwise
340      *      \end{cases}
341      *      \end{equation}
342      * </p>
343      */
344     HARD_LIGHT(20),
345 
346     /**
347      * {@usesMathJax}
348      *
349      * <p>
350      *     <img src="{@docRoot}reference/android/images/graphics/blendmode_SOFT_LIGHT.png" />
351      *     <figcaption>Makes destination lighter or darker, depending on source.</figcaption>
352      * </p>
353      * <p>
354      *     Where
355      *       \begin{equation}
356      *       m =
357      *          \begin{cases}
358      *              C_{dst} / \alpha_{dst} & \alpha_{dst} \gt 0 \\
359      *              0 & otherwise
360      *          \end{cases}
361      *       \end{equation}
362      * </p>
363      * <p>
364      *       \begin{equation}
365      *       g =
366      *          \begin{cases}
367      *              (16 * m * m + 4 * m) * (m - 1) + 7 * m & 4 * C_{dst} \leq \alpha_{dst} \\
368      *              \sqrt m - m & otherwise
369      *          \end{cases}
370      *       \end{equation}
371      * </p>
372      * <p>
373      *       \begin{equation}
374      *       f =
375      *          \begin{cases}
376      *              C_{dst} * (\alpha_{src} + (2 * C_{src} - \alpha_{src}) * (1 - m))
377      *                  & 2 * C_{src} \leq \alpha_{src} \\
378      *              C_{dst} * \alpha_{src} + \alpha_{dst} * (2 * C_{src} - \alpha_{src}) * g
379      *                  & otherwise
380      *          \end{cases}
381      *       \end{equation}
382      * </p>
383      * <p>
384      *       \begin{equation}
385      *          \alpha_{out} = \alpha_{src} + \alpha_{dst} - \alpha_{src} * \alpha_{dst}
386      *       \end{equation}
387      *       \begin{equation}
388      *          C_{out} = C_{src} / \alpha_{dst} + C_{dst} / \alpha_{src} + f
389      *       \end{equation}
390      * </p>
391      */
392     SOFT_LIGHT(21),
393 
394     /**
395      * {@usesMathJax}
396      *
397      * <p>
398      *     <img src="{@docRoot}reference/android/images/graphics/blendmode_DIFFERENCE.png" />
399      *     <figcaption>Subtracts darker from lighter with higher contrast.</figcaption>
400      * </p>
401      * <p>
402      *     \begin{equation}
403      *          \alpha_{out} = \alpha_{src} + \alpha_{dst} - \alpha_{src} * \alpha_{dst}
404      *     \end{equation}
405      * </p>
406      * <p>
407      *     \begin{equation}
408      *           C_{out} = C_{src} + C_{dst} - 2 * min(C_{src}
409      *                       * \alpha_{dst}, C_{dst} * \alpha_{src})
410      *     \end{equation}
411      * </p>
412      */
413     DIFFERENCE(22),
414 
415     /**
416      * {@usesMathJax}
417      *
418      * <p>
419      *     <img src="{@docRoot}reference/android/images/graphics/blendmode_DIFFERENCE.png" />
420      *     <figcaption>Subtracts darker from lighter with lower contrast.</figcaption>
421      * </p>
422      * <p>
423      *     \begin{equation}
424      *          \alpha_{out} = \alpha_{src} + \alpha_{dst} - \alpha_{src} * \alpha_{dst}
425      *     \end{equation}
426      * </p>
427      * <p>
428      *     \begin{equation}
429      *          C_{out} = C_{src} + C_{dst} - 2 * C_{src} * C_{dst}
430      *     \end{equation}
431      * </p>
432      */
433     EXCLUSION(23),
434 
435     /**
436      * {@usesMathJax}
437      *
438      * <p>
439      *     <img src="{@docRoot}reference/android/images/graphics/blendmode_MODULATE.png" />
440      *     <figcaption>Multiplies the source and destination pixels.</figcaption>
441      * </p>
442      * <p>\(\alpha_{out} = \alpha_{src} + \alpha_{dst} - \alpha_{src} * \alpha_{dst}\)</p>
443      * <p>\(C_{out} =
444      *      C_{src} * (1 - \alpha_{dst}) + C_{dst} * (1 - \alpha_{src}) + (C_{src} * C_{dst})\)
445      * </p>
446      */
447     MULTIPLY(24),
448 
449     /**
450      * {@usesMathJax}
451      *
452      * <p>
453      *     <img src="{@docRoot}reference/android/images/graphics/blendmode_HUE.png" />
454      *     <figcaption>
455      *         Replaces hue of destination with hue of source, leaving saturation
456      *         and luminosity unchanged.
457      *     </figcaption>
458      * </p>
459      */
460     HUE(25),
461 
462     /**
463      * {@usesMathJax}
464      *
465      * <p>
466      *     <img src="{@docRoot}reference/android/images/graphics/blendmode_SATURATION.png" />
467      *     <figcaption>
468      *          Replaces saturation of destination saturation hue of source, leaving hue and
469      *          luminosity unchanged.
470      *     </figcaption>
471      * </p>
472      */
473     SATURATION(26),
474 
475     /**
476      * {@usesMathJax}
477      *
478      * <p>
479      *     <img src="{@docRoot}reference/android/images/graphics/blendmode_COLOR.png" />
480      *     <figcaption>
481      *          Replaces hue and saturation of destination with hue and saturation of source,
482      *          leaving luminosity unchanged.
483      *     </figcaption>
484      * </p>
485      */
486     COLOR(27),
487 
488     /**
489      * {@usesMathJax}
490      *
491      * <p>
492      *     <img src="{@docRoot}reference/android/images/graphics/blendmode_LUMINOSITY.png" />
493      *     <figcaption>
494      *          Replaces luminosity of destination with luminosity of source, leaving hue and
495      *          saturation unchanged.
496      *     </figcaption>
497      * </p>
498      */
499     LUMINOSITY(28);
500 
501     private static final BlendMode[] BLEND_MODES = values();
502 
503     /**
504      * @hide
505      */
fromValue(int value)506     public static @Nullable BlendMode fromValue(int value) {
507         for (BlendMode mode : BLEND_MODES) {
508             if (mode.mXfermode.porterDuffMode == value) {
509                 return mode;
510             }
511         }
512         return null;
513     }
514 
515     /**
516      * @hide
517      */
toValue(BlendMode mode)518     public static int toValue(BlendMode mode) {
519         return mode.getXfermode().porterDuffMode;
520     }
521 
522     /**
523      * @hide
524      */
blendModeToPorterDuffMode(@ullable BlendMode mode)525     public static @Nullable PorterDuff.Mode blendModeToPorterDuffMode(@Nullable BlendMode mode) {
526         if (mode != null) {
527             switch (mode) {
528                 case CLEAR:
529                     return PorterDuff.Mode.CLEAR;
530                 case SRC:
531                     return PorterDuff.Mode.SRC;
532                 case DST:
533                     return PorterDuff.Mode.DST;
534                 case SRC_OVER:
535                     return PorterDuff.Mode.SRC_OVER;
536                 case DST_OVER:
537                     return PorterDuff.Mode.DST_OVER;
538                 case SRC_IN:
539                     return PorterDuff.Mode.SRC_IN;
540                 case DST_IN:
541                     return PorterDuff.Mode.DST_IN;
542                 case SRC_OUT:
543                     return PorterDuff.Mode.SRC_OUT;
544                 case DST_OUT:
545                     return PorterDuff.Mode.DST_OUT;
546                 case SRC_ATOP:
547                     return PorterDuff.Mode.SRC_ATOP;
548                 case DST_ATOP:
549                     return PorterDuff.Mode.DST_ATOP;
550                 case XOR:
551                     return PorterDuff.Mode.XOR;
552                 case DARKEN:
553                     return PorterDuff.Mode.DARKEN;
554                 case LIGHTEN:
555                     return PorterDuff.Mode.LIGHTEN;
556                 // b/73224934 PorterDuff Multiply maps to Skia Modulate
557                 case MODULATE:
558                     return PorterDuff.Mode.MULTIPLY;
559                 case SCREEN:
560                     return PorterDuff.Mode.SCREEN;
561                 case PLUS:
562                     return PorterDuff.Mode.ADD;
563                 case OVERLAY:
564                     return PorterDuff.Mode.OVERLAY;
565                 default:
566                     return null;
567             }
568         } else {
569             return null;
570         }
571     }
572 
573     @NonNull
574     private final Xfermode mXfermode;
575 
BlendMode(int mode)576     BlendMode(int mode) {
577         mXfermode = new Xfermode();
578         mXfermode.porterDuffMode = mode;
579     }
580 
581     /**
582      * @hide
583      */
584     @NonNull
getXfermode()585     public Xfermode getXfermode() {
586         return mXfermode;
587     }
588 }
589