1 /*
2 * Copyright (C) 2016 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 <VectorDrawable.h>
18 #include <gtest/gtest.h>
19
20 #include <SkBlendMode.h>
21 #include <SkClipStack.h>
22 #include <SkSurface_Base.h>
23 #include <string.h>
24 #include "AnimationContext.h"
25 #include "DamageAccumulator.h"
26 #include "FatalTestCanvas.h"
27 #include "IContextFactory.h"
28 #include "hwui/Paint.h"
29 #include "RecordingCanvas.h"
30 #include "SkiaCanvas.h"
31 #include "pipeline/skia/SkiaDisplayList.h"
32 #include "pipeline/skia/SkiaOpenGLPipeline.h"
33 #include "pipeline/skia/SkiaPipeline.h"
34 #include "pipeline/skia/SkiaRecordingCanvas.h"
35 #include "renderthread/CanvasContext.h"
36 #include "tests/common/TestUtils.h"
37 #include "utils/Color.h"
38
39 using namespace android;
40 using namespace android::uirenderer;
41 using namespace android::uirenderer::renderthread;
42 using namespace android::uirenderer::skiapipeline;
43
TEST(RenderNodeDrawable,create)44 TEST(RenderNodeDrawable, create) {
45 auto rootNode =
46 TestUtils::createNode(0, 0, 200, 400, [](RenderProperties& props, Canvas& canvas) {
47 canvas.drawColor(Color::Red_500, SkBlendMode::kSrcOver);
48 });
49
50 DisplayListData skLiteDL;
51 RecordingCanvas canvas;
52 canvas.reset(&skLiteDL, SkIRect::MakeWH(1, 1));
53 canvas.translate(100, 100);
54 RenderNodeDrawable drawable(rootNode.get(), &canvas);
55
56 ASSERT_EQ(drawable.getRenderNode(), rootNode.get());
57 ASSERT_EQ(&drawable.getNodeProperties(), &rootNode->properties());
58 ASSERT_EQ(drawable.getRecordedMatrix(), canvas.getTotalMatrix());
59 }
60
61 namespace {
62
drawOrderedRect(Canvas * canvas,uint8_t expectedDrawOrder)63 static void drawOrderedRect(Canvas* canvas, uint8_t expectedDrawOrder) {
64 Paint paint;
65 // order put in blue channel, transparent so overlapped content doesn't get rejected
66 paint.setColor(SkColorSetARGB(1, 0, 0, expectedDrawOrder));
67 canvas->drawRect(0, 0, 100, 100, paint);
68 }
69
drawOrderedNode(Canvas * canvas,uint8_t expectedDrawOrder,float z)70 static void drawOrderedNode(Canvas* canvas, uint8_t expectedDrawOrder, float z) {
71 auto node = TestUtils::createSkiaNode(
72 0, 0, 100, 100,
73 [expectedDrawOrder, z](RenderProperties& props, SkiaRecordingCanvas& canvas) {
74 drawOrderedRect(&canvas, expectedDrawOrder);
75 props.setTranslationZ(z);
76 });
77 canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership
78 }
79
drawOrderedNode(Canvas * canvas,uint8_t expectedDrawOrder,std::function<void (RenderProperties & props,SkiaRecordingCanvas & canvas)> setup)80 static void drawOrderedNode(
81 Canvas* canvas, uint8_t expectedDrawOrder,
82 std::function<void(RenderProperties& props, SkiaRecordingCanvas& canvas)> setup) {
83 auto node = TestUtils::createSkiaNode(
84 0, 0, 100, 100,
85 [expectedDrawOrder, setup](RenderProperties& props, SkiaRecordingCanvas& canvas) {
86 drawOrderedRect(&canvas, expectedDrawOrder);
87 if (setup) {
88 setup(props, canvas);
89 }
90 });
91 canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership
92 }
93
94 class ZReorderCanvas : public SkCanvas {
95 public:
ZReorderCanvas(int width,int height)96 ZReorderCanvas(int width, int height) : SkCanvas(width, height) {}
onDrawRect(const SkRect & rect,const SkPaint & paint)97 void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
98 int expectedOrder = SkColorGetB(paint.getColor()); // extract order from blue channel
99 EXPECT_EQ(expectedOrder, mDrawCounter++) << "An op was drawn out of order";
100 }
getIndex()101 int getIndex() { return mDrawCounter; }
102
103 protected:
104 int mDrawCounter = 0;
105 };
106
107 } // end anonymous namespace
108
TEST(RenderNodeDrawable,zReorder)109 TEST(RenderNodeDrawable, zReorder) {
110 auto parent = TestUtils::createSkiaNode(0, 0, 100, 100, [](RenderProperties& props,
111 SkiaRecordingCanvas& canvas) {
112 canvas.enableZ(true);
113 canvas.enableZ(false);
114 drawOrderedNode(&canvas, 0, 10.0f); // in reorder=false at this point, so played inorder
115 drawOrderedRect(&canvas, 1);
116 canvas.enableZ(true);
117 drawOrderedNode(&canvas, 6, 2.0f);
118 drawOrderedRect(&canvas, 3);
119 drawOrderedNode(&canvas, 4, 0.0f);
120 drawOrderedRect(&canvas, 5);
121 drawOrderedNode(&canvas, 2, -2.0f);
122 drawOrderedNode(&canvas, 7, 2.0f);
123 canvas.enableZ(false);
124 drawOrderedRect(&canvas, 8);
125 drawOrderedNode(&canvas, 9, -10.0f); // in reorder=false at this point, so played inorder
126 canvas.enableZ(true); // reorder a node ahead of drawrect op
127 drawOrderedRect(&canvas, 11);
128 drawOrderedNode(&canvas, 10, -1.0f);
129 canvas.enableZ(false);
130 canvas.enableZ(true); // test with two empty reorder sections
131 canvas.enableZ(true);
132 canvas.enableZ(false);
133 drawOrderedRect(&canvas, 12);
134 });
135
136 // create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection
137 ZReorderCanvas canvas(100, 100);
138 RenderNodeDrawable drawable(parent.get(), &canvas, false);
139 canvas.drawDrawable(&drawable);
140 EXPECT_EQ(13, canvas.getIndex());
141 }
142
TEST(RenderNodeDrawable,composeOnLayer)143 TEST(RenderNodeDrawable, composeOnLayer) {
144 auto surface = SkSurface::MakeRasterN32Premul(1, 1);
145 SkCanvas& canvas = *surface->getCanvas();
146 canvas.drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
147 ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
148
149 auto rootNode = TestUtils::createSkiaNode(
150 0, 0, 1, 1, [](RenderProperties& props, SkiaRecordingCanvas& recorder) {
151 recorder.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);
152 });
153
154 // attach a layer to the render node
155 auto surfaceLayer = SkSurface::MakeRasterN32Premul(1, 1);
156 auto canvas2 = surfaceLayer->getCanvas();
157 canvas2->drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
158 rootNode->setLayerSurface(surfaceLayer);
159
160 RenderNodeDrawable drawable1(rootNode.get(), &canvas, false);
161 canvas.drawDrawable(&drawable1);
162 ASSERT_EQ(SK_ColorRED, TestUtils::getColor(surface, 0, 0));
163
164 RenderNodeDrawable drawable2(rootNode.get(), &canvas, true);
165 canvas.drawDrawable(&drawable2);
166 ASSERT_EQ(SK_ColorWHITE, TestUtils::getColor(surface, 0, 0));
167
168 RenderNodeDrawable drawable3(rootNode.get(), &canvas, false);
169 canvas.drawDrawable(&drawable3);
170 ASSERT_EQ(SK_ColorRED, TestUtils::getColor(surface, 0, 0));
171
172 rootNode->setLayerSurface(sk_sp<SkSurface>());
173 }
174
175 namespace {
getRecorderClipBounds(const SkiaRecordingCanvas & recorder)176 static SkRect getRecorderClipBounds(const SkiaRecordingCanvas& recorder) {
177 SkRect clipBounds;
178 recorder.getClipBounds(&clipBounds);
179 return clipBounds;
180 }
181
getRecorderMatrix(const SkiaRecordingCanvas & recorder)182 static SkMatrix getRecorderMatrix(const SkiaRecordingCanvas& recorder) {
183 SkMatrix matrix;
184 recorder.getMatrix(&matrix);
185 return matrix;
186 }
187 }
188
TEST(RenderNodeDrawable,saveLayerClipAndMatrixRestore)189 TEST(RenderNodeDrawable, saveLayerClipAndMatrixRestore) {
190 auto surface = SkSurface::MakeRasterN32Premul(400, 800);
191 SkCanvas& canvas = *surface->getCanvas();
192 canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
193 ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorWHITE);
194
195 auto rootNode = TestUtils::createSkiaNode(
196 0, 0, 400, 800, [](RenderProperties& props, SkiaRecordingCanvas& recorder) {
197 SkPaint layerPaint;
198 ASSERT_EQ(SkRect::MakeLTRB(0, 0, 400, 800), getRecorderClipBounds(recorder));
199 EXPECT_TRUE(getRecorderMatrix(recorder).isIdentity());
200
201 // note we don't pass SaveFlags::MatrixClip, but matrix and clip will be saved
202 recorder.saveLayer(0, 0, 400, 400, &layerPaint);
203 ASSERT_EQ(SkRect::MakeLTRB(0, 0, 400, 400), getRecorderClipBounds(recorder));
204 EXPECT_TRUE(getRecorderMatrix(recorder).isIdentity());
205
206 recorder.clipRect(50, 50, 350, 350, SkClipOp::kIntersect);
207 ASSERT_EQ(SkRect::MakeLTRB(50, 50, 350, 350), getRecorderClipBounds(recorder));
208
209 recorder.translate(300.0f, 400.0f);
210 EXPECT_EQ(SkMatrix::Translate(300.0f, 400.0f), getRecorderMatrix(recorder));
211
212 recorder.restore();
213 ASSERT_EQ(SkRect::MakeLTRB(0, 0, 400, 800), getRecorderClipBounds(recorder));
214 EXPECT_TRUE(getRecorderMatrix(recorder).isIdentity());
215
216 Paint paint;
217 paint.setAntiAlias(true);
218 paint.setColor(SK_ColorGREEN);
219 recorder.drawRect(0.0f, 400.0f, 400.0f, 800.0f, paint);
220 });
221
222 RenderNodeDrawable drawable(rootNode.get(), &canvas, true);
223 canvas.drawDrawable(&drawable);
224 ASSERT_EQ(SK_ColorGREEN, TestUtils::getColor(surface, 200, 600));
225 }
226
227 namespace {
228 class ContextFactory : public IContextFactory {
229 public:
createAnimationContext(renderthread::TimeLord & clock)230 virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) override {
231 return new AnimationContext(clock);
232 }
233 };
234 } // end anonymous namespace
235
RENDERTHREAD_TEST(RenderNodeDrawable,projectionReorder)236 RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorder) {
237 static const int SCROLL_X = 5;
238 static const int SCROLL_Y = 10;
239 class ProjectionTestCanvas : public SkCanvas {
240 public:
241 ProjectionTestCanvas(int width, int height) : SkCanvas(width, height) {}
242 void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
243 const int index = mDrawCounter++;
244 SkMatrix expectedMatrix;
245 ;
246 switch (index) {
247 case 0: // this is node "B"
248 EXPECT_EQ(SkRect::MakeWH(100, 100), rect);
249 EXPECT_EQ(SK_ColorWHITE, paint.getColor());
250 expectedMatrix.reset();
251 EXPECT_EQ(SkRect::MakeLTRB(0, 0, 100, 100), TestUtils::getClipBounds(this));
252 break;
253 case 1: // this is node "P"
254 EXPECT_EQ(SkRect::MakeLTRB(-10, -10, 60, 60), rect);
255 EXPECT_EQ(SK_ColorDKGRAY, paint.getColor());
256 expectedMatrix.setTranslate(50 - SCROLL_X, 50 - SCROLL_Y);
257 EXPECT_EQ(SkRect::MakeLTRB(-35, -30, 45, 50),
258 TestUtils::getLocalClipBounds(this));
259 break;
260 case 2: // this is node "C"
261 EXPECT_EQ(SkRect::MakeWH(100, 50), rect);
262 EXPECT_EQ(SK_ColorBLUE, paint.getColor());
263 expectedMatrix.setTranslate(-SCROLL_X, 50 - SCROLL_Y);
264 EXPECT_EQ(SkRect::MakeLTRB(0, 40, 95, 90), TestUtils::getClipBounds(this));
265 break;
266 default:
267 ADD_FAILURE();
268 }
269 EXPECT_EQ(expectedMatrix, getTotalMatrix());
270 }
271
272 int getIndex() { return mDrawCounter; }
273
274 protected:
275 int mDrawCounter = 0;
276 };
277
278 /**
279 * Construct a tree of nodes, where the root (A) has a receiver background (B), and a child (C)
280 * with a projecting child (P) of its own. P would normally draw between B and C's "background"
281 * draw, but because it is projected backwards, it's drawn in between B and C.
282 *
283 * The parent is scrolled by SCROLL_X/SCROLL_Y, but this does not affect the background
284 * (which isn't affected by scroll).
285 */
286 auto receiverBackground = TestUtils::createSkiaNode(
287 0, 0, 100, 100,
288 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
289 properties.setProjectionReceiver(true);
290 // scroll doesn't apply to background, so undone via translationX/Y
291 // NOTE: translationX/Y only! no other transform properties may be set for a proj
292 // receiver!
293 properties.setTranslationX(SCROLL_X);
294 properties.setTranslationY(SCROLL_Y);
295
296 Paint paint;
297 paint.setColor(SK_ColorWHITE);
298 canvas.drawRect(0, 0, 100, 100, paint);
299 },
300 "B");
301
302 auto projectingRipple = TestUtils::createSkiaNode(
303 50, 0, 100, 50,
304 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
305 properties.setProjectBackwards(true);
306 properties.setClipToBounds(false);
307 Paint paint;
308 paint.setColor(SK_ColorDKGRAY);
309 canvas.drawRect(-10, -10, 60, 60, paint);
310 },
311 "P");
312 auto child = TestUtils::createSkiaNode(
313 0, 50, 100, 100,
314 [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
315 Paint paint;
316 paint.setColor(SK_ColorBLUE);
317 canvas.drawRect(0, 0, 100, 50, paint);
318 canvas.drawRenderNode(projectingRipple.get());
319 },
320 "C");
321 auto parent = TestUtils::createSkiaNode(
322 0, 0, 100, 100,
323 [&receiverBackground, &child](RenderProperties& properties,
324 SkiaRecordingCanvas& canvas) {
325 // Set a rect outline for the projecting ripple to be masked against.
326 properties.mutableOutline().setRoundRect(10, 10, 90, 90, 5, 1.0f);
327
328 canvas.save(SaveFlags::MatrixClip);
329 canvas.translate(-SCROLL_X,
330 -SCROLL_Y); // Apply scroll (note: bg undoes this internally)
331 canvas.drawRenderNode(receiverBackground.get());
332 canvas.drawRenderNode(child.get());
333 canvas.restore();
334 },
335 "A");
336 ContextFactory contextFactory;
337 std::unique_ptr<CanvasContext> canvasContext(
338 CanvasContext::create(renderThread, false, parent.get(), &contextFactory, 0, 0));
339 TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
340 DamageAccumulator damageAccumulator;
341 info.damageAccumulator = &damageAccumulator;
342 parent->prepareTree(info);
343
344 // parent(A) -> (receiverBackground, child)
345 // child(C) -> (rect[0, 0, 100, 50], projectingRipple)
346 // projectingRipple(P) -> (rect[-10, -10, 60, 60]) -> projects backwards
347 // receiverBackground(B) -> (rect[0, 0, 100, 100]) -> projection receiver
348
349 // create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection
350 ProjectionTestCanvas canvas(100, 100);
351 RenderNodeDrawable drawable(parent.get(), &canvas, true);
352 canvas.drawDrawable(&drawable);
353 EXPECT_EQ(3, canvas.getIndex());
354 }
355
RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable,emptyReceiver)356 RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, emptyReceiver) {
357 class ProjectionTestCanvas : public SkCanvas {
358 public:
359 ProjectionTestCanvas(int width, int height) : SkCanvas(width, height) {}
360 void onDrawRect(const SkRect& rect, const SkPaint& paint) override { mDrawCounter++; }
361
362 int getDrawCounter() { return mDrawCounter; }
363
364 private:
365 int mDrawCounter = 0;
366 };
367
368 auto receiverBackground = TestUtils::createSkiaNode(
369 0, 0, 100, 100,
370 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
371 properties.setProjectionReceiver(true);
372 },
373 "B"); // a receiver with an empty display list
374
375 auto projectingRipple = TestUtils::createSkiaNode(
376 0, 0, 100, 100,
377 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
378 properties.setProjectBackwards(true);
379 properties.setClipToBounds(false);
380 Paint paint;
381 canvas.drawRect(0, 0, 100, 100, paint);
382 },
383 "P");
384 auto child = TestUtils::createSkiaNode(
385 0, 0, 100, 100,
386 [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
387 Paint paint;
388 canvas.drawRect(0, 0, 100, 100, paint);
389 canvas.drawRenderNode(projectingRipple.get());
390 },
391 "C");
392 auto parent =
393 TestUtils::createSkiaNode(0, 0, 100, 100,
394 [&receiverBackground, &child](RenderProperties& properties,
395 SkiaRecordingCanvas& canvas) {
396 canvas.drawRenderNode(receiverBackground.get());
397 canvas.drawRenderNode(child.get());
398 },
399 "A");
400 ContextFactory contextFactory;
401 std::unique_ptr<CanvasContext> canvasContext(
402 CanvasContext::create(renderThread, false, parent.get(), &contextFactory, 0, 0));
403 TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
404 DamageAccumulator damageAccumulator;
405 info.damageAccumulator = &damageAccumulator;
406 parent->prepareTree(info);
407
408 // parent(A) -> (receiverBackground, child)
409 // child(C) -> (rect[0, 0, 100, 100], projectingRipple)
410 // projectingRipple(P) -> (rect[0, 0, 100, 100]) -> projects backwards
411 // receiverBackground(B) -> (empty) -> projection receiver
412
413 // create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection
414 ProjectionTestCanvas canvas(100, 100);
415 RenderNodeDrawable drawable(parent.get(), &canvas, true);
416 canvas.drawDrawable(&drawable);
417 EXPECT_EQ(2, canvas.getDrawCounter());
418 }
419
RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable,projectionHwLayer)420 RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, projectionHwLayer) {
421 /* R is backward projected on B and C is a layer.
422 A
423 / \
424 B C
425 |
426 R
427 */
428 static const int SCROLL_X = 5;
429 static const int SCROLL_Y = 10;
430 static const int CANVAS_WIDTH = 400;
431 static const int CANVAS_HEIGHT = 400;
432 static const int LAYER_WIDTH = 200;
433 static const int LAYER_HEIGHT = 200;
434 class ProjectionTestCanvas : public SkCanvas {
435 public:
436 ProjectionTestCanvas(int* drawCounter)
437 : SkCanvas(CANVAS_WIDTH, CANVAS_HEIGHT), mDrawCounter(drawCounter) {}
438 void onDrawArc(const SkRect&, SkScalar startAngle, SkScalar sweepAngle, bool useCenter,
439 const SkPaint&) override {
440 EXPECT_EQ(0, (*mDrawCounter)++); // part of painting the layer
441 EXPECT_EQ(SkRect::MakeLTRB(0, 0, LAYER_WIDTH, LAYER_HEIGHT),
442 TestUtils::getClipBounds(this));
443 }
444 void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
445 EXPECT_EQ(1, (*mDrawCounter)++);
446 EXPECT_EQ(SkRect::MakeLTRB(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT),
447 TestUtils::getClipBounds(this));
448 }
449 void onDrawOval(const SkRect&, const SkPaint&) override {
450 EXPECT_EQ(2, (*mDrawCounter)++);
451 SkMatrix expectedMatrix;
452 expectedMatrix.setTranslate(100 - SCROLL_X, 100 - SCROLL_Y);
453 EXPECT_EQ(expectedMatrix, getTotalMatrix());
454 EXPECT_EQ(SkRect::MakeLTRB(-85, -80, 295, 300), TestUtils::getLocalClipBounds(this));
455 }
456 int* mDrawCounter;
457 };
458
459 class ProjectionLayer : public SkSurface_Base {
460 public:
461 ProjectionLayer(int* drawCounter)
462 : SkSurface_Base(SkImageInfo::MakeN32Premul(LAYER_WIDTH, LAYER_HEIGHT), nullptr)
463 , mDrawCounter(drawCounter) {}
464 virtual sk_sp<SkImage> onNewImageSnapshot(const SkIRect* bounds) override {
465 EXPECT_EQ(3, (*mDrawCounter)++);
466 EXPECT_EQ(SkRect::MakeLTRB(100 - SCROLL_X, 100 - SCROLL_Y, 300 - SCROLL_X,
467 300 - SCROLL_Y),
468 TestUtils::getClipBounds(this->getCanvas()));
469 return nullptr;
470 }
471 SkCanvas* onNewCanvas() override { return new ProjectionTestCanvas(mDrawCounter); }
472 sk_sp<SkSurface> onNewSurface(const SkImageInfo&) override { return nullptr; }
473 bool onCopyOnWrite(ContentChangeMode) override { return true; }
474 int* mDrawCounter;
475 void onWritePixels(const SkPixmap&, int x, int y) {}
476 };
477
478 auto receiverBackground = TestUtils::createSkiaNode(
479 0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
480 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
481 properties.setProjectionReceiver(true);
482 // scroll doesn't apply to background, so undone via translationX/Y
483 // NOTE: translationX/Y only! no other transform properties may be set for a proj
484 // receiver!
485 properties.setTranslationX(SCROLL_X);
486 properties.setTranslationY(SCROLL_Y);
487
488 canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, Paint());
489 },
490 "B"); // B
491 auto projectingRipple = TestUtils::createSkiaNode(
492 0, 0, LAYER_WIDTH, LAYER_HEIGHT,
493 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
494 properties.setProjectBackwards(true);
495 properties.setClipToBounds(false);
496 canvas.drawOval(100, 100, 300, 300, Paint()); // drawn mostly out of layer bounds
497 },
498 "R"); // R
499 auto child = TestUtils::createSkiaNode(
500 100, 100, 300, 300,
501 [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
502 canvas.drawRenderNode(projectingRipple.get());
503 canvas.drawArc(0, 0, LAYER_WIDTH, LAYER_HEIGHT, 0.0f, 280.0f, true, Paint());
504 },
505 "C"); // C
506 auto parent = TestUtils::createSkiaNode(
507 0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
508 [&receiverBackground, &child](RenderProperties& properties,
509 SkiaRecordingCanvas& canvas) {
510 // Set a rect outline for the projecting ripple to be masked against.
511 properties.mutableOutline().setRoundRect(10, 10, 390, 390, 0, 1.0f);
512 canvas.translate(-SCROLL_X,
513 -SCROLL_Y); // Apply scroll (note: bg undoes this internally)
514 canvas.drawRenderNode(receiverBackground.get());
515 canvas.drawRenderNode(child.get());
516 },
517 "A"); // A
518
519 // prepareTree is required to find, which receivers have backward projected nodes
520 ContextFactory contextFactory;
521 std::unique_ptr<CanvasContext> canvasContext(
522 CanvasContext::create(renderThread, false, parent.get(), &contextFactory, 0, 0));
523 TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
524 DamageAccumulator damageAccumulator;
525 info.damageAccumulator = &damageAccumulator;
526 parent->prepareTree(info);
527
528 int drawCounter = 0;
529 // set a layer after prepareTree to avoid layer logic there
530 child->animatorProperties().mutateLayerProperties().setType(LayerType::RenderLayer);
531 sk_sp<SkSurface> surfaceLayer1(new ProjectionLayer(&drawCounter));
532 child->setLayerSurface(surfaceLayer1);
533 Matrix4 windowTransform;
534 windowTransform.loadTranslate(100, 100, 0);
535 child->getSkiaLayer()->inverseTransformInWindow.loadInverse(windowTransform);
536
537 LayerUpdateQueue layerUpdateQueue;
538 layerUpdateQueue.enqueueLayerWithDamage(child.get(),
539 android::uirenderer::Rect(LAYER_WIDTH, LAYER_HEIGHT));
540 auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
541 pipeline->renderLayersImpl(layerUpdateQueue, true);
542 EXPECT_EQ(1, drawCounter); // assert index 0 is drawn on the layer
543
544 RenderNodeDrawable drawable(parent.get(), surfaceLayer1->getCanvas(), true);
545 surfaceLayer1->getCanvas()->drawDrawable(&drawable);
546 EXPECT_EQ(4, drawCounter);
547
548 // clean up layer pointer, so we can safely destruct RenderNode
549 child->setLayerSurface(nullptr);
550 }
551
RENDERTHREAD_TEST(RenderNodeDrawable,projectionChildScroll)552 RENDERTHREAD_TEST(RenderNodeDrawable, projectionChildScroll) {
553 /* R is backward projected on B.
554 A
555 / \
556 B C
557 |
558 R
559 */
560 static const int SCROLL_X = 500000;
561 static const int SCROLL_Y = 0;
562 static const int CANVAS_WIDTH = 400;
563 static const int CANVAS_HEIGHT = 400;
564 class ProjectionChildScrollTestCanvas : public SkCanvas {
565 public:
566 ProjectionChildScrollTestCanvas() : SkCanvas(CANVAS_WIDTH, CANVAS_HEIGHT) {}
567 void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
568 EXPECT_EQ(0, mDrawCounter++);
569 EXPECT_TRUE(getTotalMatrix().isIdentity());
570 }
571 void onDrawOval(const SkRect&, const SkPaint&) override {
572 EXPECT_EQ(1, mDrawCounter++);
573 EXPECT_EQ(SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT), TestUtils::getClipBounds(this));
574 EXPECT_TRUE(getTotalMatrix().isIdentity());
575 }
576 int mDrawCounter = 0;
577 };
578
579 auto receiverBackground = TestUtils::createSkiaNode(
580 0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
581 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
582 properties.setProjectionReceiver(true);
583 canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, Paint());
584 },
585 "B"); // B
586 auto projectingRipple = TestUtils::createSkiaNode(
587 0, 0, 200, 200,
588 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
589 // scroll doesn't apply to background, so undone via translationX/Y
590 // NOTE: translationX/Y only! no other transform properties may be set for a proj
591 // receiver!
592 properties.setTranslationX(SCROLL_X);
593 properties.setTranslationY(SCROLL_Y);
594 properties.setProjectBackwards(true);
595 properties.setClipToBounds(false);
596 canvas.drawOval(0, 0, 200, 200, Paint());
597 },
598 "R"); // R
599 auto child = TestUtils::createSkiaNode(
600 0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
601 [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
602 // Record time clip will be ignored by projectee
603 canvas.clipRect(100, 100, 300, 300, SkClipOp::kIntersect);
604
605 canvas.translate(-SCROLL_X,
606 -SCROLL_Y); // Apply scroll (note: bg undoes this internally)
607 canvas.drawRenderNode(projectingRipple.get());
608 },
609 "C"); // C
610 auto parent =
611 TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
612 [&receiverBackground, &child](RenderProperties& properties,
613 SkiaRecordingCanvas& canvas) {
614 canvas.drawRenderNode(receiverBackground.get());
615 canvas.drawRenderNode(child.get());
616 },
617 "A"); // A
618
619 // prepareTree is required to find, which receivers have backward projected nodes
620 ContextFactory contextFactory;
621 std::unique_ptr<CanvasContext> canvasContext(
622 CanvasContext::create(renderThread, false, parent.get(), &contextFactory, 0, 0));
623 TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
624 DamageAccumulator damageAccumulator;
625 info.damageAccumulator = &damageAccumulator;
626 parent->prepareTree(info);
627
628 std::unique_ptr<ProjectionChildScrollTestCanvas> canvas(new ProjectionChildScrollTestCanvas());
629 RenderNodeDrawable drawable(parent.get(), canvas.get(), true);
630 canvas->drawDrawable(&drawable);
631 EXPECT_EQ(2, canvas->mDrawCounter);
632 }
633
634 namespace {
drawNode(RenderThread & renderThread,const sp<RenderNode> & renderNode)635 static int drawNode(RenderThread& renderThread, const sp<RenderNode>& renderNode) {
636 ContextFactory contextFactory;
637 std::unique_ptr<CanvasContext> canvasContext(
638 CanvasContext::create(renderThread, false, renderNode.get(), &contextFactory, 0, 0));
639 TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
640 DamageAccumulator damageAccumulator;
641 info.damageAccumulator = &damageAccumulator;
642 renderNode->prepareTree(info);
643
644 // create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection
645 ZReorderCanvas canvas(100, 100);
646 RenderNodeDrawable drawable(renderNode.get(), &canvas, false);
647 canvas.drawDrawable(&drawable);
648 return canvas.getIndex();
649 }
650 }
651
RENDERTHREAD_TEST(RenderNodeDrawable,projectionReorderProjectedInMiddle)652 RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectedInMiddle) {
653 /* R is backward projected on B
654 A
655 / \
656 B C
657 |
658 R
659 */
660 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, [](RenderProperties& props,
661 SkiaRecordingCanvas& canvas) {
662 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
663 props.setProjectionReceiver(true);
664 }); // nodeB
665 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
666 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
667 props.setProjectBackwards(true);
668 props.setClipToBounds(false);
669 }); // nodeR
670 }); // nodeC
671 }); // nodeA
672 EXPECT_EQ(3, drawNode(renderThread, nodeA));
673 }
674
RENDERTHREAD_TEST(RenderNodeDrawable,projectionReorderProjectLast)675 RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectLast) {
676 /* R is backward projected on E
677 A
678 / | \
679 / | \
680 B C E
681 |
682 R
683 */
684 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, [](RenderProperties& props,
685 SkiaRecordingCanvas& canvas) {
686 drawOrderedNode(&canvas, 0, nullptr); // nodeB
687 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
688 drawOrderedNode(&canvas, 3, [](RenderProperties& props,
689 SkiaRecordingCanvas& canvas) { // drawn as 2
690 props.setProjectBackwards(true);
691 props.setClipToBounds(false);
692 }); // nodeR
693 }); // nodeC
694 drawOrderedNode(&canvas, 2,
695 [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // drawn as 3
696 props.setProjectionReceiver(true);
697 }); // nodeE
698 }); // nodeA
699 EXPECT_EQ(4, drawNode(renderThread, nodeA));
700 }
701
RENDERTHREAD_TEST(RenderNodeDrawable,projectionReorderNoReceivable)702 RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderNoReceivable) {
703 /* R is backward projected without receiver
704 A
705 / \
706 B C
707 |
708 R
709 */
710 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, [](RenderProperties& props,
711 SkiaRecordingCanvas& canvas) {
712 drawOrderedNode(&canvas, 0, nullptr); // nodeB
713 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
714 drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
715 // not having a projection receiver is an undefined behavior
716 props.setProjectBackwards(true);
717 props.setClipToBounds(false);
718 }); // nodeR
719 }); // nodeC
720 }); // nodeA
721 EXPECT_EQ(2, drawNode(renderThread, nodeA));
722 }
723
RENDERTHREAD_TEST(RenderNodeDrawable,projectionReorderParentReceivable)724 RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderParentReceivable) {
725 /* R is backward projected on C
726 A
727 / \
728 B C
729 |
730 R
731 */
732 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, [](RenderProperties& props,
733 SkiaRecordingCanvas& canvas) {
734 drawOrderedNode(&canvas, 0, nullptr); // nodeB
735 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
736 props.setProjectionReceiver(true);
737 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
738 props.setProjectBackwards(true);
739 props.setClipToBounds(false);
740 }); // nodeR
741 }); // nodeC
742 }); // nodeA
743 EXPECT_EQ(3, drawNode(renderThread, nodeA));
744 }
745
RENDERTHREAD_TEST(RenderNodeDrawable,projectionReorderSameNodeReceivable)746 RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderSameNodeReceivable) {
747 /* R is backward projected on R
748 A
749 / \
750 B C
751 |
752 R
753 */
754 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, [](RenderProperties& props,
755 SkiaRecordingCanvas& canvas) {
756 drawOrderedNode(&canvas, 0, nullptr); // nodeB
757 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
758 drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
759 // having a node that is projected on itself is an undefined/unexpected behavior
760 props.setProjectionReceiver(true);
761 props.setProjectBackwards(true);
762 props.setClipToBounds(false);
763 }); // nodeR
764 }); // nodeC
765 }); // nodeA
766 EXPECT_EQ(2, drawNode(renderThread, nodeA));
767 }
768
769 // Note: the outcome for this test is different in HWUI
RENDERTHREAD_TEST(RenderNodeDrawable,projectionReorderProjectedSibling)770 RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectedSibling) {
771 /* R is set to project on B, but R is not drawn because projecting on a sibling is not allowed.
772 A
773 /|\
774 / | \
775 B C R
776 */
777 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, [](RenderProperties& props,
778 SkiaRecordingCanvas& canvas) {
779 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
780 props.setProjectionReceiver(true);
781 }); // nodeB
782 drawOrderedNode(&canvas, 1,
783 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {}); // nodeC
784 drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
785 props.setProjectBackwards(true);
786 props.setClipToBounds(false);
787 }); // nodeR
788 }); // nodeA
789 EXPECT_EQ(2, drawNode(renderThread, nodeA));
790 }
791
RENDERTHREAD_TEST(RenderNodeDrawable,projectionReorderProjectedSibling2)792 RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectedSibling2) {
793 /* R is set to project on B, but R is not drawn because projecting on a sibling is not allowed.
794 A
795 |
796 G
797 /|\
798 / | \
799 B C R
800 */
801 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, [](RenderProperties& props,
802 SkiaRecordingCanvas& canvas) {
803 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
804 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
805 props.setProjectionReceiver(true);
806 }); // nodeB
807 drawOrderedNode(&canvas, 2,
808 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {}); // nodeC
809 drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
810 props.setProjectBackwards(true);
811 props.setClipToBounds(false);
812 }); // nodeR
813 }); // nodeG
814 }); // nodeA
815 EXPECT_EQ(3, drawNode(renderThread, nodeA));
816 }
817
RENDERTHREAD_TEST(RenderNodeDrawable,projectionReorderGrandparentReceivable)818 RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderGrandparentReceivable) {
819 /* R is backward projected on B
820 A
821 |
822 B
823 |
824 C
825 |
826 R
827 */
828 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, [](RenderProperties& props,
829 SkiaRecordingCanvas& canvas) {
830 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
831 props.setProjectionReceiver(true);
832 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
833 drawOrderedNode(&canvas, 2,
834 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
835 props.setProjectBackwards(true);
836 props.setClipToBounds(false);
837 }); // nodeR
838 }); // nodeC
839 }); // nodeB
840 }); // nodeA
841 EXPECT_EQ(3, drawNode(renderThread, nodeA));
842 }
843
RENDERTHREAD_TEST(RenderNodeDrawable,projectionReorderTwoReceivables)844 RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderTwoReceivables) {
845 /* B and G are receivables, R is backward projected
846 A
847 / \
848 B C
849 / \
850 G R
851 */
852 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, [](RenderProperties& props,
853 SkiaRecordingCanvas& canvas) {
854 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // B
855 props.setProjectionReceiver(true);
856 }); // nodeB
857 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // C
858 drawOrderedNode(&canvas, 3,
859 [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // G
860 props.setProjectionReceiver(true);
861 }); // nodeG
862 drawOrderedNode(&canvas, 1,
863 [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // R
864 props.setProjectBackwards(true);
865 props.setClipToBounds(false);
866 }); // nodeR
867 }); // nodeC
868 }); // nodeA
869 EXPECT_EQ(4, drawNode(renderThread, nodeA));
870 }
871
RENDERTHREAD_TEST(RenderNodeDrawable,projectionReorderTwoReceivablesLikelyScenario)872 RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderTwoReceivablesLikelyScenario) {
873 /* B and G are receivables, G is backward projected
874 A
875 / \
876 B C
877 / \
878 G R
879 */
880 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, [](RenderProperties& props,
881 SkiaRecordingCanvas& canvas) {
882 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // B
883 props.setProjectionReceiver(true);
884 }); // nodeB
885 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // C
886 drawOrderedNode(&canvas, 1,
887 [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // G
888 props.setProjectionReceiver(true);
889 props.setProjectBackwards(true);
890 props.setClipToBounds(false);
891 }); // nodeG
892 drawOrderedNode(&canvas, 3,
893 [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // R
894 }); // nodeR
895 }); // nodeC
896 }); // nodeA
897 EXPECT_EQ(4, drawNode(renderThread, nodeA));
898 }
899
RENDERTHREAD_TEST(RenderNodeDrawable,projectionReorderTwoReceivablesDeeper)900 RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderTwoReceivablesDeeper) {
901 /* B and G are receivables, R is backward projected
902 A
903 / \
904 B C
905 / \
906 G D
907 |
908 R
909 */
910 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, [](RenderProperties& props,
911 SkiaRecordingCanvas& canvas) {
912 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // B
913 props.setProjectionReceiver(true);
914 }); // nodeB
915 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // C
916 drawOrderedNode(&canvas, 2,
917 [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // G
918 props.setProjectionReceiver(true);
919 }); // nodeG
920 drawOrderedNode(&canvas, 4,
921 [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // D
922 drawOrderedNode(&canvas, 3, [](RenderProperties& props,
923 SkiaRecordingCanvas& canvas) { // R
924 props.setProjectBackwards(true);
925 props.setClipToBounds(false);
926 }); // nodeR
927 }); // nodeD
928 }); // nodeC
929 }); // nodeA
930 EXPECT_EQ(5, drawNode(renderThread, nodeA));
931 }
932
RENDERTHREAD_TEST(RenderNodeDrawable,simple)933 RENDERTHREAD_TEST(RenderNodeDrawable, simple) {
934 static const int CANVAS_WIDTH = 100;
935 static const int CANVAS_HEIGHT = 200;
936 class SimpleTestCanvas : public TestCanvasBase {
937 public:
938 SimpleTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {}
939 void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
940 EXPECT_EQ(0, mDrawCounter++);
941 }
942 void onDrawImage2(const SkImage*, SkScalar dx, SkScalar dy, const SkSamplingOptions&,
943 const SkPaint*) override {
944 EXPECT_EQ(1, mDrawCounter++);
945 }
946 };
947
948 auto node = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
949 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
950 sk_sp<Bitmap> bitmap(TestUtils::createBitmap(25, 25));
951 canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
952 Paint());
953 canvas.drawBitmap(*bitmap, 10, 10, nullptr);
954 });
955
956 SimpleTestCanvas canvas;
957 RenderNodeDrawable drawable(node.get(), &canvas, true);
958 canvas.drawDrawable(&drawable);
959 EXPECT_EQ(2, canvas.mDrawCounter);
960 }
961
RENDERTHREAD_TEST(RenderNodeDrawable,colorOp_unbounded)962 RENDERTHREAD_TEST(RenderNodeDrawable, colorOp_unbounded) {
963 static const int CANVAS_WIDTH = 200;
964 static const int CANVAS_HEIGHT = 200;
965 class ColorTestCanvas : public TestCanvasBase {
966 public:
967 ColorTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {}
968 void onDrawPaint(const SkPaint&) {
969 switch (mDrawCounter++) {
970 case 0:
971 EXPECT_EQ(SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT),
972 TestUtils::getClipBounds(this));
973 break;
974 case 1:
975 EXPECT_EQ(SkRect::MakeWH(10, 10), TestUtils::getClipBounds(this));
976 break;
977 default:
978 ADD_FAILURE();
979 }
980 }
981 };
982
983 auto unclippedColorView = TestUtils::createSkiaNode(
984 0, 0, 10, 10, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
985 props.setClipToBounds(false);
986 canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
987 });
988
989 auto clippedColorView = TestUtils::createSkiaNode(
990 0, 0, 10, 10, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
991 canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
992 });
993
994 ColorTestCanvas canvas;
995 RenderNodeDrawable drawable(unclippedColorView.get(), &canvas, true);
996 canvas.drawDrawable(&drawable);
997 EXPECT_EQ(1, canvas.mDrawCounter);
998 RenderNodeDrawable drawable2(clippedColorView.get(), &canvas, true);
999 canvas.drawDrawable(&drawable2);
1000 EXPECT_EQ(2, canvas.mDrawCounter);
1001 }
1002
TEST(RenderNodeDrawable,renderNode)1003 TEST(RenderNodeDrawable, renderNode) {
1004 static const int CANVAS_WIDTH = 200;
1005 static const int CANVAS_HEIGHT = 200;
1006 class RenderNodeTestCanvas : public TestCanvasBase {
1007 public:
1008 RenderNodeTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {}
1009 void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
1010 switch (mDrawCounter++) {
1011 case 0:
1012 EXPECT_EQ(SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT),
1013 TestUtils::getClipBounds(this));
1014 EXPECT_EQ(SK_ColorDKGRAY, paint.getColor());
1015 break;
1016 case 1:
1017 EXPECT_EQ(SkRect::MakeLTRB(50, 50, 150, 150), TestUtils::getClipBounds(this));
1018 EXPECT_EQ(SK_ColorWHITE, paint.getColor());
1019 break;
1020 default:
1021 ADD_FAILURE();
1022 }
1023 }
1024 };
1025
1026 auto child = TestUtils::createSkiaNode(
1027 10, 10, 110, 110, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
1028 Paint paint;
1029 paint.setColor(SK_ColorWHITE);
1030 canvas.drawRect(0, 0, 100, 100, paint);
1031 });
1032
1033 auto parent = TestUtils::createSkiaNode(
1034 0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
1035 [&child](RenderProperties& props, SkiaRecordingCanvas& canvas) {
1036 Paint paint;
1037 paint.setColor(SK_ColorDKGRAY);
1038 canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, paint);
1039
1040 canvas.save(SaveFlags::MatrixClip);
1041 canvas.translate(40, 40);
1042 canvas.drawRenderNode(child.get());
1043 canvas.restore();
1044 });
1045
1046 RenderNodeTestCanvas canvas;
1047 RenderNodeDrawable drawable(parent.get(), &canvas, true);
1048 canvas.drawDrawable(&drawable);
1049 EXPECT_EQ(2, canvas.mDrawCounter);
1050 }
1051
1052 // Verify that layers are composed with linear filtering.
RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable,layerComposeQuality)1053 RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, layerComposeQuality) {
1054 static const int CANVAS_WIDTH = 1;
1055 static const int CANVAS_HEIGHT = 1;
1056 static const int LAYER_WIDTH = 1;
1057 static const int LAYER_HEIGHT = 1;
1058 class FrameTestCanvas : public TestCanvasBase {
1059 public:
1060 FrameTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {}
1061 void onDrawImageRect2(const SkImage* image, const SkRect& src, const SkRect& dst,
1062 const SkSamplingOptions& sampling, const SkPaint* paint,
1063 SrcRectConstraint constraint) override {
1064 mDrawCounter++;
1065 EXPECT_FALSE(sampling.useCubic);
1066 EXPECT_EQ(SkFilterMode::kLinear, sampling.filter);
1067 }
1068 };
1069
1070 auto layerNode = TestUtils::createSkiaNode(
1071 0, 0, LAYER_WIDTH, LAYER_HEIGHT,
1072 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
1073 canvas.drawPaint(Paint());
1074 });
1075
1076 layerNode->animatorProperties().mutateLayerProperties().setType(LayerType::RenderLayer);
1077 layerNode->setLayerSurface(SkSurface::MakeRasterN32Premul(LAYER_WIDTH, LAYER_HEIGHT));
1078
1079 FrameTestCanvas canvas;
1080 RenderNodeDrawable drawable(layerNode.get(), &canvas, true);
1081 canvas.drawDrawable(&drawable);
1082 EXPECT_EQ(1, canvas.mDrawCounter); // make sure the layer was composed
1083
1084 // clean up layer pointer, so we can safely destruct RenderNode
1085 layerNode->setLayerSurface(nullptr);
1086 }
1087
TEST(ReorderBarrierDrawable,testShadowMatrix)1088 TEST(ReorderBarrierDrawable, testShadowMatrix) {
1089 static const int CANVAS_WIDTH = 100;
1090 static const int CANVAS_HEIGHT = 100;
1091 static const float TRANSLATE_X = 11.0f;
1092 static const float TRANSLATE_Y = 22.0f;
1093 static const float CASTER_X = 40.0f;
1094 static const float CASTER_Y = 40.0f;
1095 static const float CASTER_WIDTH = 20.0f;
1096 static const float CASTER_HEIGHT = 20.0f;
1097
1098 class ShadowTestCanvas : public SkCanvas {
1099 public:
1100 ShadowTestCanvas(int width, int height) : SkCanvas(width, height) {}
1101 int getDrawCounter() { return mDrawCounter; }
1102
1103 virtual void onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) override {
1104 // Do not expect this to be called. See RecordingCanvas.cpp DrawDrawable for context.
1105 EXPECT_TRUE(false);
1106 }
1107
1108 virtual void didTranslate(SkScalar dx, SkScalar dy) override {
1109 mDrawCounter++;
1110 EXPECT_EQ(dx, TRANSLATE_X);
1111 EXPECT_EQ(dy, TRANSLATE_Y);
1112 }
1113
1114 virtual void didSetM44(const SkM44& matrix) override {
1115 mDrawCounter++;
1116 // First invocation is EndReorderBarrierDrawable::drawShadow to apply shadow matrix.
1117 // Second invocation is preparing the matrix for an elevated RenderNodeDrawable.
1118 EXPECT_TRUE(matrix == SkM44());
1119 EXPECT_TRUE(getTotalMatrix().isIdentity());
1120 }
1121
1122 virtual void didConcat44(const SkM44& matrix) override {
1123 mDrawCounter++;
1124 if (mFirstDidConcat) {
1125 // First invocation is EndReorderBarrierDrawable::drawShadow to apply shadow matrix.
1126 mFirstDidConcat = false;
1127 EXPECT_EQ(SkM44::Translate(CASTER_X + TRANSLATE_X, CASTER_Y + TRANSLATE_Y),
1128 matrix);
1129 EXPECT_EQ(SkMatrix::Translate(CASTER_X + TRANSLATE_X, CASTER_Y + TRANSLATE_Y),
1130 getTotalMatrix());
1131 } else {
1132 // Second invocation is preparing the matrix for an elevated RenderNodeDrawable.
1133 EXPECT_EQ(SkM44::Translate(TRANSLATE_X, TRANSLATE_Y), matrix);
1134 EXPECT_EQ(SkMatrix::Translate(TRANSLATE_X, TRANSLATE_Y), getTotalMatrix());
1135 }
1136 }
1137
1138 protected:
1139 int mDrawCounter = 0;
1140
1141 private:
1142 bool mFirstDidConcat = true;
1143 };
1144
1145 auto parent = TestUtils::createSkiaNode(
1146 0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
1147 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
1148 canvas.translate(TRANSLATE_X, TRANSLATE_Y);
1149 canvas.enableZ(true);
1150
1151 auto node = TestUtils::createSkiaNode(
1152 CASTER_X, CASTER_Y, CASTER_X + CASTER_WIDTH, CASTER_Y + CASTER_HEIGHT,
1153 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
1154 props.setElevation(42);
1155 props.mutableOutline().setRoundRect(0, 0, 20, 20, 5, 1);
1156 props.mutableOutline().setShouldClip(true);
1157 });
1158 canvas.drawRenderNode(node.get());
1159 canvas.enableZ(false);
1160 });
1161
1162 // create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection
1163 ShadowTestCanvas canvas(CANVAS_WIDTH, CANVAS_HEIGHT);
1164 RenderNodeDrawable drawable(parent.get(), &canvas, false);
1165 drawable.draw(&canvas);
1166 EXPECT_EQ(5, canvas.getDrawCounter());
1167 }
1168
1169 // Draw a vector drawable twice but with different bounds and verify correct bounds are used.
RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaRecordingCanvas,drawVectorDrawable)1170 RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaRecordingCanvas, drawVectorDrawable) {
1171 static const int CANVAS_WIDTH = 100;
1172 static const int CANVAS_HEIGHT = 200;
1173 class VectorDrawableTestCanvas : public TestCanvasBase {
1174 public:
1175 VectorDrawableTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {}
1176 void onDrawImageRect2(const SkImage*, const SkRect& src, const SkRect& dst,
1177 const SkSamplingOptions&, const SkPaint* paint,
1178 SrcRectConstraint constraint) override {
1179 const int index = mDrawCounter++;
1180 switch (index) {
1181 case 0:
1182 EXPECT_EQ(dst, SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT));
1183 break;
1184 case 1:
1185 EXPECT_EQ(dst, SkRect::MakeWH(CANVAS_WIDTH / 2, CANVAS_HEIGHT));
1186 break;
1187 default:
1188 ADD_FAILURE();
1189 }
1190 }
1191 };
1192
1193 VectorDrawable::Group* group = new VectorDrawable::Group();
1194 sp<VectorDrawableRoot> vectorDrawable(new VectorDrawableRoot(group));
1195 vectorDrawable->mutateStagingProperties()->setScaledSize(CANVAS_WIDTH / 10, CANVAS_HEIGHT / 10);
1196
1197 auto node =
1198 TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
1199 [&](RenderProperties& props, SkiaRecordingCanvas& canvas) {
1200 vectorDrawable->mutateStagingProperties()->setBounds(
1201 SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT));
1202 canvas.drawVectorDrawable(vectorDrawable.get());
1203 vectorDrawable->mutateStagingProperties()->setBounds(
1204 SkRect::MakeWH(CANVAS_WIDTH / 2, CANVAS_HEIGHT));
1205 canvas.drawVectorDrawable(vectorDrawable.get());
1206 });
1207
1208 VectorDrawableTestCanvas canvas;
1209 RenderNodeDrawable drawable(node.get(), &canvas, true);
1210 canvas.drawDrawable(&drawable);
1211 EXPECT_EQ(2, canvas.mDrawCounter);
1212 }
1213