blob: 25a88f73e54defdb3b57ff46ae03f5b414652ded [file] [log] [blame]
kkinnunendc0f4082015-01-26 00:14:26 -08001/*
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
16static const int kTestRectSize = 50;
17static 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
reedd053ce92016-03-22 10:17:23 -070025static sk_sp<SkColorFilter> make_detector_color_filter() {
kkinnunendc0f4082015-01-26 00:14:26 -080026 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;
reedd053ce92016-03-22 10:17:23 -070032 return SkTableColorFilter::MakeARGB(tableA, tableR, tableG, tableB);
kkinnunendc0f4082015-01-26 00:14:26 -080033}
34
35// This detector detects that color filter phase of the pixel pipeline receives the correct value.
36static void install_detector_color_filter(SkPaint* drawPaint) {
reedd053ce92016-03-22 10:17:23 -070037 drawPaint->setColorFilter(make_detector_color_filter());
kkinnunendc0f4082015-01-26 00:14:26 -080038}
39
40// This detector detects that image filter phase of the pixel pipeline receives the correct value.
41static void install_detector_image_filter(SkPaint* drawPaint) {
robertphillips5605b562016-04-05 11:50:42 -070042 sk_sp<SkColorFilter> colorFilter(make_detector_color_filter());
43 sk_sp<SkImageFilter> imageFilter(
44 SkColorFilterImageFilter::Make(std::move(colorFilter),
45 sk_ref_sp(drawPaint->getImageFilter())));
46 drawPaint->setImageFilter(std::move(imageFilter));
kkinnunendc0f4082015-01-26 00:14:26 -080047}
48
49static void no_detector_install(SkPaint*) {
50}
51
52typedef void(*InstallDetectorFunc)(SkPaint*);
53
54
55// Draws an pattern that can be optimized by alpha folding outer savelayer alpha value to
56// inner draw. Since we know that folding will happen to the inner draw, install a detector
57// to make sure that optimization does not change anything observable.
58static void draw_save_layer_draw_rect_restore_sequence(SkCanvas* canvas, SkColor shapeColor,
59 InstallDetectorFunc installDetector) {
60 SkRect targetRect(SkRect::MakeWH(SkIntToScalar(kTestRectSize), SkIntToScalar(kTestRectSize)));
61 SkPaint layerPaint;
62 layerPaint.setColor(SkColorSetARGB(128, 0, 0, 0));
63 canvas->saveLayer(&targetRect, &layerPaint);
64 SkPaint drawPaint;
65 drawPaint.setColor(shapeColor);
66 installDetector(&drawPaint);
67 canvas->drawRect(targetRect, drawPaint);
68 canvas->restore();
69}
70
71// Draws an pattern that can be optimized by alpha folding outer savelayer alpha value to
72// inner draw. A variant where the draw is not uniform color.
73static void draw_save_layer_draw_bitmap_restore_sequence(SkCanvas* canvas, SkColor shapeColor,
74 InstallDetectorFunc installDetector) {
75 SkBitmap bitmap;
76 bitmap.allocN32Pixels(kTestRectSize, kTestRectSize);
77 bitmap.eraseColor(shapeColor);
78 {
79 // Make the bitmap non-uniform color, so that it can not be optimized as uniform drawRect.
80 SkCanvas canvas(bitmap);
81 SkPaint p;
82 p.setColor(SK_ColorWHITE);
83 SkASSERT(shapeColor != SK_ColorWHITE);
84 canvas.drawRect(SkRect::MakeWH(SkIntToScalar(7), SkIntToScalar(7)), p);
85 canvas.flush();
86 }
87
88 SkRect targetRect(SkRect::MakeWH(SkIntToScalar(kTestRectSize), SkIntToScalar(kTestRectSize)));
89 SkPaint layerPaint;
90 layerPaint.setColor(SkColorSetARGB(129, 0, 0, 0));
91 canvas->saveLayer(&targetRect, &layerPaint);
92 SkPaint drawPaint;
93 installDetector(&drawPaint);
94 canvas->drawBitmap(bitmap, SkIntToScalar(0), SkIntToScalar(0), &drawPaint);
95 canvas->restore();
96}
97
98// Draws an pattern that can be optimized by alpha folding outer savelayer alpha value to
99// inner savelayer. We know that alpha folding happens to inner savelayer, so add detector there.
100static void draw_svg_opacity_and_filter_layer_sequence(SkCanvas* canvas, SkColor shapeColor,
101 InstallDetectorFunc installDetector) {
102
103 SkRect targetRect(SkRect::MakeWH(SkIntToScalar(kTestRectSize), SkIntToScalar(kTestRectSize)));
reedca2622b2016-03-18 07:25:55 -0700104 sk_sp<SkPicture> shape;
kkinnunendc0f4082015-01-26 00:14:26 -0800105 {
106 SkPictureRecorder recorder;
107 SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(kTestRectSize + 2),
108 SkIntToScalar(kTestRectSize + 2));
109 SkPaint shapePaint;
110 shapePaint.setColor(shapeColor);
111 canvas->drawRect(targetRect, shapePaint);
reedca2622b2016-03-18 07:25:55 -0700112 shape = recorder.finishRecordingAsPicture();
kkinnunendc0f4082015-01-26 00:14:26 -0800113 }
114
115 SkPaint layerPaint;
116 layerPaint.setColor(SkColorSetARGB(130, 0, 0, 0));
117 canvas->saveLayer(&targetRect, &layerPaint);
118 canvas->save();
119 canvas->clipRect(targetRect);
120 SkPaint drawPaint;
robertphillips5ff17b12016-03-28 13:13:42 -0700121 drawPaint.setImageFilter(SkPictureImageFilter::Make(shape));
kkinnunendc0f4082015-01-26 00:14:26 -0800122 installDetector(&drawPaint);
123 canvas->saveLayer(&targetRect, &drawPaint);
124 canvas->restore();
125 canvas->restore();
126 canvas->restore();
127}
128
129// Draws two columns of rectangles. The test is correct when:
130// - Left and right columns always identical
131// - First 3 rows are green, with a white dent in the middle row
132// - Next 6 rows are green, with a grey dent in the middle row
133// (the grey dent is from the color filter removing everything but the "good" green, see below)
134// - Last 6 rows are grey
135DEF_SIMPLE_GM(recordopts, canvas, (kTestRectSize+1)*2, (kTestRectSize+1)*15) {
136 canvas->clear(SK_ColorTRANSPARENT);
137
138 typedef void (*TestVariantSequence)(SkCanvas*, SkColor, InstallDetectorFunc);
139 TestVariantSequence funcs[] = {
140 draw_save_layer_draw_rect_restore_sequence,
141 draw_save_layer_draw_bitmap_restore_sequence,
142 draw_svg_opacity_and_filter_layer_sequence,
143 };
144
145 // Draw layer-related sequences that can be optimized by folding the opacity layer alpha to
146 // the inner draw operation. This tries to trigger the optimization, and relies on gm diffs
147 // to keep the color value correct over time.
148
149 // Draws two green rects side by side: one is without the optimization, the other is with
150 // the optimization applied.
151
152 SkColor shapeColor = SkColorSetARGB(255, 0, 255, 0);
153 for (size_t k = 0; k < SK_ARRAY_COUNT(funcs); ++k) {
154 canvas->save();
155
156 TestVariantSequence drawTestSequence = funcs[k];
157 drawTestSequence(canvas, shapeColor, no_detector_install);
158 canvas->flush();
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);
reedca2622b2016-03-18 07:25:55 -0700165 recorder.finishRecordingAsPicture()->playback(canvas);
kkinnunendc0f4082015-01-26 00:14:26 -0800166 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),
robertphillipsb644e9a2015-01-26 05:39:26 -0800183 SkColorSetARGB(255, 0, (kDetectorGreenValue + 1), 0) // This tests that detectors work.
kkinnunendc0f4082015-01-26 00:14:26 -0800184 };
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);
reedca2622b2016-03-18 07:25:55 -0700206 recorder.finishRecordingAsPicture()->playback(canvas);
kkinnunendc0f4082015-01-26 00:14:26 -0800207 canvas->flush();
208 }
209
210 canvas->restore();
211 canvas->translate(SkIntToScalar(0), SkIntToScalar(kTestRectSize) + SkIntToScalar(1));
212 }
213
214 }
215 }
216}