/* * Copyright (C) 2012 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 com.android.test.hwuicompare; import java.util.LinkedHashMap; import java.util.Map.Entry; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.RectF; import android.util.Log; public abstract class DisplayModifier { // automated tests ignore any combination of operations that don't together return TOTAL_MASK protected final static int TOTAL_MASK = 0x1F; // if we're filling, ensure we're not also sweeping over stroke parameters protected final static int SWEEP_STROKE_WIDTH_BIT = 0x1 << 0; protected final static int SWEEP_STROKE_CAP_BIT = 0x1 << 1; protected final static int SWEEP_STROKE_JOIN_BIT = 0x1 << 2; protected final static int SWEEP_SHADER_BIT = 0x1 << 3; // only allow non-simple shaders to use rectangle drawing protected final static int SWEEP_TRANSFORM_BIT = 0x1 << 4; // only sweep over specified transforms abstract public void modifyDrawing(Paint paint, Canvas canvas); protected int mask() { return 0x0; }; private static final RectF gRect = new RectF(0, 0, 200, 175); private static final float[] gPts = new float[] { 0, 100, 100, 0, 100, 200, 200, 100 }; private static final int NUM_PARALLEL_LINES = 24; private static final float[] gTriPts = new float[] { 75, 0, 130, 130, 130, 130, 0, 130, 0, 130, 75, 0 }; private static final float[] gLinePts = new float[NUM_PARALLEL_LINES * 8 + gTriPts.length]; 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); } }; @SuppressWarnings("serial") private static final LinkedHashMap<String, LinkedHashMap<String, DisplayModifier>> gMaps = 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); } @Override protected int mask() { return SWEEP_STROKE_WIDTH_BIT; } }); put("fillAndStroke", new DisplayModifier() { @Override public void modifyDrawing(Paint paint, Canvas canvas) { paint.setStyle(Paint.Style.FILL_AND_STROKE); } @Override protected int mask() { return SWEEP_STROKE_WIDTH_BIT; } }); } }); put("strokeWidth", new LinkedHashMap<String, DisplayModifier>() { { put("hair", new DisplayModifier() { @Override public void modifyDrawing(Paint paint, Canvas canvas) { paint.setStrokeWidth(0); } @Override protected int mask() { return SWEEP_STROKE_WIDTH_BIT; } }); 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); } @Override protected int mask() { return SWEEP_STROKE_CAP_BIT; } }); 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); } @Override protected int mask() { return SWEEP_STROKE_JOIN_BIT; } }); 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) {} @Override protected int mask() { return SWEEP_TRANSFORM_BIT; }; }); 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); } @Override protected int mask() { return SWEEP_TRANSFORM_BIT; }; }); put("rot20scl1x4", new DisplayModifier() { @Override public void modifyDrawing(Paint paint, Canvas canvas) { canvas.rotate(20); canvas.scale(1, 4); } @Override protected int mask() { return SWEEP_TRANSFORM_BIT; }; }); } }); put("shader", new LinkedHashMap<String, DisplayModifier>() { { put("noShader", new DisplayModifier() { @Override public void modifyDrawing(Paint paint, Canvas canvas) {} @Override protected int mask() { return SWEEP_SHADER_BIT; }; }); put("repeatShader", new DisplayModifier() { @Override public void modifyDrawing(Paint paint, Canvas canvas) { paint.setShader(ResourceModifiers.instance().mRepeatShader); } @Override protected int mask() { return SWEEP_SHADER_BIT; }; }); put("translatedShader", new DisplayModifier() { @Override public void modifyDrawing(Paint paint, Canvas canvas) { paint.setShader(ResourceModifiers.instance().mTranslatedShader); } }); put("scaledShader", new DisplayModifier() { @Override public void modifyDrawing(Paint paint, Canvas canvas) { paint.setShader(ResourceModifiers.instance().mScaledShader); } }); put("horGradient", new DisplayModifier() { @Override public void modifyDrawing(Paint paint, Canvas canvas) { paint.setShader(ResourceModifiers.instance().mHorGradient); } }); put("diagGradient", new DisplayModifier() { @Override public void modifyDrawing(Paint paint, Canvas canvas) { paint.setShader(ResourceModifiers.instance().mDiagGradient); } @Override protected int mask() { return SWEEP_SHADER_BIT; }; }); put("vertGradient", new DisplayModifier() { @Override public void modifyDrawing(Paint paint, Canvas canvas) { paint.setShader(ResourceModifiers.instance().mVertGradient); } }); put("radGradient", new DisplayModifier() { @Override public void modifyDrawing(Paint paint, Canvas canvas) { paint.setShader(ResourceModifiers.instance().mRadGradient); } }); put("sweepGradient", new DisplayModifier() { @Override public void modifyDrawing(Paint paint, Canvas canvas) { paint.setShader(ResourceModifiers.instance().mSweepGradient); } }); put("composeShader", new DisplayModifier() { @Override public void modifyDrawing(Paint paint, Canvas canvas) { paint.setShader(ResourceModifiers.instance().mComposeShader); } }); put("bad composeShader", new DisplayModifier() { @Override public void modifyDrawing(Paint paint, Canvas canvas) { paint.setShader(ResourceModifiers.instance().mBadComposeShader); } }); put("bad composeShader 2", new DisplayModifier() { @Override public void modifyDrawing(Paint paint, Canvas canvas) { paint.setShader(ResourceModifiers.instance().mAnotherBadComposeShader); } }); } }); // 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); } @Override protected int mask() { return SWEEP_SHADER_BIT | SWEEP_STROKE_CAP_BIT; }; }); 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); } @Override protected int mask() { return SWEEP_STROKE_CAP_BIT; }; }); 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(ResourceModifiers.instance().mBitmap, 3, 3, ResourceModifiers.instance().mBitmapVertices, 0, null, 0, null); } }); put("arc", new DisplayModifier() { @Override public void modifyDrawing(Paint paint, Canvas canvas) { canvas.drawArc(gRect, 260, 285, false, paint); } @Override protected int mask() { return SWEEP_STROKE_CAP_BIT; }; }); put("arcFromCenter", new DisplayModifier() { @Override public void modifyDrawing(Paint paint, Canvas canvas) { canvas.drawArc(gRect, 260, 285, true, paint); } @Override protected int mask() { return SWEEP_STROKE_JOIN_BIT; }; }); } }); // WARNING: DON'T PUT MORE MAPS BELOW THIS } }; private static LinkedHashMap<String, DisplayModifier> getMapAtIndex(int index) { for (LinkedHashMap<String, DisplayModifier> map : gMaps.values()) { if (index == 0) { return map; } index--; } return null; } // indices instead of iterators for easier bidirectional traversal private static final int mIndices[] = new int[gMaps.size()]; private static final String[] mLastAppliedModifications = new String[gMaps.size()]; private static boolean stepInternal(boolean forward) { int modifierMapIndex = gMaps.size() - 1; while (modifierMapIndex >= 0) { LinkedHashMap<String, DisplayModifier> map = getMapAtIndex(modifierMapIndex); mIndices[modifierMapIndex] += (forward ? 1 : -1); if (mIndices[modifierMapIndex] >= 0 && mIndices[modifierMapIndex] < map.size()) { break; } mIndices[modifierMapIndex] = (forward ? 0 : map.size() - 1); modifierMapIndex--; } return modifierMapIndex < 0; // true if resetting } public static boolean step() { boolean ret = false; do { ret |= stepInternal(true); } while (!checkModificationStateMask()); return ret; } public static boolean stepBack() { boolean ret = false; do { ret |= stepInternal(false); } while (!checkModificationStateMask()); return ret; } private static boolean checkModificationStateMask() { int operatorMask = 0x0; int mapIndex = 0; for (LinkedHashMap<String, DisplayModifier> map : gMaps.values()) { int displayModifierIndex = mIndices[mapIndex]; for (Entry<String, DisplayModifier> modifierEntry : map.entrySet()) { if (displayModifierIndex == 0) { mLastAppliedModifications[mapIndex] = modifierEntry.getKey(); operatorMask |= modifierEntry.getValue().mask(); break; } displayModifierIndex--; } mapIndex++; } return operatorMask == TOTAL_MASK; } public static void apply(Paint paint, Canvas canvas) { int mapIndex = 0; for (LinkedHashMap<String, DisplayModifier> map : gMaps.values()) { int displayModifierIndex = mIndices[mapIndex]; for (Entry<String, DisplayModifier> modifierEntry : map.entrySet()) { if (displayModifierIndex == 0) { mLastAppliedModifications[mapIndex] = modifierEntry.getKey(); modifierEntry.getValue().modifyDrawing(paint, canvas); break; } displayModifierIndex--; } mapIndex++; } } public static String[] getLastAppliedModifications() { return mLastAppliedModifications.clone(); } public static String[][] getStrings() { String[][] keys = new String[gMaps.size()][]; int i = 0; for (LinkedHashMap<String, DisplayModifier> map : gMaps.values()) { keys[i] = new String[map.size()]; int j = 0; for (String key : map.keySet()) { keys[i][j++] = key; } i++; } return keys; } public static void setIndex(int mapIndex, int newIndexValue) { mIndices[mapIndex] = newIndexValue; } public static int[] getIndices() { return mIndices; } }