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
17package com.android.test.hwuicompare;
18
19import java.util.LinkedHashMap;
20import java.util.Map.Entry;
21
22import android.graphics.Canvas;
23import android.graphics.Paint;
24import android.graphics.RectF;
25import android.util.Log;
26
27public 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
40    abstract public void modifyDrawing(Paint paint, Canvas canvas);
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
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
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
455    public static boolean step() {
456        boolean ret = false;
457        do {
458            ret |= stepInternal(true);
459        } while (!checkModificationStateMask());
460        return ret;
461    }
462
463    public static boolean stepBack() {
464        boolean ret = false;
465        do {
466            ret |= stepInternal(false);
467        } while (!checkModificationStateMask());
468        return ret;
469    }
470
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
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
505    public static String[] getLastAppliedModifications() {
506        return mLastAppliedModifications.clone();
507    }
508
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
525    public static void setIndex(int mapIndex, int newIndexValue) {
526        mIndices[mapIndex] = newIndexValue;
527    }
528
529    public static int[] getIndices() {
530        return mIndices;
531    }
532}
533