Home | History | Annotate | Download | only in unit
      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 }