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