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 constexpr int kTestRectSize = 50;
17 constexpr 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 sk_sp<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::MakeARGB(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());
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 drawPaint->setImageFilter(SkColorFilterImageFilter::Make(make_detector_color_filter(),
43 drawPaint->refImageFilter()));
44 }
45
no_detector_install(SkPaint *)46 static void no_detector_install(SkPaint*) {
47 }
48
49 typedef void(*InstallDetectorFunc)(SkPaint*);
50
51
52 // Draws an pattern that can be optimized by alpha folding outer savelayer alpha value to
53 // inner draw. Since we know that folding will happen to the inner draw, install a detector
54 // to make sure that optimization does not change anything observable.
draw_save_layer_draw_rect_restore_sequence(SkCanvas * canvas,SkColor shapeColor,InstallDetectorFunc installDetector)55 static void draw_save_layer_draw_rect_restore_sequence(SkCanvas* canvas, SkColor shapeColor,
56 InstallDetectorFunc installDetector) {
57 SkRect targetRect(SkRect::MakeWH(SkIntToScalar(kTestRectSize), SkIntToScalar(kTestRectSize)));
58 SkPaint layerPaint;
59 layerPaint.setColor(SkColorSetARGB(128, 0, 0, 0));
60 canvas->saveLayer(&targetRect, &layerPaint);
61 SkPaint drawPaint;
62 drawPaint.setColor(shapeColor);
63 installDetector(&drawPaint);
64 canvas->drawRect(targetRect, drawPaint);
65 canvas->restore();
66 }
67
68 // Draws an pattern that can be optimized by alpha folding outer savelayer alpha value to
69 // inner draw. A variant where the draw is not uniform color.
draw_save_layer_draw_bitmap_restore_sequence(SkCanvas * canvas,SkColor shapeColor,InstallDetectorFunc installDetector)70 static void draw_save_layer_draw_bitmap_restore_sequence(SkCanvas* canvas, SkColor shapeColor,
71 InstallDetectorFunc installDetector) {
72 SkBitmap bitmap;
73 bitmap.allocN32Pixels(kTestRectSize, kTestRectSize);
74 bitmap.eraseColor(shapeColor);
75 {
76 // Make the bitmap non-uniform color, so that it can not be optimized as uniform drawRect.
77 SkCanvas canvas(bitmap);
78 SkPaint p;
79 p.setColor(SK_ColorWHITE);
80 SkASSERT(shapeColor != SK_ColorWHITE);
81 canvas.drawRect(SkRect::MakeWH(SkIntToScalar(7), SkIntToScalar(7)), p);
82 canvas.flush();
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 canvas->clear(SK_ColorTRANSPARENT);
134
135 typedef void (*TestVariantSequence)(SkCanvas*, SkColor, InstallDetectorFunc);
136 TestVariantSequence funcs[] = {
137 draw_save_layer_draw_rect_restore_sequence,
138 draw_save_layer_draw_bitmap_restore_sequence,
139 draw_svg_opacity_and_filter_layer_sequence,
140 };
141
142 // Draw layer-related sequences that can be optimized by folding the opacity layer alpha to
143 // the inner draw operation. This tries to trigger the optimization, and relies on gm diffs
144 // to keep the color value correct over time.
145
146 // Draws two green rects side by side: one is without the optimization, the other is with
147 // the optimization applied.
148
149 SkColor shapeColor = SkColorSetARGB(255, 0, 255, 0);
150 for (size_t k = 0; k < SK_ARRAY_COUNT(funcs); ++k) {
151 canvas->save();
152
153 TestVariantSequence drawTestSequence = funcs[k];
154 drawTestSequence(canvas, shapeColor, no_detector_install);
155 canvas->flush();
156 canvas->translate(SkIntToScalar(kTestRectSize) + SkIntToScalar(1), SkIntToScalar(0));
157 {
158 SkPictureRecorder recorder;
159 drawTestSequence(recorder.beginRecording(SkIntToScalar(kTestRectSize),
160 SkIntToScalar(kTestRectSize)),
161 shapeColor, no_detector_install);
162 recorder.finishRecordingAsPicture()->playback(canvas);
163 canvas->flush();
164 }
165 canvas->restore();
166 canvas->translate(SkIntToScalar(0), SkIntToScalar(kTestRectSize) + SkIntToScalar(1));
167 }
168
169 // Draw the same layer related sequences, but manipulate the sequences so that the result is
170 // incorrect if the alpha is folded or folded incorrectly. These test the observable state
171 // throughout the pixel pipeline, and thus may turn off the optimizations (this is why we
172 // trigger the optimizations above).
173
174 // Draws two green rects side by side: one is without the optimization, the other is with
175 // the possibility that optimization is applied.
176 // At the end, draws the same patterns in translucent black. This tests that the detectors
177 // work, eg. that if the value the detector sees is wrong, the resulting image shows this.
178 SkColor shapeColors[] = {
179 SkColorSetARGB(255, 0, kDetectorGreenValue, 0),
180 SkColorSetARGB(255, 0, (kDetectorGreenValue + 1), 0) // This tests that detectors work.
181 };
182
183 InstallDetectorFunc detectorInstallFuncs[] = {
184 install_detector_image_filter,
185 install_detector_color_filter
186 };
187
188 for (size_t i = 0; i < SK_ARRAY_COUNT(shapeColors); ++i) {
189 shapeColor = shapeColors[i];
190 for (size_t j = 0; j < SK_ARRAY_COUNT(detectorInstallFuncs); ++j) {
191 InstallDetectorFunc detectorInstallFunc = detectorInstallFuncs[j];
192 for (size_t k = 0; k < SK_ARRAY_COUNT(funcs); ++k) {
193 TestVariantSequence drawTestSequence = funcs[k];
194 canvas->save();
195 drawTestSequence(canvas, shapeColor, detectorInstallFunc);
196 canvas->flush();
197 canvas->translate(SkIntToScalar(kTestRectSize) + SkIntToScalar(1), SkIntToScalar(0));
198 {
199 SkPictureRecorder recorder;
200 drawTestSequence(recorder.beginRecording(SkIntToScalar(kTestRectSize),
201 SkIntToScalar(kTestRectSize)),
202 shapeColor, detectorInstallFunc);
203 recorder.finishRecordingAsPicture()->playback(canvas);
204 canvas->flush();
205 }
206
207 canvas->restore();
208 canvas->translate(SkIntToScalar(0), SkIntToScalar(kTestRectSize) + SkIntToScalar(1));
209 }
210
211 }
212 }
213 }
214