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