1 /*
2  * Copyright (C) 2012 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/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, 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 
17 package com.android.test.hwuicompare;
18 
19 import java.util.LinkedHashMap;
20 import java.util.Map.Entry;
21 
22 import android.graphics.Canvas;
23 import android.graphics.Paint;
24 import android.graphics.RectF;
25 import android.util.Log;
26 
27 public abstract class DisplayModifier {
28 
29     // automated tests ignore any combination of operations that don't together return TOTAL_MASK
30     protected final static int TOTAL_MASK = 0x1F;
31 
32     // if we're filling, ensure we're not also sweeping over stroke parameters
33     protected final static int SWEEP_STROKE_WIDTH_BIT = 0x1 << 0;
34     protected final static int SWEEP_STROKE_CAP_BIT = 0x1 << 1;
35     protected final static int SWEEP_STROKE_JOIN_BIT = 0x1 << 2;
36 
37     protected final static int SWEEP_SHADER_BIT = 0x1 << 3; // only allow non-simple shaders to use rectangle drawing
38     protected final static int SWEEP_TRANSFORM_BIT = 0x1 << 4; // only sweep over specified transforms
39 
modifyDrawing(Paint paint, Canvas canvas)40     abstract public void modifyDrawing(Paint paint, Canvas canvas);
mask()41     protected int mask() { return 0x0; };
42 
43     private static final RectF gRect = new RectF(0, 0, 200, 175);
44     private static final float[] gPts = new float[] {
45             0, 100, 100, 0, 100, 200, 200, 100
46     };
47 
48     private static final int NUM_PARALLEL_LINES = 24;
49     private static final float[] gTriPts = new float[] {
50         75, 0, 130, 130, 130, 130, 0, 130, 0, 130, 75, 0
51     };
52     private static final float[] gLinePts = new float[NUM_PARALLEL_LINES * 8 + gTriPts.length];
53     static {
54         int index;
55         for (index = 0; index < gTriPts.length; index++) {
56             gLinePts[index] = gTriPts[index];
57         }
58         float val = 0;
59         for (int i = 0; i < NUM_PARALLEL_LINES; i++) {
60             gLinePts[index + 0] = 150;
61             gLinePts[index + 1] = val;
62             gLinePts[index + 2] = 300;
63             gLinePts[index + 3] = val;
64             index += 4;
65             val += 8 + (2.0f/NUM_PARALLEL_LINES);
66         }
67         val = 0;
68         for (int i = 0; i < NUM_PARALLEL_LINES; i++) {
69             gLinePts[index + 0] = val;
70             gLinePts[index + 1] = 150;
71             gLinePts[index + 2] = val;
72             gLinePts[index + 3] = 300;
73             index += 4;
74             val += 8 + (2.0f/NUM_PARALLEL_LINES);
75         }
76     };
77 
78     @SuppressWarnings("serial")
79     private static final LinkedHashMap<String, LinkedHashMap<String, DisplayModifier>> gMaps = new LinkedHashMap<String, LinkedHashMap<String, DisplayModifier>>() {
80         {
81             put("aa", new LinkedHashMap<String, DisplayModifier>() {
82                 {
83                     put("true", new DisplayModifier() {
84                         @Override
85                         public void modifyDrawing(Paint paint, Canvas canvas) {
86                             paint.setAntiAlias(true);
87                         }
88                     });
89                     put("false", new DisplayModifier() {
90                         @Override
91                         public void modifyDrawing(Paint paint, Canvas canvas) {
92                             paint.setAntiAlias(false);
93                         }
94                     });
95                 }
96             });
97             put("style", new LinkedHashMap<String, DisplayModifier>() {
98                 {
99                     put("fill", new DisplayModifier() {
100                         @Override
101                         public void modifyDrawing(Paint paint, Canvas canvas) {
102                             paint.setStyle(Paint.Style.FILL);
103                         }
104                     });
105                     put("stroke", new DisplayModifier() {
106                         @Override
107                         public void modifyDrawing(Paint paint, Canvas canvas) {
108                             paint.setStyle(Paint.Style.STROKE);
109                         }
110                         @Override
111                         protected int mask() { return SWEEP_STROKE_WIDTH_BIT; }
112                     });
113                     put("fillAndStroke", new DisplayModifier() {
114                         @Override
115                         public void modifyDrawing(Paint paint, Canvas canvas) {
116                             paint.setStyle(Paint.Style.FILL_AND_STROKE);
117                         }
118 
119                         @Override
120                         protected int mask() { return SWEEP_STROKE_WIDTH_BIT; }
121                     });
122                 }
123             });
124             put("strokeWidth", new LinkedHashMap<String, DisplayModifier>() {
125                 {
126                     put("hair", new DisplayModifier() {
127                         @Override
128                         public void modifyDrawing(Paint paint, Canvas canvas) {
129                             paint.setStrokeWidth(0);
130                         }
131                         @Override
132                         protected int mask() { return SWEEP_STROKE_WIDTH_BIT; }
133                     });
134                     put("0.3", new DisplayModifier() {
135                         @Override
136                         public void modifyDrawing(Paint paint, Canvas canvas) {
137                             paint.setStrokeWidth(0.3f);
138                         }
139                     });
140                     put("1", new DisplayModifier() {
141                         @Override
142                         public void modifyDrawing(Paint paint, Canvas canvas) {
143                             paint.setStrokeWidth(1);
144                         }
145                     });
146                     put("5", new DisplayModifier() {
147                         @Override
148                         public void modifyDrawing(Paint paint, Canvas canvas) {
149                             paint.setStrokeWidth(5);
150                         }
151                     });
152                     put("30", new DisplayModifier() {
153                         @Override
154                         public void modifyDrawing(Paint paint, Canvas canvas) {
155                             paint.setStrokeWidth(30);
156                         }
157                     });
158                 }
159             });
160             put("strokeCap", new LinkedHashMap<String, DisplayModifier>() {
161                 {
162                     put("butt", new DisplayModifier() {
163                         @Override
164                         public void modifyDrawing(Paint paint, Canvas canvas) {
165                             paint.setStrokeCap(Paint.Cap.BUTT);
166                         }
167                         @Override
168                         protected int mask() { return SWEEP_STROKE_CAP_BIT; }
169                     });
170                     put("round", new DisplayModifier() {
171                         @Override
172                         public void modifyDrawing(Paint paint, Canvas canvas) {
173                             paint.setStrokeCap(Paint.Cap.ROUND);
174                         }
175                     });
176                     put("square", new DisplayModifier() {
177                         @Override
178                         public void modifyDrawing(Paint paint, Canvas canvas) {
179                             paint.setStrokeCap(Paint.Cap.SQUARE);
180                         }
181                     });
182                 }
183             });
184             put("strokeJoin", new LinkedHashMap<String, DisplayModifier>() {
185                 {
186                     put("bevel", new DisplayModifier() {
187                         @Override
188                         public void modifyDrawing(Paint paint, Canvas canvas) {
189                             paint.setStrokeJoin(Paint.Join.BEVEL);
190                         }
191                         @Override
192                         protected int mask() { return SWEEP_STROKE_JOIN_BIT; }
193                     });
194                     put("round", new DisplayModifier() {
195                         @Override
196                         public void modifyDrawing(Paint paint, Canvas canvas) {
197                             paint.setStrokeJoin(Paint.Join.ROUND);
198                         }
199                     });
200                     put("miter", new DisplayModifier() {
201                         @Override
202                         public void modifyDrawing(Paint paint, Canvas canvas) {
203                             paint.setStrokeJoin(Paint.Join.MITER);
204                         }
205                     });
206                     // TODO: add miter0, miter1 etc to test miter distances
207                 }
208             });
209 
210             put("transform", new LinkedHashMap<String, DisplayModifier>() {
211                 {
212                     put("noTransform", new DisplayModifier() {
213                         @Override
214                         public void modifyDrawing(Paint paint, Canvas canvas) {}
215                         @Override
216                         protected int mask() { return SWEEP_TRANSFORM_BIT; };
217                     });
218                     put("rotate5", new DisplayModifier() {
219                         @Override
220                         public void modifyDrawing(Paint paint, Canvas canvas) {
221                             canvas.rotate(5);
222                         }
223                     });
224                     put("rotate45", new DisplayModifier() {
225                         @Override
226                         public void modifyDrawing(Paint paint, Canvas canvas) {
227                             canvas.rotate(45);
228                         }
229                     });
230                     put("rotate90", new DisplayModifier() {
231                         @Override
232                         public void modifyDrawing(Paint paint, Canvas canvas) {
233                             canvas.rotate(90);
234                             canvas.translate(0, -200);
235                         }
236                     });
237                     put("scale2x2", new DisplayModifier() {
238                         @Override
239                         public void modifyDrawing(Paint paint, Canvas canvas) {
240                             canvas.scale(2, 2);
241                         }
242                         @Override
243                         protected int mask() { return SWEEP_TRANSFORM_BIT; };
244                     });
245                     put("rot20scl1x4", new DisplayModifier() {
246                         @Override
247                         public void modifyDrawing(Paint paint, Canvas canvas) {
248                             canvas.rotate(20);
249                             canvas.scale(1, 4);
250                         }
251                         @Override
252                         protected int mask() { return SWEEP_TRANSFORM_BIT; };
253                     });
254                 }
255             });
256 
257             put("shader", new LinkedHashMap<String, DisplayModifier>() {
258                 {
259                     put("noShader", new DisplayModifier() {
260                         @Override
261                         public void modifyDrawing(Paint paint, Canvas canvas) {}
262                         @Override
263                         protected int mask() { return SWEEP_SHADER_BIT; };
264                     });
265                     put("repeatShader", new DisplayModifier() {
266                         @Override
267                         public void modifyDrawing(Paint paint, Canvas canvas) {
268                             paint.setShader(ResourceModifiers.instance().mRepeatShader);
269                         }
270                         @Override
271                         protected int mask() { return SWEEP_SHADER_BIT; };
272                     });
273                     put("translatedShader", new DisplayModifier() {
274                         @Override
275                         public void modifyDrawing(Paint paint, Canvas canvas) {
276                             paint.setShader(ResourceModifiers.instance().mTranslatedShader);
277                         }
278                     });
279                     put("scaledShader", new DisplayModifier() {
280                         @Override
281                         public void modifyDrawing(Paint paint, Canvas canvas) {
282                             paint.setShader(ResourceModifiers.instance().mScaledShader);
283                         }
284                     });
285                     put("horGradient", new DisplayModifier() {
286                         @Override
287                         public void modifyDrawing(Paint paint, Canvas canvas) {
288                             paint.setShader(ResourceModifiers.instance().mHorGradient);
289                         }
290                     });
291                     put("diagGradient", new DisplayModifier() {
292                         @Override
293                         public void modifyDrawing(Paint paint, Canvas canvas) {
294                             paint.setShader(ResourceModifiers.instance().mDiagGradient);
295                         }
296                         @Override
297                         protected int mask() { return SWEEP_SHADER_BIT; };
298                     });
299                     put("vertGradient", new DisplayModifier() {
300                         @Override
301                         public void modifyDrawing(Paint paint, Canvas canvas) {
302                             paint.setShader(ResourceModifiers.instance().mVertGradient);
303                         }
304                     });
305                     put("radGradient", new DisplayModifier() {
306                         @Override
307                         public void modifyDrawing(Paint paint, Canvas canvas) {
308                             paint.setShader(ResourceModifiers.instance().mRadGradient);
309                         }
310                     });
311                     put("sweepGradient", new DisplayModifier() {
312                         @Override
313                         public void modifyDrawing(Paint paint, Canvas canvas) {
314                             paint.setShader(ResourceModifiers.instance().mSweepGradient);
315                         }
316                     });
317                     put("composeShader", new DisplayModifier() {
318                         @Override
319                         public void modifyDrawing(Paint paint, Canvas canvas) {
320                             paint.setShader(ResourceModifiers.instance().mComposeShader);
321                         }
322                     });
323                     put("bad composeShader", new DisplayModifier() {
324                         @Override
325                         public void modifyDrawing(Paint paint, Canvas canvas) {
326                             paint.setShader(ResourceModifiers.instance().mBadComposeShader);
327                         }
328                     });
329                     put("bad composeShader 2", new DisplayModifier() {
330                         @Override
331                         public void modifyDrawing(Paint paint, Canvas canvas) {
332                             paint.setShader(ResourceModifiers.instance().mAnotherBadComposeShader);
333                         }
334                     });
335                 }
336             });
337 
338             // FINAL MAP: DOES ACTUAL DRAWING
339             put("drawing", new LinkedHashMap<String, DisplayModifier>() {
340                 {
341                     put("roundRect", new DisplayModifier() {
342                         @Override
343                         public void modifyDrawing(Paint paint, Canvas canvas) {
344                             canvas.drawRoundRect(gRect, 20, 20, paint);
345                         }
346                     });
347                     put("rect", new DisplayModifier() {
348                         @Override
349                         public void modifyDrawing(Paint paint, Canvas canvas) {
350                             canvas.drawRect(gRect, paint);
351                         }
352                         @Override
353                         protected int mask() { return SWEEP_SHADER_BIT | SWEEP_STROKE_CAP_BIT; };
354                     });
355                     put("circle", new DisplayModifier() {
356                         @Override
357                         public void modifyDrawing(Paint paint, Canvas canvas) {
358                             canvas.drawCircle(100, 100, 75, paint);
359                         }
360                     });
361                     put("oval", new DisplayModifier() {
362                         @Override
363                         public void modifyDrawing(Paint paint, Canvas canvas) {
364                             canvas.drawOval(gRect, paint);
365                         }
366                     });
367                     put("lines", new DisplayModifier() {
368                         @Override
369                         public void modifyDrawing(Paint paint, Canvas canvas) {
370                             canvas.drawLines(gLinePts, paint);
371                         }
372                         @Override
373                         protected int mask() { return SWEEP_STROKE_CAP_BIT; };
374                     });
375                     put("plusPoints", new DisplayModifier() {
376                         @Override
377                         public void modifyDrawing(Paint paint, Canvas canvas) {
378                             canvas.drawPoints(gPts, paint);
379                         }
380                     });
381                     put("text", new DisplayModifier() {
382                         @Override
383                         public void modifyDrawing(Paint paint, Canvas canvas) {
384                             paint.setTextSize(36);
385                             canvas.drawText("TEXTTEST", 0, 50, paint);
386                         }
387                     });
388                     put("shadowtext", new DisplayModifier() {
389                         @Override
390                         public void modifyDrawing(Paint paint, Canvas canvas) {
391                             paint.setTextSize(36);
392                             paint.setShadowLayer(3.0f, 0.0f, 3.0f, 0xffff00ff);
393                             canvas.drawText("TEXTTEST", 0, 50, paint);
394                         }
395                     });
396                     put("bitmapMesh", new DisplayModifier() {
397                         @Override
398                         public void modifyDrawing(Paint paint, Canvas canvas) {
399                             canvas.drawBitmapMesh(ResourceModifiers.instance().mBitmap, 3, 3,
400                                     ResourceModifiers.instance().mBitmapVertices, 0, null, 0, null);
401                         }
402                     });
403                     put("arc", new DisplayModifier() {
404                         @Override
405                         public void modifyDrawing(Paint paint, Canvas canvas) {
406                             canvas.drawArc(gRect, 260, 285, false, paint);
407                         }
408                         @Override
409                         protected int mask() { return SWEEP_STROKE_CAP_BIT; };
410                     });
411                     put("arcFromCenter", new DisplayModifier() {
412                         @Override
413                         public void modifyDrawing(Paint paint, Canvas canvas) {
414                             canvas.drawArc(gRect, 260, 285, true, paint);
415                         }
416                         @Override
417                         protected int mask() { return SWEEP_STROKE_JOIN_BIT; };
418                     });
419                 }
420             });
421             // WARNING: DON'T PUT MORE MAPS BELOW THIS
422         }
423     };
424 
getMapAtIndex(int index)425     private static LinkedHashMap<String, DisplayModifier> getMapAtIndex(int index) {
426         for (LinkedHashMap<String, DisplayModifier> map : gMaps.values()) {
427             if (index == 0) {
428                 return map;
429             }
430             index--;
431         }
432         return null;
433     }
434 
435     // indices instead of iterators for easier bidirectional traversal
436     private static final int mIndices[] = new int[gMaps.size()];
437     private static final String[] mLastAppliedModifications = new String[gMaps.size()];
438 
stepInternal(boolean forward)439     private static boolean stepInternal(boolean forward) {
440         int modifierMapIndex = gMaps.size() - 1;
441         while (modifierMapIndex >= 0) {
442             LinkedHashMap<String, DisplayModifier> map = getMapAtIndex(modifierMapIndex);
443             mIndices[modifierMapIndex] += (forward ? 1 : -1);
444 
445             if (mIndices[modifierMapIndex] >= 0 && mIndices[modifierMapIndex] < map.size()) {
446                 break;
447             }
448 
449             mIndices[modifierMapIndex] = (forward ? 0 : map.size() - 1);
450             modifierMapIndex--;
451         }
452         return modifierMapIndex < 0; // true if resetting
453     }
454 
step()455     public static boolean step() {
456         boolean ret = false;
457         do {
458             ret |= stepInternal(true);
459         } while (!checkModificationStateMask());
460         return ret;
461     }
462 
stepBack()463     public static boolean stepBack() {
464         boolean ret = false;
465         do {
466             ret |= stepInternal(false);
467         } while (!checkModificationStateMask());
468         return ret;
469     }
470 
checkModificationStateMask()471     private static boolean checkModificationStateMask() {
472         int operatorMask = 0x0;
473         int mapIndex = 0;
474         for (LinkedHashMap<String, DisplayModifier> map : gMaps.values()) {
475             int displayModifierIndex = mIndices[mapIndex];
476             for (Entry<String, DisplayModifier> modifierEntry : map.entrySet()) {
477                 if (displayModifierIndex == 0) {
478                     mLastAppliedModifications[mapIndex] = modifierEntry.getKey();
479                     operatorMask |= modifierEntry.getValue().mask();
480                     break;
481                 }
482                 displayModifierIndex--;
483             }
484             mapIndex++;
485         }
486         return operatorMask == TOTAL_MASK;
487     }
488 
apply(Paint paint, Canvas canvas)489     public static void apply(Paint paint, Canvas canvas) {
490         int mapIndex = 0;
491         for (LinkedHashMap<String, DisplayModifier> map : gMaps.values()) {
492             int displayModifierIndex = mIndices[mapIndex];
493             for (Entry<String, DisplayModifier> modifierEntry : map.entrySet()) {
494                 if (displayModifierIndex == 0) {
495                     mLastAppliedModifications[mapIndex] = modifierEntry.getKey();
496                     modifierEntry.getValue().modifyDrawing(paint, canvas);
497                     break;
498                 }
499                 displayModifierIndex--;
500             }
501             mapIndex++;
502         }
503     }
504 
getLastAppliedModifications()505     public static String[] getLastAppliedModifications() {
506         return mLastAppliedModifications.clone();
507     }
508 
getStrings()509     public static String[][] getStrings() {
510         String[][] keys = new String[gMaps.size()][];
511 
512         int i = 0;
513         for (LinkedHashMap<String, DisplayModifier> map : gMaps.values()) {
514             keys[i] = new String[map.size()];
515             int j = 0;
516             for (String key : map.keySet()) {
517                 keys[i][j++] = key;
518             }
519             i++;
520         }
521 
522         return keys;
523     }
524 
setIndex(int mapIndex, int newIndexValue)525     public static void setIndex(int mapIndex, int newIndexValue) {
526         mIndices[mapIndex] = newIndexValue;
527     }
528 
getIndices()529     public static int[] getIndices() {
530         return mIndices;
531     }
532 }
533