• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
TEST(RenderNodeDrawable,create)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 
drawOrderedRect(Canvas * canvas,uint8_t expectedDrawOrder)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 
drawOrderedNode(Canvas * canvas,uint8_t expectedDrawOrder,float z)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 
drawOrderedNode(Canvas * canvas,uint8_t expectedDrawOrder,std::function<void (RenderProperties & props,SkiaRecordingCanvas & canvas)> setup)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:
ZReorderCanvas(int width,int height)90     ZReorderCanvas(int width, int height) : SkCanvas(width, height) {}
onDrawRect(const SkRect & rect,const SkPaint & paint)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     }
getIndex()95     int getIndex() { return mDrawCounter; }
96 protected:
97     int mDrawCounter = 0;
98 };
99 
100 } // end anonymous namespace
101 
TEST(RenderNodeDrawable,zReorder)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 
TEST(RenderNodeDrawable,composeOnLayer)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 {
getRecorderClipBounds(const SkiaRecordingCanvas & recorder)171 static SkRect getRecorderClipBounds(const SkiaRecordingCanvas& recorder) {
172     SkRect clipBounds;
173     recorder.getClipBounds(&clipBounds);
174     return clipBounds;
175 }
176 
getRecorderMatrix(const SkiaRecordingCanvas & recorder)177 static SkMatrix getRecorderMatrix(const SkiaRecordingCanvas& recorder) {
178     SkMatrix matrix;
179     recorder.getMatrix(&matrix);
180     return matrix;
181 }
182 }
183 
TEST(RenderNodeDrawable,saveLayerClipAndMatrixRestore)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:
createAnimationContext(renderthread::TimeLord & clock)226     virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) override {
227         return new AnimationContext(clock);
228     }
229 };
230 } // end anonymous namespace
231 
RENDERTHREAD_TEST(RenderNodeDrawable,projectionReorder)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 
RENDERTHREAD_TEST(RenderNodeDrawable,projectionHwLayer)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 
RENDERTHREAD_TEST(RenderNodeDrawable,projectionChildScroll)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 {
drawNode(RenderThread & renderThread,const sp<RenderNode> & renderNode)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 
RENDERTHREAD_TEST(RenderNodeDrawable,projectionReorderProjectedInMiddle)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 
RENDERTHREAD_TEST(RenderNodeDrawable,projectionReorderProjectLast)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 
RENDERTHREAD_TEST(RenderNodeDrawable,projectionReorderNoReceivable)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 
RENDERTHREAD_TEST(RenderNodeDrawable,projectionReorderParentReceivable)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 
RENDERTHREAD_TEST(RenderNodeDrawable,projectionReorderSameNodeReceivable)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
RENDERTHREAD_TEST(RenderNodeDrawable,projectionReorderProjectedSibling)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 
RENDERTHREAD_TEST(RenderNodeDrawable,projectionReorderProjectedSibling2)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 
RENDERTHREAD_TEST(RenderNodeDrawable,projectionReorderGrandparentReceivable)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 
RENDERTHREAD_TEST(RenderNodeDrawable,projectionReorderTwoReceivables)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 
RENDERTHREAD_TEST(RenderNodeDrawable,projectionReorderTwoReceivablesLikelyScenario)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 
RENDERTHREAD_TEST(RenderNodeDrawable,projectionReorderTwoReceivablesDeeper)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 
RENDERTHREAD_TEST(RenderNodeDrawable,simple)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 
RENDERTHREAD_TEST(RenderNodeDrawable,colorOp_unbounded)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 
TEST(RenderNodeDrawable,renderNode)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 
TEST(ReorderBarrierDrawable,testShadowMatrix)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 }