1 /* 2 * Copyright (C) 2014 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/LICENSE2.0 9 * 10 * Unless required by applicable law or agreed to in riting, 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.testinfrastructure; 17 18 import android.graphics.Canvas; 19 import android.graphics.ColorFilter; 20 import android.graphics.Paint; 21 import android.graphics.PorterDuff; 22 import android.graphics.PorterDuffColorFilter; 23 import android.graphics.PorterDuffXfermode; 24 import android.graphics.RectF; 25 import android.graphics.Xfermode; 26 27 import java.util.ArrayList; 28 import java.util.LinkedHashMap; 29 import java.util.Map; 30 31 /** 32 * Modifies the canvas and paint objects when called. 33 */ 34 public abstract class DisplayModifier { 35 private static final RectF RECT = new RectF(0, 0, 100, 100); 36 private static final float[] POINTS = new float[]{ 37 0, 40, 40, 0, 40, 80, 80, 40 38 }; 39 private static final float[] TRIANGLE_POINTS = new float[]{ 40 40, 0, 80, 80, 80, 80, 0, 80, 0, 80, 40, 0 41 }; 42 private static final int NUM_PARALLEL_LINES = 10; 43 private static final float[] LINES = new float[NUM_PARALLEL_LINES * 8 44 + TRIANGLE_POINTS.length]; 45 46 public static final PorterDuff.Mode[] PORTERDUFF_MODES = new PorterDuff.Mode[] { 47 PorterDuff.Mode.SRC, PorterDuff.Mode.DST, PorterDuff.Mode.SRC_OVER, 48 PorterDuff.Mode.DST_OVER, PorterDuff.Mode.SRC_IN, PorterDuff.Mode.DST_IN, 49 PorterDuff.Mode.SRC_OUT, PorterDuff.Mode.DST_OUT, PorterDuff.Mode.SRC_ATOP, 50 PorterDuff.Mode.DST_ATOP, PorterDuff.Mode.XOR, PorterDuff.Mode.MULTIPLY, 51 PorterDuff.Mode.SCREEN 52 }; 53 54 static { 55 System.arraycopy(TRIANGLE_POINTS, 0, LINES, 0, TRIANGLE_POINTS.length); 56 int index = TRIANGLE_POINTS.length; 57 float val = 0; 58 for (int i = 0; i < NUM_PARALLEL_LINES; i++) { 59 LINES[index + 0] = 40; 60 LINES[index + 1] = val; 61 LINES[index + 2] = 80; 62 LINES[index + 3] = val; 63 index += 4; 64 val += 8 + (2.0f / NUM_PARALLEL_LINES); 65 } 66 val = 0; 67 for (int i = 0; i < NUM_PARALLEL_LINES; i++) { 68 LINES[index + 0] = val; 69 LINES[index + 1] = 40; 70 LINES[index + 2] = val; 71 LINES[index + 3] = 80; 72 index += 4; 73 val += 8 + (2.0f / NUM_PARALLEL_LINES); 74 } 75 } 76 77 // This linked hash map contains each of the different things that can be done to a canvas and 78 // paint object, like anti-aliasing or drawing. Within those LinkedHashMaps are the various 79 // options for that specific topic, which contains a displaymodifier which will affect the 80 // given canvas and paint objects. 81 public static final LinkedHashMap<String, LinkedHashMap<String, DisplayModifier>> MAPS = 82 new LinkedHashMap<String, LinkedHashMap<String,DisplayModifier>>() { 83 { 84 put("aa", new LinkedHashMap<String, DisplayModifier>() { 85 { 86 put("true", new DisplayModifier() { 87 @Override 88 public void modifyDrawing(Paint paint, Canvas canvas) { 89 paint.setAntiAlias(true); 90 } 91 }); 92 put("false", new DisplayModifier() { 93 @Override 94 public void modifyDrawing(Paint paint, Canvas canvas) { 95 paint.setAntiAlias(false); 96 } 97 }); 98 } 99 }); 100 put("style", new LinkedHashMap<String, DisplayModifier>() { 101 { 102 put("fill", new DisplayModifier() { 103 @Override 104 public void modifyDrawing(Paint paint, Canvas canvas) { 105 paint.setStyle(Paint.Style.FILL); 106 } 107 }); 108 put("stroke", new DisplayModifier() { 109 @Override 110 public void modifyDrawing(Paint paint, Canvas canvas) { 111 paint.setStyle(Paint.Style.STROKE); 112 } 113 }); 114 put("fillAndStroke", new DisplayModifier() { 115 @Override 116 public void modifyDrawing(Paint paint, Canvas canvas) { 117 paint.setStyle(Paint.Style.FILL_AND_STROKE); 118 } 119 }); 120 } 121 }); 122 put("strokeWidth", new LinkedHashMap<String, DisplayModifier>() { 123 { 124 put("hair", new DisplayModifier() { 125 @Override 126 public void modifyDrawing(Paint paint, Canvas canvas) { 127 paint.setStrokeWidth(0); 128 } 129 }); 130 put("0.3", new DisplayModifier() { 131 @Override 132 public void modifyDrawing(Paint paint, Canvas canvas) { 133 paint.setStrokeWidth(0.3f); 134 } 135 }); 136 put("1", new DisplayModifier() { 137 @Override 138 public void modifyDrawing(Paint paint, Canvas canvas) { 139 paint.setStrokeWidth(1); 140 } 141 }); 142 put("5", new DisplayModifier() { 143 @Override 144 public void modifyDrawing(Paint paint, Canvas canvas) { 145 paint.setStrokeWidth(5); 146 } 147 }); 148 put("30", new DisplayModifier() { 149 @Override 150 public void modifyDrawing(Paint paint, Canvas canvas) { 151 paint.setStrokeWidth(30); 152 } 153 }); 154 } 155 }); 156 put("strokeCap", new LinkedHashMap<String, DisplayModifier>() { 157 { 158 put("butt", new DisplayModifier() { 159 @Override 160 public void modifyDrawing(Paint paint, Canvas canvas) { 161 paint.setStrokeCap(Paint.Cap.BUTT); 162 } 163 }); 164 put("round", new DisplayModifier() { 165 @Override 166 public void modifyDrawing(Paint paint, Canvas canvas) { 167 paint.setStrokeCap(Paint.Cap.ROUND); 168 } 169 }); 170 put("square", new DisplayModifier() { 171 @Override 172 public void modifyDrawing(Paint paint, Canvas canvas) { 173 paint.setStrokeCap(Paint.Cap.SQUARE); 174 } 175 }); 176 } 177 }); 178 put("strokeJoin", new LinkedHashMap<String, DisplayModifier>() { 179 { 180 put("bevel", new DisplayModifier() { 181 @Override 182 public void modifyDrawing(Paint paint, Canvas canvas) { 183 paint.setStrokeJoin(Paint.Join.BEVEL); 184 } 185 }); 186 put("round", new DisplayModifier() { 187 @Override 188 public void modifyDrawing(Paint paint, Canvas canvas) { 189 paint.setStrokeJoin(Paint.Join.ROUND); 190 } 191 }); 192 put("miter", new DisplayModifier() { 193 @Override 194 public void modifyDrawing(Paint paint, Canvas canvas) { 195 paint.setStrokeJoin(Paint.Join.MITER); 196 } 197 }); 198 // TODO: add miter0, miter1 etc to test miter distances 199 } 200 }); 201 202 put("transform", new LinkedHashMap<String, DisplayModifier>() { 203 { 204 put("noTransform", new DisplayModifier() { 205 @Override 206 public void modifyDrawing(Paint paint, Canvas canvas) { 207 } 208 }); 209 put("rotate5", new DisplayModifier() { 210 @Override 211 public void modifyDrawing(Paint paint, Canvas canvas) { 212 canvas.rotate(5); 213 } 214 }); 215 put("rotate45", new DisplayModifier() { 216 @Override 217 public void modifyDrawing(Paint paint, Canvas canvas) { 218 canvas.rotate(45); 219 } 220 }); 221 put("rotate90", new DisplayModifier() { 222 @Override 223 public void modifyDrawing(Paint paint, Canvas canvas) { 224 canvas.rotate(90); 225 canvas.translate(0, -100); 226 } 227 }); 228 put("scale2x2", new DisplayModifier() { 229 @Override 230 public void modifyDrawing(Paint paint, Canvas canvas) { 231 canvas.scale(2, 2); 232 } 233 }); 234 put("rot20scl1x4", new DisplayModifier() { 235 @Override 236 public void modifyDrawing(Paint paint, Canvas canvas) { 237 canvas.rotate(20); 238 canvas.scale(1, 4); 239 } 240 }); 241 } 242 }); 243 244 put("shader", new LinkedHashMap<String, DisplayModifier>() { 245 { 246 put("noShader", new DisplayModifier() { 247 @Override 248 public void modifyDrawing(Paint paint, Canvas canvas) { 249 } 250 }); 251 put("repeatShader", new DisplayModifier() { 252 @Override 253 public void modifyDrawing(Paint paint, Canvas canvas) { 254 paint.setShader(ResourceModifier.instance().repeatShader); 255 } 256 }); 257 put("translatedShader", new DisplayModifier() { 258 @Override 259 public void modifyDrawing(Paint paint, Canvas canvas) { 260 paint.setShader(ResourceModifier.instance().translatedShader); 261 } 262 }); 263 put("scaledShader", new DisplayModifier() { 264 @Override 265 public void modifyDrawing(Paint paint, Canvas canvas) { 266 paint.setShader(ResourceModifier.instance().scaledShader); 267 } 268 }); 269 put("composeShader", new DisplayModifier() { 270 @Override 271 public void modifyDrawing(Paint paint, Canvas canvas) { 272 paint.setShader(ResourceModifier.instance().composeShader); 273 } 274 }); 275 /* 276 put("bad composeShader", new DisplayModifier() { 277 @Override 278 public void modifyDrawing(Paint paint, Canvas canvas) { 279 paint.setShader(ResourceModifier.instance().nestedComposeShader); 280 } 281 }); 282 put("bad composeShader 2", new DisplayModifier() { 283 @Override 284 public void modifyDrawing(Paint paint, Canvas canvas) { 285 paint.setShader( 286 ResourceModifier.instance().doubleGradientComposeShader); 287 } 288 }); 289 */ 290 put("horGradient", new DisplayModifier() { 291 @Override 292 public void modifyDrawing(Paint paint, Canvas canvas) { 293 paint.setShader(ResourceModifier.instance().horGradient); 294 } 295 }); 296 put("diagGradient", new DisplayModifier() { 297 @Override 298 public void modifyDrawing(Paint paint, Canvas canvas) { 299 paint.setShader(ResourceModifier.instance().diagGradient); 300 } 301 }); 302 put("vertGradient", new DisplayModifier() { 303 @Override 304 public void modifyDrawing(Paint paint, Canvas canvas) { 305 paint.setShader(ResourceModifier.instance().vertGradient); 306 } 307 }); 308 put("radGradient", new DisplayModifier() { 309 @Override 310 public void modifyDrawing(Paint paint, Canvas canvas) { 311 paint.setShader(ResourceModifier.instance().radGradient); 312 } 313 }); 314 put("sweepGradient", new DisplayModifier() { 315 @Override 316 public void modifyDrawing(Paint paint, Canvas canvas) { 317 paint.setShader(ResourceModifier.instance().sweepGradient); 318 } 319 }); 320 } 321 }); 322 323 put("xfermodes", new LinkedHashMap<String, DisplayModifier>() { 324 { 325 for (int i = 0 ; i < PORTERDUFF_MODES.length ; i++) { 326 put(PORTERDUFF_MODES[i].toString(), 327 new XfermodeModifier(PORTERDUFF_MODES[i])); 328 } 329 } 330 }); 331 332 put("colorfilters", new LinkedHashMap<String, DisplayModifier>() { 333 { 334 for (int i = 0 ; i < PORTERDUFF_MODES.length ; i++) { 335 put(PORTERDUFF_MODES[i].toString(), 336 new ColorFilterModifier(PORTERDUFF_MODES[i])); 337 } 338 } 339 }); 340 341 // FINAL MAP: DOES ACTUAL DRAWING 342 put("drawing", new LinkedHashMap<String, DisplayModifier>() { 343 { 344 put("roundRect", new DisplayModifier() { 345 @Override 346 public void modifyDrawing(Paint paint, Canvas canvas) { 347 canvas.drawRoundRect(RECT, 20, 20, paint); 348 } 349 }); 350 put("rect", new DisplayModifier() { 351 @Override 352 public void modifyDrawing(Paint paint, Canvas canvas) { 353 canvas.drawRect(RECT, paint); 354 } 355 }); 356 put("circle", new DisplayModifier() { 357 @Override 358 public void modifyDrawing(Paint paint, Canvas canvas) { 359 canvas.drawCircle(100, 100, 75, paint); 360 } 361 }); 362 put("oval", new DisplayModifier() { 363 @Override 364 public void modifyDrawing(Paint paint, Canvas canvas) { 365 canvas.drawOval(RECT, paint); 366 } 367 }); 368 put("lines", new DisplayModifier() { 369 @Override 370 public void modifyDrawing(Paint paint, Canvas canvas) { 371 canvas.drawLines(LINES, paint); 372 } 373 }); 374 put("plusPoints", new DisplayModifier() { 375 @Override 376 public void modifyDrawing(Paint paint, Canvas canvas) { 377 canvas.drawPoints(POINTS, paint); 378 } 379 }); 380 put("text", new DisplayModifier() { 381 @Override 382 public void modifyDrawing(Paint paint, Canvas canvas) { 383 paint.setTextSize(20); 384 canvas.drawText("TEXTTEST", 0, 50, paint); 385 } 386 }); 387 put("shadowtext", new DisplayModifier() { 388 @Override 389 public void modifyDrawing(Paint paint, Canvas canvas) { 390 paint.setTextSize(20); 391 paint.setShadowLayer(3.0f, 0.0f, 3.0f, 0xffff00ff); 392 canvas.drawText("TEXTTEST", 0, 50, paint); 393 } 394 }); 395 put("bitmapMesh", new DisplayModifier() { 396 @Override 397 public void modifyDrawing(Paint paint, Canvas canvas) { 398 canvas.drawBitmapMesh(ResourceModifier.instance().bitmap, 3, 3, 399 ResourceModifier.instance().bitmapVertices, 0, null, 0, 400 null); 401 } 402 }); 403 put("arc", new DisplayModifier() { 404 @Override 405 public void modifyDrawing(Paint paint, Canvas canvas) { 406 canvas.drawArc(RECT, 260, 285, false, paint); 407 } 408 }); 409 put("arcFromCenter", new DisplayModifier() { 410 @Override 411 public void modifyDrawing(Paint paint, Canvas canvas) { 412 canvas.drawArc(RECT, 260, 285, true, paint); 413 } 414 }); 415 } 416 }); 417 // WARNING: DON'T PUT MORE MAPS BELOW THIS 418 } 419 }; 420 421 abstract public void modifyDrawing(Paint paint, Canvas canvas); 422 423 public static class Accessor { 424 public final static int AA_MASK = 0x1 << 0; 425 public final static int STYLE_MASK = 0x1 << 1; 426 public final static int STROKE_WIDTH_MASK = 0x1 << 2; 427 public final static int STROKE_CAP_MASK = 0x1 << 3; 428 public final static int STROKE_JOIN_MASK = 0x1 << 4; 429 public final static int TRANSFORM_MASK = 0x1 << 5; 430 public final static int SHADER_MASK = 0x1 << 6; 431 public final static int XFERMODE_MASK = 0x1 << 7; 432 public final static int COLOR_FILTER_MASK = 0x1 << 8; 433 public final static int SHAPES_MASK = 0x1 << 9; 434 public final static int ALL_OPTIONS_MASK = (0x1 << 10) - 1; 435 public final static int SHAPES_INDEX = 9; 436 public final static int XFERMODE_INDEX = 7; 437 private final int mMask; 438 439 private String mDebugString; 440 private int[] mIndices; 441 private LinkedHashMap<String, LinkedHashMap<String, DisplayModifier>> mDisplayMap; 442 443 public Accessor(int mask) { 444 int totalModifiers = Integer.bitCount(mask); 445 mIndices = new int[totalModifiers]; 446 mMask = mask; 447 // Create a Display Map of the valid indices 448 mDisplayMap = new LinkedHashMap<String, LinkedHashMap<String, DisplayModifier>>(); 449 int index = 0; 450 for (String key : DisplayModifier.MAPS.keySet()) { 451 if (validIndex(index)) { 452 mDisplayMap.put(key, DisplayModifier.MAPS.get(key)); 453 } 454 index++; 455 } 456 mDebugString = ""; 457 } 458 459 private LinkedHashMap<String, DisplayModifier> getMapAtIndex(int index) { 460 int i = 0; 461 for (LinkedHashMap<String, DisplayModifier> map : mDisplayMap.values()) { 462 if (i == index) { 463 return map; 464 } 465 i++; 466 } 467 return null; 468 } 469 470 /** 471 * This will create the next combination of drawing commands. If we have done every combination, 472 * then we will return false. 473 * @return true if there is more combinations to do 474 */ 475 public boolean step() { 476 int modifierMapIndex = mIndices.length - 1; 477 // Start from the last map, and loop until it is at the front 478 while (modifierMapIndex >= 0) { 479 LinkedHashMap<String, DisplayModifier> map = getMapAtIndex(modifierMapIndex); 480 mIndices[modifierMapIndex]++; 481 482 // If we are still at a valid index, then we don't need to update any others 483 if (mIndices[modifierMapIndex] < map.size()) { 484 break; 485 } 486 487 // If we updated and it was outside the boundary, and it was the last index then 488 // we are done 489 if (modifierMapIndex == 0) { 490 return false; 491 } 492 // If we ran off the end of the map, we need to update one more down the list 493 mIndices[modifierMapIndex] = 0; 494 495 modifierMapIndex--; 496 } 497 return true; 498 } 499 500 /** 501 * Modifies the canvas and paint given for the particular combination currently 502 */ 503 public void modifyDrawing(Canvas canvas, Paint paint) { 504 final ArrayList<DisplayModifier> modifierArrayList = getModifierList(); 505 for (DisplayModifier modifier : modifierArrayList) { 506 modifier.modifyDrawing(paint, canvas); 507 } 508 } 509 510 /** 511 * Gets a list of all the current modifications to be used. 512 */ 513 private ArrayList<DisplayModifier> getModifierList() { 514 ArrayList<DisplayModifier> modifierArrayList = new ArrayList<DisplayModifier>(); 515 int mapIndex = 0; 516 mDebugString = ""; 517 518 // Through each possible category of modification 519 for (Map.Entry<String, LinkedHashMap<String, DisplayModifier>> entry : 520 mDisplayMap.entrySet()) { 521 int displayModifierIndex = mIndices[mapIndex]; 522 mDebugString += "Modification : " + entry.getKey(); 523 // Loop until we find the modification we are going to use 524 for (Map.Entry<String, DisplayModifier> modifierEntry : 525 entry.getValue().entrySet()) { 526 // Once we find the modification we want, then we will add it to the list, 527 // and the last applied modifications 528 if (displayModifierIndex == 0) { 529 mDebugString += " value : " + modifierEntry.getKey() + " "; 530 modifierArrayList.add(modifierEntry.getValue()); 531 break; 532 } 533 displayModifierIndex--; 534 } 535 mapIndex++; 536 } 537 return modifierArrayList; 538 } 539 540 public String getDebugString() { 541 return mDebugString; 542 } 543 544 /** 545 * Using the given masks, it tells if the map at the given index should be used, or not. 546 */ 547 private boolean validIndex(int index) { 548 return (mMask & (0x1 << index)) != 0; 549 } 550 } 551 552 private static class XfermodeModifier extends DisplayModifier { 553 private Xfermode mXfermode; 554 555 public XfermodeModifier(PorterDuff.Mode mode) { 556 mXfermode = new PorterDuffXfermode(mode); 557 } 558 559 @Override 560 public void modifyDrawing(Paint paint, Canvas canvas) { 561 paint.setXfermode(mXfermode); 562 } 563 } 564 565 private static class ColorFilterModifier extends DisplayModifier { 566 private static final int FILTER_COLOR = 0xFFBB0000; 567 private ColorFilter mColorFilter; 568 569 public ColorFilterModifier(PorterDuff.Mode mode) { 570 mColorFilter = new PorterDuffColorFilter(FILTER_COLOR, mode); 571 } 572 573 @Override 574 public void modifyDrawing(Paint paint, Canvas canvas) { 575 paint.setColorFilter(mColorFilter); 576 } 577 } 578 } 579