blob: 311e89b91b6a7cec10e703933ac4b52dc6b6d023 [file] [log] [blame]
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.uirendering.cts.testclasses;
import static org.junit.Assert.assertFalse;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ComposeShader;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Picture;
import android.graphics.Point;
import android.graphics.PorterDuff;
import android.graphics.RadialGradient;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
import android.graphics.Shader;
import android.uirendering.cts.R;
import android.uirendering.cts.bitmapverifiers.BitmapVerifier;
import android.uirendering.cts.bitmapverifiers.SamplePointVerifier;
import android.uirendering.cts.testinfrastructure.ActivityTestBase;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.MediumTest;
import org.junit.Test;
import org.junit.runner.RunWith;
import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
@MediumTest
@RunWith(JUnitParamsRunner.class)
public class CanvasTests extends ActivityTestBase {
private static final int PAINT_COLOR = 0xff00ff00;
private static final int BITMAP_WIDTH = 10;
private static final int BITMAP_HEIGHT = 28;
private Paint getPaint() {
Paint paint = new Paint();
paint.setColor(PAINT_COLOR);
return paint;
}
public Bitmap getImmutableBitmap() {
final Resources res = InstrumentationRegistry.getTargetContext().getResources();
BitmapFactory.Options opt = new BitmapFactory.Options();
opt.inScaled = false; // bitmap will only be immutable if not scaled during load
Bitmap immutableBitmap = BitmapFactory.decodeResource(res, R.drawable.sunset1, opt);
assertFalse(immutableBitmap.isMutable());
return immutableBitmap;
}
public Bitmap getMutableBitmap(Bitmap.Config config) {
return Bitmap.createBitmap(BITMAP_WIDTH, BITMAP_HEIGHT, config);
}
public Bitmap getMutableBitmap() {
return getMutableBitmap(Bitmap.Config.ARGB_8888);
}
@Test
public void testDrawDoubleRoundRect() {
Point[] testPoints = {
new Point(0, 0),
new Point(89, 0),
new Point(89, 89),
new Point(0, 89),
new Point(10, 10),
new Point(79, 10),
new Point(79, 79),
new Point(10, 79)
};
int[] colors = {
Color.WHITE,
Color.WHITE,
Color.WHITE,
Color.WHITE,
Color.RED,
Color.RED,
Color.RED,
Color.RED
};
createTest()
.addCanvasClient((canvas, width, height) -> {
Paint paint = new Paint();
paint.setColor(Color.WHITE);
canvas.drawRect(0, 0, width, height, paint);
paint.setColor(Color.RED);
int inset = 20;
RectF outer = new RectF(0, 0, width, height);
RectF inner = new RectF(inset,
inset,
width - inset,
height - inset);
canvas.drawDoubleRoundRect(outer, 5, 5,
inner, 10, 10, paint);
})
.runWithVerifier(new SamplePointVerifier(testPoints, colors));
}
@Test
public void testDrawDoubleRoundRectWithRadii() {
Point[] testPoints = {
new Point(0, 0),
new Point(89, 0),
new Point(89, 89),
new Point(0, 89),
new Point(9, 7),
new Point(81, 7),
new Point(75, 75),
new Point(21, 67)
};
int[] colors = {
Color.RED,
Color.WHITE,
Color.WHITE,
Color.WHITE,
Color.RED,
Color.RED,
Color.RED,
Color.RED
};
createTest()
.addCanvasClient((canvas, width, height) -> {
Paint paint = new Paint();
paint.setColor(Color.WHITE);
canvas.drawRect(0, 0, width, height, paint);
paint.setColor(Color.RED);
int inset = 30;
RectF outer = new RectF(0, 0, width, height);
RectF inner = new RectF(inset,
inset,
width - inset,
height - inset);
float[] outerRadii = {
0.0f, 0.0f, // top left
5.0f, 5.0f, // top right
10.0f, 10.0f, // bottom right
20.0f, 20.0f // bottom left
};
float[] innerRadii = {
20.0f, 20.0f,
15.0f, 15.0f,
8.0f, 8.0f,
3.0f, 3.0f
};
canvas.drawDoubleRoundRect(outer, outerRadii, inner, innerRadii, paint);
})
.runWithVerifier(new SamplePointVerifier(testPoints, colors));
}
private void drawRotatedBitmap(boolean aa, Canvas canvas, Bitmap.Config config) {
// create a black bitmap to be drawn to the canvas
Bitmap bm = getMutableBitmap();
bm.eraseColor(Color.BLACK);
// canvas density and bitmap density must match in order for no scaling to occur
// and aa to be distinguishable from non-aa
bm.setDensity(canvas.getDensity());
canvas.drawColor(Color.WHITE);
Paint aaPaint = new Paint();
aaPaint.setAntiAlias(aa);
canvas.rotate(-1.0f, 0, 0);
canvas.drawBitmap(bm, 0, 0, aaPaint);
}
private Object[] testConfigs() {
return new Object[] {
Bitmap.Config.ARGB_8888,
Bitmap.Config.RGBA_1010102
};
}
@Test
@Parameters(method = "testConfigs")
public void testDrawRotatedBitmapWithAA(Bitmap.Config config) {
createTest()
.addCanvasClient((canvas, width, height) -> {
canvas.setDensity(400);
drawRotatedBitmap(true, canvas, config);
})
// Test asserts there are more than 10 grey pixels.
.runWithVerifier(AntiAliasPixelCounter.aaVerifier(Color.WHITE, Color.BLACK, 10));
}
@Test
@Parameters(method = "testConfigs")
public void testDrawRotatedBitmapWithoutAA(Bitmap.Config config) {
createTest()
.addCanvasClient((canvas, width, height) -> {
canvas.setDensity(400);
drawRotatedBitmap(false, canvas, config);
})
// Test asserts there are no grey pixels.
.runWithVerifier(AntiAliasPixelCounter.noAAVerifier(Color.WHITE, Color.BLACK));
}
@Test(expected = IllegalArgumentException.class)
public void testDrawHwBitmap_inSwCanvas() {
Bitmap hwBitmap = getImmutableBitmap().copy(Bitmap.Config.HARDWARE, false);
// we verify this specific call should IAE
new Canvas(getMutableBitmap()).drawBitmap(hwBitmap, 0, 0, null);
}
@Test(expected = IllegalArgumentException.class)
public void testDrawHwBitmap_inPictureCanvas_inSwCanvas() {
Bitmap hwBitmap = getImmutableBitmap().copy(Bitmap.Config.HARDWARE, false);
Picture picture = new Picture();
Canvas pictureCanvas = picture.beginRecording(100, 100);
pictureCanvas.drawBitmap(hwBitmap, 0, 0, null);
// we verify this specific call should IAE
new Canvas(getMutableBitmap()).drawPicture(picture);
}
@Test(expected = IllegalArgumentException.class)
public void testDrawHwBitmap_inPictureCanvas_inPictureCanvas_inSwCanvas() {
Bitmap hwBitmap = getImmutableBitmap().copy(Bitmap.Config.HARDWARE, false);
Picture innerPicture = new Picture();
Canvas pictureCanvas = innerPicture.beginRecording(100, 100);
pictureCanvas.drawBitmap(hwBitmap, 0, 0, null);
Picture outerPicture = new Picture();
Canvas outerPictureCanvas = outerPicture.beginRecording(100, 100);
outerPictureCanvas.drawPicture(innerPicture);
// we verify this specific call should IAE
new Canvas(getMutableBitmap()).drawPicture(outerPicture);
}
@Test(expected = IllegalArgumentException.class)
public void testHwBitmapShaderInSwCanvas1() {
Bitmap hwBitmap = getImmutableBitmap().copy(Bitmap.Config.HARDWARE, false);
BitmapShader bitmapShader = new BitmapShader(hwBitmap, Shader.TileMode.REPEAT,
Shader.TileMode.REPEAT);
RadialGradient gradientShader = new RadialGradient(10, 10, 30,
Color.BLACK, Color.CYAN, Shader.TileMode.REPEAT);
Shader shader = new ComposeShader(gradientShader, bitmapShader, PorterDuff.Mode.OVERLAY);
Paint p = new Paint();
p.setShader(shader);
new Canvas(getMutableBitmap()).drawRect(0, 0, 10, 10, p);
}
@Test(expected = IllegalArgumentException.class)
public void testHwBitmapShaderInSwCanvas2() {
Bitmap hwBitmap = getImmutableBitmap().copy(Bitmap.Config.HARDWARE, false);
BitmapShader bitmapShader = new BitmapShader(hwBitmap, Shader.TileMode.REPEAT,
Shader.TileMode.REPEAT);
RadialGradient gradientShader = new RadialGradient(10, 10, 30,
Color.BLACK, Color.CYAN, Shader.TileMode.REPEAT);
Shader shader = new ComposeShader(bitmapShader, gradientShader, PorterDuff.Mode.OVERLAY);
Paint p = new Paint();
p.setShader(shader);
new Canvas(getMutableBitmap()).drawRect(0, 0, 10, 10, p);
}
@Test(expected = IllegalStateException.class)
public void testCanvasFromImmutableBitmap() {
// Should throw out IllegalStateException when creating Canvas with an ImmutableBitmap
new Canvas(getImmutableBitmap());
}
@Test(expected = RuntimeException.class)
public void testCanvasFromRecycledBitmap() {
// Should throw out RuntimeException when creating Canvas with a MutableBitmap which
// is recycled
Bitmap bitmap = getMutableBitmap();
bitmap.recycle();
new Canvas(bitmap);
}
@Test(expected = IllegalStateException.class)
public void testSetBitmapToImmutableBitmap() {
// Should throw out IllegalStateException when setting an ImmutableBitmap to a Canvas
new Canvas(getMutableBitmap()).setBitmap(getImmutableBitmap());
}
@Test(expected = RuntimeException.class)
public void testSetBitmapToRecycledBitmap() {
// Should throw out RuntimeException when setting Bitmap which is recycled to a Canvas
Bitmap bitmap = getMutableBitmap();
bitmap.recycle();
new Canvas(getMutableBitmap()).setBitmap(bitmap);
}
@Test(expected = IllegalStateException.class)
public void testRestoreWithoutSave() {
// Should throw out IllegalStateException because cannot restore Canvas before save
new Canvas(getMutableBitmap()).restore();
}
@Test(expected = IllegalArgumentException.class)
public void testRestoreToCountIllegalSaveCount() {
// Should throw out IllegalArgumentException because saveCount is less than 1
new Canvas(getMutableBitmap()).restoreToCount(0);
}
@Test(expected = ArrayIndexOutOfBoundsException.class)
public void testDrawPointsInvalidOffset() {
// Should throw out ArrayIndexOutOfBoundsException because of invalid offset
new Canvas(getMutableBitmap()).drawPoints(new float[]{
10.0f, 29.0f
}, -1, 2, getPaint());
}
@Test(expected = ArrayIndexOutOfBoundsException.class)
public void testDrawPointsInvalidCount() {
// Should throw out ArrayIndexOutOfBoundsException because of invalid count
new Canvas(getMutableBitmap()).drawPoints(new float[]{
10.0f, 29.0f
}, 0, 31, getPaint());
}
@Test(expected = ArrayIndexOutOfBoundsException.class)
public void testDrawLinesInvalidOffset() {
// Should throw out ArrayIndexOutOfBoundsException because of invalid offset
new Canvas(getMutableBitmap()).drawLines(new float[]{
0, 0, 10, 31
}, 2, 4, new Paint());
}
@Test(expected = ArrayIndexOutOfBoundsException.class)
public void testDrawLinesInvalidCount() {
// Should throw out ArrayIndexOutOfBoundsException because of invalid count
new Canvas(getMutableBitmap()).drawLines(new float[]{
0, 0, 10, 31
}, 0, 8, new Paint());
}
@Test(expected = NullPointerException.class)
public void testDrawOvalNull() {
// Should throw out NullPointerException because oval is null
new Canvas(getMutableBitmap()).drawOval(null, getPaint());
}
@Test(expected = NullPointerException.class)
public void testDrawArcNullOval() {
// Should throw NullPointerException because oval is null
new Canvas(getMutableBitmap()).drawArc(null, 10.0f, 29.0f,
true, getPaint());
}
@Test(expected = NullPointerException.class)
public void testDrawRoundRectNull() {
// Should throw out NullPointerException because RoundRect is null
new Canvas(getMutableBitmap()).drawRoundRect(null, 10.0f, 29.0f, getPaint());
}
@Test(expected = RuntimeException.class)
public void testDrawBitmapAtPointRecycled() {
Bitmap b = Bitmap.createBitmap(BITMAP_WIDTH, 29, Bitmap.Config.ARGB_8888);
b.recycle();
// Should throw out RuntimeException because bitmap has been recycled
new Canvas(getMutableBitmap()).drawBitmap(b, 10.0f, 29.0f, getPaint());
}
@Test(expected = RuntimeException.class)
public void testDrawBitmapSrcDstFloatRecycled() {
Bitmap b = Bitmap.createBitmap(BITMAP_WIDTH, 29, Bitmap.Config.ARGB_8888);
b.recycle();
// Should throw out RuntimeException because bitmap has been recycled
new Canvas(getMutableBitmap()).drawBitmap(b, null, new RectF(), getPaint());
}
@Test(expected = RuntimeException.class)
public void testDrawBitmapSrcDstIntRecycled() {
Bitmap b = Bitmap.createBitmap(BITMAP_WIDTH, 29, Bitmap.Config.ARGB_8888);
b.recycle();
// Should throw out RuntimeException because bitmap has been recycled
new Canvas(getMutableBitmap()).drawBitmap(b, null, new Rect(), getPaint());
}
@Test(expected = IllegalArgumentException.class)
public void testDrawBitmapIntsNegativeWidth() {
// Should throw out IllegalArgumentException because width is less than 0
new Canvas(getMutableBitmap()).drawBitmap(new int[2008], 10, 10, 10,
10, -1, 10, true, null);
}
@Test(expected = IllegalArgumentException.class)
public void testDrawBitmapIntsNegativeHeight() {
// Should throw out IllegalArgumentException because height is less than 0
new Canvas(getMutableBitmap()).drawBitmap(new int[2008], 10, 10, 10,
10, 10, -1, true, null);
}
@Test(expected = IllegalArgumentException.class)
public void testDrawBitmapIntsBadStride() {
// Should throw out IllegalArgumentException because stride less than width and
// bigger than -width
new Canvas(getMutableBitmap()).drawBitmap(new int[2008], 10, 5, 10,
10, 10, 10, true, null);
}
@Test(expected = ArrayIndexOutOfBoundsException.class)
public void testDrawBitmapIntsNegativeOffset() {
// Should throw out ArrayIndexOutOfBoundsException because offset less than 0
new Canvas(getMutableBitmap()).drawBitmap(new int[2008], -1, 10, 10,
10, 10, 10, true, null);
}
@Test(expected = ArrayIndexOutOfBoundsException.class)
public void testDrawBitmapIntsBadOffset() {
// Should throw out ArrayIndexOutOfBoundsException because sum of offset and width
// is bigger than colors' length
new Canvas(getMutableBitmap()).drawBitmap(new int[29], 10, 29, 10,
10, 20, 10, true, null);
}
@Test(expected = IllegalArgumentException.class)
public void testDrawBitmapFloatsNegativeWidth() {
// Should throw out IllegalArgumentException because width is less than 0
new Canvas(getMutableBitmap()).drawBitmap(new int[2008], 10, 10, 10.0f,
10.0f, -1, 10, true, null);
}
@Test(expected = IllegalArgumentException.class)
public void testDrawBitmapFloatsNegativeHeight() {
// Should throw out IllegalArgumentException because height is less than 0
new Canvas(getMutableBitmap()).drawBitmap(new int[2008], 10, 10, 10.0f,
10.0f, 10, -1, true, null);
}
@Test(expected = IllegalArgumentException.class)
public void testDrawBitmapFloatsBadStride() {
// Should throw out IllegalArgumentException because stride less than width and
// bigger than -width
new Canvas(getMutableBitmap()).drawBitmap(new int[2008], 10, 5, 10.0f,
10.0f, 10, 10, true, null);
}
@Test(expected = ArrayIndexOutOfBoundsException.class)
public void testDrawBitmapFloatsNegativeOffset() {
// Should throw out ArrayIndexOutOfBoundsException because offset less than 0
new Canvas(getMutableBitmap()).drawBitmap(new int[2008], -1, 10, 10.0f,
10.0f, 10, 10, true, null);
}
@Test(expected = ArrayIndexOutOfBoundsException.class)
public void testDrawBitmapFloatsBadOffset() {
// Should throw out ArrayIndexOutOfBoundsException because sum of offset and width
// is bigger than colors' length
new Canvas(getMutableBitmap()).drawBitmap(new int[29], 10, 29, 10.0f,
10.0f, 20, 10, true, null);
}
@Test(expected = ArrayIndexOutOfBoundsException.class)
public void testDrawBitmapMeshNegativeWidth() {
final Bitmap b = Bitmap.createBitmap(BITMAP_WIDTH, 29, Bitmap.Config.ARGB_8888);
// Should throw out ArrayIndexOutOfBoundsException because meshWidth less than 0
new Canvas(getMutableBitmap()).drawBitmapMesh(b, -1, 10,
null, 0, null, 0, null);
}
@Test(expected = ArrayIndexOutOfBoundsException.class)
public void testDrawBitmapMeshNegativeHeight() {
final Bitmap b = Bitmap.createBitmap(BITMAP_WIDTH, 29, Bitmap.Config.ARGB_8888);
// Should throw out ArrayIndexOutOfBoundsException because meshHeight is less than 0
new Canvas(getMutableBitmap()).drawBitmapMesh(b, 10, -1,
null, 0, null, 0, null);
}
@Test(expected = ArrayIndexOutOfBoundsException.class)
public void testDrawBitmapMeshNegativeVertOffset() {
final Bitmap b = Bitmap.createBitmap(BITMAP_WIDTH, 29, Bitmap.Config.ARGB_8888);
// Should throw out ArrayIndexOutOfBoundsException because vertOffset is less than 0
new Canvas(getMutableBitmap()).drawBitmapMesh(b, 10, 10,
null, -1, null, 0, null);
}
@Test(expected = ArrayIndexOutOfBoundsException.class)
public void testDrawBitmapMeshNegativeColorOffset() {
final Bitmap b = Bitmap.createBitmap(BITMAP_WIDTH, 29, Bitmap.Config.ARGB_8888);
// Should throw out ArrayIndexOutOfBoundsException because colorOffset is less than 0
new Canvas(getMutableBitmap()).drawBitmapMesh(b, 10, 10,
null, 10, null, -1, null);
}
@Test(expected = ArrayIndexOutOfBoundsException.class)
public void testDrawBitmapMeshTooFewVerts() {
final Bitmap b = Bitmap.createBitmap(BITMAP_WIDTH, 29, Bitmap.Config.ARGB_8888);
// Should throw out ArrayIndexOutOfBoundsException because verts' length is too short
new Canvas(getMutableBitmap()).drawBitmapMesh(b, 10, 10,
new float[] { 10.0f, 29.0f}, 10, null, 10, null);
}
@Test(expected = ArrayIndexOutOfBoundsException.class)
public void testDrawBitmapMeshTooFewColors() {
final Bitmap b = Bitmap.createBitmap(BITMAP_WIDTH, 29, Bitmap.Config.ARGB_8888);
// Should throw out ArrayIndexOutOfBoundsException because colors' length is too short
// abnormal case: colors' length is too short
final float[] verts = new float[2008];
new Canvas(getMutableBitmap()).drawBitmapMesh(b, 10, 10, verts,
10, new int[] { 10, 29}, 10, null);
}
@Test(expected = ArrayIndexOutOfBoundsException.class)
public void testDrawVerticesTooFewVerts() {
final float[] verts = new float[10];
final float[] texs = new float[10];
final int[] colors = new int[10];
final short[] indices = {
0, 1, 2, 3, 4, 1
};
// Should throw out ArrayIndexOutOfBoundsException because sum of vertOffset and
// vertexCount is bigger than verts' length
new Canvas(getMutableBitmap()).drawVertices(Canvas.VertexMode.TRIANGLES, 10,
verts, 8, texs, 0, colors, 0, indices,
0, 4, getPaint());
}
@Test(expected = ArrayIndexOutOfBoundsException.class)
public void testDrawVerticesTooFewTexs() {
final float[] verts = new float[10];
final float[] texs = new float[10];
final int[] colors = new int[10];
final short[] indices = {
0, 1, 2, 3, 4, 1
};
// Should throw out ArrayIndexOutOfBoundsException because sum of texOffset and
// vertexCount is bigger thatn texs' length
new Canvas(getMutableBitmap()).drawVertices(Canvas.VertexMode.TRIANGLES, 10,
verts, 0, texs, 30, colors, 0, indices,
0, 4, getPaint());
}
@Test(expected = ArrayIndexOutOfBoundsException.class)
public void testDrawVerticesTooFewColors() {
final float[] verts = new float[10];
final float[] texs = new float[10];
final int[] colors = new int[10];
final short[] indices = {
0, 1, 2, 3, 4, 1
};
// Should throw out ArrayIndexOutOfBoundsException because sum of colorOffset and
// vertexCount is bigger than colors' length
new Canvas(getMutableBitmap()).drawVertices(Canvas.VertexMode.TRIANGLES, 10,
verts, 0, texs, 0, colors, 30, indices,
0, 4, getPaint());
}
@Test(expected = ArrayIndexOutOfBoundsException.class)
public void testDrawVerticesTooFewIndices() {
final float[] verts = new float[10];
final float[] texs = new float[10];
final int[] colors = new int[10];
final short[] indices = {
0, 1, 2, 3, 4, 1
};
// Should throw out ArrayIndexOutOfBoundsException because sum of indexOffset and
// indexCount is bigger than indices' length
new Canvas(getMutableBitmap()).drawVertices(Canvas.VertexMode.TRIANGLES, 10,
verts, 0, texs, 0, colors, 0, indices,
10, 30, getPaint());
}
@Test(expected = IndexOutOfBoundsException.class)
public void testDrawArrayTextNegativeIndex() {
final char[] text = { 'a', 'n', 'd', 'r', 'o', 'i', 'd' };
// Should throw out IndexOutOfBoundsException because index is less than 0
new Canvas(getMutableBitmap()).drawText(text, -1, 7, 10, 10, getPaint());
}
@Test(expected = IndexOutOfBoundsException.class)
public void testDrawArrayTextNegativeCount() {
final char[] text = { 'a', 'n', 'd', 'r', 'o', 'i', 'd' };
// Should throw out IndexOutOfBoundsException because count is less than 0
new Canvas(getMutableBitmap()).drawText(text, 0, -1, 10, 10, getPaint());
}
@Test(expected = IndexOutOfBoundsException.class)
public void testDrawArrayTextTextLengthTooSmall() {
final char[] text = { 'a', 'n', 'd', 'r', 'o', 'i', 'd' };
// Should throw out IndexOutOfBoundsException because sum of index and count
// is bigger than text's length
new Canvas(getMutableBitmap()).drawText(text, 0, 10, 10, 10, getPaint());
}
@Test(expected = IndexOutOfBoundsException.class)
public void testDrawTextTextAtPositionWithOffsetsNegativeStart() {
// Should throw out IndexOutOfBoundsException because start is less than 0
new Canvas(getMutableBitmap()).drawText("android", -1, 7, 10, 30,
getPaint());
}
@Test(expected = IndexOutOfBoundsException.class)
public void testDrawTextTextAtPositionWithOffsetsNegativeEnd() {
// Should throw out IndexOutOfBoundsException because end is less than 0
new Canvas(getMutableBitmap()).drawText("android", 0, -1, 10, 30,
getPaint());
}
@Test(expected = IndexOutOfBoundsException.class)
public void testDrawTextTextAtPositionWithOffsetsStartEndMismatch() {
// Should throw out IndexOutOfBoundsException because start is bigger than end
new Canvas(getMutableBitmap()).drawText("android", 3, 1, 10, 30,
getPaint());
}
@Test(expected = IndexOutOfBoundsException.class)
public void testDrawTextTextAtPositionWithOffsetsTextTooLong() {
// Should throw out IndexOutOfBoundsException because end subtracts start should
// bigger than text's length
new Canvas(getMutableBitmap()).drawText("android", 0, 10, 10, 30,
getPaint());
}
@Test(expected = NullPointerException.class)
public void testDrawTextRunNullCharArray() {
// Should throw out NullPointerException because text is null
new Canvas(getMutableBitmap()).drawTextRun((char[]) null, 0, 0,
0, 0, 0.0f, 0.0f, false, new Paint());
}
@Test(expected = NullPointerException.class)
public void testDrawTextRunNullCharSequence() {
// Should throw out NullPointerException because text is null
new Canvas(getMutableBitmap()).drawTextRun((CharSequence) null, 0, 0,
0, 0, 0.0f, 0.0f, false, new Paint());
}
@Test(expected = NullPointerException.class)
public void testDrawTextRunCharArrayNullPaint() {
// Should throw out NullPointerException because paint is null
new Canvas(getMutableBitmap()).drawTextRun("android".toCharArray(), 0, 0,
0, 0, 0.0f, 0.0f, false, null);
}
@Test(expected = NullPointerException.class)
public void testDrawTextRunCharSequenceNullPaint() {
// Should throw out NullPointerException because paint is null
new Canvas(getMutableBitmap()).drawTextRun("android", 0, 0, 0,
0, 0.0f, 0.0f, false, null);
}
@Test(expected = IndexOutOfBoundsException.class)
public void testDrawTextRunNegativeIndex() {
final String text = "android";
final Paint paint = new Paint();
// Should throw out IndexOutOfBoundsException because index is less than 0
new Canvas(getMutableBitmap()).drawTextRun(text.toCharArray(), -1, text.length(),
0, text.length(), 0.0f, 0.0f,
false, new Paint());
}
@Test(expected = IndexOutOfBoundsException.class)
public void testDrawTextRunNegativeCount() {
final String text = "android";
// Should throw out IndexOutOfBoundsException because count is less than 0
new Canvas(getMutableBitmap()).drawTextRun(text.toCharArray(), 0, -1,
0, text.length(), 0.0f, 0.0f, false,
new Paint());
}
@Test(expected = IndexOutOfBoundsException.class)
public void testDrawTextRunContestIndexTooLarge() {
final String text = "android";
// Should throw out IndexOutOfBoundsException because contextIndex is bigger than index
new Canvas(getMutableBitmap()).drawTextRun(text.toCharArray(), 0, text.length(),
1, text.length(), 0.0f, 0.0f,
false, new Paint());
}
@Test(expected = IndexOutOfBoundsException.class)
public void testDrawTextRunContestIndexTooSmall() {
final String text = "android";
// Should throw out IndexOutOfBoundsException because contextIndex + contextCount
// is less than index + count
new Canvas(getMutableBitmap()).drawTextRun(text, 0, text.length(), 0,
text.length() - 1, 0.0f, 0.0f, false,
new Paint());
}
@Test(expected = IndexOutOfBoundsException.class)
public void testDrawTextRunIndexTooLarge() {
final String text = "android";
final Paint paint = new Paint();
// Should throw out IndexOutOfBoundsException because index + count is bigger than
// text length
new Canvas(getMutableBitmap()).drawTextRun(text.toCharArray(), 0,
text.length() + 1, 0, text.length() + 1,
0.0f, 0.0f, false, new Paint());
}
@Test(expected = IndexOutOfBoundsException.class)
public void testDrawTextRunNegativeContextStart() {
final String text = "android";
final Paint paint = new Paint();
// Should throw out IndexOutOfBoundsException because contextStart is less than 0
new Canvas(getMutableBitmap()).drawTextRun(text, 0, text.length(), -1,
text.length(), 0.0f, 0.0f, false,
new Paint());
}
@Test(expected = IndexOutOfBoundsException.class)
public void testDrawTextRunStartLessThanContextStart() {
final String text = "android";
// Should throw out IndexOutOfBoundsException because start is less than contextStart
new Canvas(getMutableBitmap()).drawTextRun(text, 0, text.length(), 1,
text.length(), 0.0f, 0.0f, false,
new Paint());
}
@Test(expected = IndexOutOfBoundsException.class)
public void testDrawTextRunEndLessThanStart() {
final String text = "android";
// Should throw out IndexOutOfBoundsException because end is less than start
new Canvas(getMutableBitmap()).drawTextRun(text, 1, 0, 0,
text.length(), 0.0f, 0.0f, false, new Paint());
}
@Test(expected = IndexOutOfBoundsException.class)
public void testDrawTextRunContextEndLessThanEnd() {
final String text = "android";
// Should throw out IndexOutOfBoundsException because contextEnd is less than end
new Canvas(getMutableBitmap()).drawTextRun(text, 0, text.length(), 0,
text.length() - 1, 0.0f, 0.0f, false,
new Paint());
}
@Test(expected = IndexOutOfBoundsException.class)
public void testDrawTextRunContextEndLargerThanTextLength() {
final String text = "android";
// Should throw out IndexOutOfBoundsException because contextEnd is bigger than
// text length
new Canvas(getMutableBitmap()).drawTextRun(text, 0, text.length(), 0,
text.length() + 1, 0.0f, 0.0f, false,
new Paint());
}
@Test(expected = IndexOutOfBoundsException.class)
public void testDrawPosTextWithIndexAndCountNegativeIndex() {
final char[] text = {
'a', 'n', 'd', 'r', 'o', 'i', 'd'
};
final float[] pos = new float[]{
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,
7.0f, 7.0f
};
// Should throw out IndexOutOfBoundsException because index is less than 0
new Canvas(getMutableBitmap()).drawPosText(text, -1, 7, pos, getPaint());
}
@Test(expected = IndexOutOfBoundsException.class)
public void testDrawPosTextWithIndexAndCountTextTooShort() {
final char[] text = {
'a', 'n', 'd', 'r', 'o', 'i', 'd'
};
final float[] pos = new float[]{
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,
7.0f, 7.0f
};
// Should throw out IndexOutOfBoundsException because sum of index and count is
// bigger than text's length
new Canvas(getMutableBitmap()).drawPosText(text, 1, 10, pos, getPaint());
}
@Test(expected = IndexOutOfBoundsException.class)
public void testDrawPosTextWithIndexAndCountCountTooLarge() {
final char[] text = {
'a', 'n', 'd', 'r', 'o', 'i', 'd'
};
// Should throw out IndexOutOfBoundsException because 2 times of count is
// bigger than pos' length
new Canvas(getMutableBitmap()).drawPosText(text, 1, 10, new float[] {
10.0f, 30.f
}, getPaint());
}
@Test(expected = ArrayIndexOutOfBoundsException.class)
public void testDrawTextOnPathWithIndexAndCountNegativeIndex() {
final char[] text = { 'a', 'n', 'd', 'r', 'o', 'i', 'd' };
// Should throw out ArrayIndexOutOfBoundsException because index is smaller than 0
new Canvas(getMutableBitmap()).drawTextOnPath(text, -1, 7, new Path(),
10.0f, 10.0f, getPaint());
}
@Test(expected = ArrayIndexOutOfBoundsException.class)
public void testDrawTextOnPathWithIndexAndCountTextTooShort() {
final char[] text = { 'a', 'n', 'd', 'r', 'o', 'i', 'd' };
// Should throw out ArrayIndexOutOfBoundsException because sum of index and
// count is bigger than text's length
new Canvas(getMutableBitmap()).drawTextOnPath(text, 0, 10, new Path(),
10.0f, 10.0f, getPaint());
}
@Test(expected = IndexOutOfBoundsException.class)
public void testDrawPosTextCountTooLarge() {
final String text = "android";
// Should throw out IndexOutOfBoundsException because 2 times of count is
// bigger than pos' length
new Canvas(getMutableBitmap()).drawPosText(text, new float[]{
10.0f, 30.f
}, getPaint());
}
@Test
public void testClipIntersectAndDifference() {
Point[] testPoints = {
new Point(1, 1),
new Point(10, 10),
new Point(25, 25)
};
int[] colors = {
Color.WHITE,
Color.RED,
Color.GREEN
};
createTest()
.addCanvasClient((canvas, width, height) -> {
// Base is white
Paint paint = new Paint();
paint.setColor(Color.WHITE);
canvas.drawRect(0, 0, width, height, paint);
// Fill inset with green, which will later be overwitten with
// red except for a subsequent difference clip op
canvas.clipRect(5, 5, width - 5, height - 5);
paint.setColor(Color.GREEN);
canvas.drawRect(0, 0, width, height, paint);
// Cut out the inner region, so that it remains green
canvas.clipOutRect(20, 20, width - 20, height - 20);
paint.setColor(Color.RED);
canvas.drawRect(0, 0, width, height, paint);
})
.runWithVerifier(new SamplePointVerifier(testPoints, colors));
}
// Expanding region ops (replace, reverse diff, union, and xor) are not allowed for clipping
@Test(expected = IllegalArgumentException.class)
public void testClipReplace() {
new Canvas(getMutableBitmap()).clipRect(0, 0, 10, 10, Region.Op.REPLACE);
}
@Test(expected = IllegalArgumentException.class)
public void testClipReverseDifference() {
new Canvas(getMutableBitmap()).clipRect(0, 0, 10, 10, Region.Op.REVERSE_DIFFERENCE);
}
@Test(expected = IllegalArgumentException.class)
public void testClipUnion() {
new Canvas(getMutableBitmap()).clipRect(0, 0, 10, 10, Region.Op.UNION);
}
@Test(expected = IllegalArgumentException.class)
public void testClipXor() {
new Canvas(getMutableBitmap()).clipRect(0, 0, 10, 10, Region.Op.XOR);
}
@Test
public void testAntiAliasClipping() {
createTest()
.addCanvasClient((canvas, width, height) -> {
Paint paint = new Paint();
Path clipPath = new Path();
clipPath.addCircle(
width / 2.0f, height / 2.0f,
Math.min(width, height) / 2.0f,
Path.Direction.CW
);
paint.setColor(Color.WHITE);
canvas.drawRect(0, 0, width, height, paint);
paint.setColor(Color.RED);
canvas.save();
canvas.clipPath(clipPath);
canvas.drawRect(0, 0, width, height, paint);
canvas.restore();
})
.runWithVerifier(AntiAliasPixelCounter.aaVerifier(Color.WHITE, Color.RED, 10));
}
private static class AntiAliasPixelCounter extends BitmapVerifier {
private final int mColor1;
private final int mColor2;
private final int mCountThreshold;
// when true mCountThreshold is treated as a maximum
// when false mCountThreshold is treated as a minimum
private final boolean mThresholdIsAMaxium;
// factory method for a verifier that confirms some non-target-color pixels are present
// this is only a verification that aa has occurred if a single solid color shape has been
// drawn on a solid background with at least one one visible edge, and every other possible
// thing that can change the colors has been disabled.
public static AntiAliasPixelCounter aaVerifier(int color1, int color2, int countThreshold) {
return new AntiAliasPixelCounter(color1, color2, countThreshold, false);
}
// factory method for a verifier that confirms only target color pixels are present.
public static AntiAliasPixelCounter noAAVerifier(int color1, int color2) {
return new AntiAliasPixelCounter(color1, color2, 0, true);
}
AntiAliasPixelCounter(int color1, int color2, int countThreshold, boolean thresholdIsMax) {
mColor1 = color1;
mColor2 = color2;
mCountThreshold = countThreshold;
mThresholdIsAMaxium = thresholdIsMax;
}
@Override
public boolean verify(int[] bitmap, int offset, int stride, int width, int height) {
int nonTargetColorCount = 0;
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
int pixelColor = bitmap[indexFromXAndY(x, y, stride, offset)];
if (pixelColor != mColor1 && pixelColor != mColor2) {
nonTargetColorCount++;
}
}
}
if (mThresholdIsAMaxium) {
return nonTargetColorCount <= mCountThreshold;
} else {
return nonTargetColorCount > mCountThreshold;
}
}
}
}