blob: 4d4705c0685a6bcd49eebf68fa2ef1e7157d702f [file] [log] [blame]
Stan Iliev021693b2016-10-17 16:26:15 -04001/*
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"
Stan Ilievdb45a4b2016-11-08 14:18:31 -050024#include "pipeline/skia/SkiaPipeline.h"
Stan Iliev021693b2016-10-17 16:26:15 -040025#include "pipeline/skia/SkiaRecordingCanvas.h"
26#include "renderthread/CanvasContext.h"
27#include "tests/common/TestUtils.h"
28#include "SkiaCanvas.h"
Stan Ilievdb45a4b2016-11-08 14:18:31 -050029#include <SkSurface_Base.h>
Stan Iliev021693b2016-10-17 16:26:15 -040030#include <SkLiteRecorder.h>
Stan Ilievdb45a4b2016-11-08 14:18:31 -050031#include <SkClipStack.h>
Stan Iliev52771272016-11-17 09:54:38 -050032#include "FatalTestCanvas.h"
Stan Iliev021693b2016-10-17 16:26:15 -040033#include <string.h>
34
35
36using namespace android;
37using namespace android::uirenderer;
38using namespace android::uirenderer::renderthread;
39using namespace android::uirenderer::skiapipeline;
40
Stan Iliev021693b2016-10-17 16:26:15 -040041TEST(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
Stan Ilievdb45a4b2016-11-08 14:18:31 -050058namespace {
59
Stan Iliev2f06e8a2016-11-02 15:29:03 -040060static 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);
Stan Iliev021693b2016-10-17 16:26:15 -040065}
66
Stan Iliev2f06e8a2016-11-02 15:29:03 -040067static 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}
Stan Iliev021693b2016-10-17 16:26:15 -040075
Stan Ilievdb45a4b2016-11-08 14:18:31 -050076static 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);
Stan Iliev2f06e8a2016-11-02 15:29:03 -040083 }
Stan Ilievdb45a4b2016-11-08 14:18:31 -050084 });
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
Stan Iliev52771272016-11-17 09:54:38 -050093 EXPECT_EQ(expectedOrder, mDrawCounter++) << "An op was drawn out of order";
Stan Ilievdb45a4b2016-11-08 14:18:31 -050094 }
Stan Iliev52771272016-11-17 09:54:38 -050095 int getIndex() { return mDrawCounter; }
Stan Ilievdb45a4b2016-11-08 14:18:31 -050096protected:
Stan Iliev52771272016-11-17 09:54:38 -050097 int mDrawCounter = 0;
Stan Ilievdb45a4b2016-11-08 14:18:31 -050098};
99
100} // end anonymous namespace
101
102TEST(RenderNodeDrawable, zReorder) {
Stan Iliev021693b2016-10-17 16:26:15 -0400103
Stan Iliev2f06e8a2016-11-02 15:29:03 -0400104 auto parent = TestUtils::createSkiaNode(0, 0, 100, 100,
105 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
Stan Iliev347691f2016-12-01 12:25:07 -0500106 canvas.insertReorderBarrier(true);
107 canvas.insertReorderBarrier(false);
Stan Iliev2f06e8a2016-11-02 15:29:03 -0400108 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
Stan Iliev88e08912016-11-22 18:19:29 -0500120 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);
Stan Iliev2f06e8a2016-11-02 15:29:03 -0400128 });
Stan Iliev021693b2016-10-17 16:26:15 -0400129
Stan Iliev2f06e8a2016-11-02 15:29:03 -0400130 //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);
Stan Iliev88e08912016-11-22 18:19:29 -0500134 EXPECT_EQ(13, canvas.getIndex());
Stan Iliev021693b2016-10-17 16:26:15 -0400135}
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
Stan Iliev500a0c32016-10-26 10:30:09 -0400144 auto rootNode = TestUtils::createSkiaNode(0, 0, 1, 1,
Stan Iliev021693b2016-10-17 16:26:15 -0400145 [](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);
Stan Iliev500a0c32016-10-26 10:30:09 -0400153 rootNode->setLayerSurface(surfaceLayer);
Stan Iliev021693b2016-10-17 16:26:15 -0400154
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
Stan Iliev500a0c32016-10-26 10:30:09 -0400167 rootNode->setLayerSurface(sk_sp<SkSurface>());
Stan Iliev021693b2016-10-17 16:26:15 -0400168}
169
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500170namespace {
171class ContextFactory : public IContextFactory {
172public:
173 virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) override {
174 return new AnimationContext(clock);
175 }
176};
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500177} // end anonymous namespace
Stan Iliev021693b2016-10-17 16:26:15 -0400178
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500179RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorder) {
180 static const int SCROLL_X = 5;
181 static const int SCROLL_Y = 10;
182 class ProjectionTestCanvas : public SkCanvas {
183 public:
184 ProjectionTestCanvas(int width, int height) : SkCanvas(width, height) {}
185 void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
Stan Iliev52771272016-11-17 09:54:38 -0500186 const int index = mDrawCounter++;
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500187 SkMatrix expectedMatrix;;
188 switch (index) {
189 case 0: //this is node "B"
190 EXPECT_EQ(SkRect::MakeWH(100, 100), rect);
191 EXPECT_EQ(SK_ColorWHITE, paint.getColor());
192 expectedMatrix.reset();
Stan Iliev52771272016-11-17 09:54:38 -0500193 EXPECT_EQ(SkRect::MakeLTRB(0, 0, 100, 100), TestUtils::getClipBounds(this));
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500194 break;
195 case 1: //this is node "P"
196 EXPECT_EQ(SkRect::MakeLTRB(-10, -10, 60, 60), rect);
197 EXPECT_EQ(SK_ColorDKGRAY, paint.getColor());
198 expectedMatrix.setTranslate(50 - SCROLL_X, 50 - SCROLL_Y);
Stan Iliev52771272016-11-17 09:54:38 -0500199 EXPECT_EQ(SkRect::MakeLTRB(-35, -30, 45, 50), TestUtils::getLocalClipBounds(this));
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500200 break;
201 case 2: //this is node "C"
202 EXPECT_EQ(SkRect::MakeWH(100, 50), rect);
203 EXPECT_EQ(SK_ColorBLUE, paint.getColor());
204 expectedMatrix.setTranslate(-SCROLL_X, 50 - SCROLL_Y);
Stan Iliev52771272016-11-17 09:54:38 -0500205 EXPECT_EQ(SkRect::MakeLTRB(0, 40, 95, 90), TestUtils::getClipBounds(this));
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500206 break;
207 default:
208 ADD_FAILURE();
209 }
210 EXPECT_EQ(expectedMatrix, getTotalMatrix());
211 }
Stan Iliev021693b2016-10-17 16:26:15 -0400212
Stan Iliev52771272016-11-17 09:54:38 -0500213 int getIndex() { return mDrawCounter; }
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500214 protected:
Stan Iliev52771272016-11-17 09:54:38 -0500215 int mDrawCounter = 0;
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500216 };
Stan Iliev021693b2016-10-17 16:26:15 -0400217
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500218 /**
219 * Construct a tree of nodes, where the root (A) has a receiver background (B), and a child (C)
220 * with a projecting child (P) of its own. P would normally draw between B and C's "background"
221 * draw, but because it is projected backwards, it's drawn in between B and C.
222 *
223 * The parent is scrolled by SCROLL_X/SCROLL_Y, but this does not affect the background
224 * (which isn't affected by scroll).
225 */
226 auto receiverBackground = TestUtils::createSkiaNode(0, 0, 100, 100,
227 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
228 properties.setProjectionReceiver(true);
229 // scroll doesn't apply to background, so undone via translationX/Y
230 // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
231 properties.setTranslationX(SCROLL_X);
232 properties.setTranslationY(SCROLL_Y);
Stan Iliev021693b2016-10-17 16:26:15 -0400233
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500234 SkPaint paint;
235 paint.setColor(SK_ColorWHITE);
236 canvas.drawRect(0, 0, 100, 100, paint);
237 }, "B");
238
239 auto projectingRipple = TestUtils::createSkiaNode(50, 0, 100, 50,
240 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
241 properties.setProjectBackwards(true);
242 properties.setClipToBounds(false);
243 SkPaint paint;
244 paint.setColor(SK_ColorDKGRAY);
245 canvas.drawRect(-10, -10, 60, 60, paint);
246 }, "P");
247 auto child = TestUtils::createSkiaNode(0, 50, 100, 100,
248 [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
249 SkPaint paint;
250 paint.setColor(SK_ColorBLUE);
251 canvas.drawRect(0, 0, 100, 50, paint);
252 canvas.drawRenderNode(projectingRipple.get());
253 }, "C");
254 auto parent = TestUtils::createSkiaNode(0, 0, 100, 100,
255 [&receiverBackground, &child](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
256 // Set a rect outline for the projecting ripple to be masked against.
257 properties.mutableOutline().setRoundRect(10, 10, 90, 90, 5, 1.0f);
258
259 canvas.save(SaveFlags::MatrixClip);
260 canvas.translate(-SCROLL_X, -SCROLL_Y); // Apply scroll (note: bg undoes this internally)
261 canvas.drawRenderNode(receiverBackground.get());
262 canvas.drawRenderNode(child.get());
263 canvas.restore();
264 }, "A");
265 ContextFactory contextFactory;
266 std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create(
267 renderThread, false, parent.get(), &contextFactory));
268 TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
269 DamageAccumulator damageAccumulator;
270 info.damageAccumulator = &damageAccumulator;
271 info.observer = nullptr;
272 parent->prepareTree(info);
273
274 //parent(A) -> (receiverBackground, child)
275 //child(C) -> (rect[0, 0, 100, 50], projectingRipple)
276 //projectingRipple(P) -> (rect[-10, -10, 60, 60]) -> projects backwards
277 //receiverBackground(B) -> (rect[0, 0, 100, 100]) -> projection receiver
278
279 //create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection
280 ProjectionTestCanvas canvas(100, 100);
281 RenderNodeDrawable drawable(parent.get(), &canvas, true);
282 canvas.drawDrawable(&drawable);
283 EXPECT_EQ(3, canvas.getIndex());
284}
285
286RENDERTHREAD_TEST(RenderNodeDrawable, projectionHwLayer) {
287 /* R is backward projected on B and C is a layer.
288 A
289 / \
290 B C
291 |
292 R
293 */
294 static const int SCROLL_X = 5;
295 static const int SCROLL_Y = 10;
296 static const int CANVAS_WIDTH = 400;
297 static const int CANVAS_HEIGHT = 400;
298 static const int LAYER_WIDTH = 200;
299 static const int LAYER_HEIGHT = 200;
300 class ProjectionTestCanvas : public SkCanvas {
301 public:
Mike Reed6acfe162016-11-18 17:21:09 -0500302 ProjectionTestCanvas(int* drawCounter)
303 : SkCanvas(CANVAS_WIDTH, CANVAS_HEIGHT)
Stan Iliev52771272016-11-17 09:54:38 -0500304 , mDrawCounter(drawCounter)
Mike Reed6acfe162016-11-18 17:21:09 -0500305 {}
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500306 void onDrawArc(const SkRect&, SkScalar startAngle, SkScalar sweepAngle, bool useCenter,
307 const SkPaint&) override {
Mike Reed6acfe162016-11-18 17:21:09 -0500308 EXPECT_EQ(0, (*mDrawCounter)++); //part of painting the layer
Stan Iliev52771272016-11-17 09:54:38 -0500309 EXPECT_EQ(SkRect::MakeLTRB(0, 0, LAYER_WIDTH, LAYER_HEIGHT), TestUtils::getClipBounds(this));
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500310 }
311 void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
Mike Reed6acfe162016-11-18 17:21:09 -0500312 EXPECT_EQ(1, (*mDrawCounter)++);
Stan Iliev52771272016-11-17 09:54:38 -0500313 EXPECT_EQ(SkRect::MakeLTRB(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT), TestUtils::getClipBounds(this));
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500314 }
315 void onDrawOval(const SkRect&, const SkPaint&) override {
Mike Reed6acfe162016-11-18 17:21:09 -0500316 EXPECT_EQ(2, (*mDrawCounter)++);
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500317 SkMatrix expectedMatrix;
318 expectedMatrix.setTranslate(100 - SCROLL_X, 100 - SCROLL_Y);
319 EXPECT_EQ(expectedMatrix, getTotalMatrix());
Stan Iliev52771272016-11-17 09:54:38 -0500320 EXPECT_EQ(SkRect::MakeLTRB(-85, -80, 295, 300), TestUtils::getLocalClipBounds(this));
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500321 }
Mike Reed6acfe162016-11-18 17:21:09 -0500322 int* mDrawCounter;
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500323 };
324
325 class ProjectionLayer : public SkSurface_Base {
326 public:
Mike Reed6acfe162016-11-18 17:21:09 -0500327 ProjectionLayer(int* drawCounter)
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500328 : SkSurface_Base(SkImageInfo::MakeN32Premul(LAYER_WIDTH, LAYER_HEIGHT), nullptr)
Mike Reed6acfe162016-11-18 17:21:09 -0500329 , mDrawCounter(drawCounter) {
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500330 }
331 void onDraw(SkCanvas*, SkScalar x, SkScalar y, const SkPaint*) override {
Mike Reed6acfe162016-11-18 17:21:09 -0500332 EXPECT_EQ(3, (*mDrawCounter)++);
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500333 EXPECT_EQ(SkRect::MakeLTRB(100 - SCROLL_X, 100 - SCROLL_Y, 300 - SCROLL_X,
Stan Iliev52771272016-11-17 09:54:38 -0500334 300 - SCROLL_Y), TestUtils::getClipBounds(this->getCanvas()));
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500335 }
336 SkCanvas* onNewCanvas() override {
Mike Reed6acfe162016-11-18 17:21:09 -0500337 return new ProjectionTestCanvas(mDrawCounter);
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500338 }
339 sk_sp<SkSurface> onNewSurface(const SkImageInfo&) override {
340 return sk_sp<SkSurface>();
341 }
342 sk_sp<SkImage> onNewImageSnapshot(SkBudgeted, SkCopyPixelsMode) override {
343 return sk_sp<SkImage>();
344 }
345 void onCopyOnWrite(ContentChangeMode) override {}
Mike Reed6acfe162016-11-18 17:21:09 -0500346 int* mDrawCounter;
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500347 };
348
349 auto receiverBackground = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
350 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
351 properties.setProjectionReceiver(true);
352 // scroll doesn't apply to background, so undone via translationX/Y
353 // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
354 properties.setTranslationX(SCROLL_X);
355 properties.setTranslationY(SCROLL_Y);
356
357 canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, SkPaint());
358 }, "B"); //B
359 auto projectingRipple = TestUtils::createSkiaNode(0, 0, LAYER_WIDTH, LAYER_HEIGHT,
360 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
361 properties.setProjectBackwards(true);
362 properties.setClipToBounds(false);
363 canvas.drawOval(100, 100, 300, 300, SkPaint()); // drawn mostly out of layer bounds
364 }, "R"); //R
365 auto child = TestUtils::createSkiaNode(100, 100, 300, 300,
366 [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
367 canvas.drawRenderNode(projectingRipple.get());
368 canvas.drawArc(0, 0, LAYER_WIDTH, LAYER_HEIGHT, 0.0f, 280.0f, true, SkPaint());
369 }, "C"); //C
370 auto parent = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
371 [&receiverBackground, &child](RenderProperties& properties,
372 SkiaRecordingCanvas& canvas) {
373 // Set a rect outline for the projecting ripple to be masked against.
374 properties.mutableOutline().setRoundRect(10, 10, 390, 390, 0, 1.0f);
375 canvas.translate(-SCROLL_X, -SCROLL_Y); // Apply scroll (note: bg undoes this internally)
376 canvas.drawRenderNode(receiverBackground.get());
377 canvas.drawRenderNode(child.get());
378 }, "A"); //A
379
380 //prepareTree is required to find, which receivers have backward projected nodes
381 ContextFactory contextFactory;
382 std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create(
383 renderThread, false, parent.get(), &contextFactory));
384 TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
385 DamageAccumulator damageAccumulator;
386 info.damageAccumulator = &damageAccumulator;
387 info.observer = nullptr;
388 parent->prepareTree(info);
389
Mike Reed6acfe162016-11-18 17:21:09 -0500390 int drawCounter = 0;
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500391 //set a layer after prepareTree to avoid layer logic there
392 child->animatorProperties().mutateLayerProperties().setType(LayerType::RenderLayer);
Mike Reed6acfe162016-11-18 17:21:09 -0500393 sk_sp<SkSurface> surfaceLayer1(new ProjectionLayer(&drawCounter));
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500394 child->setLayerSurface(surfaceLayer1);
395 Matrix4 windowTransform;
396 windowTransform.loadTranslate(100, 100, 0);
397 child->getSkiaLayer()->inverseTransformInWindow.loadInverse(windowTransform);
398
399 LayerUpdateQueue layerUpdateQueue;
400 layerUpdateQueue.enqueueLayerWithDamage(child.get(),
401 android::uirenderer::Rect(LAYER_WIDTH, LAYER_HEIGHT));
402 SkiaPipeline::renderLayersImpl(layerUpdateQueue, true);
Mike Reed6acfe162016-11-18 17:21:09 -0500403 EXPECT_EQ(1, drawCounter); //assert index 0 is drawn on the layer
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500404
Mike Reed6acfe162016-11-18 17:21:09 -0500405 RenderNodeDrawable drawable(parent.get(), surfaceLayer1->getCanvas(), true);
406 surfaceLayer1->getCanvas()->drawDrawable(&drawable);
407 EXPECT_EQ(4, drawCounter);
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500408
409 // clean up layer pointer, so we can safely destruct RenderNode
410 child->setLayerSurface(nullptr);
411}
412
413RENDERTHREAD_TEST(RenderNodeDrawable, projectionChildScroll) {
414 /* R is backward projected on B.
415 A
416 / \
417 B C
418 |
419 R
420 */
421 static const int SCROLL_X = 500000;
422 static const int SCROLL_Y = 0;
423 static const int CANVAS_WIDTH = 400;
424 static const int CANVAS_HEIGHT = 400;
425 class ProjectionChildScrollTestCanvas : public SkCanvas {
426 public:
427 ProjectionChildScrollTestCanvas() : SkCanvas(CANVAS_WIDTH, CANVAS_HEIGHT) {}
428 void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
Stan Iliev52771272016-11-17 09:54:38 -0500429 EXPECT_EQ(0, mDrawCounter++);
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500430 EXPECT_TRUE(getTotalMatrix().isIdentity());
431 }
432 void onDrawOval(const SkRect&, const SkPaint&) override {
Stan Iliev52771272016-11-17 09:54:38 -0500433 EXPECT_EQ(1, mDrawCounter++);
434 EXPECT_EQ(SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT), TestUtils::getClipBounds(this));
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500435 EXPECT_TRUE(getTotalMatrix().isIdentity());
436 }
Stan Iliev52771272016-11-17 09:54:38 -0500437 int mDrawCounter = 0;
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500438 };
439
440 auto receiverBackground = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
441 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
442 properties.setProjectionReceiver(true);
443 canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, SkPaint());
444 }, "B"); //B
445 auto projectingRipple = TestUtils::createSkiaNode(0, 0, 200, 200,
446 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
447 // scroll doesn't apply to background, so undone via translationX/Y
448 // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
449 properties.setTranslationX(SCROLL_X);
450 properties.setTranslationY(SCROLL_Y);
451 properties.setProjectBackwards(true);
452 properties.setClipToBounds(false);
453 canvas.drawOval(0, 0, 200, 200, SkPaint());
454 }, "R"); //R
455 auto child = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
456 [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
457 // Record time clip will be ignored by projectee
Mike Reed6e49c9f2016-12-02 15:36:59 -0500458 canvas.clipRect(100, 100, 300, 300, kIntersect_SkClipOp);
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500459
460 canvas.translate(-SCROLL_X, -SCROLL_Y); // Apply scroll (note: bg undoes this internally)
461 canvas.drawRenderNode(projectingRipple.get());
462 }, "C"); //C
463 auto parent = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
464 [&receiverBackground, &child](RenderProperties& properties,
465 SkiaRecordingCanvas& canvas) {
466 canvas.drawRenderNode(receiverBackground.get());
467 canvas.drawRenderNode(child.get());
468 }, "A"); //A
469
470 //prepareTree is required to find, which receivers have backward projected nodes
471 ContextFactory contextFactory;
472 std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create(
473 renderThread, false, parent.get(), &contextFactory));
474 TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
475 DamageAccumulator damageAccumulator;
476 info.damageAccumulator = &damageAccumulator;
477 info.observer = nullptr;
478 parent->prepareTree(info);
479
Mike Reed6acfe162016-11-18 17:21:09 -0500480 std::unique_ptr<ProjectionChildScrollTestCanvas> canvas(new ProjectionChildScrollTestCanvas());
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500481 RenderNodeDrawable drawable(parent.get(), canvas.get(), true);
482 canvas->drawDrawable(&drawable);
Stan Iliev52771272016-11-17 09:54:38 -0500483 EXPECT_EQ(2, canvas->mDrawCounter);
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500484}
485
486namespace {
487static int drawNode(RenderThread& renderThread, const sp<RenderNode>& renderNode)
488{
489 ContextFactory contextFactory;
490 std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create(
491 renderThread, false, renderNode.get(), &contextFactory));
492 TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
493 DamageAccumulator damageAccumulator;
494 info.damageAccumulator = &damageAccumulator;
495 info.observer = nullptr;
496 renderNode->prepareTree(info);
497
498 //create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection
499 ZReorderCanvas canvas(100, 100);
500 RenderNodeDrawable drawable(renderNode.get(), &canvas, false);
501 canvas.drawDrawable(&drawable);
502 return canvas.getIndex();
503}
504}
505
506RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectedInMiddle) {
507 /* R is backward projected on B
508 A
509 / \
510 B C
511 |
512 R
513 */
514 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
515 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
516 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
517 props.setProjectionReceiver(true);
518 } ); //nodeB
519 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
520 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
521 props.setProjectBackwards(true);
522 props.setClipToBounds(false);
523 } ); //nodeR
524 } ); //nodeC
525 }); //nodeA
526 EXPECT_EQ(3, drawNode(renderThread, nodeA));
527}
528
529RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectLast) {
530 /* R is backward projected on E
531 A
532 / | \
533 / | \
534 B C E
535 |
536 R
537 */
538 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
539 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
540 drawOrderedNode(&canvas, 0, nullptr); //nodeB
541 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
542 drawOrderedNode(&canvas, 3, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //drawn as 2
543 props.setProjectBackwards(true);
544 props.setClipToBounds(false);
545 } ); //nodeR
546 } ); //nodeC
547 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //drawn as 3
548 props.setProjectionReceiver(true);
549 } ); //nodeE
550 }); //nodeA
551 EXPECT_EQ(4, drawNode(renderThread, nodeA));
552}
553
554RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderNoReceivable) {
555 /* R is backward projected without receiver
556 A
557 / \
558 B C
559 |
560 R
561 */
562 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
563 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
564 drawOrderedNode(&canvas, 0, nullptr); //nodeB
565 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
566 drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
567 //not having a projection receiver is an undefined behavior
568 props.setProjectBackwards(true);
569 props.setClipToBounds(false);
570 } ); //nodeR
571 } ); //nodeC
572 }); //nodeA
573 EXPECT_EQ(2, drawNode(renderThread, nodeA));
574}
575
576RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderParentReceivable) {
577 /* R is backward projected on C
578 A
579 / \
580 B C
581 |
582 R
583 */
584 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
585 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
586 drawOrderedNode(&canvas, 0, nullptr); //nodeB
587 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
588 props.setProjectionReceiver(true);
589 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
590 props.setProjectBackwards(true);
591 props.setClipToBounds(false);
592 } ); //nodeR
593 } ); //nodeC
594 }); //nodeA
595 EXPECT_EQ(3, drawNode(renderThread, nodeA));
596}
597
598RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderSameNodeReceivable) {
599 /* R is backward projected on R
600 A
601 / \
602 B C
603 |
604 R
605 */
606 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
607 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
608 drawOrderedNode(&canvas, 0, nullptr); //nodeB
609 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
610 drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
611 //having a node that is projected on itself is an undefined/unexpected behavior
612 props.setProjectionReceiver(true);
613 props.setProjectBackwards(true);
614 props.setClipToBounds(false);
615 } ); //nodeR
616 } ); //nodeC
617 }); //nodeA
618 EXPECT_EQ(2, drawNode(renderThread, nodeA));
619}
620
621//Note: the outcome for this test is different in HWUI
622RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectedSibling) {
623 /* R is set to project on B, but R is not drawn because projecting on a sibling is not allowed.
624 A
625 /|\
626 / | \
627 B C R
628 */
629 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
630 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
631 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
632 props.setProjectionReceiver(true);
633 } ); //nodeB
634 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
635 } ); //nodeC
636 drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
637 props.setProjectBackwards(true);
638 props.setClipToBounds(false);
639 } ); //nodeR
640 }); //nodeA
641 EXPECT_EQ(2, drawNode(renderThread, nodeA));
642}
643
644RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectedSibling2) {
645 /* R is set to project on B, but R is not drawn because projecting on a sibling is not allowed.
646 A
647 |
648 G
649 /|\
650 / | \
651 B C R
652 */
653 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
654 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
655 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
656 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
657 props.setProjectionReceiver(true);
658 } ); //nodeB
659 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
660 } ); //nodeC
661 drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
662 props.setProjectBackwards(true);
663 props.setClipToBounds(false);
664 } ); //nodeR
665 } ); //nodeG
666 }); //nodeA
667 EXPECT_EQ(3, drawNode(renderThread, nodeA));
668}
669
670RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderGrandparentReceivable) {
671 /* R is backward projected on B
672 A
673 |
674 B
675 |
676 C
677 |
678 R
679 */
680 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
681 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
682 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
683 props.setProjectionReceiver(true);
684 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
685 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
686 props.setProjectBackwards(true);
687 props.setClipToBounds(false);
688 } ); //nodeR
689 } ); //nodeC
690 } ); //nodeB
691 }); //nodeA
692 EXPECT_EQ(3, drawNode(renderThread, nodeA));
693}
694
695RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderTwoReceivables) {
696 /* B and G are receivables, R is backward projected
697 A
698 / \
699 B C
700 / \
701 G R
702 */
703 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
704 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
705 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //B
706 props.setProjectionReceiver(true);
707 } ); //nodeB
708 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //C
709 drawOrderedNode(&canvas, 3, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //G
710 props.setProjectionReceiver(true);
711 } ); //nodeG
712 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //R
713 props.setProjectBackwards(true);
714 props.setClipToBounds(false);
715 } ); //nodeR
716 } ); //nodeC
717 }); //nodeA
718 EXPECT_EQ(4, drawNode(renderThread, nodeA));
719}
720
721RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderTwoReceivablesLikelyScenario) {
722 /* B and G are receivables, G is backward projected
723 A
724 / \
725 B C
726 / \
727 G R
728 */
729 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
730 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
731 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //B
732 props.setProjectionReceiver(true);
733 } ); //nodeB
734 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //C
735 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //G
736 props.setProjectionReceiver(true);
737 props.setProjectBackwards(true);
738 props.setClipToBounds(false);
739 } ); //nodeG
740 drawOrderedNode(&canvas, 3, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //R
741 } ); //nodeR
742 } ); //nodeC
743 }); //nodeA
744 EXPECT_EQ(4, drawNode(renderThread, nodeA));
745}
746
747RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderTwoReceivablesDeeper) {
748 /* B and G are receivables, R is backward projected
749 A
750 / \
751 B C
752 / \
753 G D
754 |
755 R
756 */
757 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
758 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
759 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //B
760 props.setProjectionReceiver(true);
761 } ); //nodeB
762 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //C
763 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //G
764 props.setProjectionReceiver(true);
765 } ); //nodeG
766 drawOrderedNode(&canvas, 4, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //D
767 drawOrderedNode(&canvas, 3, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //R
768 props.setProjectBackwards(true);
769 props.setClipToBounds(false);
770 } ); //nodeR
771 } ); //nodeD
772 } ); //nodeC
773 }); //nodeA
774 EXPECT_EQ(5, drawNode(renderThread, nodeA));
Stan Iliev021693b2016-10-17 16:26:15 -0400775}
Stan Iliev52771272016-11-17 09:54:38 -0500776
777RENDERTHREAD_TEST(RenderNodeDrawable, simple) {
778 static const int CANVAS_WIDTH = 100;
779 static const int CANVAS_HEIGHT = 200;
780 class SimpleTestCanvas : public TestCanvasBase {
781 public:
782 SimpleTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {
783 }
784 void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
785 EXPECT_EQ(0, mDrawCounter++);
786 }
787 void onDrawImage(const SkImage*, SkScalar dx, SkScalar dy, const SkPaint*) override {
788 EXPECT_EQ(1, mDrawCounter++);
789 }
790 };
791
792 auto node = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
793 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
794 sk_sp<Bitmap> bitmap(TestUtils::createBitmap(25, 25));
795 canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, SkPaint());
796 canvas.drawBitmap(*bitmap, 10, 10, nullptr);
797 });
798
799 SimpleTestCanvas canvas;
800 RenderNodeDrawable drawable(node.get(), &canvas, true);
801 canvas.drawDrawable(&drawable);
802 EXPECT_EQ(2, canvas.mDrawCounter);
803}
804
805RENDERTHREAD_TEST(RenderNodeDrawable, colorOp_unbounded) {
806 static const int CANVAS_WIDTH = 200;
807 static const int CANVAS_HEIGHT = 200;
808 class ColorTestCanvas : public TestCanvasBase {
809 public:
810 ColorTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {
811 }
812 void onDrawPaint(const SkPaint&) {
813 switch (mDrawCounter++) {
814 case 0:
815 // While this mirrors FrameBuilder::colorOp_unbounded, this value is different
816 // because there is no root (root is clipped in SkiaPipeline::renderFrame).
817 // SkiaPipeline.clipped and clip_replace verify the root clip.
818 EXPECT_TRUE(TestUtils::getClipBounds(this).isEmpty());
819 break;
820 case 1:
821 EXPECT_EQ(SkRect::MakeWH(10, 10), TestUtils::getClipBounds(this));
822 break;
823 default:
824 ADD_FAILURE();
825 }
826 }
827 };
828
829 auto unclippedColorView = TestUtils::createSkiaNode(0, 0, 10, 10,
830 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
831 props.setClipToBounds(false);
832 canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
833 });
834
835 auto clippedColorView = TestUtils::createSkiaNode(0, 0, 10, 10,
836 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
837 canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
838 });
839
840 ColorTestCanvas canvas;
841 RenderNodeDrawable drawable(unclippedColorView.get(), &canvas, true);
842 canvas.drawDrawable(&drawable);
843 EXPECT_EQ(1, canvas.mDrawCounter);
844 RenderNodeDrawable drawable2(clippedColorView.get(), &canvas, true);
845 canvas.drawDrawable(&drawable2);
846 EXPECT_EQ(2, canvas.mDrawCounter);
847}
848
849TEST(RenderNodeDrawable, renderNode) {
850 static const int CANVAS_WIDTH = 200;
851 static const int CANVAS_HEIGHT = 200;
852 class RenderNodeTestCanvas : public TestCanvasBase {
853 public:
854 RenderNodeTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {
855 }
856 void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
857 switch(mDrawCounter++) {
858 case 0:
859 EXPECT_EQ(SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT), TestUtils::getClipBounds(this));
860 EXPECT_EQ(SK_ColorDKGRAY, paint.getColor());
861 break;
862 case 1:
863 EXPECT_EQ(SkRect::MakeLTRB(50, 50, 150, 150), TestUtils::getClipBounds(this));
864 EXPECT_EQ(SK_ColorWHITE, paint.getColor());
865 break;
866 default:
867 ADD_FAILURE();
868 }
869 }
870 };
871
872 auto child = TestUtils::createSkiaNode(10, 10, 110, 110,
873 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
874 SkPaint paint;
875 paint.setColor(SK_ColorWHITE);
876 canvas.drawRect(0, 0, 100, 100, paint);
877 });
878
879 auto parent = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
880 [&child](RenderProperties& props, SkiaRecordingCanvas& canvas) {
881 SkPaint paint;
882 paint.setColor(SK_ColorDKGRAY);
883 canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, paint);
884
885 canvas.save(SaveFlags::MatrixClip);
886 canvas.translate(40, 40);
887 canvas.drawRenderNode(child.get());
888 canvas.restore();
889 });
890
891 RenderNodeTestCanvas canvas;
892 RenderNodeDrawable drawable(parent.get(), &canvas, true);
893 canvas.drawDrawable(&drawable);
894 EXPECT_EQ(2, canvas.mDrawCounter);
895}
896