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                }
306            });
307
308            // FINAL MAP: DOES ACTUAL DRAWING
309            put("drawing", new LinkedHashMap<String, DisplayModifier>() {
310                {
311                    put("roundRect", new DisplayModifier() {
312                        @Override
313                        public void modifyDrawing(Paint paint, Canvas canvas) {
314                            canvas.drawRoundRect(gRect, 20, 20, paint);
315                        }
316                    });
317                    put("rect", new DisplayModifier() {
318                        @Override
319                        public void modifyDrawing(Paint paint, Canvas canvas) {
320                            canvas.drawRect(gRect, paint);
321                        }
322                        @Override
323                        protected int mask() { return SWEEP_SHADER_BIT | SWEEP_STROKE_CAP_BIT; };
324                    });
325                    put("circle", new DisplayModifier() {
326                        @Override
327                        public void modifyDrawing(Paint paint, Canvas canvas) {
328                            canvas.drawCircle(100, 100, 75, paint);
329                        }
330                    });
331                    put("oval", new DisplayModifier() {
332                        @Override
333                        public void modifyDrawing(Paint paint, Canvas canvas) {
334                            canvas.drawOval(gRect, paint);
335                        }
336                    });
337                    put("lines", new DisplayModifier() {
338                        @Override
339                        public void modifyDrawing(Paint paint, Canvas canvas) {
340                            canvas.drawLines(gLinePts, paint);
341                        }
342                        @Override
343                        protected int mask() { return SWEEP_STROKE_CAP_BIT; };
344                    });
345                    put("plusPoints", new DisplayModifier() {
346                        @Override
347                        public void modifyDrawing(Paint paint, Canvas canvas) {
348                            canvas.drawPoints(gPts, paint);
349                        }
350                    });
351                    put("text", new DisplayModifier() {
352                        @Override
353                        public void modifyDrawing(Paint paint, Canvas canvas) {
354                            paint.setTextSize(36);
355                            canvas.drawText("TEXTTEST", 0, 50, paint);
356                        }
357                    });
358                    put("shadowtext", new DisplayModifier() {
359                        @Override
360                        public void modifyDrawing(Paint paint, Canvas canvas) {
361                            paint.setTextSize(36);
362                            paint.setShadowLayer(3.0f, 0.0f, 3.0f, 0xffff00ff);
363                            canvas.drawText("TEXTTEST", 0, 50, paint);
364                        }
365                    });
366                    put("bitmapMesh", new DisplayModifier() {
367                        @Override
368                        public void modifyDrawing(Paint paint, Canvas canvas) {
369                            canvas.drawBitmapMesh(ResourceModifiers.instance().mBitmap, 3, 3,
370                                    ResourceModifiers.instance().mBitmapVertices, 0, null, 0, null);
371                        }
372                    });
373                    put("arc", new DisplayModifier() {
374                        @Override
375                        public void modifyDrawing(Paint paint, Canvas canvas) {
376                            canvas.drawArc(gRect, 260, 285, false, paint);
377                        }
378                        @Override
379                        protected int mask() { return SWEEP_STROKE_CAP_BIT; };
380                    });
381                    put("arcFromCenter", new DisplayModifier() {
382                        @Override
383                        public void modifyDrawing(Paint paint, Canvas canvas) {
384                            canvas.drawArc(gRect, 260, 285, true, paint);
385                        }
386                        @Override
387                        protected int mask() { return SWEEP_STROKE_JOIN_BIT; };
388                    });
389                }
390            });
391            // WARNING: DON'T PUT MORE MAPS BELOW THIS
392        }
393    };
394
395    private static LinkedHashMap<String, DisplayModifier> getMapAtIndex(int index) {
396        for (LinkedHashMap<String, DisplayModifier> map : gMaps.values()) {
397            if (index == 0) {
398                return map;
399            }
400            index--;
401        }
402        return null;
403    }
404
405    // indices instead of iterators for easier bidirectional traversal
406    private static final int mIndices[] = new int[gMaps.size()];
407    private static final String[] mLastAppliedModifications = new String[gMaps.size()];
408
409    private static boolean stepInternal(boolean forward) {
410        int modifierMapIndex = gMaps.size() - 1;
411        while (modifierMapIndex >= 0) {
412            LinkedHashMap<String, DisplayModifier> map = getMapAtIndex(modifierMapIndex);
413            mIndices[modifierMapIndex] += (forward ? 1 : -1);
414
415            if (mIndices[modifierMapIndex] >= 0 && mIndices[modifierMapIndex] < map.size()) {
416                break;
417            }
418
419            mIndices[modifierMapIndex] = (forward ? 0 : map.size() - 1);
420            modifierMapIndex--;
421        }
422        return modifierMapIndex < 0; // true if resetting
423    }
424
425    public static boolean step() {
426        boolean ret = false;
427        do {
428            ret |= stepInternal(true);
429        } while (!checkModificationStateMask());
430        return ret;
431    }
432
433    public static boolean stepBack() {
434        boolean ret = false;
435        do {
436            ret |= stepInternal(false);
437        } while (!checkModificationStateMask());
438        return ret;
439    }
440
441    private static boolean checkModificationStateMask() {
442        int operatorMask = 0x0;
443        int mapIndex = 0;
444        for (LinkedHashMap<String, DisplayModifier> map : gMaps.values()) {
445            int displayModifierIndex = mIndices[mapIndex];
446            for (Entry<String, DisplayModifier> modifierEntry : map.entrySet()) {
447                if (displayModifierIndex == 0) {
448                    mLastAppliedModifications[mapIndex] = modifierEntry.getKey();
449                    operatorMask |= modifierEntry.getValue().mask();
450                    break;
451                }
452                displayModifierIndex--;
453            }
454            mapIndex++;
455        }
456        return operatorMask == TOTAL_MASK;
457    }
458
459    public static void apply(Paint paint, Canvas canvas) {
460        int mapIndex = 0;
461        for (LinkedHashMap<String, DisplayModifier> map : gMaps.values()) {
462            int displayModifierIndex = mIndices[mapIndex];
463            for (Entry<String, DisplayModifier> modifierEntry : map.entrySet()) {
464                if (displayModifierIndex == 0) {
465                    mLastAppliedModifications[mapIndex] = modifierEntry.getKey();
466                    modifierEntry.getValue().modifyDrawing(paint, canvas);
467                    break;
468                }
469                displayModifierIndex--;
470            }
471            mapIndex++;
472        }
473    }
474
475    public static String[] getLastAppliedModifications() {
476        return mLastAppliedModifications.clone();
477    }
478
479    public static String[][] getStrings() {
480        String[][] keys = new String[gMaps.size()][];
481
482        int i = 0;
483        for (LinkedHashMap<String, DisplayModifier> map : gMaps.values()) {
484            keys[i] = new String[map.size()];
485            int j = 0;
486            for (String key : map.keySet()) {
487                keys[i][j++] = key;
488            }
489            i++;
490        }
491
492        return keys;
493    }
494
495    public static void setIndex(int mapIndex, int newIndexValue) {
496        mIndices[mapIndex] = newIndexValue;
497    }
498
499    public static int[] getIndices() {
500        return mIndices;
501    }
502}
503