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