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