1fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot/*
2fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot * Copyright 2014 Google Inc.
3fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot *
4fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot * Use of this source code is governed by a BSD-style license that can be
5fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot * found in the LICENSE file.
6fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot */
7fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
8fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "gm.h"
9fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkCanvas.h"
10fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkPath.h"
11fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkPictureRecorder.h"
12fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkTableColorFilter.h"
13fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkColorFilterImageFilter.h"
14fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkPictureImageFilter.h"
15fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
16fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotconstexpr int kTestRectSize = 50;
17fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotconstexpr int kDetectorGreenValue = 50;
18fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
19fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// Below are few functions to install "detector" color filters. The filter is there to assert that
20fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// the color value it sees is the expected. It will trigger only with kDetectorGreenValue, and
21fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// turn that value into full green. The idea is that if an optimization incorrectly changes
22fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// kDetectorGreenValue and then the incorrect value is observable by some part of the drawing
23fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// pipeline, that pixel will remain empty.
24fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
25fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstatic sk_sp<SkColorFilter> make_detector_color_filter() {
26fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    uint8_t tableA[256] = { 0, };
27fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    uint8_t tableR[256] = { 0, };
28fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    uint8_t tableG[256] = { 0, };
29fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    uint8_t tableB[256] = { 0, };
30fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    tableA[255] = 255;
31fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    tableG[kDetectorGreenValue] = 255;
32fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return SkTableColorFilter::MakeARGB(tableA, tableR, tableG, tableB);
33fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
34fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
35fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// This detector detects that color filter phase of the pixel pipeline receives the correct value.
36fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstatic void install_detector_color_filter(SkPaint* drawPaint) {
37fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    drawPaint->setColorFilter(make_detector_color_filter());
38fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
39fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
40fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// This detector detects that image filter phase of the pixel pipeline receives the correct value.
41fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstatic void install_detector_image_filter(SkPaint* drawPaint) {
42fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    drawPaint->setImageFilter(SkColorFilterImageFilter::Make(make_detector_color_filter(),
43fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                             drawPaint->refImageFilter()));
44fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
45fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
46fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstatic void no_detector_install(SkPaint*) {
47fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
48fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
49fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robottypedef void(*InstallDetectorFunc)(SkPaint*);
50fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
51fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
52fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// Draws an pattern that can be optimized by alpha folding outer savelayer alpha value to
53fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// inner draw. Since we know that folding will happen to the inner draw, install a detector
54fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// to make sure that optimization does not change anything observable.
55fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstatic void draw_save_layer_draw_rect_restore_sequence(SkCanvas* canvas, SkColor shapeColor,
56fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                       InstallDetectorFunc installDetector) {
57fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkRect targetRect(SkRect::MakeWH(SkIntToScalar(kTestRectSize), SkIntToScalar(kTestRectSize)));
58fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPaint layerPaint;
59fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    layerPaint.setColor(SkColorSetARGB(128, 0, 0, 0));
60fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    canvas->saveLayer(&targetRect, &layerPaint);
61fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkPaint drawPaint;
62fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        drawPaint.setColor(shapeColor);
63fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        installDetector(&drawPaint);
64fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        canvas->drawRect(targetRect, drawPaint);
65fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    canvas->restore();
66fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
67fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
68fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// Draws an pattern that can be optimized by alpha folding outer savelayer alpha value to
69fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// inner draw. A variant where the draw is not uniform color.
70fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstatic void draw_save_layer_draw_bitmap_restore_sequence(SkCanvas* canvas, SkColor shapeColor,
71fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                         InstallDetectorFunc installDetector) {
72fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkBitmap bitmap;
73fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    bitmap.allocN32Pixels(kTestRectSize, kTestRectSize);
74fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    bitmap.eraseColor(shapeColor);
75fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    {
76fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // Make the bitmap non-uniform color, so that it can not be optimized as uniform drawRect.
77fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkCanvas canvas(bitmap);
78fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkPaint p;
79fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        p.setColor(SK_ColorWHITE);
80fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT(shapeColor != SK_ColorWHITE);
81fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        canvas.drawRect(SkRect::MakeWH(SkIntToScalar(7), SkIntToScalar(7)), p);
82fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        canvas.flush();
83fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
84fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
85fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkRect targetRect(SkRect::MakeWH(SkIntToScalar(kTestRectSize), SkIntToScalar(kTestRectSize)));
86fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPaint layerPaint;
87fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    layerPaint.setColor(SkColorSetARGB(129, 0, 0, 0));
88fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    canvas->saveLayer(&targetRect, &layerPaint);
89fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkPaint drawPaint;
90fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        installDetector(&drawPaint);
91fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        canvas->drawBitmap(bitmap, SkIntToScalar(0), SkIntToScalar(0), &drawPaint);
92fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    canvas->restore();
93fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
94fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
95fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// Draws an pattern that can be optimized by alpha folding outer savelayer alpha value to
96fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// inner savelayer. We know that alpha folding happens to inner savelayer, so add detector there.
97fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstatic void draw_svg_opacity_and_filter_layer_sequence(SkCanvas* canvas, SkColor shapeColor,
98fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                       InstallDetectorFunc installDetector) {
99fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
100fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkRect targetRect(SkRect::MakeWH(SkIntToScalar(kTestRectSize), SkIntToScalar(kTestRectSize)));
101fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    sk_sp<SkPicture> shape;
102fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    {
103fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkPictureRecorder recorder;
104fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(kTestRectSize + 2),
105fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                   SkIntToScalar(kTestRectSize + 2));
106fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkPaint shapePaint;
107fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        shapePaint.setColor(shapeColor);
108fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        canvas->drawRect(targetRect, shapePaint);
109fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        shape = recorder.finishRecordingAsPicture();
110fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
111fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
112fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPaint layerPaint;
113fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    layerPaint.setColor(SkColorSetARGB(130, 0, 0, 0));
114fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    canvas->saveLayer(&targetRect, &layerPaint);
115fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        canvas->save();
116fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            canvas->clipRect(targetRect);
117fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkPaint drawPaint;
118fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            drawPaint.setImageFilter(SkPictureImageFilter::Make(shape));
119fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            installDetector(&drawPaint);
120fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            canvas->saveLayer(&targetRect, &drawPaint);
121fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            canvas->restore();
122fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        canvas->restore();
123fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    canvas->restore();
124fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
125fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
126fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// Draws two columns of rectangles. The test is correct when:
127fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot//  - Left and right columns always identical
128fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot//  - First 3 rows are green, with a white dent in the middle row
129fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot//  - Next 6 rows are green, with a grey dent in the middle row
130fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot//    (the grey dent is from the color filter removing everything but the "good" green, see below)
131fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot//  - Last 6 rows are grey
132fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team RobotDEF_SIMPLE_GM(recordopts, canvas, (kTestRectSize+1)*2, (kTestRectSize+1)*15) {
133fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    canvas->clear(SK_ColorTRANSPARENT);
134fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
135fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    typedef void (*TestVariantSequence)(SkCanvas*, SkColor, InstallDetectorFunc);
136fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    TestVariantSequence funcs[] = {
137fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        draw_save_layer_draw_rect_restore_sequence,
138fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        draw_save_layer_draw_bitmap_restore_sequence,
139fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        draw_svg_opacity_and_filter_layer_sequence,
140fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    };
141fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
142fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Draw layer-related sequences that can be optimized by folding the opacity layer alpha to
143fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // the inner draw operation. This tries to trigger the optimization, and relies on gm diffs
144fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // to keep the color value correct over time.
145fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
146fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Draws two green rects side by side: one is without the optimization, the other is with
147fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // the optimization applied.
148fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
149fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkColor shapeColor = SkColorSetARGB(255, 0, 255, 0);
150fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    for (size_t k = 0; k < SK_ARRAY_COUNT(funcs); ++k) {
151fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        canvas->save();
152fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
153fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        TestVariantSequence drawTestSequence = funcs[k];
154fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        drawTestSequence(canvas, shapeColor, no_detector_install);
155fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        canvas->flush();
156fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        canvas->translate(SkIntToScalar(kTestRectSize) + SkIntToScalar(1), SkIntToScalar(0));
157fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        {
158fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkPictureRecorder recorder;
159fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            drawTestSequence(recorder.beginRecording(SkIntToScalar(kTestRectSize),
160fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                     SkIntToScalar(kTestRectSize)),
161fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                             shapeColor, no_detector_install);
162fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            recorder.finishRecordingAsPicture()->playback(canvas);
163fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            canvas->flush();
164fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
165fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        canvas->restore();
166fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        canvas->translate(SkIntToScalar(0), SkIntToScalar(kTestRectSize) + SkIntToScalar(1));
167fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
168fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
169fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Draw the same layer related sequences, but manipulate the sequences so that the result is
170fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // incorrect if the alpha is folded or folded incorrectly. These test the observable state
171fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // throughout the pixel pipeline, and thus may turn off the optimizations (this is why we
172fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // trigger the optimizations above).
173fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
174fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Draws two green rects side by side: one is without the optimization, the other is with
175fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // the possibility that optimization is applied.
176fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // At the end, draws the same patterns in translucent black. This tests that the detectors
177fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // work, eg. that if the value the detector sees is wrong, the resulting image shows this.
178fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkColor shapeColors[] = {
179fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkColorSetARGB(255, 0, kDetectorGreenValue, 0),
180fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkColorSetARGB(255, 0, (kDetectorGreenValue + 1), 0) // This tests that detectors work.
181fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    };
182fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
183fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    InstallDetectorFunc detectorInstallFuncs[] = {
184fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        install_detector_image_filter,
185fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        install_detector_color_filter
186fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    };
187fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
188fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    for (size_t i = 0; i < SK_ARRAY_COUNT(shapeColors); ++i) {
189fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        shapeColor = shapeColors[i];
190fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        for (size_t j = 0; j < SK_ARRAY_COUNT(detectorInstallFuncs); ++j) {
191fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            InstallDetectorFunc detectorInstallFunc = detectorInstallFuncs[j];
192fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            for (size_t k = 0; k < SK_ARRAY_COUNT(funcs); ++k) {
193fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                TestVariantSequence drawTestSequence = funcs[k];
194fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                canvas->save();
195fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                drawTestSequence(canvas, shapeColor, detectorInstallFunc);
196fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                canvas->flush();
197fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                canvas->translate(SkIntToScalar(kTestRectSize) + SkIntToScalar(1), SkIntToScalar(0));
198fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                {
199fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    SkPictureRecorder recorder;
200fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    drawTestSequence(recorder.beginRecording(SkIntToScalar(kTestRectSize),
201fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                             SkIntToScalar(kTestRectSize)),
202fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                     shapeColor, detectorInstallFunc);
203fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    recorder.finishRecordingAsPicture()->playback(canvas);
204fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    canvas->flush();
205fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                }
206fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
207fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                canvas->restore();
208fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                canvas->translate(SkIntToScalar(0), SkIntToScalar(kTestRectSize) + SkIntToScalar(1));
209fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
210fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
211fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
212fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
213fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
214