1 /*
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 <DeferredLayerUpdater.h>
20 #include <RecordedOp.h>
21 #include <RecordingCanvas.h>
22 #include <hwui/Paint.h>
23 #include <minikin/Layout.h>
24 #include <tests/common/TestUtils.h>
25 #include <utils/Color.h>
26 
27 #include <SkGradientShader.h>
28 #include <SkShader.h>
29 
30 namespace android {
31 namespace uirenderer {
32 
playbackOps(const DisplayList & displayList,std::function<void (const RecordedOp &)> opReceiver)33 static void playbackOps(const DisplayList& displayList,
34         std::function<void(const RecordedOp&)> opReceiver) {
35     for (auto& chunk : displayList.getChunks()) {
36         for (size_t opIndex = chunk.beginOpIndex; opIndex < chunk.endOpIndex; opIndex++) {
37             RecordedOp* op = displayList.getOps()[opIndex];
38             opReceiver(*op);
39         }
40     }
41 }
42 
validateSingleOp(std::unique_ptr<DisplayList> & dl,std::function<void (const RecordedOp & op)> opValidator)43 static void validateSingleOp(std::unique_ptr<DisplayList>& dl,
44         std::function<void(const RecordedOp& op)> opValidator) {
45     ASSERT_EQ(1u, dl->getOps().size()) << "Must be exactly one op";
46     opValidator(*(dl->getOps()[0]));
47 }
48 
TEST(RecordingCanvas,emptyPlayback)49 TEST(RecordingCanvas, emptyPlayback) {
50     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
51         canvas.save(SaveFlags::MatrixClip);
52         canvas.restore();
53     });
54     playbackOps(*dl, [](const RecordedOp& op) { ADD_FAILURE(); });
55 }
56 
TEST(RecordingCanvas,clipRect)57 TEST(RecordingCanvas, clipRect) {
58     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [](RecordingCanvas& canvas) {
59         canvas.save(SaveFlags::MatrixClip);
60         canvas.clipRect(0, 0, 100, 100, SkRegion::kIntersect_Op);
61         canvas.drawRect(0, 0, 50, 50, SkPaint());
62         canvas.drawRect(50, 50, 100, 100, SkPaint());
63         canvas.restore();
64     });
65 
66     ASSERT_EQ(2u, dl->getOps().size()) << "Must be exactly two ops";
67     EXPECT_CLIP_RECT(Rect(100, 100), dl->getOps()[0]->localClip);
68     EXPECT_CLIP_RECT(Rect(100, 100), dl->getOps()[1]->localClip);
69     EXPECT_EQ(dl->getOps()[0]->localClip, dl->getOps()[1]->localClip)
70             << "Clip should be serialized once";
71 }
72 
TEST(RecordingCanvas,emptyClipRect)73 TEST(RecordingCanvas, emptyClipRect) {
74     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
75         canvas.save(SaveFlags::MatrixClip);
76         canvas.clipRect(0, 0, 100, 100, SkRegion::kIntersect_Op);
77         canvas.clipRect(100, 100, 200, 200, SkRegion::kIntersect_Op);
78         canvas.drawRect(0, 0, 50, 50, SkPaint()); // rejected at record time
79         canvas.restore();
80     });
81     ASSERT_EQ(0u, dl->getOps().size()) << "Must be zero ops. Rect should be rejected.";
82 }
83 
TEST(RecordingCanvas,drawArc)84 TEST(RecordingCanvas, drawArc) {
85     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
86         canvas.drawArc(0, 0, 200, 200, 0, 180, true, SkPaint());
87         canvas.drawArc(0, 0, 100, 100, 0, 360, true, SkPaint());
88     });
89 
90     auto&& ops = dl->getOps();
91     ASSERT_EQ(2u, ops.size()) << "Must be exactly two ops";
92     EXPECT_EQ(RecordedOpId::ArcOp, ops[0]->opId);
93     EXPECT_EQ(Rect(200, 200), ops[0]->unmappedBounds);
94 
95     EXPECT_EQ(RecordedOpId::OvalOp, ops[1]->opId)
96             << "Circular arcs should be converted to ovals";
97     EXPECT_EQ(Rect(100, 100), ops[1]->unmappedBounds);
98 }
99 
TEST(RecordingCanvas,drawLines)100 TEST(RecordingCanvas, drawLines) {
101     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
102         SkPaint paint;
103         paint.setStrokeWidth(20); // doesn't affect recorded bounds - would be resolved at bake time
104         float points[] = { 0, 0, 20, 10, 30, 40, 90 }; // NB: only 1 valid line
105         canvas.drawLines(&points[0], 7, paint);
106     });
107 
108     ASSERT_EQ(1u, dl->getOps().size()) << "Must be exactly one op";
109     auto op = dl->getOps()[0];
110     ASSERT_EQ(RecordedOpId::LinesOp, op->opId);
111     EXPECT_EQ(4, ((LinesOp*)op)->floatCount)
112             << "float count must be rounded down to closest multiple of 4";
113     EXPECT_EQ(Rect(20, 10), op->unmappedBounds)
114             << "unmapped bounds must be size of line, and not outset for stroke width";
115 }
116 
TEST(RecordingCanvas,drawRect)117 TEST(RecordingCanvas, drawRect) {
118     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
119         canvas.drawRect(10, 20, 90, 180, SkPaint());
120     });
121 
122     ASSERT_EQ(1u, dl->getOps().size()) << "Must be exactly one op";
123     auto op = *(dl->getOps()[0]);
124     ASSERT_EQ(RecordedOpId::RectOp, op.opId);
125     EXPECT_EQ(nullptr, op.localClip);
126     EXPECT_EQ(Rect(10, 20, 90, 180), op.unmappedBounds);
127 }
128 
TEST(RecordingCanvas,drawRoundRect)129 TEST(RecordingCanvas, drawRoundRect) {
130     // Round case - stays rounded
131     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
132         canvas.drawRoundRect(0, 0, 100, 100, 10, 10, SkPaint());
133     });
134     ASSERT_EQ(1u, dl->getOps().size()) << "Must be exactly one op";
135     ASSERT_EQ(RecordedOpId::RoundRectOp, dl->getOps()[0]->opId);
136 
137     // Non-rounded case - turned into drawRect
138     dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
139         canvas.drawRoundRect(0, 0, 100, 100, 0, -1, SkPaint());
140     });
141     ASSERT_EQ(1u, dl->getOps().size()) << "Must be exactly one op";
142     ASSERT_EQ(RecordedOpId::RectOp, dl->getOps()[0]->opId)
143         << "Non-rounded rects should be converted";
144 }
145 
TEST(RecordingCanvas,drawGlyphs)146 TEST(RecordingCanvas, drawGlyphs) {
147     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
148         SkPaint paint;
149         paint.setAntiAlias(true);
150         paint.setTextSize(20);
151         paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
152         TestUtils::drawUtf8ToCanvas(&canvas, "test text", paint, 25, 25);
153     });
154 
155     int count = 0;
156     playbackOps(*dl, [&count](const RecordedOp& op) {
157         count++;
158         ASSERT_EQ(RecordedOpId::TextOp, op.opId);
159         EXPECT_EQ(nullptr, op.localClip);
160         EXPECT_TRUE(op.localMatrix.isIdentity());
161         EXPECT_TRUE(op.unmappedBounds.contains(25, 15, 50, 25))
162                 << "Op expected to be 25+ pixels wide, 10+ pixels tall";
163     });
164     ASSERT_EQ(1, count);
165 }
166 
TEST(RecordingCanvas,drawGlyphs_strikeThruAndUnderline)167 TEST(RecordingCanvas, drawGlyphs_strikeThruAndUnderline) {
168     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
169         SkPaint paint;
170         paint.setAntiAlias(true);
171         paint.setTextSize(20);
172         paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
173         for (int i = 0; i < 2; i++) {
174             for (int j = 0; j < 2; j++) {
175                 paint.setUnderlineText(i != 0);
176                 paint.setStrikeThruText(j != 0);
177                 TestUtils::drawUtf8ToCanvas(&canvas, "test text", paint, 25, 25);
178             }
179         }
180     });
181 
182     auto ops = dl->getOps();
183     ASSERT_EQ(8u, ops.size());
184 
185     int index = 0;
186     EXPECT_EQ(RecordedOpId::TextOp, ops[index++]->opId); // no underline or strikethrough
187 
188     EXPECT_EQ(RecordedOpId::TextOp, ops[index++]->opId);
189     EXPECT_EQ(RecordedOpId::RectOp, ops[index++]->opId); // strikethrough only
190 
191     EXPECT_EQ(RecordedOpId::TextOp, ops[index++]->opId);
192     EXPECT_EQ(RecordedOpId::RectOp, ops[index++]->opId); // underline only
193 
194     EXPECT_EQ(RecordedOpId::TextOp, ops[index++]->opId);
195     EXPECT_EQ(RecordedOpId::RectOp, ops[index++]->opId); // underline
196     EXPECT_EQ(RecordedOpId::RectOp, ops[index++]->opId); // strikethrough
197 }
198 
TEST(RecordingCanvas,drawGlyphs_forceAlignLeft)199 TEST(RecordingCanvas, drawGlyphs_forceAlignLeft) {
200     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
201         SkPaint paint;
202         paint.setAntiAlias(true);
203         paint.setTextSize(20);
204         paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
205         paint.setTextAlign(SkPaint::kLeft_Align);
206         TestUtils::drawUtf8ToCanvas(&canvas, "test text", paint, 25, 25);
207         paint.setTextAlign(SkPaint::kCenter_Align);
208         TestUtils::drawUtf8ToCanvas(&canvas, "test text", paint, 25, 25);
209         paint.setTextAlign(SkPaint::kRight_Align);
210         TestUtils::drawUtf8ToCanvas(&canvas, "test text", paint, 25, 25);
211     });
212 
213     int count = 0;
214     float lastX = FLT_MAX;
215     playbackOps(*dl, [&count, &lastX](const RecordedOp& op) {
216         count++;
217         ASSERT_EQ(RecordedOpId::TextOp, op.opId);
218         EXPECT_EQ(SkPaint::kLeft_Align, op.paint->getTextAlign())
219                 << "recorded drawText commands must force kLeft_Align on their paint";
220 
221         // verify TestUtils alignment offsetting (TODO: move asserts to Canvas base class)
222         EXPECT_GT(lastX, ((const TextOp&)op).x)
223                 << "x coordinate should reduce across each of the draw commands, from alignment";
224         lastX = ((const TextOp&)op).x;
225     });
226     ASSERT_EQ(3, count);
227 }
228 
TEST(RecordingCanvas,drawColor)229 TEST(RecordingCanvas, drawColor) {
230     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
231         canvas.drawColor(Color::Black, SkXfermode::kSrcOver_Mode);
232     });
233 
234     ASSERT_EQ(1u, dl->getOps().size()) << "Must be exactly one op";
235     auto op = *(dl->getOps()[0]);
236     EXPECT_EQ(RecordedOpId::ColorOp, op.opId);
237     EXPECT_EQ(nullptr, op.localClip);
238     EXPECT_TRUE(op.unmappedBounds.isEmpty()) << "Expect undefined recorded bounds";
239 }
240 
TEST(RecordingCanvas,backgroundAndImage)241 TEST(RecordingCanvas, backgroundAndImage) {
242     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
243         SkBitmap bitmap;
244         bitmap.setInfo(SkImageInfo::MakeUnknown(25, 25));
245         SkPaint paint;
246         paint.setColor(SK_ColorBLUE);
247 
248         canvas.save(SaveFlags::MatrixClip);
249         {
250             // a background!
251             canvas.save(SaveFlags::MatrixClip);
252             canvas.drawRect(0, 0, 100, 200, paint);
253             canvas.restore();
254         }
255         {
256             // an image!
257             canvas.save(SaveFlags::MatrixClip);
258             canvas.translate(25, 25);
259             canvas.scale(2, 2);
260             canvas.drawBitmap(bitmap, 0, 0, nullptr);
261             canvas.restore();
262         }
263         canvas.restore();
264     });
265 
266     int count = 0;
267     playbackOps(*dl, [&count](const RecordedOp& op) {
268         if (count == 0) {
269             ASSERT_EQ(RecordedOpId::RectOp, op.opId);
270             ASSERT_NE(nullptr, op.paint);
271             EXPECT_EQ(SK_ColorBLUE, op.paint->getColor());
272             EXPECT_EQ(Rect(100, 200), op.unmappedBounds);
273             EXPECT_EQ(nullptr, op.localClip);
274 
275             Matrix4 expectedMatrix;
276             expectedMatrix.loadIdentity();
277             EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix);
278         } else {
279             ASSERT_EQ(RecordedOpId::BitmapOp, op.opId);
280             EXPECT_EQ(nullptr, op.paint);
281             EXPECT_EQ(Rect(25, 25), op.unmappedBounds);
282             EXPECT_EQ(nullptr, op.localClip);
283 
284             Matrix4 expectedMatrix;
285             expectedMatrix.loadTranslate(25, 25, 0);
286             expectedMatrix.scale(2, 2, 1);
287             EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix);
288         }
289         count++;
290     });
291     ASSERT_EQ(2, count);
292 }
293 
RENDERTHREAD_TEST(RecordingCanvas,textureLayer)294 RENDERTHREAD_TEST(RecordingCanvas, textureLayer) {
295     auto layerUpdater = TestUtils::createTextureLayerUpdater(renderThread, 100, 100,
296             SkMatrix::MakeTrans(5, 5));
297 
298     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200,
299             [&layerUpdater](RecordingCanvas& canvas) {
300         canvas.drawLayer(layerUpdater.get());
301     });
302 
303     validateSingleOp(dl, [] (const RecordedOp& op) {
304         ASSERT_EQ(RecordedOpId::TextureLayerOp, op.opId);
305         ASSERT_TRUE(op.localMatrix.isIdentity()) << "Op must not apply matrix at record time.";
306     });
307 }
308 
TEST(RecordingCanvas,saveLayer_simple)309 TEST(RecordingCanvas, saveLayer_simple) {
310     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
311         canvas.saveLayerAlpha(10, 20, 190, 180, 128, SaveFlags::ClipToLayer);
312         canvas.drawRect(10, 20, 190, 180, SkPaint());
313         canvas.restore();
314     });
315     int count = 0;
316     playbackOps(*dl, [&count](const RecordedOp& op) {
317         Matrix4 expectedMatrix;
318         switch(count++) {
319         case 0:
320             EXPECT_EQ(RecordedOpId::BeginLayerOp, op.opId);
321             EXPECT_EQ(Rect(10, 20, 190, 180), op.unmappedBounds);
322             EXPECT_EQ(nullptr, op.localClip);
323             EXPECT_TRUE(op.localMatrix.isIdentity());
324             break;
325         case 1:
326             EXPECT_EQ(RecordedOpId::RectOp, op.opId);
327             EXPECT_CLIP_RECT(Rect(180, 160), op.localClip);
328             EXPECT_EQ(Rect(10, 20, 190, 180), op.unmappedBounds);
329             expectedMatrix.loadTranslate(-10, -20, 0);
330             EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix);
331             break;
332         case 2:
333             EXPECT_EQ(RecordedOpId::EndLayerOp, op.opId);
334             // Don't bother asserting recording state data - it's not used
335             break;
336         default:
337             ADD_FAILURE();
338         }
339     });
340     EXPECT_EQ(3, count);
341 }
342 
TEST(RecordingCanvas,saveLayer_missingRestore)343 TEST(RecordingCanvas, saveLayer_missingRestore) {
344     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
345         canvas.saveLayerAlpha(0, 0, 200, 200, 128, SaveFlags::ClipToLayer);
346         canvas.drawRect(0, 0, 200, 200, SkPaint());
347         // Note: restore omitted, shouldn't result in unmatched save
348     });
349     int count = 0;
350     playbackOps(*dl, [&count](const RecordedOp& op) {
351         if (count++ == 2) {
352             EXPECT_EQ(RecordedOpId::EndLayerOp, op.opId);
353         }
354     });
355     EXPECT_EQ(3, count) << "Missing a restore shouldn't result in an unmatched saveLayer";
356 }
357 
TEST(RecordingCanvas,saveLayer_simpleUnclipped)358 TEST(RecordingCanvas, saveLayer_simpleUnclipped) {
359     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
360         canvas.saveLayerAlpha(10, 20, 190, 180, 128, (SaveFlags::Flags)0); // unclipped
361         canvas.drawRect(10, 20, 190, 180, SkPaint());
362         canvas.restore();
363     });
364     int count = 0;
365     playbackOps(*dl, [&count](const RecordedOp& op) {
366         switch(count++) {
367         case 0:
368             EXPECT_EQ(RecordedOpId::BeginUnclippedLayerOp, op.opId);
369             EXPECT_EQ(Rect(10, 20, 190, 180), op.unmappedBounds);
370             EXPECT_EQ(nullptr, op.localClip);
371             EXPECT_TRUE(op.localMatrix.isIdentity());
372             break;
373         case 1:
374             EXPECT_EQ(RecordedOpId::RectOp, op.opId);
375             EXPECT_EQ(nullptr, op.localClip);
376             EXPECT_EQ(Rect(10, 20, 190, 180), op.unmappedBounds);
377             EXPECT_TRUE(op.localMatrix.isIdentity());
378             break;
379         case 2:
380             EXPECT_EQ(RecordedOpId::EndUnclippedLayerOp, op.opId);
381             // Don't bother asserting recording state data - it's not used
382             break;
383         default:
384             ADD_FAILURE();
385         }
386     });
387     EXPECT_EQ(3, count);
388 }
389 
TEST(RecordingCanvas,saveLayer_addClipFlag)390 TEST(RecordingCanvas, saveLayer_addClipFlag) {
391     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
392         canvas.save(SaveFlags::MatrixClip);
393         canvas.clipRect(10, 20, 190, 180, SkRegion::kIntersect_Op);
394         canvas.saveLayerAlpha(10, 20, 190, 180, 128, (SaveFlags::Flags)0); // unclipped
395         canvas.drawRect(10, 20, 190, 180, SkPaint());
396         canvas.restore();
397         canvas.restore();
398     });
399     int count = 0;
400     playbackOps(*dl, [&count](const RecordedOp& op) {
401         if (count++ == 0) {
402             EXPECT_EQ(RecordedOpId::BeginLayerOp, op.opId)
403                     << "Clip + unclipped saveLayer should result in a clipped layer";
404         }
405     });
406     EXPECT_EQ(3, count);
407 }
408 
TEST(RecordingCanvas,saveLayer_viewportCrop)409 TEST(RecordingCanvas, saveLayer_viewportCrop) {
410     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
411         // shouldn't matter, since saveLayer will clip to its bounds
412         canvas.clipRect(-1000, -1000, 1000, 1000, SkRegion::kReplace_Op);
413 
414         canvas.saveLayerAlpha(100, 100, 300, 300, 128, SaveFlags::ClipToLayer);
415         canvas.drawRect(0, 0, 400, 400, SkPaint());
416         canvas.restore();
417     });
418     int count = 0;
419     playbackOps(*dl, [&count](const RecordedOp& op) {
420         if (count++ == 1) {
421             Matrix4 expectedMatrix;
422             EXPECT_EQ(RecordedOpId::RectOp, op.opId);
423             EXPECT_CLIP_RECT(Rect(100, 100), op.localClip) // Recorded clip rect should be
424             // intersection of viewport and saveLayer bounds, in layer space;
425             EXPECT_EQ(Rect(400, 400), op.unmappedBounds);
426             expectedMatrix.loadTranslate(-100, -100, 0);
427             EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix);
428         }
429     });
430     EXPECT_EQ(3, count);
431 }
432 
TEST(RecordingCanvas,saveLayer_rotateUnclipped)433 TEST(RecordingCanvas, saveLayer_rotateUnclipped) {
434     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
435         canvas.save(SaveFlags::MatrixClip);
436         canvas.translate(100, 100);
437         canvas.rotate(45);
438         canvas.translate(-50, -50);
439 
440         canvas.saveLayerAlpha(0, 0, 100, 100, 128, SaveFlags::ClipToLayer);
441         canvas.drawRect(0, 0, 100, 100, SkPaint());
442         canvas.restore();
443 
444         canvas.restore();
445     });
446     int count = 0;
447     playbackOps(*dl, [&count](const RecordedOp& op) {
448         if (count++ == 1) {
449             EXPECT_EQ(RecordedOpId::RectOp, op.opId);
450             EXPECT_CLIP_RECT(Rect(100, 100), op.localClip);
451             EXPECT_EQ(Rect(100, 100), op.unmappedBounds);
452             EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), op.localMatrix)
453                     << "Recorded op shouldn't see any canvas transform before the saveLayer";
454         }
455     });
456     EXPECT_EQ(3, count);
457 }
458 
TEST(RecordingCanvas,saveLayer_rotateClipped)459 TEST(RecordingCanvas, saveLayer_rotateClipped) {
460     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
461         canvas.save(SaveFlags::MatrixClip);
462         canvas.translate(100, 100);
463         canvas.rotate(45);
464         canvas.translate(-200, -200);
465 
466         // area of saveLayer will be clipped to parent viewport, so we ask for 400x400...
467         canvas.saveLayerAlpha(0, 0, 400, 400, 128, SaveFlags::ClipToLayer);
468         canvas.drawRect(0, 0, 400, 400, SkPaint());
469         canvas.restore();
470 
471         canvas.restore();
472     });
473     int count = 0;
474     playbackOps(*dl, [&count](const RecordedOp& op) {
475         if (count++ == 1) {
476             Matrix4 expectedMatrix;
477             EXPECT_EQ(RecordedOpId::RectOp, op.opId);
478 
479             // ...and get about 58.6, 58.6, 341.4 341.4, because the bounds are clipped by
480             // the parent 200x200 viewport, but prior to rotation
481             ASSERT_NE(nullptr, op.localClip);
482             ASSERT_EQ(ClipMode::Rectangle, op.localClip->mode);
483             // NOTE: this check relies on saveLayer altering the clip post-viewport init. This
484             // causes the clip to be recorded by contained draw commands, though it's not necessary
485             // since the same clip will be computed at draw time. If such a change is made, this
486             // check could be done at record time by querying the clip, or the clip could be altered
487             // slightly so that it is serialized.
488             EXPECT_EQ(Rect(59, 59, 341, 341), op.localClip->rect);
489             EXPECT_EQ(Rect(400, 400), op.unmappedBounds);
490             expectedMatrix.loadIdentity();
491             EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix);
492         }
493     });
494     EXPECT_EQ(3, count);
495 }
496 
TEST(RecordingCanvas,drawRenderNode_rejection)497 TEST(RecordingCanvas, drawRenderNode_rejection) {
498     auto child = TestUtils::createNode(50, 50, 150, 150,
499             [](RenderProperties& props, RecordingCanvas& canvas) {
500         SkPaint paint;
501         paint.setColor(SK_ColorWHITE);
502         canvas.drawRect(0, 0, 100, 100, paint);
503     });
504 
505     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [&child](RecordingCanvas& canvas) {
506         canvas.clipRect(0, 0, 0, 0, SkRegion::kIntersect_Op); // empty clip, reject node
507         canvas.drawRenderNode(child.get()); // shouldn't crash when rejecting node...
508     });
509     ASSERT_TRUE(dl->isEmpty());
510 }
511 
TEST(RecordingCanvas,drawRenderNode_projection)512 TEST(RecordingCanvas, drawRenderNode_projection) {
513     sp<RenderNode> background = TestUtils::createNode(50, 50, 150, 150,
514             [](RenderProperties& props, RecordingCanvas& canvas) {
515         SkPaint paint;
516         paint.setColor(SK_ColorWHITE);
517         canvas.drawRect(0, 0, 100, 100, paint);
518     });
519     {
520         background->mutateStagingProperties().setProjectionReceiver(false);
521 
522         // NO RECEIVER PRESENT
523         auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200,
524                     [&background](RecordingCanvas& canvas) {
525             canvas.drawRect(0, 0, 100, 100, SkPaint());
526             canvas.drawRenderNode(background.get());
527             canvas.drawRect(0, 0, 100, 100, SkPaint());
528         });
529         EXPECT_EQ(-1, dl->projectionReceiveIndex)
530                 << "no projection receiver should have been observed";
531     }
532     {
533         background->mutateStagingProperties().setProjectionReceiver(true);
534 
535         // RECEIVER PRESENT
536         auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200,
537                     [&background](RecordingCanvas& canvas) {
538             canvas.drawRect(0, 0, 100, 100, SkPaint());
539             canvas.drawRenderNode(background.get());
540             canvas.drawRect(0, 0, 100, 100, SkPaint());
541         });
542 
543         ASSERT_EQ(3u, dl->getOps().size()) << "Must be three ops";
544         auto op = dl->getOps()[1];
545         EXPECT_EQ(RecordedOpId::RenderNodeOp, op->opId);
546         EXPECT_EQ(1, dl->projectionReceiveIndex)
547                 << "correct projection receiver not identified";
548 
549         // verify the behavior works even though projection receiver hasn't been sync'd yet
550         EXPECT_TRUE(background->stagingProperties().isProjectionReceiver());
551         EXPECT_FALSE(background->properties().isProjectionReceiver());
552     }
553 }
554 
TEST(RecordingCanvas,firstClipWillReplace)555 TEST(RecordingCanvas, firstClipWillReplace) {
556     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
557         canvas.save(SaveFlags::MatrixClip);
558         // since no explicit clip set on canvas, this should be the one observed on op:
559         canvas.clipRect(-100, -100, 300, 300, SkRegion::kIntersect_Op);
560 
561         SkPaint paint;
562         paint.setColor(SK_ColorWHITE);
563         canvas.drawRect(0, 0, 100, 100, paint);
564 
565         canvas.restore();
566     });
567     ASSERT_EQ(1u, dl->getOps().size()) << "Must have one op";
568     // first clip must be preserved, even if it extends beyond canvas bounds
569     EXPECT_CLIP_RECT(Rect(-100, -100, 300, 300), dl->getOps()[0]->localClip);
570 }
571 
TEST(RecordingCanvas,replaceClipIntersectWithRoot)572 TEST(RecordingCanvas, replaceClipIntersectWithRoot) {
573     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [](RecordingCanvas& canvas) {
574         canvas.save(SaveFlags::MatrixClip);
575         canvas.clipRect(-10, -10, 110, 110, SkRegion::kReplace_Op);
576         canvas.drawColor(SK_ColorWHITE, SkXfermode::Mode::kSrcOver_Mode);
577         canvas.restore();
578     });
579     ASSERT_EQ(1u, dl->getOps().size()) << "Must have one op";
580     // first clip must be preserved, even if it extends beyond canvas bounds
581     EXPECT_CLIP_RECT(Rect(-10, -10, 110, 110), dl->getOps()[0]->localClip);
582     EXPECT_TRUE(dl->getOps()[0]->localClip->intersectWithRoot);
583 }
584 
TEST(RecordingCanvas,insertReorderBarrier)585 TEST(RecordingCanvas, insertReorderBarrier) {
586     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
587         canvas.drawRect(0, 0, 400, 400, SkPaint());
588         canvas.insertReorderBarrier(true);
589         canvas.insertReorderBarrier(false);
590         canvas.insertReorderBarrier(false);
591         canvas.insertReorderBarrier(true);
592         canvas.drawRect(0, 0, 400, 400, SkPaint());
593         canvas.insertReorderBarrier(false);
594     });
595 
596     auto chunks = dl->getChunks();
597     EXPECT_EQ(0u, chunks[0].beginOpIndex);
598     EXPECT_EQ(1u, chunks[0].endOpIndex);
599     EXPECT_FALSE(chunks[0].reorderChildren);
600 
601     EXPECT_EQ(1u, chunks[1].beginOpIndex);
602     EXPECT_EQ(2u, chunks[1].endOpIndex);
603     EXPECT_TRUE(chunks[1].reorderChildren);
604 }
605 
TEST(RecordingCanvas,insertReorderBarrier_clip)606 TEST(RecordingCanvas, insertReorderBarrier_clip) {
607     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
608         // first chunk: no recorded clip
609         canvas.insertReorderBarrier(true);
610         canvas.drawRect(0, 0, 400, 400, SkPaint());
611 
612         // second chunk: no recorded clip, since inorder region
613         canvas.clipRect(0, 0, 200, 200, SkRegion::kIntersect_Op);
614         canvas.insertReorderBarrier(false);
615         canvas.drawRect(0, 0, 400, 400, SkPaint());
616 
617         // third chunk: recorded clip
618         canvas.insertReorderBarrier(true);
619         canvas.drawRect(0, 0, 400, 400, SkPaint());
620     });
621 
622     auto chunks = dl->getChunks();
623     ASSERT_EQ(3u, chunks.size());
624 
625     EXPECT_TRUE(chunks[0].reorderChildren);
626     EXPECT_EQ(nullptr, chunks[0].reorderClip);
627 
628     EXPECT_FALSE(chunks[1].reorderChildren);
629     EXPECT_EQ(nullptr, chunks[1].reorderClip);
630 
631     EXPECT_TRUE(chunks[2].reorderChildren);
632     ASSERT_NE(nullptr, chunks[2].reorderClip);
633     EXPECT_EQ(Rect(200, 200), chunks[2].reorderClip->rect);
634 }
635 
TEST(RecordingCanvas,refPaint)636 TEST(RecordingCanvas, refPaint) {
637     SkPaint paint;
638 
639     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [&paint](RecordingCanvas& canvas) {
640         paint.setColor(SK_ColorBLUE);
641         // first two should use same paint
642         canvas.drawRect(0, 0, 200, 10, paint);
643         SkPaint paintCopy(paint);
644         canvas.drawRect(0, 10, 200, 20, paintCopy);
645 
646         // only here do we use different paint ptr
647         paint.setColor(SK_ColorRED);
648         canvas.drawRect(0, 20, 200, 30, paint);
649     });
650     auto ops = dl->getOps();
651     ASSERT_EQ(3u, ops.size());
652 
653     // first two are the same
654     EXPECT_NE(nullptr, ops[0]->paint);
655     EXPECT_NE(&paint, ops[0]->paint);
656     EXPECT_EQ(ops[0]->paint, ops[1]->paint);
657 
658     // last is different, but still copied / non-null
659     EXPECT_NE(nullptr, ops[2]->paint);
660     EXPECT_NE(ops[0]->paint, ops[2]->paint);
661     EXPECT_NE(&paint, ops[2]->paint);
662 }
663 
TEST(RecordingCanvas,refBitmap)664 TEST(RecordingCanvas, refBitmap) {
665     SkBitmap bitmap = TestUtils::createSkBitmap(100, 100);
666     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [&bitmap](RecordingCanvas& canvas) {
667         canvas.drawBitmap(bitmap, 0, 0, nullptr);
668     });
669     auto& bitmaps = dl->getBitmapResources();
670     EXPECT_EQ(1u, bitmaps.size());
671 }
672 
TEST(RecordingCanvas,refBitmapInShader_bitmapShader)673 TEST(RecordingCanvas, refBitmapInShader_bitmapShader) {
674     SkBitmap bitmap = TestUtils::createSkBitmap(100, 100);
675     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [&bitmap](RecordingCanvas& canvas) {
676         SkPaint paint;
677         SkAutoTUnref<SkShader> shader(SkShader::CreateBitmapShader(bitmap,
678                 SkShader::TileMode::kClamp_TileMode,
679                 SkShader::TileMode::kClamp_TileMode));
680         paint.setShader(shader);
681         canvas.drawRoundRect(0, 0, 100, 100, 20.0f, 20.0f, paint);
682     });
683     auto& bitmaps = dl->getBitmapResources();
684     EXPECT_EQ(1u, bitmaps.size());
685 }
686 
TEST(RecordingCanvas,refBitmapInShader_composeShader)687 TEST(RecordingCanvas, refBitmapInShader_composeShader) {
688     SkBitmap bitmap = TestUtils::createSkBitmap(100, 100);
689     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [&bitmap](RecordingCanvas& canvas) {
690         SkPaint paint;
691         SkAutoTUnref<SkShader> shader1(SkShader::CreateBitmapShader(bitmap,
692                 SkShader::TileMode::kClamp_TileMode,
693                 SkShader::TileMode::kClamp_TileMode));
694 
695         SkPoint center;
696         center.set(50, 50);
697         SkColor colors[2];
698         colors[0] = Color::Black;
699         colors[1] = Color::White;
700         SkAutoTUnref<SkShader> shader2(SkGradientShader::CreateRadial(center, 50, colors, nullptr, 2,
701                 SkShader::TileMode::kRepeat_TileMode));
702 
703         SkAutoTUnref<SkShader> composeShader(SkShader::CreateComposeShader(shader1, shader2,
704                 SkXfermode::Mode::kMultiply_Mode));
705         paint.setShader(composeShader);
706         canvas.drawRoundRect(0, 0, 100, 100, 20.0f, 20.0f, paint);
707     });
708     auto& bitmaps = dl->getBitmapResources();
709     EXPECT_EQ(1u, bitmaps.size());
710 }
711 
TEST(RecordingCanvas,drawText)712 TEST(RecordingCanvas, drawText) {
713     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
714         Paint paint;
715         paint.setAntiAlias(true);
716         paint.setTextSize(20);
717         paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
718         std::unique_ptr<uint16_t[]> dst = TestUtils::asciiToUtf16("HELLO");
719         canvas.drawText(dst.get(), 0, 5, 5, 25, 25, kBidi_Force_LTR, paint, NULL);
720     });
721 
722     int count = 0;
723     playbackOps(*dl, [&count](const RecordedOp& op) {
724         count++;
725         ASSERT_EQ(RecordedOpId::TextOp, op.opId);
726         EXPECT_EQ(nullptr, op.localClip);
727         EXPECT_TRUE(op.localMatrix.isIdentity());
728         EXPECT_TRUE(op.unmappedBounds.getHeight() >= 10);
729         EXPECT_TRUE(op.unmappedBounds.getWidth() >= 25);
730     });
731     ASSERT_EQ(1, count);
732 }
733 
TEST(RecordingCanvas,drawTextInHighContrast)734 TEST(RecordingCanvas, drawTextInHighContrast) {
735     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
736         canvas.setHighContrastText(true);
737         Paint paint;
738         paint.setColor(SK_ColorWHITE);
739         paint.setAntiAlias(true);
740         paint.setTextSize(20);
741         paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
742         std::unique_ptr<uint16_t[]> dst = TestUtils::asciiToUtf16("HELLO");
743         canvas.drawText(dst.get(), 0, 5, 5, 25, 25, kBidi_Force_LTR, paint, NULL);
744     });
745 
746     int count = 0;
747     playbackOps(*dl, [&count](const RecordedOp& op) {
748         ASSERT_EQ(RecordedOpId::TextOp, op.opId);
749         if (count++ == 0) {
750             EXPECT_EQ(SK_ColorBLACK, op.paint->getColor());
751             EXPECT_EQ(SkPaint::kStrokeAndFill_Style, op.paint->getStyle());
752         } else {
753             EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
754             EXPECT_EQ(SkPaint::kFill_Style, op.paint->getStyle());
755         }
756 
757     });
758     ASSERT_EQ(2, count);
759 }
760 
761 } // namespace uirenderer
762 } // namespace android
763