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