• Home
  • History
  • Annotate
  • 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 "SkCanvas.h"
10 #include "SkPath.h"
11 #include "SkPictureRecorder.h"
12 #include "SkTableColorFilter.h"
13 #include "SkColorFilterImageFilter.h"
14 #include "SkPictureImageFilter.h"
15 
16 static const int kTestRectSize = 50;
17 static 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 
make_detector_color_filter()25 static 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.
install_detector_color_filter(SkPaint * drawPaint)36 static 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.
install_detector_image_filter(SkPaint * drawPaint)41 static 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 
no_detector_install(SkPaint *)48 static void no_detector_install(SkPaint*) {
49 }
50 
51 typedef 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.
draw_save_layer_draw_rect_restore_sequence(SkCanvas * canvas,SkColor shapeColor,InstallDetectorFunc installDetector)57 static 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.
draw_save_layer_draw_bitmap_restore_sequence(SkCanvas * canvas,SkColor shapeColor,InstallDetectorFunc installDetector)72 static 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.
draw_svg_opacity_and_filter_layer_sequence(SkCanvas * canvas,SkColor shapeColor,InstallDetectorFunc installDetector)99 static 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
134 DEF_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