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 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 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 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 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: 94 ZReorderCanvas(int width, int height) : SkCanvas(width, height) {} 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 } 99 int getIndex() { return mDrawCounter; } 100 101 protected: 102 int mDrawCounter = 0; 103 }; 104 105 } // end anonymous namespace 106 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 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 { 174 static SkRect getRecorderClipBounds(const SkiaRecordingCanvas& recorder) { 175 SkRect clipBounds; 176 recorder.getClipBounds(&clipBounds); 177 return clipBounds; 178 } 179 180 static SkMatrix getRecorderMatrix(const SkiaRecordingCanvas& recorder) { 181 SkMatrix matrix; 182 recorder.getMatrix(&matrix); 183 return matrix; 184 } 185 } 186 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: 228 virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) override { 229 return new AnimationContext(clock); 230 } 231 }; 232 } // end anonymous namespace 233 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 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 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 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 { 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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. 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 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. 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