blob: 1c240db15e9c0b309725654b5b4431e03438b041 [file] [log] [blame]
Chris Craikb565df12015-10-05 13:00:52 -07001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <gtest/gtest.h>
18
19#include <RecordedOp.h>
20#include <RecordingCanvas.h>
sergeyvdccca442016-03-21 15:38:21 -070021#include <hwui/Paint.h>
22#include <minikin/Layout.h>
Chris Craik8160f202015-12-02 14:50:25 -080023#include <tests/common/TestUtils.h>
Chris Craik1713c772016-02-18 17:49:44 -080024#include <utils/Color.h>
Chris Craikb565df12015-10-05 13:00:52 -070025
Chris Craik734f1462016-03-24 11:10:08 -070026#include <SkGradientShader.h>
27#include <SkShader.h>
28
Chris Craikb565df12015-10-05 13:00:52 -070029namespace android {
30namespace uirenderer {
31
Chris Craikb36af872015-10-16 14:23:12 -070032static void playbackOps(const DisplayList& displayList,
Chris Craik6fe991e52015-10-20 09:39:42 -070033 std::function<void(const RecordedOp&)> opReceiver) {
Chris Craik161f54b2015-11-05 11:08:52 -080034 for (auto& chunk : displayList.getChunks()) {
Chris Craikb565df12015-10-05 13:00:52 -070035 for (size_t opIndex = chunk.beginOpIndex; opIndex < chunk.endOpIndex; opIndex++) {
Chris Craikb36af872015-10-16 14:23:12 -070036 RecordedOp* op = displayList.getOps()[opIndex];
Chris Craik6fe991e52015-10-20 09:39:42 -070037 opReceiver(*op);
Chris Craikb565df12015-10-05 13:00:52 -070038 }
39 }
40}
41
42TEST(RecordingCanvas, emptyPlayback) {
Chris Craikb36af872015-10-16 14:23:12 -070043 auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
Florin Malitaeecff562015-12-21 10:43:01 -050044 canvas.save(SaveFlags::MatrixClip);
Chris Craikb565df12015-10-05 13:00:52 -070045 canvas.restore();
46 });
Chris Craik818c9fb2015-10-23 14:33:42 -070047 playbackOps(*dl, [](const RecordedOp& op) { ADD_FAILURE(); });
Chris Craikb565df12015-10-05 13:00:52 -070048}
49
Chris Craike4db79d2015-12-22 16:32:23 -080050TEST(RecordingCanvas, clipRect) {
51 auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [](RecordingCanvas& canvas) {
Florin Malitaeecff562015-12-21 10:43:01 -050052 canvas.save(SaveFlags::MatrixClip);
Chris Craike4db79d2015-12-22 16:32:23 -080053 canvas.clipRect(0, 0, 100, 100, SkRegion::kIntersect_Op);
54 canvas.drawRect(0, 0, 50, 50, SkPaint());
55 canvas.drawRect(50, 50, 100, 100, SkPaint());
56 canvas.restore();
57 });
58
59 ASSERT_EQ(2u, dl->getOps().size()) << "Must be exactly two ops";
60 EXPECT_CLIP_RECT(Rect(100, 100), dl->getOps()[0]->localClip);
61 EXPECT_CLIP_RECT(Rect(100, 100), dl->getOps()[1]->localClip);
62 EXPECT_EQ(dl->getOps()[0]->localClip, dl->getOps()[1]->localClip)
63 << "Clip should be serialized once";
64}
65
Chris Craik261725f2016-02-29 12:52:33 -080066TEST(RecordingCanvas, emptyClipRect) {
67 auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
68 canvas.save(SaveFlags::MatrixClip);
69 canvas.clipRect(0, 0, 100, 100, SkRegion::kIntersect_Op);
70 canvas.clipRect(100, 100, 200, 200, SkRegion::kIntersect_Op);
71 canvas.drawRect(0, 0, 50, 50, SkPaint()); // rejected at record time
72 canvas.restore();
73 });
74 ASSERT_EQ(0u, dl->getOps().size()) << "Must be zero ops. Rect should be rejected.";
75}
76
Chris Craikcaa24182016-02-19 15:20:35 -080077TEST(RecordingCanvas, drawArc) {
78 auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
79 canvas.drawArc(0, 0, 200, 200, 0, 180, true, SkPaint());
80 canvas.drawArc(0, 0, 100, 100, 0, 360, true, SkPaint());
81 });
82
83 auto&& ops = dl->getOps();
84 ASSERT_EQ(2u, ops.size()) << "Must be exactly two ops";
85 EXPECT_EQ(RecordedOpId::ArcOp, ops[0]->opId);
86 EXPECT_EQ(Rect(200, 200), ops[0]->unmappedBounds);
87
88 EXPECT_EQ(RecordedOpId::OvalOp, ops[1]->opId)
89 << "Circular arcs should be converted to ovals";
90 EXPECT_EQ(Rect(100, 100), ops[1]->unmappedBounds);
91}
92
Chris Craika1717272015-11-19 13:02:43 -080093TEST(RecordingCanvas, drawLines) {
94 auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
95 SkPaint paint;
Chris Craik386aa032015-12-07 17:08:25 -080096 paint.setStrokeWidth(20); // doesn't affect recorded bounds - would be resolved at bake time
Chris Craika1717272015-11-19 13:02:43 -080097 float points[] = { 0, 0, 20, 10, 30, 40, 90 }; // NB: only 1 valid line
98 canvas.drawLines(&points[0], 7, paint);
99 });
100
101 ASSERT_EQ(1u, dl->getOps().size()) << "Must be exactly one op";
102 auto op = dl->getOps()[0];
103 ASSERT_EQ(RecordedOpId::LinesOp, op->opId);
104 EXPECT_EQ(4, ((LinesOp*)op)->floatCount)
105 << "float count must be rounded down to closest multiple of 4";
Chris Craik5430ab22015-12-10 16:28:16 -0800106 EXPECT_EQ(Rect(20, 10), op->unmappedBounds)
Chris Craik386aa032015-12-07 17:08:25 -0800107 << "unmapped bounds must be size of line, and not outset for stroke width";
Chris Craika1717272015-11-19 13:02:43 -0800108}
109
110TEST(RecordingCanvas, drawRect) {
Chris Craikb36af872015-10-16 14:23:12 -0700111 auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
Chris Craikb565df12015-10-05 13:00:52 -0700112 canvas.drawRect(10, 20, 90, 180, SkPaint());
113 });
114
Chris Craika1717272015-11-19 13:02:43 -0800115 ASSERT_EQ(1u, dl->getOps().size()) << "Must be exactly one op";
116 auto op = *(dl->getOps()[0]);
117 ASSERT_EQ(RecordedOpId::RectOp, op.opId);
Chris Craike4db79d2015-12-22 16:32:23 -0800118 EXPECT_EQ(nullptr, op.localClip);
Chris Craika1717272015-11-19 13:02:43 -0800119 EXPECT_EQ(Rect(10, 20, 90, 180), op.unmappedBounds);
120}
121
Chris Craik2dbb4c42016-03-11 18:58:37 -0800122TEST(RecordingCanvas, drawRoundRect) {
123 // Round case - stays rounded
124 auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
125 canvas.drawRoundRect(0, 0, 100, 100, 10, 10, SkPaint());
126 });
127 ASSERT_EQ(1u, dl->getOps().size()) << "Must be exactly one op";
128 ASSERT_EQ(RecordedOpId::RoundRectOp, dl->getOps()[0]->opId);
129
130 // Non-rounded case - turned into drawRect
131 dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
132 canvas.drawRoundRect(0, 0, 100, 100, 0, -1, SkPaint());
133 });
134 ASSERT_EQ(1u, dl->getOps().size()) << "Must be exactly one op";
135 ASSERT_EQ(RecordedOpId::RectOp, dl->getOps()[0]->opId)
136 << "Non-rounded rects should be converted";
137}
138
sergeyvdccca442016-03-21 15:38:21 -0700139TEST(RecordingCanvas, drawGlyphs) {
Chris Craika1717272015-11-19 13:02:43 -0800140 auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
141 SkPaint paint;
142 paint.setAntiAlias(true);
143 paint.setTextSize(20);
Chris Craik42a54072015-11-24 11:41:54 -0800144 paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
sergeyvdccca442016-03-21 15:38:21 -0700145 TestUtils::drawUtf8ToCanvas(&canvas, "test text", paint, 25, 25);
Chris Craika1717272015-11-19 13:02:43 -0800146 });
147
Chris Craikb565df12015-10-05 13:00:52 -0700148 int count = 0;
Chris Craikb36af872015-10-16 14:23:12 -0700149 playbackOps(*dl, [&count](const RecordedOp& op) {
Chris Craikb565df12015-10-05 13:00:52 -0700150 count++;
Chris Craika1717272015-11-19 13:02:43 -0800151 ASSERT_EQ(RecordedOpId::TextOp, op.opId);
Chris Craike4db79d2015-12-22 16:32:23 -0800152 EXPECT_EQ(nullptr, op.localClip);
Chris Craika1717272015-11-19 13:02:43 -0800153 EXPECT_TRUE(op.localMatrix.isIdentity());
154 EXPECT_TRUE(op.unmappedBounds.contains(25, 15, 50, 25))
155 << "Op expected to be 25+ pixels wide, 10+ pixels tall";
Chris Craikb565df12015-10-05 13:00:52 -0700156 });
Chris Craika6ac95e2015-11-02 18:06:59 -0800157 ASSERT_EQ(1, count);
Chris Craikb565df12015-10-05 13:00:52 -0700158}
159
sergeyvdccca442016-03-21 15:38:21 -0700160TEST(RecordingCanvas, drawGlyphs_strikeThruAndUnderline) {
Chris Craika1717272015-11-19 13:02:43 -0800161 auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
162 SkPaint paint;
163 paint.setAntiAlias(true);
164 paint.setTextSize(20);
Chris Craik42a54072015-11-24 11:41:54 -0800165 paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
Chris Craika1717272015-11-19 13:02:43 -0800166 for (int i = 0; i < 2; i++) {
167 for (int j = 0; j < 2; j++) {
168 paint.setUnderlineText(i != 0);
169 paint.setStrikeThruText(j != 0);
sergeyvdccca442016-03-21 15:38:21 -0700170 TestUtils::drawUtf8ToCanvas(&canvas, "test text", paint, 25, 25);
Chris Craika1717272015-11-19 13:02:43 -0800171 }
172 }
173 });
174
175 auto ops = dl->getOps();
176 ASSERT_EQ(8u, ops.size());
177
178 int index = 0;
179 EXPECT_EQ(RecordedOpId::TextOp, ops[index++]->opId); // no underline or strikethrough
180
181 EXPECT_EQ(RecordedOpId::TextOp, ops[index++]->opId);
182 EXPECT_EQ(RecordedOpId::RectOp, ops[index++]->opId); // strikethrough only
183
184 EXPECT_EQ(RecordedOpId::TextOp, ops[index++]->opId);
185 EXPECT_EQ(RecordedOpId::RectOp, ops[index++]->opId); // underline only
186
187 EXPECT_EQ(RecordedOpId::TextOp, ops[index++]->opId);
188 EXPECT_EQ(RecordedOpId::RectOp, ops[index++]->opId); // underline
189 EXPECT_EQ(RecordedOpId::RectOp, ops[index++]->opId); // strikethrough
190}
191
sergeyvdccca442016-03-21 15:38:21 -0700192TEST(RecordingCanvas, drawGlyphs_forceAlignLeft) {
Chris Craika1717272015-11-19 13:02:43 -0800193 auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
194 SkPaint paint;
195 paint.setAntiAlias(true);
196 paint.setTextSize(20);
Chris Craik42a54072015-11-24 11:41:54 -0800197 paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
Chris Craika1717272015-11-19 13:02:43 -0800198 paint.setTextAlign(SkPaint::kLeft_Align);
sergeyvdccca442016-03-21 15:38:21 -0700199 TestUtils::drawUtf8ToCanvas(&canvas, "test text", paint, 25, 25);
Chris Craika1717272015-11-19 13:02:43 -0800200 paint.setTextAlign(SkPaint::kCenter_Align);
sergeyvdccca442016-03-21 15:38:21 -0700201 TestUtils::drawUtf8ToCanvas(&canvas, "test text", paint, 25, 25);
Chris Craika1717272015-11-19 13:02:43 -0800202 paint.setTextAlign(SkPaint::kRight_Align);
sergeyvdccca442016-03-21 15:38:21 -0700203 TestUtils::drawUtf8ToCanvas(&canvas, "test text", paint, 25, 25);
Chris Craika1717272015-11-19 13:02:43 -0800204 });
205
206 int count = 0;
Chris Craik42a54072015-11-24 11:41:54 -0800207 float lastX = FLT_MAX;
208 playbackOps(*dl, [&count, &lastX](const RecordedOp& op) {
Chris Craika1717272015-11-19 13:02:43 -0800209 count++;
210 ASSERT_EQ(RecordedOpId::TextOp, op.opId);
211 EXPECT_EQ(SkPaint::kLeft_Align, op.paint->getTextAlign())
212 << "recorded drawText commands must force kLeft_Align on their paint";
Chris Craik42a54072015-11-24 11:41:54 -0800213
214 // verify TestUtils alignment offsetting (TODO: move asserts to Canvas base class)
215 EXPECT_GT(lastX, ((const TextOp&)op).x)
216 << "x coordinate should reduce across each of the draw commands, from alignment";
217 lastX = ((const TextOp&)op).x;
Chris Craika1717272015-11-19 13:02:43 -0800218 });
219 ASSERT_EQ(3, count);
220}
221
Chris Craik1713c772016-02-18 17:49:44 -0800222TEST(RecordingCanvas, drawColor) {
223 auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
224 canvas.drawColor(Color::Black, SkXfermode::kSrcOver_Mode);
225 });
226
227 ASSERT_EQ(1u, dl->getOps().size()) << "Must be exactly one op";
228 auto op = *(dl->getOps()[0]);
Chris Craika2048482016-03-25 14:17:49 -0700229 EXPECT_EQ(RecordedOpId::ColorOp, op.opId);
Chris Craik1713c772016-02-18 17:49:44 -0800230 EXPECT_EQ(nullptr, op.localClip);
Chris Craika2048482016-03-25 14:17:49 -0700231 EXPECT_TRUE(op.unmappedBounds.isEmpty()) << "Expect undefined recorded bounds";
Chris Craik1713c772016-02-18 17:49:44 -0800232}
233
Chris Craikb565df12015-10-05 13:00:52 -0700234TEST(RecordingCanvas, backgroundAndImage) {
Chris Craikb36af872015-10-16 14:23:12 -0700235 auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
Chris Craikb565df12015-10-05 13:00:52 -0700236 SkBitmap bitmap;
237 bitmap.setInfo(SkImageInfo::MakeUnknown(25, 25));
238 SkPaint paint;
239 paint.setColor(SK_ColorBLUE);
240
Florin Malitaeecff562015-12-21 10:43:01 -0500241 canvas.save(SaveFlags::MatrixClip);
Chris Craikb565df12015-10-05 13:00:52 -0700242 {
243 // a background!
Florin Malitaeecff562015-12-21 10:43:01 -0500244 canvas.save(SaveFlags::MatrixClip);
Chris Craikb565df12015-10-05 13:00:52 -0700245 canvas.drawRect(0, 0, 100, 200, paint);
246 canvas.restore();
247 }
248 {
249 // an image!
Florin Malitaeecff562015-12-21 10:43:01 -0500250 canvas.save(SaveFlags::MatrixClip);
Chris Craikb565df12015-10-05 13:00:52 -0700251 canvas.translate(25, 25);
252 canvas.scale(2, 2);
253 canvas.drawBitmap(bitmap, 0, 0, nullptr);
254 canvas.restore();
255 }
256 canvas.restore();
257 });
258
259 int count = 0;
Chris Craikb36af872015-10-16 14:23:12 -0700260 playbackOps(*dl, [&count](const RecordedOp& op) {
Chris Craikb565df12015-10-05 13:00:52 -0700261 if (count == 0) {
262 ASSERT_EQ(RecordedOpId::RectOp, op.opId);
263 ASSERT_NE(nullptr, op.paint);
264 EXPECT_EQ(SK_ColorBLUE, op.paint->getColor());
Chris Craik5430ab22015-12-10 16:28:16 -0800265 EXPECT_EQ(Rect(100, 200), op.unmappedBounds);
Chris Craike4db79d2015-12-22 16:32:23 -0800266 EXPECT_EQ(nullptr, op.localClip);
Chris Craikb565df12015-10-05 13:00:52 -0700267
268 Matrix4 expectedMatrix;
269 expectedMatrix.loadIdentity();
270 EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix);
271 } else {
272 ASSERT_EQ(RecordedOpId::BitmapOp, op.opId);
273 EXPECT_EQ(nullptr, op.paint);
Chris Craik5430ab22015-12-10 16:28:16 -0800274 EXPECT_EQ(Rect(25, 25), op.unmappedBounds);
Chris Craike4db79d2015-12-22 16:32:23 -0800275 EXPECT_EQ(nullptr, op.localClip);
Chris Craikb565df12015-10-05 13:00:52 -0700276
277 Matrix4 expectedMatrix;
278 expectedMatrix.loadTranslate(25, 25, 0);
279 expectedMatrix.scale(2, 2, 1);
280 EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix);
281 }
282 count++;
283 });
Chris Craika6ac95e2015-11-02 18:06:59 -0800284 ASSERT_EQ(2, count);
Chris Craikb565df12015-10-05 13:00:52 -0700285}
286
Chris Craika1717272015-11-19 13:02:43 -0800287TEST(RecordingCanvas, saveLayer_simple) {
Chris Craik6fe991e52015-10-20 09:39:42 -0700288 auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
Florin Malitaeecff562015-12-21 10:43:01 -0500289 canvas.saveLayerAlpha(10, 20, 190, 180, 128, SaveFlags::ClipToLayer);
Chris Craik6fe991e52015-10-20 09:39:42 -0700290 canvas.drawRect(10, 20, 190, 180, SkPaint());
291 canvas.restore();
292 });
293 int count = 0;
294 playbackOps(*dl, [&count](const RecordedOp& op) {
295 Matrix4 expectedMatrix;
296 switch(count++) {
297 case 0:
298 EXPECT_EQ(RecordedOpId::BeginLayerOp, op.opId);
Chris Craika6ac95e2015-11-02 18:06:59 -0800299 EXPECT_EQ(Rect(10, 20, 190, 180), op.unmappedBounds);
Chris Craike4db79d2015-12-22 16:32:23 -0800300 EXPECT_EQ(nullptr, op.localClip);
Chris Craika6ac95e2015-11-02 18:06:59 -0800301 EXPECT_TRUE(op.localMatrix.isIdentity());
Chris Craik6fe991e52015-10-20 09:39:42 -0700302 break;
303 case 1:
304 EXPECT_EQ(RecordedOpId::RectOp, op.opId);
Chris Craike4db79d2015-12-22 16:32:23 -0800305 EXPECT_CLIP_RECT(Rect(180, 160), op.localClip);
Chris Craik6fe991e52015-10-20 09:39:42 -0700306 EXPECT_EQ(Rect(10, 20, 190, 180), op.unmappedBounds);
307 expectedMatrix.loadTranslate(-10, -20, 0);
308 EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix);
309 break;
310 case 2:
311 EXPECT_EQ(RecordedOpId::EndLayerOp, op.opId);
Chris Craika6ac95e2015-11-02 18:06:59 -0800312 // Don't bother asserting recording state data - it's not used
Chris Craik6fe991e52015-10-20 09:39:42 -0700313 break;
314 default:
Chris Craik818c9fb2015-10-23 14:33:42 -0700315 ADD_FAILURE();
Chris Craik6fe991e52015-10-20 09:39:42 -0700316 }
317 });
318 EXPECT_EQ(3, count);
Chris Craikb565df12015-10-05 13:00:52 -0700319}
Chris Craik6fe991e52015-10-20 09:39:42 -0700320
Chris Craikb87eadd2016-01-06 09:16:05 -0800321TEST(RecordingCanvas, saveLayer_missingRestore) {
322 auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
Florin Malitaeecff562015-12-21 10:43:01 -0500323 canvas.saveLayerAlpha(0, 0, 200, 200, 128, SaveFlags::ClipToLayer);
Chris Craikb87eadd2016-01-06 09:16:05 -0800324 canvas.drawRect(0, 0, 200, 200, SkPaint());
325 // Note: restore omitted, shouldn't result in unmatched save
326 });
327 int count = 0;
328 playbackOps(*dl, [&count](const RecordedOp& op) {
329 if (count++ == 2) {
330 EXPECT_EQ(RecordedOpId::EndLayerOp, op.opId);
331 }
332 });
333 EXPECT_EQ(3, count) << "Missing a restore shouldn't result in an unmatched saveLayer";
334}
335
336TEST(RecordingCanvas, saveLayer_simpleUnclipped) {
337 auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
Florin Malitaeecff562015-12-21 10:43:01 -0500338 canvas.saveLayerAlpha(10, 20, 190, 180, 128, (SaveFlags::Flags)0); // unclipped
Chris Craikb87eadd2016-01-06 09:16:05 -0800339 canvas.drawRect(10, 20, 190, 180, SkPaint());
340 canvas.restore();
341 });
342 int count = 0;
343 playbackOps(*dl, [&count](const RecordedOp& op) {
344 switch(count++) {
345 case 0:
346 EXPECT_EQ(RecordedOpId::BeginUnclippedLayerOp, op.opId);
347 EXPECT_EQ(Rect(10, 20, 190, 180), op.unmappedBounds);
348 EXPECT_EQ(nullptr, op.localClip);
349 EXPECT_TRUE(op.localMatrix.isIdentity());
350 break;
351 case 1:
352 EXPECT_EQ(RecordedOpId::RectOp, op.opId);
353 EXPECT_EQ(nullptr, op.localClip);
354 EXPECT_EQ(Rect(10, 20, 190, 180), op.unmappedBounds);
355 EXPECT_TRUE(op.localMatrix.isIdentity());
356 break;
357 case 2:
358 EXPECT_EQ(RecordedOpId::EndUnclippedLayerOp, op.opId);
359 // Don't bother asserting recording state data - it's not used
360 break;
361 default:
362 ADD_FAILURE();
363 }
364 });
365 EXPECT_EQ(3, count);
366}
367
368TEST(RecordingCanvas, saveLayer_addClipFlag) {
369 auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
Florin Malitaeecff562015-12-21 10:43:01 -0500370 canvas.save(SaveFlags::MatrixClip);
Chris Craikb87eadd2016-01-06 09:16:05 -0800371 canvas.clipRect(10, 20, 190, 180, SkRegion::kIntersect_Op);
Florin Malitaeecff562015-12-21 10:43:01 -0500372 canvas.saveLayerAlpha(10, 20, 190, 180, 128, (SaveFlags::Flags)0); // unclipped
Chris Craikb87eadd2016-01-06 09:16:05 -0800373 canvas.drawRect(10, 20, 190, 180, SkPaint());
374 canvas.restore();
375 canvas.restore();
376 });
377 int count = 0;
378 playbackOps(*dl, [&count](const RecordedOp& op) {
379 if (count++ == 0) {
380 EXPECT_EQ(RecordedOpId::BeginLayerOp, op.opId)
381 << "Clip + unclipped saveLayer should result in a clipped layer";
382 }
383 });
384 EXPECT_EQ(3, count);
385}
386
Chris Craika1717272015-11-19 13:02:43 -0800387TEST(RecordingCanvas, saveLayer_viewportCrop) {
Chris Craik6fe991e52015-10-20 09:39:42 -0700388 auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
389 // shouldn't matter, since saveLayer will clip to its bounds
390 canvas.clipRect(-1000, -1000, 1000, 1000, SkRegion::kReplace_Op);
391
Florin Malitaeecff562015-12-21 10:43:01 -0500392 canvas.saveLayerAlpha(100, 100, 300, 300, 128, SaveFlags::ClipToLayer);
Chris Craik6fe991e52015-10-20 09:39:42 -0700393 canvas.drawRect(0, 0, 400, 400, SkPaint());
394 canvas.restore();
395 });
396 int count = 0;
397 playbackOps(*dl, [&count](const RecordedOp& op) {
398 if (count++ == 1) {
399 Matrix4 expectedMatrix;
400 EXPECT_EQ(RecordedOpId::RectOp, op.opId);
Chris Craike4db79d2015-12-22 16:32:23 -0800401 EXPECT_CLIP_RECT(Rect(100, 100), op.localClip) // Recorded clip rect should be
402 // intersection of viewport and saveLayer bounds, in layer space;
Chris Craik5430ab22015-12-10 16:28:16 -0800403 EXPECT_EQ(Rect(400, 400), op.unmappedBounds);
Chris Craik6fe991e52015-10-20 09:39:42 -0700404 expectedMatrix.loadTranslate(-100, -100, 0);
405 EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix);
406 }
407 });
408 EXPECT_EQ(3, count);
Chris Craikb565df12015-10-05 13:00:52 -0700409}
Chris Craik6fe991e52015-10-20 09:39:42 -0700410
Chris Craika1717272015-11-19 13:02:43 -0800411TEST(RecordingCanvas, saveLayer_rotateUnclipped) {
Chris Craik6fe991e52015-10-20 09:39:42 -0700412 auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
Florin Malitaeecff562015-12-21 10:43:01 -0500413 canvas.save(SaveFlags::MatrixClip);
Chris Craik6fe991e52015-10-20 09:39:42 -0700414 canvas.translate(100, 100);
415 canvas.rotate(45);
416 canvas.translate(-50, -50);
417
Florin Malitaeecff562015-12-21 10:43:01 -0500418 canvas.saveLayerAlpha(0, 0, 100, 100, 128, SaveFlags::ClipToLayer);
Chris Craik6fe991e52015-10-20 09:39:42 -0700419 canvas.drawRect(0, 0, 100, 100, SkPaint());
420 canvas.restore();
421
422 canvas.restore();
423 });
424 int count = 0;
425 playbackOps(*dl, [&count](const RecordedOp& op) {
426 if (count++ == 1) {
Chris Craik6fe991e52015-10-20 09:39:42 -0700427 EXPECT_EQ(RecordedOpId::RectOp, op.opId);
Chris Craike4db79d2015-12-22 16:32:23 -0800428 EXPECT_CLIP_RECT(Rect(100, 100), op.localClip);
Chris Craik5430ab22015-12-10 16:28:16 -0800429 EXPECT_EQ(Rect(100, 100), op.unmappedBounds);
Chris Craika6ac95e2015-11-02 18:06:59 -0800430 EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), op.localMatrix)
431 << "Recorded op shouldn't see any canvas transform before the saveLayer";
Chris Craik6fe991e52015-10-20 09:39:42 -0700432 }
433 });
434 EXPECT_EQ(3, count);
435}
436
Chris Craika1717272015-11-19 13:02:43 -0800437TEST(RecordingCanvas, saveLayer_rotateClipped) {
Chris Craik6fe991e52015-10-20 09:39:42 -0700438 auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
Florin Malitaeecff562015-12-21 10:43:01 -0500439 canvas.save(SaveFlags::MatrixClip);
Chris Craik6fe991e52015-10-20 09:39:42 -0700440 canvas.translate(100, 100);
441 canvas.rotate(45);
442 canvas.translate(-200, -200);
443
444 // area of saveLayer will be clipped to parent viewport, so we ask for 400x400...
Florin Malitaeecff562015-12-21 10:43:01 -0500445 canvas.saveLayerAlpha(0, 0, 400, 400, 128, SaveFlags::ClipToLayer);
Chris Craik6fe991e52015-10-20 09:39:42 -0700446 canvas.drawRect(0, 0, 400, 400, SkPaint());
447 canvas.restore();
448
449 canvas.restore();
450 });
451 int count = 0;
452 playbackOps(*dl, [&count](const RecordedOp& op) {
453 if (count++ == 1) {
454 Matrix4 expectedMatrix;
455 EXPECT_EQ(RecordedOpId::RectOp, op.opId);
456
457 // ...and get about 58.6, 58.6, 341.4 341.4, because the bounds are clipped by
458 // the parent 200x200 viewport, but prior to rotation
Chris Craike4db79d2015-12-22 16:32:23 -0800459 ASSERT_NE(nullptr, op.localClip);
460 ASSERT_EQ(ClipMode::Rectangle, op.localClip->mode);
461 // NOTE: this check relies on saveLayer altering the clip post-viewport init. This
462 // causes the clip to be recorded by contained draw commands, though it's not necessary
463 // since the same clip will be computed at draw time. If such a change is made, this
464 // check could be done at record time by querying the clip, or the clip could be altered
465 // slightly so that it is serialized.
Chris Craik69aeabe2016-03-03 12:58:39 -0800466 EXPECT_EQ(Rect(59, 59, 341, 341), op.localClip->rect);
Chris Craik5430ab22015-12-10 16:28:16 -0800467 EXPECT_EQ(Rect(400, 400), op.unmappedBounds);
Chris Craik6fe991e52015-10-20 09:39:42 -0700468 expectedMatrix.loadIdentity();
469 EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix);
470 }
471 });
472 EXPECT_EQ(3, count);
473}
474
Chris Craik1367d252016-03-10 15:43:13 -0800475TEST(RecordingCanvas, drawRenderNode_rejection) {
476 auto child = TestUtils::createNode(50, 50, 150, 150,
477 [](RenderProperties& props, RecordingCanvas& canvas) {
478 SkPaint paint;
479 paint.setColor(SK_ColorWHITE);
480 canvas.drawRect(0, 0, 100, 100, paint);
481 });
482
483 auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [&child](RecordingCanvas& canvas) {
484 canvas.clipRect(0, 0, 0, 0, SkRegion::kIntersect_Op); // empty clip, reject node
485 canvas.drawRenderNode(child.get()); // shouldn't crash when rejecting node...
486 });
487 ASSERT_TRUE(dl->isEmpty());
488}
489
Chris Craik8d1f2122015-11-24 16:40:09 -0800490TEST(RecordingCanvas, drawRenderNode_projection) {
491 sp<RenderNode> background = TestUtils::createNode(50, 50, 150, 150,
492 [](RenderProperties& props, RecordingCanvas& canvas) {
493 SkPaint paint;
494 paint.setColor(SK_ColorWHITE);
495 canvas.drawRect(0, 0, 100, 100, paint);
496 });
497 {
498 background->mutateStagingProperties().setProjectionReceiver(false);
499
500 // NO RECEIVER PRESENT
501 auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200,
502 [&background](RecordingCanvas& canvas) {
503 canvas.drawRect(0, 0, 100, 100, SkPaint());
504 canvas.drawRenderNode(background.get());
505 canvas.drawRect(0, 0, 100, 100, SkPaint());
506 });
507 EXPECT_EQ(-1, dl->projectionReceiveIndex)
508 << "no projection receiver should have been observed";
509 }
510 {
511 background->mutateStagingProperties().setProjectionReceiver(true);
512
513 // RECEIVER PRESENT
514 auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200,
515 [&background](RecordingCanvas& canvas) {
516 canvas.drawRect(0, 0, 100, 100, SkPaint());
517 canvas.drawRenderNode(background.get());
518 canvas.drawRect(0, 0, 100, 100, SkPaint());
519 });
520
521 ASSERT_EQ(3u, dl->getOps().size()) << "Must be three ops";
522 auto op = dl->getOps()[1];
523 EXPECT_EQ(RecordedOpId::RenderNodeOp, op->opId);
524 EXPECT_EQ(1, dl->projectionReceiveIndex)
525 << "correct projection receiver not identified";
526
527 // verify the behavior works even though projection receiver hasn't been sync'd yet
528 EXPECT_TRUE(background->stagingProperties().isProjectionReceiver());
529 EXPECT_FALSE(background->properties().isProjectionReceiver());
530 }
531}
532
Chris Craik7fc1b032016-02-03 19:45:06 -0800533TEST(RecordingCanvas, firstClipWillReplace) {
534 auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
535 canvas.save(SaveFlags::MatrixClip);
536 // since no explicit clip set on canvas, this should be the one observed on op:
537 canvas.clipRect(-100, -100, 300, 300, SkRegion::kIntersect_Op);
538
539 SkPaint paint;
540 paint.setColor(SK_ColorWHITE);
541 canvas.drawRect(0, 0, 100, 100, paint);
542
543 canvas.restore();
544 });
545 ASSERT_EQ(1u, dl->getOps().size()) << "Must have one op";
546 // first clip must be preserved, even if it extends beyond canvas bounds
547 EXPECT_CLIP_RECT(Rect(-100, -100, 300, 300), dl->getOps()[0]->localClip);
548}
549
Chris Craika1717272015-11-19 13:02:43 -0800550TEST(RecordingCanvas, insertReorderBarrier) {
Chris Craik161f54b2015-11-05 11:08:52 -0800551 auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
552 canvas.drawRect(0, 0, 400, 400, SkPaint());
553 canvas.insertReorderBarrier(true);
554 canvas.insertReorderBarrier(false);
555 canvas.insertReorderBarrier(false);
556 canvas.insertReorderBarrier(true);
557 canvas.drawRect(0, 0, 400, 400, SkPaint());
558 canvas.insertReorderBarrier(false);
559 });
560
561 auto chunks = dl->getChunks();
562 EXPECT_EQ(0u, chunks[0].beginOpIndex);
563 EXPECT_EQ(1u, chunks[0].endOpIndex);
564 EXPECT_FALSE(chunks[0].reorderChildren);
565
566 EXPECT_EQ(1u, chunks[1].beginOpIndex);
567 EXPECT_EQ(2u, chunks[1].endOpIndex);
568 EXPECT_TRUE(chunks[1].reorderChildren);
569}
570
Chris Craik42a54072015-11-24 11:41:54 -0800571TEST(RecordingCanvas, refPaint) {
572 SkPaint paint;
Chris Craik42a54072015-11-24 11:41:54 -0800573
574 auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [&paint](RecordingCanvas& canvas) {
575 paint.setColor(SK_ColorBLUE);
Derek Sollenberger79abbf22016-03-24 11:07:19 -0400576 // first two should use same paint
Chris Craik42a54072015-11-24 11:41:54 -0800577 canvas.drawRect(0, 0, 200, 10, paint);
578 SkPaint paintCopy(paint);
579 canvas.drawRect(0, 10, 200, 20, paintCopy);
Chris Craik42a54072015-11-24 11:41:54 -0800580
581 // only here do we use different paint ptr
582 paint.setColor(SK_ColorRED);
583 canvas.drawRect(0, 20, 200, 30, paint);
584 });
585 auto ops = dl->getOps();
Derek Sollenberger79abbf22016-03-24 11:07:19 -0400586 ASSERT_EQ(3u, ops.size());
Chris Craik42a54072015-11-24 11:41:54 -0800587
Derek Sollenberger79abbf22016-03-24 11:07:19 -0400588 // first two are the same
Chris Craik42a54072015-11-24 11:41:54 -0800589 EXPECT_NE(nullptr, ops[0]->paint);
590 EXPECT_NE(&paint, ops[0]->paint);
591 EXPECT_EQ(ops[0]->paint, ops[1]->paint);
Chris Craik42a54072015-11-24 11:41:54 -0800592
593 // last is different, but still copied / non-null
Derek Sollenberger79abbf22016-03-24 11:07:19 -0400594 EXPECT_NE(nullptr, ops[2]->paint);
595 EXPECT_NE(ops[0]->paint, ops[2]->paint);
596 EXPECT_NE(&paint, ops[2]->paint);
Chris Craik42a54072015-11-24 11:41:54 -0800597}
598
Chris Craik734f1462016-03-24 11:10:08 -0700599TEST(RecordingCanvas, refBitmapInShader_bitmapShader) {
600 SkBitmap bitmap = TestUtils::createSkBitmap(100, 100);
601 auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [&bitmap](RecordingCanvas& canvas) {
602 SkPaint paint;
603 SkAutoTUnref<SkShader> shader(SkShader::CreateBitmapShader(bitmap,
604 SkShader::TileMode::kClamp_TileMode,
605 SkShader::TileMode::kClamp_TileMode));
606 paint.setShader(shader);
607 canvas.drawRoundRect(0, 0, 100, 100, 20.0f, 20.0f, paint);
608 });
609 auto& bitmaps = dl->getBitmapResources();
610 EXPECT_EQ(1u, bitmaps.size());
611}
612
613TEST(RecordingCanvas, refBitmapInShader_composeShader) {
614 SkBitmap bitmap = TestUtils::createSkBitmap(100, 100);
615 auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [&bitmap](RecordingCanvas& canvas) {
616 SkPaint paint;
617 SkAutoTUnref<SkShader> shader1(SkShader::CreateBitmapShader(bitmap,
618 SkShader::TileMode::kClamp_TileMode,
619 SkShader::TileMode::kClamp_TileMode));
620
621 SkPoint center;
622 center.set(50, 50);
623 SkColor colors[2];
624 colors[0] = Color::Black;
625 colors[1] = Color::White;
626 SkAutoTUnref<SkShader> shader2(SkGradientShader::CreateRadial(center, 50, colors, nullptr, 2,
627 SkShader::TileMode::kRepeat_TileMode));
628
629 SkAutoTUnref<SkShader> composeShader(SkShader::CreateComposeShader(shader1, shader2,
630 SkXfermode::Mode::kMultiply_Mode));
631 paint.setShader(composeShader);
632 canvas.drawRoundRect(0, 0, 100, 100, 20.0f, 20.0f, paint);
633 });
634 auto& bitmaps = dl->getBitmapResources();
635 EXPECT_EQ(1u, bitmaps.size());
636}
637
sergeyvdccca442016-03-21 15:38:21 -0700638TEST(RecordingCanvas, drawText) {
639 auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
640 Paint paint;
641 paint.setAntiAlias(true);
642 paint.setTextSize(20);
643 paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
Derek Sollenberger79abbf22016-03-24 11:07:19 -0400644 std::unique_ptr<uint16_t[]> dst = TestUtils::asciiToUtf16("HELLO");
sergeyvdccca442016-03-21 15:38:21 -0700645 canvas.drawText(dst.get(), 0, 5, 5, 25, 25, kBidi_Force_LTR, paint, NULL);
646 });
647
648 int count = 0;
649 playbackOps(*dl, [&count](const RecordedOp& op) {
650 count++;
651 ASSERT_EQ(RecordedOpId::TextOp, op.opId);
652 EXPECT_EQ(nullptr, op.localClip);
653 EXPECT_TRUE(op.localMatrix.isIdentity());
654 EXPECT_TRUE(op.unmappedBounds.getHeight() >= 10);
655 EXPECT_TRUE(op.unmappedBounds.getWidth() >= 25);
656 });
657 ASSERT_EQ(1, count);
658}
659
660TEST(RecordingCanvas, drawTextInHighContrast) {
661 auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
662 canvas.setHighContrastText(true);
663 Paint paint;
664 paint.setColor(SK_ColorWHITE);
665 paint.setAntiAlias(true);
666 paint.setTextSize(20);
667 paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
Derek Sollenberger79abbf22016-03-24 11:07:19 -0400668 std::unique_ptr<uint16_t[]> dst = TestUtils::asciiToUtf16("HELLO");
sergeyvdccca442016-03-21 15:38:21 -0700669 canvas.drawText(dst.get(), 0, 5, 5, 25, 25, kBidi_Force_LTR, paint, NULL);
670 });
671
672 int count = 0;
673 playbackOps(*dl, [&count](const RecordedOp& op) {
674 ASSERT_EQ(RecordedOpId::TextOp, op.opId);
675 if (count++ == 0) {
676 EXPECT_EQ(SK_ColorBLACK, op.paint->getColor());
677 EXPECT_EQ(SkPaint::kStrokeAndFill_Style, op.paint->getStyle());
678 } else {
679 EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
680 EXPECT_EQ(SkPaint::kFill_Style, op.paint->getStyle());
681 }
682
683 });
684 ASSERT_EQ(2, count);
685}
686
Chris Craik6fe991e52015-10-20 09:39:42 -0700687} // namespace uirenderer
688} // namespace android