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