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