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