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