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