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