blob: 341b7ce9242218988a9291a074413ddb8834d964 [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
mtkleindbfd7ab2016-09-01 11:24:54 -070016constexpr int kTestRectSize = 50;
17constexpr int kDetectorGreenValue = 50;
kkinnunendc0f4082015-01-26 00:14:26 -080018
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) {
Mike Reed693fdbd2017-01-12 10:13:40 -050042 drawPaint->setImageFilter(SkColorFilterImageFilter::Make(make_detector_color_filter(),
43 drawPaint->refImageFilter()));
kkinnunendc0f4082015-01-26 00:14:26 -080044}
45
46static void no_detector_install(SkPaint*) {
47}
48
49typedef 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.
55static 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.
70static 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.
97static 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)));
reedca2622b2016-03-18 07:25:55 -0700101 sk_sp<SkPicture> shape;
kkinnunendc0f4082015-01-26 00:14:26 -0800102 {
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);
reedca2622b2016-03-18 07:25:55 -0700109 shape = recorder.finishRecordingAsPicture();
kkinnunendc0f4082015-01-26 00:14:26 -0800110 }
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;
robertphillips5ff17b12016-03-28 13:13:42 -0700118 drawPaint.setImageFilter(SkPictureImageFilter::Make(shape));
kkinnunendc0f4082015-01-26 00:14:26 -0800119 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
132DEF_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);
reedca2622b2016-03-18 07:25:55 -0700162 recorder.finishRecordingAsPicture()->playback(canvas);
kkinnunendc0f4082015-01-26 00:14:26 -0800163 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),
robertphillipsb644e9a2015-01-26 05:39:26 -0800180 SkColorSetARGB(255, 0, (kDetectorGreenValue + 1), 0) // This tests that detectors work.
kkinnunendc0f4082015-01-26 00:14:26 -0800181 };
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];
brianosman42a41492016-06-23 11:40:14 -0700190 for (size_t j = 0; j < SK_ARRAY_COUNT(detectorInstallFuncs); ++j) {
kkinnunendc0f4082015-01-26 00:14:26 -0800191 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);
reedca2622b2016-03-18 07:25:55 -0700203 recorder.finishRecordingAsPicture()->playback(canvas);
kkinnunendc0f4082015-01-26 00:14:26 -0800204 canvas->flush();
205 }
206
207 canvas->restore();
208 canvas->translate(SkIntToScalar(0), SkIntToScalar(kTestRectSize) + SkIntToScalar(1));
209 }
210
211 }
212 }
213}