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.uirendering.cts.testclasses; 18 19 import android.graphics.Bitmap; 20 import android.graphics.BlendMode; 21 import android.graphics.Canvas; 22 import android.graphics.Color; 23 import android.graphics.Paint; 24 import android.graphics.Point; 25 import android.uirendering.cts.bitmapverifiers.SamplePointVerifier; 26 import android.uirendering.cts.testinfrastructure.ActivityTestBase; 27 import android.uirendering.cts.testinfrastructure.CanvasClient; 28 29 import androidx.test.filters.MediumTest; 30 import androidx.test.runner.AndroidJUnit4; 31 32 import org.junit.Test; 33 import org.junit.runner.RunWith; 34 35 @MediumTest 36 @RunWith(AndroidJUnit4.class) 37 public class BlendModeTest extends ActivityTestBase { 38 39 private static final int BG_COLOR = Color.WHITE; 40 private static final int DST_COLOR = Color.RED; 41 private static final int SRC_COLOR = Color.BLUE; 42 43 private static final int LEFT_X = TEST_WIDTH / 4; 44 private static final int RIGHT_X = TEST_WIDTH * 3 / 4; 45 private static final int TOP_Y = TEST_HEIGHT / 4; 46 private static final int BOTTOM_Y = TEST_HEIGHT * 3 / 4; 47 48 private class BlendModeCanvasClient implements CanvasClient { 49 final Paint mPaint = new Paint(); 50 51 private final Bitmap mSrcBitmap; 52 private final Bitmap mDstBitmap; 53 private final BlendMode mBlendmode; 54 55 BlendModeCanvasClient(BlendMode mode, Bitmap dstBitmap, Bitmap srcBitmap) { 56 mDstBitmap = dstBitmap; 57 mSrcBitmap = srcBitmap; 58 mBlendmode = mode; 59 } 60 61 @Override 62 public void draw(Canvas canvas, int width, int height) { 63 canvas.drawColor(Color.WHITE); 64 65 int sc = canvas.saveLayer(0, 0, TEST_WIDTH, TEST_HEIGHT, null); 66 67 canvas.drawBitmap(mDstBitmap, 0, 0, null); 68 mPaint.setBlendMode(mBlendmode); 69 70 canvas.drawBitmap(mSrcBitmap, 0, 0, mPaint); 71 72 canvas.restoreToCount(sc); 73 } 74 } 75 76 private static final Point[] SAMPLE_POINTS = { 77 new Point(LEFT_X, TOP_Y), 78 new Point(LEFT_X, BOTTOM_Y), 79 new Point(RIGHT_X, BOTTOM_Y) 80 }; 81 82 private void testBlendMode(BlendMode mode, 83 int topLeftColor, 84 int bottomLeftColor, 85 int bottomRightColor) { 86 87 BlendModeCanvasClient client = new BlendModeCanvasClient(mode, 88 createBlendmodeDst(), createBlendmodeSrc()); 89 90 int[] colors = { topLeftColor, bottomLeftColor, bottomRightColor }; 91 createTest().addCanvasClient(client) 92 .runWithVerifier(new SamplePointVerifier(SAMPLE_POINTS, colors)); 93 } 94 95 private Bitmap createBlendmodeDst() { 96 Bitmap srcB = Bitmap.createBitmap(TEST_WIDTH, TEST_HEIGHT, Bitmap.Config.ARGB_8888); 97 Canvas srcCanvas = new Canvas(srcB); 98 Paint srcPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 99 srcPaint.setColor(DST_COLOR); 100 srcCanvas.drawRect(0, 0, TEST_WIDTH / 2, TEST_HEIGHT, srcPaint); 101 return srcB; 102 } 103 104 private Bitmap createBlendmodeSrc() { 105 Bitmap dstB = Bitmap.createBitmap(TEST_WIDTH, TEST_HEIGHT, Bitmap.Config.ARGB_8888); 106 Canvas dstCanvas = new Canvas(dstB); 107 Paint dstPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 108 dstPaint.setColor(SRC_COLOR); 109 dstCanvas.drawRect(0, TEST_HEIGHT / 2, TEST_WIDTH, TEST_HEIGHT, dstPaint); 110 return dstB; 111 } 112 113 @Test 114 public void testBlendMode_CLEAR() { 115 testBlendMode(BlendMode.CLEAR, Color.WHITE, Color.WHITE, Color.WHITE); 116 } 117 118 @Test 119 public void testBlendMode_SRC() { 120 testBlendMode(BlendMode.SRC, BG_COLOR, SRC_COLOR, SRC_COLOR); 121 } 122 123 @Test 124 public void testBlendMode_DST() { 125 testBlendMode(BlendMode.DST, DST_COLOR, DST_COLOR, BG_COLOR); 126 } 127 128 @Test 129 public void testBlendMode_SRC_OVER() { 130 testBlendMode(BlendMode.SRC_OVER, DST_COLOR, SRC_COLOR, SRC_COLOR); 131 } 132 133 @Test 134 public void testBlendMode_DST_OVER() { 135 testBlendMode(BlendMode.DST_OVER, Color.RED, Color.RED, Color.BLUE); 136 } 137 138 @Test 139 public void testBlendMode_SRC_IN() { 140 testBlendMode(BlendMode.SRC_IN, BG_COLOR, Color.BLUE, BG_COLOR); 141 } 142 143 @Test 144 public void testBlendMode_DST_IN() { 145 testBlendMode(BlendMode.DST_IN, BG_COLOR, DST_COLOR, BG_COLOR); 146 } 147 148 @Test 149 public void testBlendMode_SRC_OUT() { 150 testBlendMode(BlendMode.SRC_OUT, BG_COLOR, BG_COLOR, Color.BLUE); 151 } 152 153 @Test 154 public void testBlendMode_DST_OUT() { 155 testBlendMode(BlendMode.DST_OUT, DST_COLOR, BG_COLOR, BG_COLOR); 156 } 157 158 @Test 159 public void testBlendMode_SRC_ATOP() { 160 testBlendMode(BlendMode.SRC_ATOP, DST_COLOR, Color.BLUE, BG_COLOR); 161 } 162 163 @Test 164 public void testBlendMode_DST_ATOP() { 165 testBlendMode(BlendMode.DST_ATOP, BG_COLOR, DST_COLOR, Color.BLUE); 166 } 167 168 @Test 169 public void testBlendMode_XOR() { 170 testBlendMode(BlendMode.XOR, DST_COLOR, BG_COLOR, Color.BLUE); 171 } 172 173 @Test 174 public void testBlendMode_PLUS() { 175 testBlendMode(BlendMode.PLUS, DST_COLOR, Color.MAGENTA, Color.BLUE); 176 } 177 178 @Test 179 public void testBlendMode_MODULATE() { 180 int alpha = (Color.alpha(DST_COLOR) * Color.alpha(Color.BLUE)) / 255; 181 int red = (Color.red(DST_COLOR) * Color.red(Color.BLUE)) / 255; 182 int green = (Color.green(Color.GREEN) * Color.green(Color.BLUE)) / 255; 183 int blue = (Color.blue(DST_COLOR) * Color.blue(Color.BLUE)) / 255; 184 int resultColor = Color.argb(alpha, red, green, blue); 185 testBlendMode(BlendMode.MODULATE, BG_COLOR, resultColor, BG_COLOR); 186 } 187 188 @Test 189 public void testBlendMode_SCREEN() { 190 testBlendMode(BlendMode.SCREEN, DST_COLOR, Color.MAGENTA, Color.BLUE); 191 } 192 193 @Test 194 public void testBlendMode_OVERLAY() { 195 int alphaDst = Color.alpha(DST_COLOR); 196 int alphaSrc = Color.alpha(SRC_COLOR); 197 198 int redDst = Color.red(DST_COLOR); 199 int redSrc = Color.red(SRC_COLOR); 200 201 int greenDst = Color.green(DST_COLOR); 202 int greenSrc = Color.green(SRC_COLOR); 203 204 int blueDst = Color.blue(DST_COLOR); 205 int blueSrc = Color.blue(SRC_COLOR); 206 207 int alpha = (alphaSrc + alphaDst) - (alphaSrc * alphaDst) / 255; 208 int red = computeOverlay(alphaSrc, alphaDst, redSrc, redDst); 209 int green = computeOverlay(alphaSrc, alphaDst, greenSrc, greenDst); 210 int blue = computeOverlay(alphaSrc, alphaDst, blueSrc, blueDst); 211 int result = Color.argb(alpha, red, green, blue); 212 testBlendMode(BlendMode.OVERLAY, DST_COLOR, result, SRC_COLOR); 213 } 214 215 private int computeOverlay(int alphaSrc, int alphaDst, int colorSrc, int colorDst) { 216 if (2 * colorDst < alphaDst) { 217 return (2 * colorSrc * colorDst) / 255; 218 } else { 219 return (alphaSrc * alphaDst) / 255 220 - (2 * (alphaDst - colorSrc) * (alphaSrc - colorDst)); 221 } 222 } 223 224 @Test 225 public void testBlendMode_DARKEN() { 226 int alphaDst = Color.alpha(DST_COLOR); 227 int alphaSrc = Color.alpha(SRC_COLOR); 228 229 int redDst = Color.red(DST_COLOR); 230 int redSrc = Color.red(SRC_COLOR); 231 232 int greenDst = Color.green(DST_COLOR); 233 int greenSrc = Color.green(SRC_COLOR); 234 235 int blueDst = Color.blue(DST_COLOR); 236 int blueSrc = Color.blue(SRC_COLOR); 237 238 int alphaOut = (alphaSrc + alphaDst) - (alphaSrc * alphaDst) / 255; 239 int red = computeDarken(alphaDst, alphaSrc, redDst, redSrc); 240 int green = computeDarken(alphaDst, alphaSrc, greenDst, greenSrc); 241 int blue = computeDarken(alphaDst, alphaSrc, blueDst, blueSrc); 242 int result = Color.argb(alphaOut, red, green, blue); 243 testBlendMode(BlendMode.DARKEN, DST_COLOR, result, SRC_COLOR); 244 } 245 246 private int computeDarken(int alphaDst, int alphaSrc, int colorDst, int colorSrc) { 247 return (((255 - alphaDst)) * (colorSrc)) / 255 + ((255 - alphaSrc) * colorDst) / 255 248 + Math.min(colorSrc, colorDst); 249 } 250 251 @Test 252 public void testBlendMode_LIGHTEN() { 253 int alphaDst = Color.alpha(DST_COLOR); 254 int alphaSrc = Color.alpha(SRC_COLOR); 255 256 int redDst = Color.red(DST_COLOR); 257 int redSrc = Color.red(SRC_COLOR); 258 259 int greenDst = Color.green(DST_COLOR); 260 int greenSrc = Color.green(SRC_COLOR); 261 262 int blueDst = Color.blue(DST_COLOR); 263 int blueSrc = Color.blue(SRC_COLOR); 264 265 int alphaOut = (alphaSrc + alphaDst) - (alphaSrc * alphaDst) / 255; 266 int red = computeLighten(alphaDst, alphaSrc, redDst, redSrc); 267 int green = computeLighten(alphaDst, alphaSrc, greenDst, greenSrc); 268 int blue = computeLighten(alphaDst, alphaSrc, blueDst, blueSrc); 269 int result = Color.argb(alphaOut, red, green, blue); 270 testBlendMode(BlendMode.LIGHTEN, DST_COLOR, result, SRC_COLOR); 271 } 272 273 private int computeLighten(int alphaDst, int alphaSrc, int colorDst, int colorSrc) { 274 return (((255 - alphaDst)) * (colorSrc)) / 255 + ((255 - alphaSrc) * colorDst) / 255 275 + Math.max(colorSrc, colorDst); 276 } 277 278 @Test 279 public void testBlendMode_COLOR_DODGE() { 280 int alphaDst = Color.alpha(DST_COLOR); 281 int alphaSrc = Color.alpha(SRC_COLOR); 282 283 int redDst = Color.red(DST_COLOR); 284 int redSrc = Color.red(SRC_COLOR); 285 286 int greenDst = Color.green(DST_COLOR); 287 int greenSrc = Color.green(SRC_COLOR); 288 289 int blueDst = Color.blue(DST_COLOR); 290 int blueSrc = Color.blue(SRC_COLOR); 291 292 int alphaOut = (alphaSrc + alphaDst) - (alphaSrc * alphaDst) / 255; 293 int red = computeColorDodge(alphaDst, alphaSrc, redDst, redSrc); 294 int green = computeColorDodge(alphaDst, alphaSrc, greenDst, greenSrc); 295 int blue = computeColorDodge(alphaDst, alphaSrc, blueDst, blueSrc); 296 int result = Color.argb(alphaOut, red, green, blue); 297 testBlendMode(BlendMode.COLOR_DODGE, DST_COLOR, result, SRC_COLOR); 298 299 } 300 301 private int computeColorDodge(int alphaDst, int alphaSrc, int colorDst, int colorSrc) { 302 if (colorDst == 0) { 303 return (colorSrc * (255 - alphaDst)) / 255; 304 } else if (colorSrc == alphaSrc) { 305 return colorSrc + alphaDst * (255 - alphaSrc) / 255; 306 } else { 307 float alphaRatio = (float) alphaSrc / ((float) alphaSrc - colorSrc); 308 return Math.round((alphaSrc * Math.min(alphaDst, colorDst * alphaRatio)) / 255 309 + colorSrc * (255 - alphaDst) / 255.0f 310 + alphaDst * (255 - alphaSrc) / 255.0f); 311 } 312 } 313 314 @Test 315 public void testBlendMode_COLOR_BURN() { 316 int alphaDst = Color.alpha(DST_COLOR); 317 int alphaSrc = Color.alpha(SRC_COLOR); 318 319 int redDst = Color.red(DST_COLOR); 320 int redSrc = Color.red(SRC_COLOR); 321 322 int greenDst = Color.green(DST_COLOR); 323 int greenSrc = Color.green(SRC_COLOR); 324 325 int blueDst = Color.blue(DST_COLOR); 326 int blueSrc = Color.blue(SRC_COLOR); 327 328 int alphaOut = (alphaSrc + alphaDst) - (alphaSrc * alphaDst) / 255; 329 int red = computeColorBurn(alphaDst, alphaSrc, redDst, redSrc); 330 int green = computeColorBurn(alphaDst, alphaSrc, greenDst, greenSrc); 331 int blue = computeColorBurn(alphaDst, alphaSrc, blueDst, blueSrc); 332 int result = Color.argb(alphaOut, red, green, blue); 333 testBlendMode(BlendMode.COLOR_BURN, DST_COLOR, result, SRC_COLOR); 334 } 335 336 private int computeColorBurn(int alphaDst, int alphaSrc, int colorDst, int colorSrc) { 337 if (colorDst == alphaDst) { 338 return colorDst + (colorSrc * (255 - alphaDst)) / 255; 339 } else if (colorSrc == 0) { 340 return alphaDst * (255 - alphaSrc) / 255; 341 } else { 342 return alphaSrc * (alphaDst - Math.min(alphaDst, 343 (alphaDst - colorDst) * alphaSrc / colorSrc)) 344 + colorSrc * (255 - alphaDst) / 255 + alphaDst * (255 - alphaSrc) / 255; 345 } 346 } 347 348 @Test 349 public void testBlendMode_HARD_LIGHT() { 350 int alphaDst = Color.alpha(DST_COLOR); 351 int alphaSrc = Color.alpha(SRC_COLOR); 352 353 int redDst = Color.red(DST_COLOR); 354 int redSrc = Color.red(SRC_COLOR); 355 356 int greenDst = Color.green(DST_COLOR); 357 int greenSrc = Color.green(SRC_COLOR); 358 359 int blueDst = Color.blue(DST_COLOR); 360 int blueSrc = Color.blue(SRC_COLOR); 361 362 int alphaOut = (alphaSrc + alphaDst) - (alphaSrc * alphaDst) / 255; 363 int red = computeHardLight(alphaDst, alphaSrc, redDst, redSrc); 364 int green = computeHardLight(alphaDst, alphaSrc, greenDst, greenSrc); 365 int blue = computeHardLight(alphaDst, alphaSrc, blueDst, blueSrc); 366 int result = Color.argb(alphaOut, red, green, blue); 367 testBlendMode(BlendMode.HARD_LIGHT, DST_COLOR, result, SRC_COLOR); 368 } 369 370 private int computeHardLight(int alphaDst, int alphaSrc, int colorDst, int colorSrc) { 371 if (2 * colorSrc <= alphaSrc) { 372 return 2 * colorSrc * colorDst / 255 + colorSrc * (255 - alphaDst) / 255 373 + colorDst * (255 - alphaSrc) / 255; 374 } else { 375 return colorSrc * (255 + alphaDst) / 255 376 + colorDst * (255 + alphaSrc) / 255 377 - (alphaSrc * alphaDst) / 255 378 - 2 * colorSrc * colorDst / 255; 379 } 380 } 381 382 @Test 383 public void testBlendMode_SOFT_LIGHT() { 384 int alphaDst = Color.alpha(DST_COLOR); 385 int alphaSrc = Color.alpha(SRC_COLOR); 386 387 int redDst = Color.red(DST_COLOR); 388 int redSrc = Color.red(SRC_COLOR); 389 390 int greenDst = Color.green(DST_COLOR); 391 int greenSrc = Color.green(SRC_COLOR); 392 393 int blueDst = Color.blue(DST_COLOR); 394 int blueSrc = Color.blue(SRC_COLOR); 395 396 int alphaOut = (alphaSrc + alphaDst) - (alphaSrc * alphaDst) / 255; 397 int red = computeSoftLight(alphaDst, alphaSrc, redDst, redSrc); 398 int green = computeSoftLight(alphaDst, alphaSrc, greenDst, greenSrc); 399 int blue = computeSoftLight(alphaDst, alphaSrc, blueDst, blueSrc); 400 int result = Color.argb(alphaOut, red, green, blue); 401 testBlendMode(BlendMode.SOFT_LIGHT, DST_COLOR, result, SRC_COLOR); 402 } 403 404 private int computeSoftLight(int alphaDst, int alphaSrc, int colorDst, int colorSrc) { 405 float m = alphaDst > 0 ? (float) colorDst / alphaDst : 0.0f; 406 if (2 * colorSrc <= alphaSrc) { 407 return Math.round( 408 colorDst * (alphaSrc + (2 * colorSrc - alphaSrc) * (1 - m)) / 255.0f 409 + colorSrc * (255 - alphaDst) / 255.0f 410 + (float) colorDst * (255 - alphaSrc) / 255.0f 411 ); 412 } else if ((4 * colorDst) <= alphaDst) { 413 return Math.round( 414 (alphaDst * (2 * colorSrc - alphaSrc)) / 255.0f 415 * (16 * m * m * m - 12 * m * m - 3 * m) 416 + colorSrc - (colorSrc * alphaDst) / 255.0f + colorDst 417 ); 418 419 } else { 420 return (int) Math.round( 421 (alphaDst * (2 * colorSrc - alphaSrc)) / 255.0f 422 * (Math.sqrt(m) - m) 423 + colorSrc - (colorSrc * alphaDst) / 255.0f + colorDst); 424 } 425 } 426 427 @Test 428 public void testBlendMode_DIFFERENCE() { 429 int alphaDst = Color.alpha(DST_COLOR); 430 int alphaSrc = Color.alpha(SRC_COLOR); 431 432 int redDst = Color.red(DST_COLOR); 433 int redSrc = Color.red(SRC_COLOR); 434 435 int greenDst = Color.green(DST_COLOR); 436 int greenSrc = Color.green(SRC_COLOR); 437 438 int blueDst = Color.blue(DST_COLOR); 439 int blueSrc = Color.blue(SRC_COLOR); 440 441 int alphaOut = (alphaSrc + alphaDst) - (alphaSrc * alphaDst) / 255; 442 int red = computeDifference(alphaDst, alphaSrc, redDst, redSrc); 443 int green = computeDifference(alphaDst, alphaSrc, greenDst, greenSrc); 444 int blue = computeDifference(alphaDst, alphaSrc, blueDst, blueSrc); 445 int result = Color.argb(alphaOut, red, green, blue); 446 testBlendMode(BlendMode.DIFFERENCE, DST_COLOR, result, SRC_COLOR); 447 } 448 449 private int computeDifference(int alphaDst, int alphaSrc, int colorDst, int colorSrc) { 450 return colorSrc + colorDst - 2 * Math.min(colorSrc * alphaDst, colorDst * alphaSrc) / 255; 451 } 452 453 @Test 454 public void testBlendMode_EXCLUSION() { 455 int alphaDst = Color.alpha(DST_COLOR); 456 int alphaSrc = Color.alpha(SRC_COLOR); 457 458 int redDst = Color.red(DST_COLOR); 459 int redSrc = Color.red(SRC_COLOR); 460 461 int greenDst = Color.green(DST_COLOR); 462 int greenSrc = Color.green(SRC_COLOR); 463 464 int blueDst = Color.blue(DST_COLOR); 465 int blueSrc = Color.blue(SRC_COLOR); 466 467 int alphaOut = (alphaSrc + alphaDst) - (alphaSrc * alphaDst) / 255; 468 int red = computeExclusion(redDst, redSrc); 469 int green = computeExclusion(greenDst, greenSrc); 470 int blue = computeExclusion(blueDst, blueSrc); 471 int result = Color.argb(alphaOut, red, green, blue); 472 testBlendMode(BlendMode.EXCLUSION, DST_COLOR, result, SRC_COLOR); 473 } 474 475 private int computeExclusion(int colorDst, int colorSrc) { 476 return colorSrc + colorDst - (2 * colorSrc * colorDst) / 255; 477 } 478 479 int computeMultiply(int dstColor, int srcColor, int dstAlpha, int srcAlpha) { 480 return (srcColor * dstColor) / 255 481 + (srcColor * (255 - dstAlpha)) / 255 482 + (dstColor * (255 - srcAlpha)) / 255; 483 } 484 485 @Test 486 public void testBlendMode_MULTIPLY() { 487 int redAlpha = Color.alpha(Color.RED); 488 int blueAlpha = Color.alpha(Color.BLUE); 489 int alpha = (redAlpha + blueAlpha) - (redAlpha * blueAlpha) / 255; 490 491 int dstRed = Color.red(DST_COLOR); 492 int srcRed = Color.red(SRC_COLOR); 493 494 int dstGreen = Color.green(DST_COLOR); 495 int srcGreen = Color.green(SRC_COLOR); 496 497 int dstBlue = Color.blue(DST_COLOR); 498 int srcBlue = Color.blue(SRC_COLOR); 499 500 int red = computeMultiply(dstRed, srcRed, redAlpha, blueAlpha); 501 int green = computeMultiply(dstGreen, srcGreen, redAlpha, blueAlpha); 502 int blue = computeMultiply(dstBlue, srcBlue, redAlpha, blueAlpha); 503 int resultColor = Color.argb(alpha, red, green, blue); 504 testBlendMode(BlendMode.MULTIPLY, DST_COLOR, resultColor, SRC_COLOR); 505 } 506 507 @Test 508 public void testBlendMode_COLOR() { 509 testBlendMode(BlendMode.COLOR, DST_COLOR, 0xFF3636FF, SRC_COLOR); 510 } 511 512 @Test 513 public void testBlendMode_SATURATION() { 514 testBlendMode(BlendMode.SATURATION, DST_COLOR, DST_COLOR, SRC_COLOR); 515 } 516 517 @Test 518 public void testBlendMode_LUMINOSITY() { 519 testBlendMode(BlendMode.LUMINOSITY, DST_COLOR, 0xFF5e0000, SRC_COLOR); 520 } 521 522 @Test 523 public void testBlendMode_HUE() { 524 testBlendMode(BlendMode.HUE, DST_COLOR, 0xFF3636FF, SRC_COLOR); 525 } 526 } 527