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 <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