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 package android.uirendering.cts.testclasses; 17 18 import static org.junit.Assert.assertFalse; 19 20 import android.content.res.Resources; 21 import android.graphics.Bitmap; 22 import android.graphics.BitmapFactory; 23 import android.graphics.BitmapShader; 24 import android.graphics.Canvas; 25 import android.graphics.Color; 26 import android.graphics.ComposeShader; 27 import android.graphics.Matrix44; 28 import android.graphics.Paint; 29 import android.graphics.Path; 30 import android.graphics.Picture; 31 import android.graphics.Point; 32 import android.graphics.PorterDuff; 33 import android.graphics.RadialGradient; 34 import android.graphics.Rect; 35 import android.graphics.RectF; 36 import android.graphics.Region; 37 import android.graphics.Shader; 38 import android.platform.test.annotations.RequiresFlagsEnabled; 39 import android.platform.test.flag.junit.CheckFlagsRule; 40 import android.platform.test.flag.junit.DeviceFlagsValueProvider; 41 import android.uirendering.cts.R; 42 import android.uirendering.cts.bitmapverifiers.BitmapVerifier; 43 import android.uirendering.cts.bitmapverifiers.SamplePointVerifier; 44 import android.uirendering.cts.testinfrastructure.ActivityTestBase; 45 46 import androidx.test.InstrumentationRegistry; 47 import androidx.test.filters.MediumTest; 48 49 import com.android.graphics.hwui.flags.Flags; 50 51 import org.junit.Rule; 52 import org.junit.Test; 53 import org.junit.runner.RunWith; 54 55 import junitparams.JUnitParamsRunner; 56 import junitparams.Parameters; 57 58 @MediumTest 59 @RunWith(JUnitParamsRunner.class) 60 public class CanvasTests extends ActivityTestBase { 61 62 private static final int PAINT_COLOR = 0xff00ff00; 63 private static final int BITMAP_WIDTH = 10; 64 private static final int BITMAP_HEIGHT = 28; 65 66 @Rule 67 public final CheckFlagsRule mCheckFlagsRule = 68 DeviceFlagsValueProvider.createCheckFlagsRule(); 69 getPaint()70 private Paint getPaint() { 71 Paint paint = new Paint(); 72 paint.setColor(PAINT_COLOR); 73 return paint; 74 } 75 getImmutableBitmap()76 public Bitmap getImmutableBitmap() { 77 final Resources res = InstrumentationRegistry.getTargetContext().getResources(); 78 BitmapFactory.Options opt = new BitmapFactory.Options(); 79 opt.inScaled = false; // bitmap will only be immutable if not scaled during load 80 Bitmap immutableBitmap = BitmapFactory.decodeResource(res, R.drawable.sunset1, opt); 81 assertFalse(immutableBitmap.isMutable()); 82 return immutableBitmap; 83 } 84 getMutableBitmap(Bitmap.Config config)85 public Bitmap getMutableBitmap(Bitmap.Config config) { 86 return Bitmap.createBitmap(BITMAP_WIDTH, BITMAP_HEIGHT, config); 87 } 88 getMutableBitmap()89 public Bitmap getMutableBitmap() { 90 return getMutableBitmap(Bitmap.Config.ARGB_8888); 91 } 92 93 @Test testDrawDoubleRoundRect()94 public void testDrawDoubleRoundRect() { 95 Point[] testPoints = { 96 new Point(0, 0), 97 new Point(89, 0), 98 new Point(89, 89), 99 new Point(0, 89), 100 new Point(10, 10), 101 new Point(79, 10), 102 new Point(79, 79), 103 new Point(10, 79) 104 }; 105 int[] colors = { 106 Color.WHITE, 107 Color.WHITE, 108 Color.WHITE, 109 Color.WHITE, 110 Color.RED, 111 Color.RED, 112 Color.RED, 113 Color.RED 114 }; 115 createTest() 116 .addCanvasClient((canvas, width, height) -> { 117 Paint paint = new Paint(); 118 paint.setColor(Color.WHITE); 119 canvas.drawRect(0, 0, width, height, paint); 120 121 paint.setColor(Color.RED); 122 int inset = 20; 123 RectF outer = new RectF(0, 0, width, height); 124 RectF inner = new RectF(inset, 125 inset, 126 width - inset, 127 height - inset); 128 canvas.drawDoubleRoundRect(outer, 5, 5, 129 inner, 10, 10, paint); 130 }) 131 .runWithVerifier(new SamplePointVerifier(testPoints, colors)); 132 } 133 134 @Test testDrawDoubleRoundRectWithRadii()135 public void testDrawDoubleRoundRectWithRadii() { 136 Point[] testPoints = { 137 new Point(0, 0), 138 new Point(89, 0), 139 new Point(89, 89), 140 new Point(0, 89), 141 new Point(9, 7), 142 new Point(81, 7), 143 new Point(75, 75), 144 new Point(21, 67) 145 }; 146 int[] colors = { 147 Color.RED, 148 Color.WHITE, 149 Color.WHITE, 150 Color.WHITE, 151 Color.RED, 152 Color.RED, 153 Color.RED, 154 Color.RED 155 }; 156 createTest() 157 .addCanvasClient((canvas, width, height) -> { 158 Paint paint = new Paint(); 159 paint.setColor(Color.WHITE); 160 canvas.drawRect(0, 0, width, height, paint); 161 162 paint.setColor(Color.RED); 163 int inset = 30; 164 RectF outer = new RectF(0, 0, width, height); 165 RectF inner = new RectF(inset, 166 inset, 167 width - inset, 168 height - inset); 169 170 float[] outerRadii = { 171 0.0f, 0.0f, // top left 172 5.0f, 5.0f, // top right 173 10.0f, 10.0f, // bottom right 174 20.0f, 20.0f // bottom left 175 }; 176 177 float[] innerRadii = { 178 20.0f, 20.0f, 179 15.0f, 15.0f, 180 8.0f, 8.0f, 181 3.0f, 3.0f 182 }; 183 canvas.drawDoubleRoundRect(outer, outerRadii, inner, innerRadii, paint); 184 }) 185 .runWithVerifier(new SamplePointVerifier(testPoints, colors)); 186 } 187 188 @RequiresFlagsEnabled(Flags.FLAG_MATRIX_44) 189 @Test testDrawWithConcatenatedMatrix44()190 public void testDrawWithConcatenatedMatrix44() { 191 Point[] testPoints = { 192 new Point(0, 0), 193 new Point(45, 45), 194 new Point(45, 30), 195 new Point(45, 60), 196 new Point(20, 45), 197 new Point(60, 30), 198 new Point(60, 60) 199 200 }; 201 int[] colors = { 202 Color.WHITE, 203 Color.RED, 204 Color.RED, 205 Color.RED, 206 Color.RED, 207 Color.WHITE, 208 Color.WHITE 209 }; 210 createTest() 211 .addCanvasClient((canvas, width, height) -> { 212 canvas.drawColor(Color.WHITE); 213 214 Paint paint = new Paint(); 215 paint.setColor(Color.RED); 216 paint.setStyle(Paint.Style.FILL); 217 218 Matrix44 mat = new Matrix44(); 219 // column vector operations are read backwards, so the rect should be centered 220 mat.translate((float) width / 2, (float) height / 2, 0) 221 .rotate(45, 1, 0, 0) 222 .scale(2, 2, 0) 223 .rotate(45, 0, 0, 1); 224 canvas.concat(mat); 225 canvas.drawRect(-10, -10, 10, 10, paint); 226 }) 227 .runWithVerifier(new SamplePointVerifier(testPoints, colors)); 228 } 229 drawRotatedBitmap(boolean aa, Canvas canvas, Bitmap.Config config)230 private void drawRotatedBitmap(boolean aa, Canvas canvas, Bitmap.Config config) { 231 // create a black bitmap to be drawn to the canvas 232 Bitmap bm = getMutableBitmap(); 233 bm.eraseColor(Color.BLACK); 234 235 // canvas density and bitmap density must match in order for no scaling to occur 236 // and aa to be distinguishable from non-aa 237 bm.setDensity(canvas.getDensity()); 238 239 canvas.drawColor(Color.WHITE); 240 241 Paint aaPaint = new Paint(); 242 aaPaint.setAntiAlias(aa); 243 244 canvas.rotate(-1.0f, 0, 0); 245 canvas.drawBitmap(bm, 0, 0, aaPaint); 246 } 247 testConfigs()248 private Object[] testConfigs() { 249 return new Object[] { 250 Bitmap.Config.ARGB_8888, 251 Bitmap.Config.RGBA_1010102 252 }; 253 } 254 255 @Test 256 @Parameters(method = "testConfigs") testDrawRotatedBitmapWithAA(Bitmap.Config config)257 public void testDrawRotatedBitmapWithAA(Bitmap.Config config) { 258 createTest() 259 .addCanvasClient((canvas, width, height) -> { 260 canvas.setDensity(400); 261 drawRotatedBitmap(true, canvas, config); 262 }) 263 // Test asserts there are more than 10 grey pixels. 264 .runWithVerifier(AntiAliasPixelCounter.aaVerifier(Color.WHITE, Color.BLACK, 10)); 265 } 266 267 @Test 268 @Parameters(method = "testConfigs") testDrawRotatedBitmapWithoutAA(Bitmap.Config config)269 public void testDrawRotatedBitmapWithoutAA(Bitmap.Config config) { 270 createTest() 271 .addCanvasClient((canvas, width, height) -> { 272 canvas.setDensity(400); 273 drawRotatedBitmap(false, canvas, config); 274 }) 275 // Test asserts there are no grey pixels. 276 .runWithVerifier(AntiAliasPixelCounter.noAAVerifier(Color.WHITE, Color.BLACK)); 277 } 278 279 @Test(expected = IllegalArgumentException.class) testDrawHwBitmap_inSwCanvas()280 public void testDrawHwBitmap_inSwCanvas() { 281 Bitmap hwBitmap = getImmutableBitmap().copy(Bitmap.Config.HARDWARE, false); 282 // we verify this specific call should IAE 283 new Canvas(getMutableBitmap()).drawBitmap(hwBitmap, 0, 0, null); 284 } 285 286 @Test(expected = IllegalArgumentException.class) testDrawHwBitmap_inPictureCanvas_inSwCanvas()287 public void testDrawHwBitmap_inPictureCanvas_inSwCanvas() { 288 Bitmap hwBitmap = getImmutableBitmap().copy(Bitmap.Config.HARDWARE, false); 289 Picture picture = new Picture(); 290 Canvas pictureCanvas = picture.beginRecording(100, 100); 291 pictureCanvas.drawBitmap(hwBitmap, 0, 0, null); 292 // we verify this specific call should IAE 293 new Canvas(getMutableBitmap()).drawPicture(picture); 294 } 295 296 @Test(expected = IllegalArgumentException.class) testDrawHwBitmap_inPictureCanvas_inPictureCanvas_inSwCanvas()297 public void testDrawHwBitmap_inPictureCanvas_inPictureCanvas_inSwCanvas() { 298 Bitmap hwBitmap = getImmutableBitmap().copy(Bitmap.Config.HARDWARE, false); 299 Picture innerPicture = new Picture(); 300 Canvas pictureCanvas = innerPicture.beginRecording(100, 100); 301 pictureCanvas.drawBitmap(hwBitmap, 0, 0, null); 302 303 Picture outerPicture = new Picture(); 304 Canvas outerPictureCanvas = outerPicture.beginRecording(100, 100); 305 outerPictureCanvas.drawPicture(innerPicture); 306 // we verify this specific call should IAE 307 new Canvas(getMutableBitmap()).drawPicture(outerPicture); 308 } 309 310 @Test(expected = IllegalArgumentException.class) testHwBitmapShaderInSwCanvas1()311 public void testHwBitmapShaderInSwCanvas1() { 312 Bitmap hwBitmap = getImmutableBitmap().copy(Bitmap.Config.HARDWARE, false); 313 BitmapShader bitmapShader = new BitmapShader(hwBitmap, Shader.TileMode.REPEAT, 314 Shader.TileMode.REPEAT); 315 RadialGradient gradientShader = new RadialGradient(10, 10, 30, 316 Color.BLACK, Color.CYAN, Shader.TileMode.REPEAT); 317 Shader shader = new ComposeShader(gradientShader, bitmapShader, PorterDuff.Mode.OVERLAY); 318 Paint p = new Paint(); 319 p.setShader(shader); 320 new Canvas(getMutableBitmap()).drawRect(0, 0, 10, 10, p); 321 } 322 323 @Test(expected = IllegalArgumentException.class) testHwBitmapShaderInSwCanvas2()324 public void testHwBitmapShaderInSwCanvas2() { 325 Bitmap hwBitmap = getImmutableBitmap().copy(Bitmap.Config.HARDWARE, false); 326 BitmapShader bitmapShader = new BitmapShader(hwBitmap, Shader.TileMode.REPEAT, 327 Shader.TileMode.REPEAT); 328 RadialGradient gradientShader = new RadialGradient(10, 10, 30, 329 Color.BLACK, Color.CYAN, Shader.TileMode.REPEAT); 330 Shader shader = new ComposeShader(bitmapShader, gradientShader, PorterDuff.Mode.OVERLAY); 331 Paint p = new Paint(); 332 p.setShader(shader); 333 new Canvas(getMutableBitmap()).drawRect(0, 0, 10, 10, p); 334 } 335 336 @Test(expected = IllegalStateException.class) testCanvasFromImmutableBitmap()337 public void testCanvasFromImmutableBitmap() { 338 // Should throw out IllegalStateException when creating Canvas with an ImmutableBitmap 339 new Canvas(getImmutableBitmap()); 340 } 341 342 @Test(expected = RuntimeException.class) testCanvasFromRecycledBitmap()343 public void testCanvasFromRecycledBitmap() { 344 // Should throw out RuntimeException when creating Canvas with a MutableBitmap which 345 // is recycled 346 Bitmap bitmap = getMutableBitmap(); 347 bitmap.recycle(); 348 new Canvas(bitmap); 349 } 350 351 @Test(expected = IllegalStateException.class) testSetBitmapToImmutableBitmap()352 public void testSetBitmapToImmutableBitmap() { 353 // Should throw out IllegalStateException when setting an ImmutableBitmap to a Canvas 354 new Canvas(getMutableBitmap()).setBitmap(getImmutableBitmap()); 355 } 356 357 @Test(expected = RuntimeException.class) testSetBitmapToRecycledBitmap()358 public void testSetBitmapToRecycledBitmap() { 359 // Should throw out RuntimeException when setting Bitmap which is recycled to a Canvas 360 Bitmap bitmap = getMutableBitmap(); 361 bitmap.recycle(); 362 new Canvas(getMutableBitmap()).setBitmap(bitmap); 363 } 364 365 @Test(expected = IllegalStateException.class) testRestoreWithoutSave()366 public void testRestoreWithoutSave() { 367 // Should throw out IllegalStateException because cannot restore Canvas before save 368 new Canvas(getMutableBitmap()).restore(); 369 } 370 371 @Test(expected = IllegalArgumentException.class) testRestoreToCountIllegalSaveCount()372 public void testRestoreToCountIllegalSaveCount() { 373 // Should throw out IllegalArgumentException because saveCount is less than 1 374 new Canvas(getMutableBitmap()).restoreToCount(0); 375 } 376 377 @Test(expected = ArrayIndexOutOfBoundsException.class) testDrawPointsInvalidOffset()378 public void testDrawPointsInvalidOffset() { 379 // Should throw out ArrayIndexOutOfBoundsException because of invalid offset 380 new Canvas(getMutableBitmap()).drawPoints(new float[]{ 381 10.0f, 29.0f 382 }, -1, 2, getPaint()); 383 } 384 385 @Test(expected = ArrayIndexOutOfBoundsException.class) testDrawPointsInvalidCount()386 public void testDrawPointsInvalidCount() { 387 // Should throw out ArrayIndexOutOfBoundsException because of invalid count 388 new Canvas(getMutableBitmap()).drawPoints(new float[]{ 389 10.0f, 29.0f 390 }, 0, 31, getPaint()); 391 } 392 393 @Test(expected = ArrayIndexOutOfBoundsException.class) testDrawLinesInvalidOffset()394 public void testDrawLinesInvalidOffset() { 395 // Should throw out ArrayIndexOutOfBoundsException because of invalid offset 396 new Canvas(getMutableBitmap()).drawLines(new float[]{ 397 0, 0, 10, 31 398 }, 2, 4, new Paint()); 399 } 400 401 @Test(expected = ArrayIndexOutOfBoundsException.class) testDrawLinesInvalidCount()402 public void testDrawLinesInvalidCount() { 403 // Should throw out ArrayIndexOutOfBoundsException because of invalid count 404 new Canvas(getMutableBitmap()).drawLines(new float[]{ 405 0, 0, 10, 31 406 }, 0, 8, new Paint()); 407 } 408 409 @Test(expected = NullPointerException.class) testDrawOvalNull()410 public void testDrawOvalNull() { 411 // Should throw out NullPointerException because oval is null 412 new Canvas(getMutableBitmap()).drawOval(null, getPaint()); 413 } 414 415 @Test(expected = NullPointerException.class) testDrawArcNullOval()416 public void testDrawArcNullOval() { 417 // Should throw NullPointerException because oval is null 418 new Canvas(getMutableBitmap()).drawArc(null, 10.0f, 29.0f, 419 true, getPaint()); 420 } 421 422 @Test(expected = NullPointerException.class) testDrawRoundRectNull()423 public void testDrawRoundRectNull() { 424 // Should throw out NullPointerException because RoundRect is null 425 new Canvas(getMutableBitmap()).drawRoundRect(null, 10.0f, 29.0f, getPaint()); 426 } 427 428 @Test(expected = RuntimeException.class) testDrawBitmapAtPointRecycled()429 public void testDrawBitmapAtPointRecycled() { 430 Bitmap b = Bitmap.createBitmap(BITMAP_WIDTH, 29, Bitmap.Config.ARGB_8888); 431 b.recycle(); 432 433 // Should throw out RuntimeException because bitmap has been recycled 434 new Canvas(getMutableBitmap()).drawBitmap(b, 10.0f, 29.0f, getPaint()); 435 } 436 437 @Test(expected = RuntimeException.class) testDrawBitmapSrcDstFloatRecycled()438 public void testDrawBitmapSrcDstFloatRecycled() { 439 Bitmap b = Bitmap.createBitmap(BITMAP_WIDTH, 29, Bitmap.Config.ARGB_8888); 440 b.recycle(); 441 442 // Should throw out RuntimeException because bitmap has been recycled 443 new Canvas(getMutableBitmap()).drawBitmap(b, null, new RectF(), getPaint()); 444 } 445 446 @Test(expected = RuntimeException.class) testDrawBitmapSrcDstIntRecycled()447 public void testDrawBitmapSrcDstIntRecycled() { 448 Bitmap b = Bitmap.createBitmap(BITMAP_WIDTH, 29, Bitmap.Config.ARGB_8888); 449 b.recycle(); 450 451 // Should throw out RuntimeException because bitmap has been recycled 452 new Canvas(getMutableBitmap()).drawBitmap(b, null, new Rect(), getPaint()); 453 } 454 455 @Test(expected = IllegalArgumentException.class) testDrawBitmapIntsNegativeWidth()456 public void testDrawBitmapIntsNegativeWidth() { 457 // Should throw out IllegalArgumentException because width is less than 0 458 new Canvas(getMutableBitmap()).drawBitmap(new int[2008], 10, 10, 10, 459 10, -1, 10, true, null); 460 } 461 462 @Test(expected = IllegalArgumentException.class) testDrawBitmapIntsNegativeHeight()463 public void testDrawBitmapIntsNegativeHeight() { 464 // Should throw out IllegalArgumentException because height is less than 0 465 new Canvas(getMutableBitmap()).drawBitmap(new int[2008], 10, 10, 10, 466 10, 10, -1, true, null); 467 } 468 469 @Test(expected = IllegalArgumentException.class) testDrawBitmapIntsBadStride()470 public void testDrawBitmapIntsBadStride() { 471 // Should throw out IllegalArgumentException because stride less than width and 472 // bigger than -width 473 new Canvas(getMutableBitmap()).drawBitmap(new int[2008], 10, 5, 10, 474 10, 10, 10, true, null); 475 } 476 477 @Test(expected = ArrayIndexOutOfBoundsException.class) testDrawBitmapIntsNegativeOffset()478 public void testDrawBitmapIntsNegativeOffset() { 479 // Should throw out ArrayIndexOutOfBoundsException because offset less than 0 480 new Canvas(getMutableBitmap()).drawBitmap(new int[2008], -1, 10, 10, 481 10, 10, 10, true, null); 482 } 483 484 @Test(expected = ArrayIndexOutOfBoundsException.class) testDrawBitmapIntsBadOffset()485 public void testDrawBitmapIntsBadOffset() { 486 // Should throw out ArrayIndexOutOfBoundsException because sum of offset and width 487 // is bigger than colors' length 488 new Canvas(getMutableBitmap()).drawBitmap(new int[29], 10, 29, 10, 489 10, 20, 10, true, null); 490 } 491 492 @Test(expected = IllegalArgumentException.class) testDrawBitmapFloatsNegativeWidth()493 public void testDrawBitmapFloatsNegativeWidth() { 494 // Should throw out IllegalArgumentException because width is less than 0 495 new Canvas(getMutableBitmap()).drawBitmap(new int[2008], 10, 10, 10.0f, 496 10.0f, -1, 10, true, null); 497 } 498 499 @Test(expected = IllegalArgumentException.class) testDrawBitmapFloatsNegativeHeight()500 public void testDrawBitmapFloatsNegativeHeight() { 501 // Should throw out IllegalArgumentException because height is less than 0 502 new Canvas(getMutableBitmap()).drawBitmap(new int[2008], 10, 10, 10.0f, 503 10.0f, 10, -1, true, null); 504 } 505 506 @Test(expected = IllegalArgumentException.class) testDrawBitmapFloatsBadStride()507 public void testDrawBitmapFloatsBadStride() { 508 // Should throw out IllegalArgumentException because stride less than width and 509 // bigger than -width 510 new Canvas(getMutableBitmap()).drawBitmap(new int[2008], 10, 5, 10.0f, 511 10.0f, 10, 10, true, null); 512 } 513 514 @Test(expected = ArrayIndexOutOfBoundsException.class) testDrawBitmapFloatsNegativeOffset()515 public void testDrawBitmapFloatsNegativeOffset() { 516 // Should throw out ArrayIndexOutOfBoundsException because offset less than 0 517 new Canvas(getMutableBitmap()).drawBitmap(new int[2008], -1, 10, 10.0f, 518 10.0f, 10, 10, true, null); 519 } 520 521 @Test(expected = ArrayIndexOutOfBoundsException.class) testDrawBitmapFloatsBadOffset()522 public void testDrawBitmapFloatsBadOffset() { 523 // Should throw out ArrayIndexOutOfBoundsException because sum of offset and width 524 // is bigger than colors' length 525 new Canvas(getMutableBitmap()).drawBitmap(new int[29], 10, 29, 10.0f, 526 10.0f, 20, 10, true, null); 527 } 528 529 @Test(expected = ArrayIndexOutOfBoundsException.class) testDrawBitmapMeshNegativeWidth()530 public void testDrawBitmapMeshNegativeWidth() { 531 final Bitmap b = Bitmap.createBitmap(BITMAP_WIDTH, 29, Bitmap.Config.ARGB_8888); 532 533 // Should throw out ArrayIndexOutOfBoundsException because meshWidth less than 0 534 new Canvas(getMutableBitmap()).drawBitmapMesh(b, -1, 10, 535 null, 0, null, 0, null); 536 } 537 538 @Test(expected = ArrayIndexOutOfBoundsException.class) testDrawBitmapMeshNegativeHeight()539 public void testDrawBitmapMeshNegativeHeight() { 540 final Bitmap b = Bitmap.createBitmap(BITMAP_WIDTH, 29, Bitmap.Config.ARGB_8888); 541 542 // Should throw out ArrayIndexOutOfBoundsException because meshHeight is less than 0 543 new Canvas(getMutableBitmap()).drawBitmapMesh(b, 10, -1, 544 null, 0, null, 0, null); 545 } 546 547 @Test(expected = ArrayIndexOutOfBoundsException.class) testDrawBitmapMeshNegativeVertOffset()548 public void testDrawBitmapMeshNegativeVertOffset() { 549 final Bitmap b = Bitmap.createBitmap(BITMAP_WIDTH, 29, Bitmap.Config.ARGB_8888); 550 551 // Should throw out ArrayIndexOutOfBoundsException because vertOffset is less than 0 552 new Canvas(getMutableBitmap()).drawBitmapMesh(b, 10, 10, 553 null, -1, null, 0, null); 554 } 555 556 @Test(expected = ArrayIndexOutOfBoundsException.class) testDrawBitmapMeshNegativeColorOffset()557 public void testDrawBitmapMeshNegativeColorOffset() { 558 final Bitmap b = Bitmap.createBitmap(BITMAP_WIDTH, 29, Bitmap.Config.ARGB_8888); 559 560 // Should throw out ArrayIndexOutOfBoundsException because colorOffset is less than 0 561 new Canvas(getMutableBitmap()).drawBitmapMesh(b, 10, 10, 562 null, 10, null, -1, null); 563 } 564 565 @Test(expected = ArrayIndexOutOfBoundsException.class) testDrawBitmapMeshTooFewVerts()566 public void testDrawBitmapMeshTooFewVerts() { 567 final Bitmap b = Bitmap.createBitmap(BITMAP_WIDTH, 29, Bitmap.Config.ARGB_8888); 568 569 // Should throw out ArrayIndexOutOfBoundsException because verts' length is too short 570 new Canvas(getMutableBitmap()).drawBitmapMesh(b, 10, 10, 571 new float[] { 10.0f, 29.0f}, 10, null, 10, null); 572 } 573 574 @Test(expected = ArrayIndexOutOfBoundsException.class) testDrawBitmapMeshTooFewColors()575 public void testDrawBitmapMeshTooFewColors() { 576 final Bitmap b = Bitmap.createBitmap(BITMAP_WIDTH, 29, Bitmap.Config.ARGB_8888); 577 578 // Should throw out ArrayIndexOutOfBoundsException because colors' length is too short 579 // abnormal case: colors' length is too short 580 final float[] verts = new float[2008]; 581 new Canvas(getMutableBitmap()).drawBitmapMesh(b, 10, 10, verts, 582 10, new int[] { 10, 29}, 10, null); 583 } 584 585 @Test(expected = ArrayIndexOutOfBoundsException.class) testDrawVerticesTooFewVerts()586 public void testDrawVerticesTooFewVerts() { 587 final float[] verts = new float[10]; 588 final float[] texs = new float[10]; 589 final int[] colors = new int[10]; 590 final short[] indices = { 591 0, 1, 2, 3, 4, 1 592 }; 593 594 // Should throw out ArrayIndexOutOfBoundsException because sum of vertOffset and 595 // vertexCount is bigger than verts' length 596 new Canvas(getMutableBitmap()).drawVertices(Canvas.VertexMode.TRIANGLES, 10, 597 verts, 8, texs, 0, colors, 0, indices, 598 0, 4, getPaint()); 599 } 600 601 @Test(expected = ArrayIndexOutOfBoundsException.class) testDrawVerticesTooFewTexs()602 public void testDrawVerticesTooFewTexs() { 603 final float[] verts = new float[10]; 604 final float[] texs = new float[10]; 605 final int[] colors = new int[10]; 606 final short[] indices = { 607 0, 1, 2, 3, 4, 1 608 }; 609 610 // Should throw out ArrayIndexOutOfBoundsException because sum of texOffset and 611 // vertexCount is bigger thatn texs' length 612 new Canvas(getMutableBitmap()).drawVertices(Canvas.VertexMode.TRIANGLES, 10, 613 verts, 0, texs, 30, colors, 0, indices, 614 0, 4, getPaint()); 615 } 616 617 @Test(expected = ArrayIndexOutOfBoundsException.class) testDrawVerticesTooFewColors()618 public void testDrawVerticesTooFewColors() { 619 final float[] verts = new float[10]; 620 final float[] texs = new float[10]; 621 final int[] colors = new int[10]; 622 final short[] indices = { 623 0, 1, 2, 3, 4, 1 624 }; 625 626 // Should throw out ArrayIndexOutOfBoundsException because sum of colorOffset and 627 // vertexCount is bigger than colors' length 628 new Canvas(getMutableBitmap()).drawVertices(Canvas.VertexMode.TRIANGLES, 10, 629 verts, 0, texs, 0, colors, 30, indices, 630 0, 4, getPaint()); 631 } 632 633 @Test(expected = ArrayIndexOutOfBoundsException.class) testDrawVerticesTooFewIndices()634 public void testDrawVerticesTooFewIndices() { 635 final float[] verts = new float[10]; 636 final float[] texs = new float[10]; 637 final int[] colors = new int[10]; 638 final short[] indices = { 639 0, 1, 2, 3, 4, 1 640 }; 641 642 // Should throw out ArrayIndexOutOfBoundsException because sum of indexOffset and 643 // indexCount is bigger than indices' length 644 new Canvas(getMutableBitmap()).drawVertices(Canvas.VertexMode.TRIANGLES, 10, 645 verts, 0, texs, 0, colors, 0, indices, 646 10, 30, getPaint()); 647 } 648 649 @Test(expected = IndexOutOfBoundsException.class) testDrawArrayTextNegativeIndex()650 public void testDrawArrayTextNegativeIndex() { 651 final char[] text = { 'a', 'n', 'd', 'r', 'o', 'i', 'd' }; 652 653 // Should throw out IndexOutOfBoundsException because index is less than 0 654 new Canvas(getMutableBitmap()).drawText(text, -1, 7, 10, 10, getPaint()); 655 } 656 657 @Test(expected = IndexOutOfBoundsException.class) testDrawArrayTextNegativeCount()658 public void testDrawArrayTextNegativeCount() { 659 final char[] text = { 'a', 'n', 'd', 'r', 'o', 'i', 'd' }; 660 661 // Should throw out IndexOutOfBoundsException because count is less than 0 662 new Canvas(getMutableBitmap()).drawText(text, 0, -1, 10, 10, getPaint()); 663 } 664 665 @Test(expected = IndexOutOfBoundsException.class) testDrawArrayTextTextLengthTooSmall()666 public void testDrawArrayTextTextLengthTooSmall() { 667 final char[] text = { 'a', 'n', 'd', 'r', 'o', 'i', 'd' }; 668 669 // Should throw out IndexOutOfBoundsException because sum of index and count 670 // is bigger than text's length 671 new Canvas(getMutableBitmap()).drawText(text, 0, 10, 10, 10, getPaint()); 672 } 673 674 @Test(expected = IndexOutOfBoundsException.class) testDrawTextTextAtPositionWithOffsetsNegativeStart()675 public void testDrawTextTextAtPositionWithOffsetsNegativeStart() { 676 // Should throw out IndexOutOfBoundsException because start is less than 0 677 new Canvas(getMutableBitmap()).drawText("android", -1, 7, 10, 30, 678 getPaint()); 679 } 680 681 @Test(expected = IndexOutOfBoundsException.class) testDrawTextTextAtPositionWithOffsetsNegativeEnd()682 public void testDrawTextTextAtPositionWithOffsetsNegativeEnd() { 683 // Should throw out IndexOutOfBoundsException because end is less than 0 684 new Canvas(getMutableBitmap()).drawText("android", 0, -1, 10, 30, 685 getPaint()); 686 } 687 688 @Test(expected = IndexOutOfBoundsException.class) testDrawTextTextAtPositionWithOffsetsStartEndMismatch()689 public void testDrawTextTextAtPositionWithOffsetsStartEndMismatch() { 690 // Should throw out IndexOutOfBoundsException because start is bigger than end 691 new Canvas(getMutableBitmap()).drawText("android", 3, 1, 10, 30, 692 getPaint()); 693 } 694 695 @Test(expected = IndexOutOfBoundsException.class) testDrawTextTextAtPositionWithOffsetsTextTooLong()696 public void testDrawTextTextAtPositionWithOffsetsTextTooLong() { 697 // Should throw out IndexOutOfBoundsException because end subtracts start should 698 // bigger than text's length 699 new Canvas(getMutableBitmap()).drawText("android", 0, 10, 10, 30, 700 getPaint()); 701 } 702 703 @Test(expected = NullPointerException.class) testDrawTextRunNullCharArray()704 public void testDrawTextRunNullCharArray() { 705 // Should throw out NullPointerException because text is null 706 new Canvas(getMutableBitmap()).drawTextRun((char[]) null, 0, 0, 707 0, 0, 0.0f, 0.0f, false, new Paint()); 708 } 709 710 @Test(expected = NullPointerException.class) testDrawTextRunNullCharSequence()711 public void testDrawTextRunNullCharSequence() { 712 // Should throw out NullPointerException because text is null 713 new Canvas(getMutableBitmap()).drawTextRun((CharSequence) null, 0, 0, 714 0, 0, 0.0f, 0.0f, false, new Paint()); 715 } 716 717 @Test(expected = NullPointerException.class) testDrawTextRunCharArrayNullPaint()718 public void testDrawTextRunCharArrayNullPaint() { 719 // Should throw out NullPointerException because paint is null 720 new Canvas(getMutableBitmap()).drawTextRun("android".toCharArray(), 0, 0, 721 0, 0, 0.0f, 0.0f, false, null); 722 } 723 724 @Test(expected = NullPointerException.class) testDrawTextRunCharSequenceNullPaint()725 public void testDrawTextRunCharSequenceNullPaint() { 726 // Should throw out NullPointerException because paint is null 727 new Canvas(getMutableBitmap()).drawTextRun("android", 0, 0, 0, 728 0, 0.0f, 0.0f, false, null); 729 } 730 731 @Test(expected = IndexOutOfBoundsException.class) testDrawTextRunNegativeIndex()732 public void testDrawTextRunNegativeIndex() { 733 final String text = "android"; 734 final Paint paint = new Paint(); 735 736 // Should throw out IndexOutOfBoundsException because index is less than 0 737 new Canvas(getMutableBitmap()).drawTextRun(text.toCharArray(), -1, text.length(), 738 0, text.length(), 0.0f, 0.0f, 739 false, new Paint()); 740 } 741 742 @Test(expected = IndexOutOfBoundsException.class) testDrawTextRunNegativeCount()743 public void testDrawTextRunNegativeCount() { 744 final String text = "android"; 745 746 // Should throw out IndexOutOfBoundsException because count is less than 0 747 new Canvas(getMutableBitmap()).drawTextRun(text.toCharArray(), 0, -1, 748 0, text.length(), 0.0f, 0.0f, false, 749 new Paint()); 750 } 751 752 @Test(expected = IndexOutOfBoundsException.class) testDrawTextRunContestIndexTooLarge()753 public void testDrawTextRunContestIndexTooLarge() { 754 final String text = "android"; 755 756 // Should throw out IndexOutOfBoundsException because contextIndex is bigger than index 757 new Canvas(getMutableBitmap()).drawTextRun(text.toCharArray(), 0, text.length(), 758 1, text.length(), 0.0f, 0.0f, 759 false, new Paint()); 760 } 761 762 @Test(expected = IndexOutOfBoundsException.class) testDrawTextRunContestIndexTooSmall()763 public void testDrawTextRunContestIndexTooSmall() { 764 final String text = "android"; 765 766 // Should throw out IndexOutOfBoundsException because contextIndex + contextCount 767 // is less than index + count 768 new Canvas(getMutableBitmap()).drawTextRun(text, 0, text.length(), 0, 769 text.length() - 1, 0.0f, 0.0f, false, 770 new Paint()); 771 } 772 773 @Test(expected = IndexOutOfBoundsException.class) testDrawTextRunIndexTooLarge()774 public void testDrawTextRunIndexTooLarge() { 775 final String text = "android"; 776 final Paint paint = new Paint(); 777 778 // Should throw out IndexOutOfBoundsException because index + count is bigger than 779 // text length 780 new Canvas(getMutableBitmap()).drawTextRun(text.toCharArray(), 0, 781 text.length() + 1, 0, text.length() + 1, 782 0.0f, 0.0f, false, new Paint()); 783 } 784 785 @Test(expected = IndexOutOfBoundsException.class) testDrawTextRunNegativeContextStart()786 public void testDrawTextRunNegativeContextStart() { 787 final String text = "android"; 788 final Paint paint = new Paint(); 789 790 // Should throw out IndexOutOfBoundsException because contextStart is less than 0 791 new Canvas(getMutableBitmap()).drawTextRun(text, 0, text.length(), -1, 792 text.length(), 0.0f, 0.0f, false, 793 new Paint()); 794 } 795 796 @Test(expected = IndexOutOfBoundsException.class) testDrawTextRunStartLessThanContextStart()797 public void testDrawTextRunStartLessThanContextStart() { 798 final String text = "android"; 799 800 // Should throw out IndexOutOfBoundsException because start is less than contextStart 801 new Canvas(getMutableBitmap()).drawTextRun(text, 0, text.length(), 1, 802 text.length(), 0.0f, 0.0f, false, 803 new Paint()); 804 } 805 806 @Test(expected = IndexOutOfBoundsException.class) testDrawTextRunEndLessThanStart()807 public void testDrawTextRunEndLessThanStart() { 808 final String text = "android"; 809 810 // Should throw out IndexOutOfBoundsException because end is less than start 811 new Canvas(getMutableBitmap()).drawTextRun(text, 1, 0, 0, 812 text.length(), 0.0f, 0.0f, false, new Paint()); 813 } 814 815 @Test(expected = IndexOutOfBoundsException.class) testDrawTextRunContextEndLessThanEnd()816 public void testDrawTextRunContextEndLessThanEnd() { 817 final String text = "android"; 818 819 // Should throw out IndexOutOfBoundsException because contextEnd is less than end 820 new Canvas(getMutableBitmap()).drawTextRun(text, 0, text.length(), 0, 821 text.length() - 1, 0.0f, 0.0f, false, 822 new Paint()); 823 } 824 825 @Test(expected = IndexOutOfBoundsException.class) testDrawTextRunContextEndLargerThanTextLength()826 public void testDrawTextRunContextEndLargerThanTextLength() { 827 final String text = "android"; 828 829 // Should throw out IndexOutOfBoundsException because contextEnd is bigger than 830 // text length 831 new Canvas(getMutableBitmap()).drawTextRun(text, 0, text.length(), 0, 832 text.length() + 1, 0.0f, 0.0f, false, 833 new Paint()); 834 } 835 836 @Test(expected = IndexOutOfBoundsException.class) testDrawPosTextWithIndexAndCountNegativeIndex()837 public void testDrawPosTextWithIndexAndCountNegativeIndex() { 838 final char[] text = { 839 'a', 'n', 'd', 'r', 'o', 'i', 'd' 840 }; 841 final float[] pos = new float[]{ 842 0.0f, 0.0f, 1.0f, 1.0f, 2.0f, 2.0f, 3.0f, 3.0f, 4.0f, 4.0f, 5.0f, 5.0f, 6.0f, 6.0f, 843 7.0f, 7.0f 844 }; 845 846 // Should throw out IndexOutOfBoundsException because index is less than 0 847 new Canvas(getMutableBitmap()).drawPosText(text, -1, 7, pos, getPaint()); 848 } 849 850 @Test(expected = IndexOutOfBoundsException.class) testDrawPosTextWithIndexAndCountTextTooShort()851 public void testDrawPosTextWithIndexAndCountTextTooShort() { 852 final char[] text = { 853 'a', 'n', 'd', 'r', 'o', 'i', 'd' 854 }; 855 final float[] pos = new float[]{ 856 0.0f, 0.0f, 1.0f, 1.0f, 2.0f, 2.0f, 3.0f, 3.0f, 4.0f, 4.0f, 5.0f, 5.0f, 6.0f, 6.0f, 857 7.0f, 7.0f 858 }; 859 860 // Should throw out IndexOutOfBoundsException because sum of index and count is 861 // bigger than text's length 862 new Canvas(getMutableBitmap()).drawPosText(text, 1, 10, pos, getPaint()); 863 } 864 865 @Test(expected = IndexOutOfBoundsException.class) testDrawPosTextWithIndexAndCountCountTooLarge()866 public void testDrawPosTextWithIndexAndCountCountTooLarge() { 867 final char[] text = { 868 'a', 'n', 'd', 'r', 'o', 'i', 'd' 869 }; 870 871 // Should throw out IndexOutOfBoundsException because 2 times of count is 872 // bigger than pos' length 873 new Canvas(getMutableBitmap()).drawPosText(text, 1, 10, new float[] { 874 10.0f, 30.f 875 }, getPaint()); 876 } 877 878 @Test(expected = ArrayIndexOutOfBoundsException.class) testDrawTextOnPathWithIndexAndCountNegativeIndex()879 public void testDrawTextOnPathWithIndexAndCountNegativeIndex() { 880 final char[] text = { 'a', 'n', 'd', 'r', 'o', 'i', 'd' }; 881 882 // Should throw out ArrayIndexOutOfBoundsException because index is smaller than 0 883 new Canvas(getMutableBitmap()).drawTextOnPath(text, -1, 7, new Path(), 884 10.0f, 10.0f, getPaint()); 885 } 886 887 @Test(expected = ArrayIndexOutOfBoundsException.class) testDrawTextOnPathWithIndexAndCountTextTooShort()888 public void testDrawTextOnPathWithIndexAndCountTextTooShort() { 889 final char[] text = { 'a', 'n', 'd', 'r', 'o', 'i', 'd' }; 890 891 // Should throw out ArrayIndexOutOfBoundsException because sum of index and 892 // count is bigger than text's length 893 new Canvas(getMutableBitmap()).drawTextOnPath(text, 0, 10, new Path(), 894 10.0f, 10.0f, getPaint()); 895 } 896 897 @Test(expected = IndexOutOfBoundsException.class) testDrawPosTextCountTooLarge()898 public void testDrawPosTextCountTooLarge() { 899 final String text = "android"; 900 901 // Should throw out IndexOutOfBoundsException because 2 times of count is 902 // bigger than pos' length 903 new Canvas(getMutableBitmap()).drawPosText(text, new float[]{ 904 10.0f, 30.f 905 }, getPaint()); 906 } 907 908 @Test testClipIntersectAndDifference()909 public void testClipIntersectAndDifference() { 910 Point[] testPoints = { 911 new Point(1, 1), 912 new Point(10, 10), 913 new Point(25, 25) 914 }; 915 int[] colors = { 916 Color.WHITE, 917 Color.RED, 918 Color.GREEN 919 }; 920 createTest() 921 .addCanvasClient((canvas, width, height) -> { 922 // Base is white 923 Paint paint = new Paint(); 924 paint.setColor(Color.WHITE); 925 canvas.drawRect(0, 0, width, height, paint); 926 927 // Fill inset with green, which will later be overwitten with 928 // red except for a subsequent difference clip op 929 canvas.clipRect(5, 5, width - 5, height - 5); 930 paint.setColor(Color.GREEN); 931 canvas.drawRect(0, 0, width, height, paint); 932 933 // Cut out the inner region, so that it remains green 934 canvas.clipOutRect(20, 20, width - 20, height - 20); 935 paint.setColor(Color.RED); 936 canvas.drawRect(0, 0, width, height, paint); 937 }) 938 .runWithVerifier(new SamplePointVerifier(testPoints, colors)); 939 } 940 941 // Expanding region ops (replace, reverse diff, union, and xor) are not allowed for clipping 942 @Test(expected = IllegalArgumentException.class) testClipReplace()943 public void testClipReplace() { 944 new Canvas(getMutableBitmap()).clipRect(0, 0, 10, 10, Region.Op.REPLACE); 945 } 946 947 @Test(expected = IllegalArgumentException.class) testClipReverseDifference()948 public void testClipReverseDifference() { 949 new Canvas(getMutableBitmap()).clipRect(0, 0, 10, 10, Region.Op.REVERSE_DIFFERENCE); 950 } 951 952 @Test(expected = IllegalArgumentException.class) testClipUnion()953 public void testClipUnion() { 954 new Canvas(getMutableBitmap()).clipRect(0, 0, 10, 10, Region.Op.UNION); 955 } 956 957 @Test(expected = IllegalArgumentException.class) testClipXor()958 public void testClipXor() { 959 new Canvas(getMutableBitmap()).clipRect(0, 0, 10, 10, Region.Op.XOR); 960 } 961 962 @Test testAntiAliasClipping()963 public void testAntiAliasClipping() { 964 createTest() 965 .addCanvasClient((canvas, width, height) -> { 966 Paint paint = new Paint(); 967 Path clipPath = new Path(); 968 969 clipPath.addCircle( 970 width / 2.0f, height / 2.0f, 971 Math.min(width, height) / 2.0f, 972 Path.Direction.CW 973 ); 974 975 paint.setColor(Color.WHITE); 976 canvas.drawRect(0, 0, width, height, paint); 977 978 paint.setColor(Color.RED); 979 980 canvas.save(); 981 982 canvas.clipPath(clipPath); 983 canvas.drawRect(0, 0, width, height, paint); 984 985 canvas.restore(); 986 987 }) 988 .runWithVerifier(AntiAliasPixelCounter.aaVerifier(Color.WHITE, Color.RED, 10)); 989 } 990 991 private static class AntiAliasPixelCounter extends BitmapVerifier { 992 993 private final int mColor1; 994 private final int mColor2; 995 private final int mCountThreshold; 996 // when true mCountThreshold is treated as a maximum 997 // when false mCountThreshold is treated as a minimum 998 private final boolean mThresholdIsAMaxium; 999 1000 // factory method for a verifier that confirms some non-target-color pixels are present 1001 // this is only a verification that aa has occurred if a single solid color shape has been 1002 // drawn on a solid background with at least one one visible edge, and every other possible 1003 // thing that can change the colors has been disabled. aaVerifier(int color1, int color2, int countThreshold)1004 public static AntiAliasPixelCounter aaVerifier(int color1, int color2, int countThreshold) { 1005 return new AntiAliasPixelCounter(color1, color2, countThreshold, false); 1006 } 1007 // factory method for a verifier that confirms only target color pixels are present. noAAVerifier(int color1, int color2)1008 public static AntiAliasPixelCounter noAAVerifier(int color1, int color2) { 1009 return new AntiAliasPixelCounter(color1, color2, 0, true); 1010 } 1011 AntiAliasPixelCounter(int color1, int color2, int countThreshold, boolean thresholdIsMax)1012 AntiAliasPixelCounter(int color1, int color2, int countThreshold, boolean thresholdIsMax) { 1013 mColor1 = color1; 1014 mColor2 = color2; 1015 mCountThreshold = countThreshold; 1016 mThresholdIsAMaxium = thresholdIsMax; 1017 } 1018 1019 @Override verify(int[] bitmap, int offset, int stride, int width, int height)1020 public boolean verify(int[] bitmap, int offset, int stride, int width, int height) { 1021 int nonTargetColorCount = 0; 1022 for (int x = 0; x < width; x++) { 1023 for (int y = 0; y < height; y++) { 1024 int pixelColor = bitmap[indexFromXAndY(x, y, stride, offset)]; 1025 if (pixelColor != mColor1 && pixelColor != mColor2) { 1026 nonTargetColorCount++; 1027 } 1028 } 1029 } 1030 if (mThresholdIsAMaxium) { 1031 return nonTargetColorCount <= mCountThreshold; 1032 } else { 1033 return nonTargetColorCount > mCountThreshold; 1034 } 1035 } 1036 } 1037 } 1038