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