blob: 684293d751912d689c57763600fc3d6f020079b3 [file] [log] [blame]
/*
* Copyright (C) 2014 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/LICENSE2.0
*
* Unless required by applicable law or agreed to in riting, 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.testinfrastructure;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.graphics.Xfermode;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Modifies the canvas and paint objects when called.
*/
public abstract class DisplayModifier {
private static final RectF gRect = new RectF(0, 0, 100, 100);
private static final float[] gPts = new float[]{
0, 100, 100, 0, 100, 200, 200, 100
};
private static final float[] gTriPts = new float[]{
75, 0, 130, 130, 130, 130, 0, 130, 0, 130, 75, 0
};
private static final int NUM_PARALLEL_LINES = 24;
private static final float[] gLinePts = new float[NUM_PARALLEL_LINES * 8 + gTriPts.length];
protected static final int MODIFIER_WIDTH = 180;
protected static final int MODIFIER_HEIGHT = 180;
public static final PorterDuff.Mode[] PORTERDUFF_MODES = new PorterDuff.Mode[] {
PorterDuff.Mode.SRC, PorterDuff.Mode.DST, PorterDuff.Mode.SRC_OVER,
PorterDuff.Mode.DST_OVER, PorterDuff.Mode.SRC_IN, PorterDuff.Mode.DST_IN,
PorterDuff.Mode.SRC_OUT, PorterDuff.Mode.DST_OUT, PorterDuff.Mode.SRC_ATOP,
PorterDuff.Mode.DST_ATOP, PorterDuff.Mode.XOR, PorterDuff.Mode.MULTIPLY,
PorterDuff.Mode.SCREEN
};
static {
int index;
for (index = 0; index < gTriPts.length; index++) {
gLinePts[index] = gTriPts[index];
}
float val = 0;
for (int i = 0; i < NUM_PARALLEL_LINES; i++) {
gLinePts[index + 0] = 150;
gLinePts[index + 1] = val;
gLinePts[index + 2] = 300;
gLinePts[index + 3] = val;
index += 4;
val += 8 + (2.0f / NUM_PARALLEL_LINES);
}
val = 0;
for (int i = 0; i < NUM_PARALLEL_LINES; i++) {
gLinePts[index + 0] = val;
gLinePts[index + 1] = 150;
gLinePts[index + 2] = val;
gLinePts[index + 3] = 300;
index += 4;
val += 8 + (2.0f / NUM_PARALLEL_LINES);
}
}
// This linked hash map contains each of the different things that can be done to a canvas and
// paint object, like anti-aliasing or drawing. Within those LinkedHashMaps are the various
// options for that specific topic, which contains a displaymodifier which will affect the
// given canvas and paint objects.
public static final LinkedHashMap<String, LinkedHashMap<String, DisplayModifier>> sMaps =
new LinkedHashMap<String, LinkedHashMap<String,DisplayModifier>>() {
{
put("aa", new LinkedHashMap<String, DisplayModifier>() {
{
put("true", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
paint.setAntiAlias(true);
}
});
put("false", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
paint.setAntiAlias(false);
}
});
}
});
put("style", new LinkedHashMap<String, DisplayModifier>() {
{
put("fill", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
paint.setStyle(Paint.Style.FILL);
}
});
put("stroke", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
paint.setStyle(Paint.Style.STROKE);
}
});
put("fillAndStroke", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
paint.setStyle(Paint.Style.FILL_AND_STROKE);
}
});
}
});
put("strokeWidth", new LinkedHashMap<String, DisplayModifier>() {
{
put("hair", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
paint.setStrokeWidth(0);
}
});
put("0.3", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
paint.setStrokeWidth(0.3f);
}
});
put("1", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
paint.setStrokeWidth(1);
}
});
put("5", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
paint.setStrokeWidth(5);
}
});
put("30", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
paint.setStrokeWidth(30);
}
});
}
});
put("strokeCap", new LinkedHashMap<String, DisplayModifier>() {
{
put("butt", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
paint.setStrokeCap(Paint.Cap.BUTT);
}
});
put("round", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
paint.setStrokeCap(Paint.Cap.ROUND);
}
});
put("square", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
paint.setStrokeCap(Paint.Cap.SQUARE);
}
});
}
});
put("strokeJoin", new LinkedHashMap<String, DisplayModifier>() {
{
put("bevel", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
paint.setStrokeJoin(Paint.Join.BEVEL);
}
});
put("round", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
paint.setStrokeJoin(Paint.Join.ROUND);
}
});
put("miter", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
paint.setStrokeJoin(Paint.Join.MITER);
}
});
// TODO: add miter0, miter1 etc to test miter distances
}
});
put("transform", new LinkedHashMap<String, DisplayModifier>() {
{
put("noTransform", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
}
});
put("rotate5", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
canvas.rotate(5);
}
});
put("rotate45", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
canvas.rotate(45);
}
});
put("rotate90", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
canvas.rotate(90);
canvas.translate(0, -200);
}
});
put("scale2x2", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
canvas.scale(2, 2);
}
});
put("rot20scl1x4", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
canvas.rotate(20);
canvas.scale(1, 4);
}
});
}
});
put("shader", new LinkedHashMap<String, DisplayModifier>() {
{
put("noShader", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
}
});
put("repeatShader", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
paint.setShader(ResourceModifier.instance().repeatShader);
}
});
put("translatedShader", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
paint.setShader(ResourceModifier.instance().translatedShader);
}
});
put("scaledShader", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
paint.setShader(ResourceModifier.instance().scaledShader);
}
});
put("composeShader", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
paint.setShader(ResourceModifier.instance().composeShader);
}
});
/*
put("bad composeShader", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
paint.setShader(ResourceModifier.instance().nestedComposeShader);
}
});
put("bad composeShader 2", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
paint.setShader(
ResourceModifier.instance().doubleGradientComposeShader);
}
});
*/
put("horGradient", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
paint.setShader(ResourceModifier.instance().horGradient);
}
});
put("diagGradient", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
paint.setShader(ResourceModifier.instance().diagGradient);
}
});
put("vertGradient", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
paint.setShader(ResourceModifier.instance().vertGradient);
}
});
put("radGradient", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
paint.setShader(ResourceModifier.instance().radGradient);
}
});
put("sweepGradient", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
paint.setShader(ResourceModifier.instance().sweepGradient);
}
});
}
});
put("xfermodes", new LinkedHashMap<String, DisplayModifier>() {
{
for (int i = 0 ; i < PORTERDUFF_MODES.length ; i++) {
put(PORTERDUFF_MODES[i].toString(),
new XfermodeModifier(PORTERDUFF_MODES[i]));
}
}
});
put("colorfilters", new LinkedHashMap<String, DisplayModifier>() {
{
for (int i = 0 ; i < PORTERDUFF_MODES.length ; i++) {
put(PORTERDUFF_MODES[i].toString(),
new ColorFilterModifier(PORTERDUFF_MODES[i]));
}
}
});
// FINAL MAP: DOES ACTUAL DRAWING
put("drawing", new LinkedHashMap<String, DisplayModifier>() {
{
put("roundRect", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
canvas.drawRoundRect(gRect, 20, 20, paint);
}
});
put("rect", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
canvas.drawRect(gRect, paint);
}
});
put("circle", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
canvas.drawCircle(100, 100, 75, paint);
}
});
put("oval", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
canvas.drawOval(gRect, paint);
}
});
put("lines", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
canvas.drawLines(gLinePts, paint);
}
});
/* drawPoints does not work with zero stroke width,
* but it isn't a regression
* TODO: fix hardware canvas so that drawPoints works
put("plusPoints", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
canvas.drawPoints(gPts, paint);
}
});
*/
put("text", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
paint.setTextSize(36);
canvas.drawText("TEXTTEST", 0, 50, paint);
}
});
put("shadowtext", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
paint.setTextSize(36);
paint.setShadowLayer(3.0f, 0.0f, 3.0f, 0xffff00ff);
canvas.drawText("TEXTTEST", 0, 50, paint);
}
});
put("bitmapMesh", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
canvas.drawBitmapMesh(ResourceModifier.instance().bitmap, 3, 3,
ResourceModifier.instance().bitmapVertices, 0, null, 0,
null);
}
});
put("arc", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
canvas.drawArc(gRect, 260, 285, false, paint);
}
});
put("arcFromCenter", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
canvas.drawArc(gRect, 260, 285, true, paint);
}
});
}
});
// WARNING: DON'T PUT MORE MAPS BELOW THIS
}
};
abstract public void modifyDrawing(Paint paint, Canvas canvas);
public static class Accessor {
public final static int AA_MASK = 0x1 << 0;
public final static int STYLE_MASK = 0x1 << 1;
public final static int STROKE_WIDTH_MASK = 0x1 << 2;
public final static int STROKE_CAP_MASK = 0x1 << 3;
public final static int STROKE_JOIN_MASK = 0x1 << 4;
public final static int TRANSFORM_MASK = 0x1 << 5;
public final static int SHADER_MASK = 0x1 << 6;
public final static int XFERMODE_MASK = 0x1 << 7;
public final static int COLOR_FILTER_MASK = 0x1 << 8;
public final static int SHAPES_MASK = 0x1 << 9;
public final static int ALL_OPTIONS_MASK = (0x1 << 10) - 1;
public final static int SHAPES_INDEX = 9;
public final static int XFERMODE_INDEX = 7;
private final int mMask;
private String mDebugString;
private int[] mIndices;
private LinkedHashMap<String, LinkedHashMap<String, DisplayModifier>> mDisplayMap;
public Accessor(int mask) {
int totalModifiers = Integer.bitCount(mask);
mIndices = new int[totalModifiers];
mMask = mask;
// Create a Display Map of the valid indices
mDisplayMap = new LinkedHashMap<String, LinkedHashMap<String, DisplayModifier>>();
int index = 0;
for (String key : DisplayModifier.sMaps.keySet()) {
if (validIndex(index)) {
mDisplayMap.put(key, DisplayModifier.sMaps.get(key));
}
index++;
}
mDebugString = "";
}
private LinkedHashMap<String, DisplayModifier> getMapAtIndex(int index) {
int i = 0;
for (LinkedHashMap<String, DisplayModifier> map : mDisplayMap.values()) {
if (i == index) {
return map;
}
i++;
}
return null;
}
/**
* This will create the next combination of drawing commands. If we have done every combination,
* then we will return false.
* @return true if there is more combinations to do
*/
public boolean step() {
int modifierMapIndex = mIndices.length - 1;
// Start from the last map, and loop until it is at the front
while (modifierMapIndex >= 0) {
LinkedHashMap<String, DisplayModifier> map = getMapAtIndex(modifierMapIndex);
mIndices[modifierMapIndex]++;
// If we are still at a valid index, then we don't need to update any others
if (mIndices[modifierMapIndex] < map.size()) {
break;
}
// If we updated and it was outside the boundary, and it was the last index then
// we are done
if (modifierMapIndex == 0) {
return false;
}
// If we ran off the end of the map, we need to update one more down the list
mIndices[modifierMapIndex] = 0;
modifierMapIndex--;
}
return true;
}
/**
* Modifies the canvas and paint given for the particular combination currently
*/
public void modifyDrawing(Canvas canvas, Paint paint) {
final ArrayList<DisplayModifier> modifierArrayList = getModifierList();
for (DisplayModifier modifier : modifierArrayList) {
modifier.modifyDrawing(paint, canvas);
}
}
/**
* Gets a list of all the current modifications to be used.
*/
private ArrayList<DisplayModifier> getModifierList() {
ArrayList<DisplayModifier> modifierArrayList = new ArrayList<DisplayModifier>();
int mapIndex = 0;
mDebugString = "";
// Through each possible category of modification
for (Map.Entry<String, LinkedHashMap<String, DisplayModifier>> entry :
mDisplayMap.entrySet()) {
int displayModifierIndex = mIndices[mapIndex];
mDebugString += "Modification : " + entry.getKey();
// Loop until we find the modification we are going to use
for (Map.Entry<String, DisplayModifier> modifierEntry :
entry.getValue().entrySet()) {
// Once we find the modification we want, then we will add it to the list,
// and the last applied modifications
if (displayModifierIndex == 0) {
mDebugString += " value : " + modifierEntry.getKey() + " ";
modifierArrayList.add(modifierEntry.getValue());
break;
}
displayModifierIndex--;
}
mapIndex++;
}
return modifierArrayList;
}
public String getDebugString() {
return mDebugString;
}
/**
* Using the given masks, it tells if the map at the given index should be used, or not.
*/
private boolean validIndex(int index) {
return (mMask & (0x1 << index)) != 0;
}
}
private static class XfermodeModifier extends DisplayModifier {
private Xfermode mXfermode;
public XfermodeModifier(PorterDuff.Mode mode) {
mXfermode = new PorterDuffXfermode(mode);
}
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
paint.setXfermode(mXfermode);
}
}
private static class ColorFilterModifier extends DisplayModifier {
private static final int FILTER_COLOR = 0xFFBB0000;
private ColorFilter mColorFilter;
public ColorFilterModifier(PorterDuff.Mode mode) {
mColorFilter = new PorterDuffColorFilter(FILTER_COLOR, mode);
}
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
paint.setColorFilter(mColorFilter);
}
}
}