RenderNodeDrawableTests.cpp revision 68885e38b86405b333e3f8fd4ff0a104889147c4
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
36using namespace android;
37using namespace android::uirenderer;
38using namespace android::uirenderer::renderthread;
39using namespace android::uirenderer::skiapipeline;
40
41TEST(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    auto skLiteDL = SkLiteDL::New(SkRect::MakeWH(1, 1));
48    SkLiteRecorder canvas;
49    canvas.reset(skLiteDL.get());
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
58namespace {
59
60static 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
67static 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
76static 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
88class ZReorderCanvas : public SkCanvas {
89public:
90    ZReorderCanvas(int width, int height) : SkCanvas(width, height) {}
91    void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
92        int expectedOrder = SkColorGetB(paint.getColor()); // extract order from blue channel
93        EXPECT_EQ(expectedOrder, mDrawCounter++) << "An op was drawn out of order";
94    }
95    int getIndex() { return mDrawCounter; }
96protected:
97    int mDrawCounter = 0;
98};
99
100} // end anonymous namespace
101
102TEST(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
137TEST(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
170namespace {
171static SkRect getRecorderClipBounds(const SkiaRecordingCanvas& recorder) {
172    SkRect clipBounds;
173    recorder.getClipBounds(&clipBounds);
174    return clipBounds;
175}
176
177static SkMatrix getRecorderMatrix(const SkiaRecordingCanvas& recorder) {
178    SkMatrix matrix;
179    recorder.getMatrix(&matrix);
180    return matrix;
181}
182}
183
184TEST(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
223namespace {
224class ContextFactory : public IContextFactory {
225public:
226    virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) override {
227        return new AnimationContext(clock);
228    }
229};
230} // end anonymous namespace
231
232RENDERTHREAD_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    info.observer = nullptr;
325    parent->prepareTree(info);
326
327    //parent(A)             -> (receiverBackground, child)
328    //child(C)              -> (rect[0, 0, 100, 50], projectingRipple)
329    //projectingRipple(P)   -> (rect[-10, -10, 60, 60]) -> projects backwards
330    //receiverBackground(B) -> (rect[0, 0, 100, 100]) -> projection receiver
331
332    //create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection
333    ProjectionTestCanvas canvas(100, 100);
334    RenderNodeDrawable drawable(parent.get(), &canvas, true);
335    canvas.drawDrawable(&drawable);
336    EXPECT_EQ(3, canvas.getIndex());
337}
338
339RENDERTHREAD_TEST(RenderNodeDrawable, projectionHwLayer) {
340    /* R is backward projected on B and C is a layer.
341                A
342               / \
343              B   C
344                  |
345                  R
346    */
347    static const int SCROLL_X = 5;
348    static const int SCROLL_Y = 10;
349    static const int CANVAS_WIDTH = 400;
350    static const int CANVAS_HEIGHT = 400;
351    static const int LAYER_WIDTH = 200;
352    static const int LAYER_HEIGHT = 200;
353    class ProjectionTestCanvas : public SkCanvas {
354    public:
355        ProjectionTestCanvas(int* drawCounter)
356            : SkCanvas(CANVAS_WIDTH, CANVAS_HEIGHT)
357            , mDrawCounter(drawCounter)
358        {}
359        void onDrawArc(const SkRect&, SkScalar startAngle, SkScalar sweepAngle, bool useCenter,
360                const SkPaint&) override {
361            EXPECT_EQ(0, (*mDrawCounter)++); //part of painting the layer
362            EXPECT_EQ(SkRect::MakeLTRB(0, 0, LAYER_WIDTH, LAYER_HEIGHT), TestUtils::getClipBounds(this));
363        }
364        void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
365            EXPECT_EQ(1, (*mDrawCounter)++);
366            EXPECT_EQ(SkRect::MakeLTRB(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT), TestUtils::getClipBounds(this));
367        }
368        void onDrawOval(const SkRect&, const SkPaint&) override {
369            EXPECT_EQ(2, (*mDrawCounter)++);
370            SkMatrix expectedMatrix;
371            expectedMatrix.setTranslate(100 - SCROLL_X, 100 - SCROLL_Y);
372            EXPECT_EQ(expectedMatrix, getTotalMatrix());
373            EXPECT_EQ(SkRect::MakeLTRB(-85, -80, 295, 300), TestUtils::getLocalClipBounds(this));
374        }
375        int* mDrawCounter;
376    };
377
378    class ProjectionLayer : public SkSurface_Base {
379    public:
380        ProjectionLayer(int* drawCounter)
381            : SkSurface_Base(SkImageInfo::MakeN32Premul(LAYER_WIDTH, LAYER_HEIGHT), nullptr)
382            , mDrawCounter(drawCounter) {
383        }
384        void onDraw(SkCanvas*, SkScalar x, SkScalar y, const SkPaint*) override {
385            EXPECT_EQ(3, (*mDrawCounter)++);
386            EXPECT_EQ(SkRect::MakeLTRB(100 - SCROLL_X, 100 - SCROLL_Y, 300 - SCROLL_X,
387                   300 - SCROLL_Y), TestUtils::getClipBounds(this->getCanvas()));
388        }
389        SkCanvas* onNewCanvas() override {
390            return new ProjectionTestCanvas(mDrawCounter);
391        }
392        sk_sp<SkSurface> onNewSurface(const SkImageInfo&) override {
393            return sk_sp<SkSurface>();
394        }
395        sk_sp<SkImage> onNewImageSnapshot(SkBudgeted, SkCopyPixelsMode) override {
396            return sk_sp<SkImage>();
397        }
398        void onCopyOnWrite(ContentChangeMode) override {}
399        int* mDrawCounter;
400    };
401
402    auto receiverBackground = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
403            [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
404        properties.setProjectionReceiver(true);
405        // scroll doesn't apply to background, so undone via translationX/Y
406        // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
407        properties.setTranslationX(SCROLL_X);
408        properties.setTranslationY(SCROLL_Y);
409
410        canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, SkPaint());
411    }, "B"); //B
412    auto projectingRipple = TestUtils::createSkiaNode(0, 0, LAYER_WIDTH, LAYER_HEIGHT,
413            [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
414        properties.setProjectBackwards(true);
415        properties.setClipToBounds(false);
416        canvas.drawOval(100, 100, 300, 300, SkPaint()); // drawn mostly out of layer bounds
417    }, "R"); //R
418    auto child = TestUtils::createSkiaNode(100, 100, 300, 300,
419            [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
420        canvas.drawRenderNode(projectingRipple.get());
421        canvas.drawArc(0, 0, LAYER_WIDTH, LAYER_HEIGHT, 0.0f, 280.0f, true, SkPaint());
422    }, "C"); //C
423    auto parent = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
424            [&receiverBackground, &child](RenderProperties& properties,
425            SkiaRecordingCanvas& canvas) {
426        // Set a rect outline for the projecting ripple to be masked against.
427        properties.mutableOutline().setRoundRect(10, 10, 390, 390, 0, 1.0f);
428        canvas.translate(-SCROLL_X, -SCROLL_Y); // Apply scroll (note: bg undoes this internally)
429        canvas.drawRenderNode(receiverBackground.get());
430        canvas.drawRenderNode(child.get());
431    }, "A"); //A
432
433    //prepareTree is required to find, which receivers have backward projected nodes
434    ContextFactory contextFactory;
435    std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create(
436            renderThread, false, parent.get(), &contextFactory));
437    TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
438    DamageAccumulator damageAccumulator;
439    info.damageAccumulator = &damageAccumulator;
440    info.observer = nullptr;
441    parent->prepareTree(info);
442
443    int drawCounter = 0;
444    //set a layer after prepareTree to avoid layer logic there
445    child->animatorProperties().mutateLayerProperties().setType(LayerType::RenderLayer);
446    sk_sp<SkSurface> surfaceLayer1(new ProjectionLayer(&drawCounter));
447    child->setLayerSurface(surfaceLayer1);
448    Matrix4 windowTransform;
449    windowTransform.loadTranslate(100, 100, 0);
450    child->getSkiaLayer()->inverseTransformInWindow.loadInverse(windowTransform);
451
452    LayerUpdateQueue layerUpdateQueue;
453    layerUpdateQueue.enqueueLayerWithDamage(child.get(),
454            android::uirenderer::Rect(LAYER_WIDTH, LAYER_HEIGHT));
455    SkiaPipeline::renderLayersImpl(layerUpdateQueue, true);
456    EXPECT_EQ(1, drawCounter);  //assert index 0 is drawn on the layer
457
458    RenderNodeDrawable drawable(parent.get(), surfaceLayer1->getCanvas(), true);
459    surfaceLayer1->getCanvas()->drawDrawable(&drawable);
460    EXPECT_EQ(4, drawCounter);
461
462    // clean up layer pointer, so we can safely destruct RenderNode
463    child->setLayerSurface(nullptr);
464}
465
466RENDERTHREAD_TEST(RenderNodeDrawable, projectionChildScroll) {
467    /* R is backward projected on B.
468                A
469               / \
470              B   C
471                  |
472                  R
473    */
474    static const int SCROLL_X = 500000;
475    static const int SCROLL_Y = 0;
476    static const int CANVAS_WIDTH = 400;
477    static const int CANVAS_HEIGHT = 400;
478    class ProjectionChildScrollTestCanvas : public SkCanvas {
479    public:
480        ProjectionChildScrollTestCanvas() : SkCanvas(CANVAS_WIDTH, CANVAS_HEIGHT) {}
481        void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
482            EXPECT_EQ(0, mDrawCounter++);
483            EXPECT_TRUE(getTotalMatrix().isIdentity());
484        }
485        void onDrawOval(const SkRect&, const SkPaint&) override {
486            EXPECT_EQ(1, mDrawCounter++);
487            EXPECT_EQ(SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT), TestUtils::getClipBounds(this));
488            EXPECT_TRUE(getTotalMatrix().isIdentity());
489        }
490        int mDrawCounter = 0;
491    };
492
493    auto receiverBackground = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
494            [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
495        properties.setProjectionReceiver(true);
496        canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, SkPaint());
497    }, "B"); //B
498    auto projectingRipple = TestUtils::createSkiaNode(0, 0, 200, 200,
499            [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
500        // scroll doesn't apply to background, so undone via translationX/Y
501        // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
502        properties.setTranslationX(SCROLL_X);
503        properties.setTranslationY(SCROLL_Y);
504        properties.setProjectBackwards(true);
505        properties.setClipToBounds(false);
506        canvas.drawOval(0, 0, 200, 200, SkPaint());
507    }, "R"); //R
508    auto child = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
509            [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
510        // Record time clip will be ignored by projectee
511        canvas.clipRect(100, 100, 300, 300, SkClipOp::kIntersect);
512
513        canvas.translate(-SCROLL_X, -SCROLL_Y); // Apply scroll (note: bg undoes this internally)
514        canvas.drawRenderNode(projectingRipple.get());
515    }, "C"); //C
516    auto parent = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
517            [&receiverBackground, &child](RenderProperties& properties,
518            SkiaRecordingCanvas& canvas) {
519        canvas.drawRenderNode(receiverBackground.get());
520        canvas.drawRenderNode(child.get());
521    }, "A"); //A
522
523    //prepareTree is required to find, which receivers have backward projected nodes
524    ContextFactory contextFactory;
525    std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create(
526            renderThread, false, parent.get(), &contextFactory));
527    TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
528    DamageAccumulator damageAccumulator;
529    info.damageAccumulator = &damageAccumulator;
530    info.observer = nullptr;
531    parent->prepareTree(info);
532
533    std::unique_ptr<ProjectionChildScrollTestCanvas> canvas(new ProjectionChildScrollTestCanvas());
534    RenderNodeDrawable drawable(parent.get(), canvas.get(), true);
535    canvas->drawDrawable(&drawable);
536    EXPECT_EQ(2, canvas->mDrawCounter);
537}
538
539namespace {
540static int drawNode(RenderThread& renderThread, const sp<RenderNode>& renderNode)
541{
542    ContextFactory contextFactory;
543    std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create(
544            renderThread, false, renderNode.get(), &contextFactory));
545    TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
546    DamageAccumulator damageAccumulator;
547    info.damageAccumulator = &damageAccumulator;
548    info.observer = nullptr;
549    renderNode->prepareTree(info);
550
551    //create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection
552    ZReorderCanvas canvas(100, 100);
553    RenderNodeDrawable drawable(renderNode.get(), &canvas, false);
554    canvas.drawDrawable(&drawable);
555    return canvas.getIndex();
556}
557}
558
559RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectedInMiddle) {
560    /* R is backward projected on B
561                A
562               / \
563              B   C
564                  |
565                  R
566    */
567    auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
568            [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
569        drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
570            props.setProjectionReceiver(true);
571        } ); //nodeB
572        drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
573            drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
574                props.setProjectBackwards(true);
575                props.setClipToBounds(false);
576            } ); //nodeR
577        } ); //nodeC
578    }); //nodeA
579    EXPECT_EQ(3, drawNode(renderThread, nodeA));
580}
581
582RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectLast) {
583    /* R is backward projected on E
584                  A
585                / | \
586               /  |  \
587              B   C   E
588                  |
589                  R
590    */
591    auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
592            [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
593        drawOrderedNode(&canvas, 0, nullptr); //nodeB
594        drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
595            drawOrderedNode(&canvas, 3, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //drawn as 2
596                props.setProjectBackwards(true);
597                props.setClipToBounds(false);
598            } ); //nodeR
599        } ); //nodeC
600        drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //drawn as 3
601            props.setProjectionReceiver(true);
602        } ); //nodeE
603    }); //nodeA
604    EXPECT_EQ(4, drawNode(renderThread, nodeA));
605}
606
607RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderNoReceivable) {
608    /* R is backward projected without receiver
609                A
610               / \
611              B   C
612                  |
613                  R
614    */
615     auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
616            [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
617        drawOrderedNode(&canvas, 0, nullptr); //nodeB
618        drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
619            drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
620                //not having a projection receiver is an undefined behavior
621                props.setProjectBackwards(true);
622                props.setClipToBounds(false);
623            } ); //nodeR
624        } ); //nodeC
625    }); //nodeA
626    EXPECT_EQ(2, drawNode(renderThread, nodeA));
627}
628
629RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderParentReceivable) {
630    /* R is backward projected on C
631                A
632               / \
633              B   C
634                  |
635                  R
636    */
637     auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
638            [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
639        drawOrderedNode(&canvas, 0, nullptr); //nodeB
640        drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
641            props.setProjectionReceiver(true);
642            drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
643                props.setProjectBackwards(true);
644                props.setClipToBounds(false);
645            } ); //nodeR
646        } ); //nodeC
647    }); //nodeA
648    EXPECT_EQ(3, drawNode(renderThread, nodeA));
649}
650
651RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderSameNodeReceivable) {
652    /* R is backward projected on R
653                A
654               / \
655              B   C
656                  |
657                  R
658    */
659     auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
660            [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
661        drawOrderedNode(&canvas, 0, nullptr); //nodeB
662        drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
663            drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
664                //having a node that is projected on itself is an undefined/unexpected behavior
665                props.setProjectionReceiver(true);
666                props.setProjectBackwards(true);
667                props.setClipToBounds(false);
668            } ); //nodeR
669        } ); //nodeC
670    }); //nodeA
671    EXPECT_EQ(2, drawNode(renderThread, nodeA));
672}
673
674//Note: the outcome for this test is different in HWUI
675RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectedSibling) {
676    /* R is set to project on B, but R is not drawn because projecting on a sibling is not allowed.
677                A
678               /|\
679              / | \
680             B  C  R
681    */
682    auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
683            [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
684        drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
685            props.setProjectionReceiver(true);
686        } ); //nodeB
687        drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
688        } ); //nodeC
689        drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
690            props.setProjectBackwards(true);
691            props.setClipToBounds(false);
692        } ); //nodeR
693    }); //nodeA
694    EXPECT_EQ(2, drawNode(renderThread, nodeA));
695}
696
697RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectedSibling2) {
698    /* R is set to project on B, but R is not drawn because projecting on a sibling is not allowed.
699                A
700                |
701                G
702               /|\
703              / | \
704             B  C  R
705    */
706    auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
707            [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
708        drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
709            drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
710                props.setProjectionReceiver(true);
711            } ); //nodeB
712            drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
713            } ); //nodeC
714            drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
715                props.setProjectBackwards(true);
716                props.setClipToBounds(false);
717            } ); //nodeR
718        } ); //nodeG
719    }); //nodeA
720    EXPECT_EQ(3, drawNode(renderThread, nodeA));
721}
722
723RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderGrandparentReceivable) {
724    /* R is backward projected on B
725                A
726                |
727                B
728                |
729                C
730                |
731                R
732    */
733    auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
734            [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
735        drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
736            props.setProjectionReceiver(true);
737            drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
738                drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
739                    props.setProjectBackwards(true);
740                    props.setClipToBounds(false);
741                } ); //nodeR
742            } ); //nodeC
743        } ); //nodeB
744    }); //nodeA
745    EXPECT_EQ(3, drawNode(renderThread, nodeA));
746}
747
748RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderTwoReceivables) {
749    /* B and G are receivables, R is backward projected
750                A
751               / \
752              B   C
753                 / \
754                G   R
755    */
756    auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
757            [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
758        drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //B
759            props.setProjectionReceiver(true);
760        } ); //nodeB
761        drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //C
762            drawOrderedNode(&canvas, 3, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //G
763                props.setProjectionReceiver(true);
764            } ); //nodeG
765            drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //R
766                props.setProjectBackwards(true);
767                props.setClipToBounds(false);
768            } ); //nodeR
769        } ); //nodeC
770    }); //nodeA
771    EXPECT_EQ(4, drawNode(renderThread, nodeA));
772}
773
774RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderTwoReceivablesLikelyScenario) {
775    /* B and G are receivables, G is backward projected
776                A
777               / \
778              B   C
779                 / \
780                G   R
781    */
782    auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
783            [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
784        drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //B
785            props.setProjectionReceiver(true);
786        } ); //nodeB
787        drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //C
788            drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //G
789                props.setProjectionReceiver(true);
790                props.setProjectBackwards(true);
791                props.setClipToBounds(false);
792            } ); //nodeG
793            drawOrderedNode(&canvas, 3, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //R
794            } ); //nodeR
795        } ); //nodeC
796    }); //nodeA
797    EXPECT_EQ(4, drawNode(renderThread, nodeA));
798}
799
800RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderTwoReceivablesDeeper) {
801    /* B and G are receivables, R is backward projected
802                A
803               / \
804              B   C
805                 / \
806                G   D
807                    |
808                    R
809    */
810    auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
811            [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
812        drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //B
813            props.setProjectionReceiver(true);
814        } ); //nodeB
815        drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //C
816            drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //G
817                props.setProjectionReceiver(true);
818            } ); //nodeG
819            drawOrderedNode(&canvas, 4, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //D
820                drawOrderedNode(&canvas, 3, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //R
821                    props.setProjectBackwards(true);
822                    props.setClipToBounds(false);
823                } ); //nodeR
824            } ); //nodeD
825        } ); //nodeC
826    }); //nodeA
827    EXPECT_EQ(5, drawNode(renderThread, nodeA));
828}
829
830RENDERTHREAD_TEST(RenderNodeDrawable, simple) {
831    static const int CANVAS_WIDTH = 100;
832    static const int CANVAS_HEIGHT = 200;
833    class SimpleTestCanvas : public TestCanvasBase {
834    public:
835        SimpleTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {
836        }
837        void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
838            EXPECT_EQ(0, mDrawCounter++);
839        }
840        void onDrawImage(const SkImage*, SkScalar dx, SkScalar dy, const SkPaint*) override {
841            EXPECT_EQ(1, mDrawCounter++);
842        }
843    };
844
845    auto node = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
846            [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
847        sk_sp<Bitmap> bitmap(TestUtils::createBitmap(25, 25));
848        canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, SkPaint());
849        canvas.drawBitmap(*bitmap, 10, 10, nullptr);
850    });
851
852    SimpleTestCanvas canvas;
853    RenderNodeDrawable drawable(node.get(), &canvas, true);
854    canvas.drawDrawable(&drawable);
855    EXPECT_EQ(2, canvas.mDrawCounter);
856}
857
858RENDERTHREAD_TEST(RenderNodeDrawable, colorOp_unbounded) {
859    static const int CANVAS_WIDTH = 200;
860    static const int CANVAS_HEIGHT = 200;
861    class ColorTestCanvas : public TestCanvasBase {
862    public:
863        ColorTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {
864        }
865        void onDrawPaint(const SkPaint&) {
866            switch (mDrawCounter++) {
867            case 0:
868                // While this mirrors FrameBuilder::colorOp_unbounded, this value is different
869                // because there is no root (root is clipped in SkiaPipeline::renderFrame).
870                // SkiaPipeline.clipped and clip_replace verify the root clip.
871                EXPECT_TRUE(TestUtils::getClipBounds(this).isEmpty());
872                break;
873            case 1:
874                EXPECT_EQ(SkRect::MakeWH(10, 10), TestUtils::getClipBounds(this));
875                break;
876            default:
877                ADD_FAILURE();
878            }
879        }
880    };
881
882    auto unclippedColorView = TestUtils::createSkiaNode(0, 0, 10, 10,
883            [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
884        props.setClipToBounds(false);
885        canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
886    });
887
888    auto clippedColorView = TestUtils::createSkiaNode(0, 0, 10, 10,
889            [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
890        canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
891    });
892
893    ColorTestCanvas canvas;
894    RenderNodeDrawable drawable(unclippedColorView.get(), &canvas, true);
895    canvas.drawDrawable(&drawable);
896    EXPECT_EQ(1, canvas.mDrawCounter);
897    RenderNodeDrawable drawable2(clippedColorView.get(), &canvas, true);
898    canvas.drawDrawable(&drawable2);
899    EXPECT_EQ(2, canvas.mDrawCounter);
900}
901
902TEST(RenderNodeDrawable, renderNode) {
903    static const int CANVAS_WIDTH = 200;
904    static const int CANVAS_HEIGHT = 200;
905    class RenderNodeTestCanvas : public TestCanvasBase {
906    public:
907        RenderNodeTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {
908        }
909        void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
910            switch(mDrawCounter++) {
911            case 0:
912                EXPECT_EQ(SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT), TestUtils::getClipBounds(this));
913                EXPECT_EQ(SK_ColorDKGRAY, paint.getColor());
914                break;
915            case 1:
916                EXPECT_EQ(SkRect::MakeLTRB(50, 50, 150, 150), TestUtils::getClipBounds(this));
917                EXPECT_EQ(SK_ColorWHITE, paint.getColor());
918                break;
919            default:
920                ADD_FAILURE();
921            }
922        }
923    };
924
925    auto child = TestUtils::createSkiaNode(10, 10, 110, 110,
926            [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
927        SkPaint paint;
928        paint.setColor(SK_ColorWHITE);
929        canvas.drawRect(0, 0, 100, 100, paint);
930    });
931
932    auto parent = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
933            [&child](RenderProperties& props, SkiaRecordingCanvas& canvas) {
934        SkPaint paint;
935        paint.setColor(SK_ColorDKGRAY);
936        canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, paint);
937
938        canvas.save(SaveFlags::MatrixClip);
939        canvas.translate(40, 40);
940        canvas.drawRenderNode(child.get());
941        canvas.restore();
942    });
943
944    RenderNodeTestCanvas canvas;
945    RenderNodeDrawable drawable(parent.get(), &canvas, true);
946    canvas.drawDrawable(&drawable);
947    EXPECT_EQ(2, canvas.mDrawCounter);
948}
949
950