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/GrDirectContext.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 bitmapCanvas(bitmap);
87 SkPaint p;
88 p.setColor(SK_ColorWHITE);
89 SkASSERT(shapeColor != SK_ColorWHITE);
90 bitmapCanvas.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->drawImage(bitmap.asImage(), 0, 0, SkSamplingOptions(), &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* recordCanvas = recorder.beginRecording(SkIntToScalar(kTestRectSize + 2),
113 SkIntToScalar(kTestRectSize + 2));
114 SkPaint shapePaint;
115 shapePaint.setColor(shapeColor);
116 recordCanvas->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 auto direct = GrAsDirectContext(canvas->recordingContext());
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 (direct) {
165 direct->flushAndSubmit();
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 (direct) {
175 direct->flushAndSubmit();
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 (direct) {
210 direct->flushAndSubmit();
211 }
212 canvas->translate(SkIntToScalar(kTestRectSize + 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 (direct) {
220 direct->flushAndSubmit();
221 }
222 }
223
224 canvas->restore();
225 canvas->translate(SkIntToScalar(0), SkIntToScalar(kTestRectSize + 1));
226 }
227
228 }
229 }
230 }
231