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