1 /* 2 * Copyright (C) 2014 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 java.awt.Composite; 20 import java.awt.CompositeContext; 21 import java.awt.RenderingHints; 22 import java.awt.image.ColorModel; 23 import java.awt.image.DataBuffer; 24 import java.awt.image.Raster; 25 import java.awt.image.WritableRaster; 26 27 /* 28 * (non-Javadoc) 29 * The class is adapted from a demo tool for Blending Modes written by 30 * Romain Guy (romainguy@android.com). The tool is available at 31 * http://www.curious-creature.org/2006/09/20/new-blendings-modes-for-java2d/ 32 */ 33 public final class BlendComposite implements Composite { 34 public enum BlendingMode { 35 NORMAL, 36 AVERAGE, 37 MULTIPLY, 38 SCREEN, 39 DARKEN, 40 LIGHTEN, 41 OVERLAY, 42 HARD_LIGHT, 43 SOFT_LIGHT, 44 DIFFERENCE, 45 NEGATION, 46 EXCLUSION, 47 COLOR_DODGE, 48 INVERSE_COLOR_DODGE, 49 SOFT_DODGE, 50 COLOR_BURN, 51 INVERSE_COLOR_BURN, 52 SOFT_BURN, 53 REFLECT, 54 GLOW, 55 FREEZE, 56 HEAT, 57 ADD, 58 SUBTRACT, 59 STAMP, 60 RED, 61 GREEN, 62 BLUE, 63 HUE, 64 SATURATION, 65 COLOR, 66 LUMINOSITY 67 } 68 69 public static final BlendComposite Normal = new BlendComposite(BlendingMode.NORMAL); 70 public static final BlendComposite Average = new BlendComposite(BlendingMode.AVERAGE); 71 public static final BlendComposite Multiply = new BlendComposite(BlendingMode.MULTIPLY); 72 public static final BlendComposite Screen = new BlendComposite(BlendingMode.SCREEN); 73 public static final BlendComposite Darken = new BlendComposite(BlendingMode.DARKEN); 74 public static final BlendComposite Lighten = new BlendComposite(BlendingMode.LIGHTEN); 75 public static final BlendComposite Overlay = new BlendComposite(BlendingMode.OVERLAY); 76 public static final BlendComposite HardLight = new BlendComposite(BlendingMode.HARD_LIGHT); 77 public static final BlendComposite SoftLight = new BlendComposite(BlendingMode.SOFT_LIGHT); 78 public static final BlendComposite Difference = new BlendComposite(BlendingMode.DIFFERENCE); 79 public static final BlendComposite Negation = new BlendComposite(BlendingMode.NEGATION); 80 public static final BlendComposite Exclusion = new BlendComposite(BlendingMode.EXCLUSION); 81 public static final BlendComposite ColorDodge = new BlendComposite(BlendingMode.COLOR_DODGE); 82 public static final BlendComposite InverseColorDodge = new BlendComposite(BlendingMode.INVERSE_COLOR_DODGE); 83 public static final BlendComposite SoftDodge = new BlendComposite(BlendingMode.SOFT_DODGE); 84 public static final BlendComposite ColorBurn = new BlendComposite(BlendingMode.COLOR_BURN); 85 public static final BlendComposite InverseColorBurn = new BlendComposite(BlendingMode.INVERSE_COLOR_BURN); 86 public static final BlendComposite SoftBurn = new BlendComposite(BlendingMode.SOFT_BURN); 87 public static final BlendComposite Reflect = new BlendComposite(BlendingMode.REFLECT); 88 public static final BlendComposite Glow = new BlendComposite(BlendingMode.GLOW); 89 public static final BlendComposite Freeze = new BlendComposite(BlendingMode.FREEZE); 90 public static final BlendComposite Heat = new BlendComposite(BlendingMode.HEAT); 91 public static final BlendComposite Add = new BlendComposite(BlendingMode.ADD); 92 public static final BlendComposite Subtract = new BlendComposite(BlendingMode.SUBTRACT); 93 public static final BlendComposite Stamp = new BlendComposite(BlendingMode.STAMP); 94 public static final BlendComposite Red = new BlendComposite(BlendingMode.RED); 95 public static final BlendComposite Green = new BlendComposite(BlendingMode.GREEN); 96 public static final BlendComposite Blue = new BlendComposite(BlendingMode.BLUE); 97 public static final BlendComposite Hue = new BlendComposite(BlendingMode.HUE); 98 public static final BlendComposite Saturation = new BlendComposite(BlendingMode.SATURATION); 99 public static final BlendComposite Color = new BlendComposite(BlendingMode.COLOR); 100 public static final BlendComposite Luminosity = new BlendComposite(BlendingMode.LUMINOSITY); 101 102 private float alpha; 103 private BlendingMode mode; 104 BlendComposite(BlendingMode mode)105 private BlendComposite(BlendingMode mode) { 106 this(mode, 1.0f); 107 } 108 BlendComposite(BlendingMode mode, float alpha)109 private BlendComposite(BlendingMode mode, float alpha) { 110 this.mode = mode; 111 setAlpha(alpha); 112 } 113 getInstance(BlendingMode mode)114 public static BlendComposite getInstance(BlendingMode mode) { 115 return new BlendComposite(mode); 116 } 117 getInstance(BlendingMode mode, float alpha)118 public static BlendComposite getInstance(BlendingMode mode, float alpha) { 119 return new BlendComposite(mode, alpha); 120 } 121 derive(BlendingMode mode)122 public BlendComposite derive(BlendingMode mode) { 123 return this.mode == mode ? this : new BlendComposite(mode, getAlpha()); 124 } 125 derive(float alpha)126 public BlendComposite derive(float alpha) { 127 return this.alpha == alpha ? this : new BlendComposite(getMode(), alpha); 128 } 129 getAlpha()130 public float getAlpha() { 131 return alpha; 132 } 133 getMode()134 public BlendingMode getMode() { 135 return mode; 136 } 137 setAlpha(float alpha)138 private void setAlpha(float alpha) { 139 if (alpha < 0.0f || alpha > 1.0f) { 140 throw new IllegalArgumentException( 141 "alpha must be comprised between 0.0f and 1.0f"); 142 } 143 144 this.alpha = alpha; 145 } 146 147 @Override hashCode()148 public int hashCode() { 149 return Float.floatToIntBits(alpha) * 31 + mode.ordinal(); 150 } 151 152 @Override equals(Object obj)153 public boolean equals(Object obj) { 154 if (!(obj instanceof BlendComposite)) { 155 return false; 156 } 157 158 BlendComposite bc = (BlendComposite) obj; 159 160 if (mode != bc.mode) { 161 return false; 162 } 163 164 return alpha == bc.alpha; 165 } 166 createContext(ColorModel srcColorModel, ColorModel dstColorModel, RenderingHints hints)167 public CompositeContext createContext(ColorModel srcColorModel, 168 ColorModel dstColorModel, 169 RenderingHints hints) { 170 return new BlendingContext(this); 171 } 172 173 private static final class BlendingContext implements CompositeContext { 174 private final Blender blender; 175 private final BlendComposite composite; 176 BlendingContext(BlendComposite composite)177 private BlendingContext(BlendComposite composite) { 178 this.composite = composite; 179 this.blender = Blender.getBlenderFor(composite); 180 } 181 dispose()182 public void dispose() { 183 } 184 compose(Raster src, Raster dstIn, WritableRaster dstOut)185 public void compose(Raster src, Raster dstIn, WritableRaster dstOut) { 186 if (src.getSampleModel().getDataType() != DataBuffer.TYPE_INT || 187 dstIn.getSampleModel().getDataType() != DataBuffer.TYPE_INT || 188 dstOut.getSampleModel().getDataType() != DataBuffer.TYPE_INT) { 189 throw new IllegalStateException( 190 "Source and destination must store pixels as INT."); 191 } 192 193 int width = Math.min(src.getWidth(), dstIn.getWidth()); 194 int height = Math.min(src.getHeight(), dstIn.getHeight()); 195 196 float alpha = composite.getAlpha(); 197 198 int[] srcPixel = new int[4]; 199 int[] dstPixel = new int[4]; 200 int[] result = new int[4]; 201 int[] srcPixels = new int[width]; 202 int[] dstPixels = new int[width]; 203 204 for (int y = 0; y < height; y++) { 205 dstIn.getDataElements(0, y, width, 1, dstPixels); 206 if (alpha != 0) { 207 src.getDataElements(0, y, width, 1, srcPixels); 208 for (int x = 0; x < width; x++) { 209 // pixels are stored as INT_ARGB 210 // our arrays are [R, G, B, A] 211 int pixel = srcPixels[x]; 212 srcPixel[0] = (pixel >> 16) & 0xFF; 213 srcPixel[1] = (pixel >> 8) & 0xFF; 214 srcPixel[2] = (pixel ) & 0xFF; 215 srcPixel[3] = (pixel >> 24) & 0xFF; 216 217 pixel = dstPixels[x]; 218 dstPixel[0] = (pixel >> 16) & 0xFF; 219 dstPixel[1] = (pixel >> 8) & 0xFF; 220 dstPixel[2] = (pixel ) & 0xFF; 221 dstPixel[3] = (pixel >> 24) & 0xFF; 222 223 result = blender.blend(srcPixel, dstPixel, result); 224 225 // mixes the result with the opacity 226 if (alpha == 1) { 227 dstPixels[x] = (result[3] & 0xFF) << 24 | 228 (result[0] & 0xFF) << 16 | 229 (result[1] & 0xFF) << 8 | 230 result[2] & 0xFF; 231 } else { 232 dstPixels[x] = 233 ((int) (dstPixel[3] + (result[3] - dstPixel[3]) * alpha) & 0xFF) << 24 | 234 ((int) (dstPixel[0] + (result[0] - dstPixel[0]) * alpha) & 0xFF) << 16 | 235 ((int) (dstPixel[1] + (result[1] - dstPixel[1]) * alpha) & 0xFF) << 8 | 236 (int) (dstPixel[2] + (result[2] - dstPixel[2]) * alpha) & 0xFF; 237 } 238 239 } 240 } 241 dstOut.setDataElements(0, y, width, 1, dstPixels); 242 } 243 } 244 } 245 246 private static abstract class Blender { blend(int[] src, int[] dst, int[] result)247 public abstract int[] blend(int[] src, int[] dst, int[] result); 248 RGBtoHSL(int r, int g, int b, float[] hsl)249 private static void RGBtoHSL(int r, int g, int b, float[] hsl) { 250 float var_R = (r / 255f); 251 float var_G = (g / 255f); 252 float var_B = (b / 255f); 253 254 float var_Min; 255 float var_Max; 256 float del_Max; 257 258 if (var_R > var_G) { 259 var_Min = var_G; 260 var_Max = var_R; 261 } else { 262 var_Min = var_R; 263 var_Max = var_G; 264 } 265 if (var_B > var_Max) { 266 var_Max = var_B; 267 } 268 if (var_B < var_Min) { 269 var_Min = var_B; 270 } 271 272 del_Max = var_Max - var_Min; 273 274 float H, S, L; 275 L = (var_Max + var_Min) / 2f; 276 277 if (del_Max - 0.01f <= 0.0f) { 278 H = 0; 279 S = 0; 280 } else { 281 if (L < 0.5f) { 282 S = del_Max / (var_Max + var_Min); 283 } else { 284 S = del_Max / (2 - var_Max - var_Min); 285 } 286 287 float del_R = (((var_Max - var_R) / 6f) + (del_Max / 2f)) / del_Max; 288 float del_G = (((var_Max - var_G) / 6f) + (del_Max / 2f)) / del_Max; 289 float del_B = (((var_Max - var_B) / 6f) + (del_Max / 2f)) / del_Max; 290 291 if (var_R == var_Max) { 292 H = del_B - del_G; 293 } else if (var_G == var_Max) { 294 H = (1 / 3f) + del_R - del_B; 295 } else { 296 H = (2 / 3f) + del_G - del_R; 297 } 298 if (H < 0) { 299 H += 1; 300 } 301 if (H > 1) { 302 H -= 1; 303 } 304 } 305 306 hsl[0] = H; 307 hsl[1] = S; 308 hsl[2] = L; 309 } 310 HSLtoRGB(float h, float s, float l, int[] rgb)311 private static void HSLtoRGB(float h, float s, float l, int[] rgb) { 312 int R, G, B; 313 314 if (s - 0.01f <= 0.0f) { 315 R = (int) (l * 255.0f); 316 G = (int) (l * 255.0f); 317 B = (int) (l * 255.0f); 318 } else { 319 float var_1, var_2; 320 if (l < 0.5f) { 321 var_2 = l * (1 + s); 322 } else { 323 var_2 = (l + s) - (s * l); 324 } 325 var_1 = 2 * l - var_2; 326 327 R = (int) (255.0f * hue2RGB(var_1, var_2, h + (1.0f / 3.0f))); 328 G = (int) (255.0f * hue2RGB(var_1, var_2, h)); 329 B = (int) (255.0f * hue2RGB(var_1, var_2, h - (1.0f / 3.0f))); 330 } 331 332 rgb[0] = R; 333 rgb[1] = G; 334 rgb[2] = B; 335 } 336 hue2RGB(float v1, float v2, float vH)337 private static float hue2RGB(float v1, float v2, float vH) { 338 if (vH < 0.0f) { 339 vH += 1.0f; 340 } 341 if (vH > 1.0f) { 342 vH -= 1.0f; 343 } 344 if ((6.0f * vH) < 1.0f) { 345 return (v1 + (v2 - v1) * 6.0f * vH); 346 } 347 if ((2.0f * vH) < 1.0f) { 348 return (v2); 349 } 350 if ((3.0f * vH) < 2.0f) { 351 return (v1 + (v2 - v1) * ((2.0f / 3.0f) - vH) * 6.0f); 352 } 353 return (v1); 354 } 355 getBlenderFor(BlendComposite composite)356 public static Blender getBlenderFor(BlendComposite composite) { 357 switch (composite.getMode()) { 358 case NORMAL: 359 return new Blender() { 360 @Override 361 public int[] blend(int[] src, int[] dst, int[] result) { 362 System.arraycopy(src, 0, result, 0, 4); 363 return result; 364 } 365 }; 366 case ADD: 367 return new Blender() { 368 @Override 369 public int[] blend(int[] src, int[] dst, int[] result) { 370 for (int i = 0; i < 4; i++) { 371 result[i] = Math.min(255, src[i] + dst[i]); 372 } 373 return result; 374 } 375 }; 376 case AVERAGE: 377 return new Blender() { 378 @Override 379 public int[] blend(int[] src, int[] dst, int[] result) { 380 for (int i = 0; i < 3; i++) { 381 result[i] = (src[i] + dst[i]) >> 1; 382 } 383 result[3] = Math.min(255, src[3] + dst[3]); 384 return result; 385 } 386 }; 387 case BLUE: 388 return new Blender() { 389 @Override 390 public int[] blend(int[] src, int[] dst, int[] result) { 391 System.arraycopy(dst, 0, result, 0, 3); 392 result[3] = Math.min(255, src[3] + dst[3]); 393 return result; 394 } 395 }; 396 case COLOR: 397 return new Blender() { 398 @Override 399 public int[] blend(int[] src, int[] dst, int[] result) { 400 float[] srcHSL = new float[3]; 401 RGBtoHSL(src[0], src[1], src[2], srcHSL); 402 float[] dstHSL = new float[3]; 403 RGBtoHSL(dst[0], dst[1], dst[2], dstHSL); 404 405 HSLtoRGB(srcHSL[0], srcHSL[1], dstHSL[2], result); 406 result[3] = Math.min(255, src[3] + dst[3]); 407 408 return result; 409 } 410 }; 411 case COLOR_BURN: 412 return new Blender() { 413 @Override 414 public int[] blend(int[] src, int[] dst, int[] result) { 415 for (int i = 0; i < 3; i++) { 416 result[i] = src[i] == 0 ? 0 : 417 Math.max(0, 255 - (((255 - dst[i]) << 8) / src[i])); 418 } 419 result[3] = Math.min(255, src[3] + dst[3]); 420 return result; 421 } 422 }; 423 case COLOR_DODGE: 424 return new Blender() { 425 @Override 426 public int[] blend(int[] src, int[] dst, int[] result) { 427 for (int i = 0; i < 3; i++) { 428 result[i] = src[i] == 255 ? 255 : 429 Math.min((dst[i] << 8) / (255 - src[i]), 255); 430 } 431 result[3] = Math.min(255, src[3] + dst[3]); 432 return result; 433 } 434 }; 435 case DARKEN: 436 return new Blender() { 437 @Override 438 public int[] blend(int[] src, int[] dst, int[] result) { 439 for (int i = 0; i < 3; i++) { 440 result[i] = Math.min(src[i], dst[i]); 441 } 442 result[3] = Math.min(255, src[3] + dst[3]); 443 return result; 444 } 445 }; 446 case DIFFERENCE: 447 return new Blender() { 448 @Override 449 public int[] blend(int[] src, int[] dst, int[] result) { 450 for (int i = 0; i < 3; i++) { 451 result[i] = dst[i] + src[i] - (dst[i] * src[i] >> 7); 452 } 453 result[3] = Math.min(255, src[3] + dst[3]); 454 return result; 455 } 456 }; 457 case EXCLUSION: 458 return new Blender() { 459 @Override 460 public int[] blend(int[] src, int[] dst, int[] result) { 461 for (int i = 0; i < 3; i++) { 462 result[i] = dst[i] + src[i] - (dst[i] * src[i] >> 7); 463 } 464 result[3] = Math.min(255, src[3] + dst[3]); 465 return result; 466 } 467 }; 468 case FREEZE: 469 return new Blender() { 470 @Override 471 public int[] blend(int[] src, int[] dst, int[] result) { 472 for (int i = 0; i < 3; i++) { 473 result[i] = src[i] == 0 ? 0 : 474 Math.max(0, 255 - (255 - dst[i]) * (255 - dst[i]) / src[i]); 475 } 476 result[3] = Math.min(255, src[3] + dst[3]); 477 return result; 478 } 479 }; 480 case GLOW: 481 return new Blender() { 482 @Override 483 public int[] blend(int[] src, int[] dst, int[] result) { 484 for (int i = 0; i < 3; i++) { 485 result[i] = dst[i] == 255 ? 255 : 486 Math.min(255, src[i] * src[i] / (255 - dst[i])); 487 } 488 result[3] = Math.min(255, src[3] + dst[3]); 489 return result; 490 } 491 }; 492 case GREEN: 493 return new Blender() { 494 @Override 495 public int[] blend(int[] src, int[] dst, int[] result) { 496 return new int[] { 497 dst[0], 498 dst[1], 499 src[2], 500 Math.min(255, src[3] + dst[3]) 501 }; 502 } 503 }; 504 case HARD_LIGHT: 505 return new Blender() { 506 @Override 507 public int[] blend(int[] src, int[] dst, int[] result) { 508 return new int[] { 509 src[0] < 128 ? dst[0] * src[0] >> 7 : 510 255 - ((255 - src[0]) * (255 - dst[0]) >> 7), 511 src[1] < 128 ? dst[1] * src[1] >> 7 : 512 255 - ((255 - src[1]) * (255 - dst[1]) >> 7), 513 src[2] < 128 ? dst[2] * src[2] >> 7 : 514 255 - ((255 - src[2]) * (255 - dst[2]) >> 7), 515 Math.min(255, src[3] + dst[3]) 516 }; 517 } 518 }; 519 case HEAT: 520 return new Blender() { 521 @Override 522 public int[] blend(int[] src, int[] dst, int[] result) { 523 return new int[] { 524 dst[0] == 0 ? 0 : Math.max(0, 255 - (255 - src[0]) * (255 - src[0]) / dst[0]), 525 dst[1] == 0 ? 0 : Math.max(0, 255 - (255 - src[1]) * (255 - src[1]) / dst[1]), 526 dst[2] == 0 ? 0 : Math.max(0, 255 - (255 - src[2]) * (255 - src[2]) / dst[2]), 527 Math.min(255, src[3] + dst[3]) 528 }; 529 } 530 }; 531 case HUE: 532 return new Blender() { 533 @Override 534 public int[] blend(int[] src, int[] dst, int[] result) { 535 float[] srcHSL = new float[3]; 536 RGBtoHSL(src[0], src[1], src[2], srcHSL); 537 float[] dstHSL = new float[3]; 538 RGBtoHSL(dst[0], dst[1], dst[2], dstHSL); 539 540 HSLtoRGB(srcHSL[0], dstHSL[1], dstHSL[2], result); 541 result[3] = Math.min(255, src[3] + dst[3]); 542 543 return result; 544 } 545 }; 546 case INVERSE_COLOR_BURN: 547 return new Blender() { 548 @Override 549 public int[] blend(int[] src, int[] dst, int[] result) { 550 return new int[] { 551 dst[0] == 0 ? 0 : 552 Math.max(0, 255 - (((255 - src[0]) << 8) / dst[0])), 553 dst[1] == 0 ? 0 : 554 Math.max(0, 255 - (((255 - src[1]) << 8) / dst[1])), 555 dst[2] == 0 ? 0 : 556 Math.max(0, 255 - (((255 - src[2]) << 8) / dst[2])), 557 Math.min(255, src[3] + dst[3]) 558 }; 559 } 560 }; 561 case INVERSE_COLOR_DODGE: 562 return new Blender() { 563 @Override 564 public int[] blend(int[] src, int[] dst, int[] result) { 565 return new int[] { 566 dst[0] == 255 ? 255 : 567 Math.min((src[0] << 8) / (255 - dst[0]), 255), 568 dst[1] == 255 ? 255 : 569 Math.min((src[1] << 8) / (255 - dst[1]), 255), 570 dst[2] == 255 ? 255 : 571 Math.min((src[2] << 8) / (255 - dst[2]), 255), 572 Math.min(255, src[3] + dst[3]) 573 }; 574 } 575 }; 576 case LIGHTEN: 577 return new Blender() { 578 @Override 579 public int[] blend(int[] src, int[] dst, int[] result) { 580 for (int i = 0; i < 3; i++) { 581 result[i] = Math.max(src[i], dst[i]); 582 } 583 result[3] = Math.min(255, src[3] + dst[3]); 584 return result; 585 } 586 }; 587 case LUMINOSITY: 588 return new Blender() { 589 @Override 590 public int[] blend(int[] src, int[] dst, int[] result) { 591 float[] srcHSL = new float[3]; 592 RGBtoHSL(src[0], src[1], src[2], srcHSL); 593 float[] dstHSL = new float[3]; 594 RGBtoHSL(dst[0], dst[1], dst[2], dstHSL); 595 596 HSLtoRGB(dstHSL[0], dstHSL[1], srcHSL[2], result); 597 result[3] = Math.min(255, src[3] + dst[3]); 598 599 return result; 600 } 601 }; 602 case MULTIPLY: 603 return new Blender() { 604 @Override 605 public int[] blend(int[] src, int[] dst, int[] result) { 606 for (int i = 0; i < 3; i++) { 607 result[i] = (src[i] * dst[i]) >> 8; 608 } 609 result[3] = Math.min(255, src[3] + dst[3]); 610 return result; 611 } 612 }; 613 case NEGATION: 614 return new Blender() { 615 @Override 616 public int[] blend(int[] src, int[] dst, int[] result) { 617 return new int[] { 618 255 - Math.abs(255 - dst[0] - src[0]), 619 255 - Math.abs(255 - dst[1] - src[1]), 620 255 - Math.abs(255 - dst[2] - src[2]), 621 Math.min(255, src[3] + dst[3]) 622 }; 623 } 624 }; 625 case OVERLAY: 626 return new Blender() { 627 @Override 628 public int[] blend(int[] src, int[] dst, int[] result) { 629 for (int i = 0; i < 3; i++) { 630 result[i] = dst[i] < 128 ? dst[i] * src[i] >> 7 : 631 255 - ((255 - dst[i]) * (255 - src[i]) >> 7); 632 } 633 result[3] = Math.min(255, src[3] + dst[3]); 634 return result; 635 } 636 }; 637 case RED: 638 return new Blender() { 639 @Override 640 public int[] blend(int[] src, int[] dst, int[] result) { 641 return new int[] { 642 src[0], 643 dst[1], 644 dst[2], 645 Math.min(255, src[3] + dst[3]) 646 }; 647 } 648 }; 649 case REFLECT: 650 return new Blender() { 651 @Override 652 public int[] blend(int[] src, int[] dst, int[] result) { 653 return new int[] { 654 src[0] == 255 ? 255 : Math.min(255, dst[0] * dst[0] / (255 - src[0])), 655 src[1] == 255 ? 255 : Math.min(255, dst[1] * dst[1] / (255 - src[1])), 656 src[2] == 255 ? 255 : Math.min(255, dst[2] * dst[2] / (255 - src[2])), 657 Math.min(255, src[3] + dst[3]) 658 }; 659 } 660 }; 661 case SATURATION: 662 return new Blender() { 663 @Override 664 public int[] blend(int[] src, int[] dst, int[] result) { 665 float[] srcHSL = new float[3]; 666 RGBtoHSL(src[0], src[1], src[2], srcHSL); 667 float[] dstHSL = new float[3]; 668 RGBtoHSL(dst[0], dst[1], dst[2], dstHSL); 669 670 HSLtoRGB(dstHSL[0], srcHSL[1], dstHSL[2], result); 671 result[3] = Math.min(255, src[3] + dst[3]); 672 673 return result; 674 } 675 }; 676 case SCREEN: 677 return new Blender() { 678 @Override 679 public int[] blend(int[] src, int[] dst, int[] result) { 680 return new int[] { 681 255 - ((255 - src[0]) * (255 - dst[0]) >> 8), 682 255 - ((255 - src[1]) * (255 - dst[1]) >> 8), 683 255 - ((255 - src[2]) * (255 - dst[2]) >> 8), 684 Math.min(255, src[3] + dst[3]) 685 }; 686 } 687 }; 688 case SOFT_BURN: 689 return new Blender() { 690 @Override 691 public int[] blend(int[] src, int[] dst, int[] result) { 692 return new int[] { 693 dst[0] + src[0] < 256 ? 694 (dst[0] == 255 ? 255 : 695 Math.min(255, (src[0] << 7) / (255 - dst[0]))) : 696 Math.max(0, 255 - (((255 - dst[0]) << 7) / src[0])), 697 dst[1] + src[1] < 256 ? 698 (dst[1] == 255 ? 255 : 699 Math.min(255, (src[1] << 7) / (255 - dst[1]))) : 700 Math.max(0, 255 - (((255 - dst[1]) << 7) / src[1])), 701 dst[2] + src[2] < 256 ? 702 (dst[2] == 255 ? 255 : 703 Math.min(255, (src[2] << 7) / (255 - dst[2]))) : 704 Math.max(0, 255 - (((255 - dst[2]) << 7) / src[2])), 705 Math.min(255, src[3] + dst[3]) 706 }; 707 } 708 }; 709 case SOFT_DODGE: 710 return new Blender() { 711 @Override 712 public int[] blend(int[] src, int[] dst, int[] result) { 713 return new int[] { 714 dst[0] + src[0] < 256 ? 715 (src[0] == 255 ? 255 : 716 Math.min(255, (dst[0] << 7) / (255 - src[0]))) : 717 Math.max(0, 255 - (((255 - src[0]) << 7) / dst[0])), 718 dst[1] + src[1] < 256 ? 719 (src[1] == 255 ? 255 : 720 Math.min(255, (dst[1] << 7) / (255 - src[1]))) : 721 Math.max(0, 255 - (((255 - src[1]) << 7) / dst[1])), 722 dst[2] + src[2] < 256 ? 723 (src[2] == 255 ? 255 : 724 Math.min(255, (dst[2] << 7) / (255 - src[2]))) : 725 Math.max(0, 255 - (((255 - src[2]) << 7) / dst[2])), 726 Math.min(255, src[3] + dst[3]) 727 }; 728 } 729 }; 730 case SOFT_LIGHT: 731 break; 732 case STAMP: 733 return new Blender() { 734 @Override 735 public int[] blend(int[] src, int[] dst, int[] result) { 736 return new int[] { 737 Math.max(0, Math.min(255, dst[0] + 2 * src[0] - 256)), 738 Math.max(0, Math.min(255, dst[1] + 2 * src[1] - 256)), 739 Math.max(0, Math.min(255, dst[2] + 2 * src[2] - 256)), 740 Math.min(255, src[3] + dst[3]) 741 }; 742 } 743 }; 744 case SUBTRACT: 745 return new Blender() { 746 @Override 747 public int[] blend(int[] src, int[] dst, int[] result) { 748 return new int[] { 749 Math.max(0, src[0] + dst[0] - 256), 750 Math.max(0, src[1] + dst[1] - 256), 751 Math.max(0, src[2] + dst[2] - 256), 752 Math.min(255, src[3] + dst[3]) 753 }; 754 } 755 }; 756 } 757 throw new IllegalArgumentException("Blender not implement for " + 758 composite.getMode().name()); 759 } 760 } 761 } 762