blob: ae4f0f42e6696755e3e20443afe5af3eb5a11438 [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 Iliev021693b2016-10-17 16:26:15 -040032#include <string.h>
33
34
35using namespace android;
36using namespace android::uirenderer;
37using namespace android::uirenderer::renderthread;
38using namespace android::uirenderer::skiapipeline;
39
Stan Iliev021693b2016-10-17 16:26:15 -040040TEST(RenderNodeDrawable, create) {
41 auto rootNode = TestUtils::createNode(0, 0, 200, 400,
42 [](RenderProperties& props, Canvas& canvas) {
43 canvas.drawColor(Color::Red_500, SkBlendMode::kSrcOver);
44 });
45
46 auto skLiteDL = SkLiteDL::New(SkRect::MakeWH(1, 1));
47 SkLiteRecorder canvas;
48 canvas.reset(skLiteDL.get());
49 canvas.translate(100, 100);
50 RenderNodeDrawable drawable(rootNode.get(), &canvas);
51
52 ASSERT_EQ(drawable.getRenderNode(), rootNode.get());
53 ASSERT_EQ(&drawable.getNodeProperties(), &rootNode->properties());
54 ASSERT_EQ(drawable.getRecordedMatrix(), canvas.getTotalMatrix());
55}
56
Stan Ilievdb45a4b2016-11-08 14:18:31 -050057namespace {
58
Stan Iliev2f06e8a2016-11-02 15:29:03 -040059static void drawOrderedRect(Canvas* canvas, uint8_t expectedDrawOrder) {
60 SkPaint paint;
61 // order put in blue channel, transparent so overlapped content doesn't get rejected
62 paint.setColor(SkColorSetARGB(1, 0, 0, expectedDrawOrder));
63 canvas->drawRect(0, 0, 100, 100, paint);
Stan Iliev021693b2016-10-17 16:26:15 -040064}
65
Stan Iliev2f06e8a2016-11-02 15:29:03 -040066static void drawOrderedNode(Canvas* canvas, uint8_t expectedDrawOrder, float z) {
67 auto node = TestUtils::createSkiaNode(0, 0, 100, 100,
68 [expectedDrawOrder, z](RenderProperties& props, SkiaRecordingCanvas& canvas) {
69 drawOrderedRect(&canvas, expectedDrawOrder);
70 props.setTranslationZ(z);
71 });
72 canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership
73}
Stan Iliev021693b2016-10-17 16:26:15 -040074
Stan Ilievdb45a4b2016-11-08 14:18:31 -050075static void drawOrderedNode(Canvas* canvas, uint8_t expectedDrawOrder,
76 std::function<void(RenderProperties& props, SkiaRecordingCanvas& canvas)> setup) {
77 auto node = TestUtils::createSkiaNode(0, 0, 100, 100,
78 [expectedDrawOrder, setup](RenderProperties& props, SkiaRecordingCanvas& canvas) {
79 drawOrderedRect(&canvas, expectedDrawOrder);
80 if (setup) {
81 setup(props, canvas);
Stan Iliev2f06e8a2016-11-02 15:29:03 -040082 }
Stan Ilievdb45a4b2016-11-08 14:18:31 -050083 });
84 canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership
85}
86
87class ZReorderCanvas : public SkCanvas {
88public:
89 ZReorderCanvas(int width, int height) : SkCanvas(width, height) {}
90 void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
91 int expectedOrder = SkColorGetB(paint.getColor()); // extract order from blue channel
92 EXPECT_EQ(expectedOrder, mIndex++) << "An op was drawn out of order";
93 }
94 int getIndex() { return mIndex; }
95protected:
96 int mIndex = 0;
97};
98
99} // end anonymous namespace
100
101TEST(RenderNodeDrawable, zReorder) {
Stan Iliev021693b2016-10-17 16:26:15 -0400102
Stan Iliev2f06e8a2016-11-02 15:29:03 -0400103 auto parent = TestUtils::createSkiaNode(0, 0, 100, 100,
104 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
105 drawOrderedNode(&canvas, 0, 10.0f); // in reorder=false at this point, so played inorder
106 drawOrderedRect(&canvas, 1);
107 canvas.insertReorderBarrier(true);
108 drawOrderedNode(&canvas, 6, 2.0f);
109 drawOrderedRect(&canvas, 3);
110 drawOrderedNode(&canvas, 4, 0.0f);
111 drawOrderedRect(&canvas, 5);
112 drawOrderedNode(&canvas, 2, -2.0f);
113 drawOrderedNode(&canvas, 7, 2.0f);
114 canvas.insertReorderBarrier(false);
115 drawOrderedRect(&canvas, 8);
116 drawOrderedNode(&canvas, 9, -10.0f); // in reorder=false at this point, so played inorder
117 });
Stan Iliev021693b2016-10-17 16:26:15 -0400118
Stan Iliev2f06e8a2016-11-02 15:29:03 -0400119 //create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection
120 ZReorderCanvas canvas(100, 100);
121 RenderNodeDrawable drawable(parent.get(), &canvas, false);
122 canvas.drawDrawable(&drawable);
123 EXPECT_EQ(10, canvas.getIndex());
Stan Iliev021693b2016-10-17 16:26:15 -0400124}
125
126TEST(RenderNodeDrawable, composeOnLayer)
127{
128 auto surface = SkSurface::MakeRasterN32Premul(1, 1);
129 SkCanvas& canvas = *surface->getCanvas();
130 canvas.drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
131 ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
132
Stan Iliev500a0c32016-10-26 10:30:09 -0400133 auto rootNode = TestUtils::createSkiaNode(0, 0, 1, 1,
Stan Iliev021693b2016-10-17 16:26:15 -0400134 [](RenderProperties& props, SkiaRecordingCanvas& recorder) {
135 recorder.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);
136 });
137
138 //attach a layer to the render node
139 auto surfaceLayer = SkSurface::MakeRasterN32Premul(1, 1);
140 auto canvas2 = surfaceLayer->getCanvas();
141 canvas2->drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
Stan Iliev500a0c32016-10-26 10:30:09 -0400142 rootNode->setLayerSurface(surfaceLayer);
Stan Iliev021693b2016-10-17 16:26:15 -0400143
144 RenderNodeDrawable drawable1(rootNode.get(), &canvas, false);
145 canvas.drawDrawable(&drawable1);
146 ASSERT_EQ(SK_ColorRED, TestUtils::getColor(surface, 0, 0));
147
148 RenderNodeDrawable drawable2(rootNode.get(), &canvas, true);
149 canvas.drawDrawable(&drawable2);
150 ASSERT_EQ(SK_ColorWHITE, TestUtils::getColor(surface, 0, 0));
151
152 RenderNodeDrawable drawable3(rootNode.get(), &canvas, false);
153 canvas.drawDrawable(&drawable3);
154 ASSERT_EQ(SK_ColorRED, TestUtils::getColor(surface, 0, 0));
155
Stan Iliev500a0c32016-10-26 10:30:09 -0400156 rootNode->setLayerSurface(sk_sp<SkSurface>());
Stan Iliev021693b2016-10-17 16:26:15 -0400157}
158
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500159namespace {
160class ContextFactory : public IContextFactory {
161public:
162 virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) override {
163 return new AnimationContext(clock);
164 }
165};
Stan Iliev021693b2016-10-17 16:26:15 -0400166
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500167inline SkRect getBounds(const SkCanvas* canvas) {
168 SkClipStack::BoundsType boundType;
169 SkRect clipBounds;
170 canvas->getClipStack()->getBounds(&clipBounds, &boundType);
171 return clipBounds;
172}
173inline SkRect getLocalBounds(const SkCanvas* canvas) {
174 SkMatrix invertedTotalMatrix;
175 EXPECT_TRUE(canvas->getTotalMatrix().invert(&invertedTotalMatrix));
176 SkRect outlineInDeviceCoord = getBounds(canvas);
177 SkRect outlineInLocalCoord;
178 invertedTotalMatrix.mapRect(&outlineInLocalCoord, outlineInDeviceCoord);
179 return outlineInLocalCoord;
180}
181} // end anonymous namespace
Stan Iliev021693b2016-10-17 16:26:15 -0400182
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500183RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorder) {
184 static const int SCROLL_X = 5;
185 static const int SCROLL_Y = 10;
186 class ProjectionTestCanvas : public SkCanvas {
187 public:
188 ProjectionTestCanvas(int width, int height) : SkCanvas(width, height) {}
189 void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
190 const int index = mIndex++;
191 SkMatrix expectedMatrix;;
192 switch (index) {
193 case 0: //this is node "B"
194 EXPECT_EQ(SkRect::MakeWH(100, 100), rect);
195 EXPECT_EQ(SK_ColorWHITE, paint.getColor());
196 expectedMatrix.reset();
197 EXPECT_EQ(SkRect::MakeLTRB(0, 0, 100, 100), getBounds(this));
198 break;
199 case 1: //this is node "P"
200 EXPECT_EQ(SkRect::MakeLTRB(-10, -10, 60, 60), rect);
201 EXPECT_EQ(SK_ColorDKGRAY, paint.getColor());
202 expectedMatrix.setTranslate(50 - SCROLL_X, 50 - SCROLL_Y);
203 EXPECT_EQ(SkRect::MakeLTRB(-35, -30, 45, 50), getLocalBounds(this));
204 break;
205 case 2: //this is node "C"
206 EXPECT_EQ(SkRect::MakeWH(100, 50), rect);
207 EXPECT_EQ(SK_ColorBLUE, paint.getColor());
208 expectedMatrix.setTranslate(-SCROLL_X, 50 - SCROLL_Y);
209 EXPECT_EQ(SkRect::MakeLTRB(0, 40, 95, 90), getBounds(this));
210 break;
211 default:
212 ADD_FAILURE();
213 }
214 EXPECT_EQ(expectedMatrix, getTotalMatrix());
215 }
Stan Iliev021693b2016-10-17 16:26:15 -0400216
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500217 int getIndex() { return mIndex; }
218 protected:
219 int mIndex = 0;
220 };
Stan Iliev021693b2016-10-17 16:26:15 -0400221
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500222 /**
223 * Construct a tree of nodes, where the root (A) has a receiver background (B), and a child (C)
224 * with a projecting child (P) of its own. P would normally draw between B and C's "background"
225 * draw, but because it is projected backwards, it's drawn in between B and C.
226 *
227 * The parent is scrolled by SCROLL_X/SCROLL_Y, but this does not affect the background
228 * (which isn't affected by scroll).
229 */
230 auto receiverBackground = TestUtils::createSkiaNode(0, 0, 100, 100,
231 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
232 properties.setProjectionReceiver(true);
233 // scroll doesn't apply to background, so undone via translationX/Y
234 // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
235 properties.setTranslationX(SCROLL_X);
236 properties.setTranslationY(SCROLL_Y);
Stan Iliev021693b2016-10-17 16:26:15 -0400237
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500238 SkPaint paint;
239 paint.setColor(SK_ColorWHITE);
240 canvas.drawRect(0, 0, 100, 100, paint);
241 }, "B");
242
243 auto projectingRipple = TestUtils::createSkiaNode(50, 0, 100, 50,
244 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
245 properties.setProjectBackwards(true);
246 properties.setClipToBounds(false);
247 SkPaint paint;
248 paint.setColor(SK_ColorDKGRAY);
249 canvas.drawRect(-10, -10, 60, 60, paint);
250 }, "P");
251 auto child = TestUtils::createSkiaNode(0, 50, 100, 100,
252 [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
253 SkPaint paint;
254 paint.setColor(SK_ColorBLUE);
255 canvas.drawRect(0, 0, 100, 50, paint);
256 canvas.drawRenderNode(projectingRipple.get());
257 }, "C");
258 auto parent = TestUtils::createSkiaNode(0, 0, 100, 100,
259 [&receiverBackground, &child](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
260 // Set a rect outline for the projecting ripple to be masked against.
261 properties.mutableOutline().setRoundRect(10, 10, 90, 90, 5, 1.0f);
262
263 canvas.save(SaveFlags::MatrixClip);
264 canvas.translate(-SCROLL_X, -SCROLL_Y); // Apply scroll (note: bg undoes this internally)
265 canvas.drawRenderNode(receiverBackground.get());
266 canvas.drawRenderNode(child.get());
267 canvas.restore();
268 }, "A");
269 ContextFactory contextFactory;
270 std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create(
271 renderThread, false, parent.get(), &contextFactory));
272 TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
273 DamageAccumulator damageAccumulator;
274 info.damageAccumulator = &damageAccumulator;
275 info.observer = nullptr;
276 parent->prepareTree(info);
277
278 //parent(A) -> (receiverBackground, child)
279 //child(C) -> (rect[0, 0, 100, 50], projectingRipple)
280 //projectingRipple(P) -> (rect[-10, -10, 60, 60]) -> projects backwards
281 //receiverBackground(B) -> (rect[0, 0, 100, 100]) -> projection receiver
282
283 //create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection
284 ProjectionTestCanvas canvas(100, 100);
285 RenderNodeDrawable drawable(parent.get(), &canvas, true);
286 canvas.drawDrawable(&drawable);
287 EXPECT_EQ(3, canvas.getIndex());
288}
289
290RENDERTHREAD_TEST(RenderNodeDrawable, projectionHwLayer) {
291 /* R is backward projected on B and C is a layer.
292 A
293 / \
294 B C
295 |
296 R
297 */
298 static const int SCROLL_X = 5;
299 static const int SCROLL_Y = 10;
300 static const int CANVAS_WIDTH = 400;
301 static const int CANVAS_HEIGHT = 400;
302 static const int LAYER_WIDTH = 200;
303 static const int LAYER_HEIGHT = 200;
304 class ProjectionTestCanvas : public SkCanvas {
305 public:
306 ProjectionTestCanvas() : SkCanvas(CANVAS_WIDTH, CANVAS_HEIGHT) {}
307 void onDrawArc(const SkRect&, SkScalar startAngle, SkScalar sweepAngle, bool useCenter,
308 const SkPaint&) override {
309 EXPECT_EQ(0, mIndex++); //part of painting the layer
310 EXPECT_EQ(SkRect::MakeLTRB(0, 0, LAYER_WIDTH, LAYER_HEIGHT), getBounds(this));
311 }
312 void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
313 EXPECT_EQ(1, mIndex++);
314 EXPECT_EQ(SkRect::MakeLTRB(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT), getBounds(this));
315 }
316 void onDrawOval(const SkRect&, const SkPaint&) override {
317 EXPECT_EQ(2, mIndex++);
318 SkMatrix expectedMatrix;
319 expectedMatrix.setTranslate(100 - SCROLL_X, 100 - SCROLL_Y);
320 EXPECT_EQ(expectedMatrix, getTotalMatrix());
321 EXPECT_EQ(SkRect::MakeLTRB(-85, -80, 295, 300), getLocalBounds(this));
322 }
323 int mIndex = 0;
324 };
325
326 class ProjectionLayer : public SkSurface_Base {
327 public:
328 ProjectionLayer(ProjectionTestCanvas *canvas)
329 : SkSurface_Base(SkImageInfo::MakeN32Premul(LAYER_WIDTH, LAYER_HEIGHT), nullptr)
330 , mCanvas(canvas) {
331 }
332 void onDraw(SkCanvas*, SkScalar x, SkScalar y, const SkPaint*) override {
333 EXPECT_EQ(3, mCanvas->mIndex++);
334 EXPECT_EQ(SkRect::MakeLTRB(100 - SCROLL_X, 100 - SCROLL_Y, 300 - SCROLL_X,
335 300 - SCROLL_Y), getBounds(mCanvas));
336 }
337 SkCanvas* onNewCanvas() override {
338 mCanvas->ref();
339 return mCanvas;
340 }
341 sk_sp<SkSurface> onNewSurface(const SkImageInfo&) override {
342 return sk_sp<SkSurface>();
343 }
344 sk_sp<SkImage> onNewImageSnapshot(SkBudgeted, SkCopyPixelsMode) override {
345 return sk_sp<SkImage>();
346 }
347 void onCopyOnWrite(ContentChangeMode) override {}
348 ProjectionTestCanvas* mCanvas;
349 };
350
351 auto receiverBackground = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
352 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
353 properties.setProjectionReceiver(true);
354 // scroll doesn't apply to background, so undone via translationX/Y
355 // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
356 properties.setTranslationX(SCROLL_X);
357 properties.setTranslationY(SCROLL_Y);
358
359 canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, SkPaint());
360 }, "B"); //B
361 auto projectingRipple = TestUtils::createSkiaNode(0, 0, LAYER_WIDTH, LAYER_HEIGHT,
362 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
363 properties.setProjectBackwards(true);
364 properties.setClipToBounds(false);
365 canvas.drawOval(100, 100, 300, 300, SkPaint()); // drawn mostly out of layer bounds
366 }, "R"); //R
367 auto child = TestUtils::createSkiaNode(100, 100, 300, 300,
368 [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
369 canvas.drawRenderNode(projectingRipple.get());
370 canvas.drawArc(0, 0, LAYER_WIDTH, LAYER_HEIGHT, 0.0f, 280.0f, true, SkPaint());
371 }, "C"); //C
372 auto parent = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
373 [&receiverBackground, &child](RenderProperties& properties,
374 SkiaRecordingCanvas& canvas) {
375 // Set a rect outline for the projecting ripple to be masked against.
376 properties.mutableOutline().setRoundRect(10, 10, 390, 390, 0, 1.0f);
377 canvas.translate(-SCROLL_X, -SCROLL_Y); // Apply scroll (note: bg undoes this internally)
378 canvas.drawRenderNode(receiverBackground.get());
379 canvas.drawRenderNode(child.get());
380 }, "A"); //A
381
382 //prepareTree is required to find, which receivers have backward projected nodes
383 ContextFactory contextFactory;
384 std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create(
385 renderThread, false, parent.get(), &contextFactory));
386 TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
387 DamageAccumulator damageAccumulator;
388 info.damageAccumulator = &damageAccumulator;
389 info.observer = nullptr;
390 parent->prepareTree(info);
391
392 sk_sp<ProjectionTestCanvas> canvas(new ProjectionTestCanvas());
393 //set a layer after prepareTree to avoid layer logic there
394 child->animatorProperties().mutateLayerProperties().setType(LayerType::RenderLayer);
395 sk_sp<SkSurface> surfaceLayer1(new ProjectionLayer(canvas.get()));
396 child->setLayerSurface(surfaceLayer1);
397 Matrix4 windowTransform;
398 windowTransform.loadTranslate(100, 100, 0);
399 child->getSkiaLayer()->inverseTransformInWindow.loadInverse(windowTransform);
400
401 LayerUpdateQueue layerUpdateQueue;
402 layerUpdateQueue.enqueueLayerWithDamage(child.get(),
403 android::uirenderer::Rect(LAYER_WIDTH, LAYER_HEIGHT));
404 SkiaPipeline::renderLayersImpl(layerUpdateQueue, true);
405 EXPECT_EQ(1, canvas->mIndex); //assert index 0 is drawn on the layer
406
407 RenderNodeDrawable drawable(parent.get(), canvas.get(), true);
408 canvas->drawDrawable(&drawable);
409 EXPECT_EQ(4, canvas->mIndex);
410
411 // clean up layer pointer, so we can safely destruct RenderNode
412 child->setLayerSurface(nullptr);
413}
414
415RENDERTHREAD_TEST(RenderNodeDrawable, projectionChildScroll) {
416 /* R is backward projected on B.
417 A
418 / \
419 B C
420 |
421 R
422 */
423 static const int SCROLL_X = 500000;
424 static const int SCROLL_Y = 0;
425 static const int CANVAS_WIDTH = 400;
426 static const int CANVAS_HEIGHT = 400;
427 class ProjectionChildScrollTestCanvas : public SkCanvas {
428 public:
429 ProjectionChildScrollTestCanvas() : SkCanvas(CANVAS_WIDTH, CANVAS_HEIGHT) {}
430 void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
431 EXPECT_EQ(0, mIndex++);
432 EXPECT_TRUE(getTotalMatrix().isIdentity());
433 }
434 void onDrawOval(const SkRect&, const SkPaint&) override {
435 EXPECT_EQ(1, mIndex++);
436 EXPECT_EQ(SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT), getBounds(this));
437 EXPECT_TRUE(getTotalMatrix().isIdentity());
438 }
439 int mIndex = 0;
440 };
441
442 auto receiverBackground = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
443 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
444 properties.setProjectionReceiver(true);
445 canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, SkPaint());
446 }, "B"); //B
447 auto projectingRipple = TestUtils::createSkiaNode(0, 0, 200, 200,
448 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
449 // scroll doesn't apply to background, so undone via translationX/Y
450 // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
451 properties.setTranslationX(SCROLL_X);
452 properties.setTranslationY(SCROLL_Y);
453 properties.setProjectBackwards(true);
454 properties.setClipToBounds(false);
455 canvas.drawOval(0, 0, 200, 200, SkPaint());
456 }, "R"); //R
457 auto child = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
458 [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
459 // Record time clip will be ignored by projectee
460 canvas.clipRect(100, 100, 300, 300, SkRegion::kIntersect_Op);
461
462 canvas.translate(-SCROLL_X, -SCROLL_Y); // Apply scroll (note: bg undoes this internally)
463 canvas.drawRenderNode(projectingRipple.get());
464 }, "C"); //C
465 auto parent = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
466 [&receiverBackground, &child](RenderProperties& properties,
467 SkiaRecordingCanvas& canvas) {
468 canvas.drawRenderNode(receiverBackground.get());
469 canvas.drawRenderNode(child.get());
470 }, "A"); //A
471
472 //prepareTree is required to find, which receivers have backward projected nodes
473 ContextFactory contextFactory;
474 std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create(
475 renderThread, false, parent.get(), &contextFactory));
476 TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
477 DamageAccumulator damageAccumulator;
478 info.damageAccumulator = &damageAccumulator;
479 info.observer = nullptr;
480 parent->prepareTree(info);
481
482 sk_sp<ProjectionChildScrollTestCanvas> canvas(new ProjectionChildScrollTestCanvas());
483 RenderNodeDrawable drawable(parent.get(), canvas.get(), true);
484 canvas->drawDrawable(&drawable);
485 EXPECT_EQ(2, canvas->mIndex);
486}
487
488namespace {
489static int drawNode(RenderThread& renderThread, const sp<RenderNode>& renderNode)
490{
491 ContextFactory contextFactory;
492 std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create(
493 renderThread, false, renderNode.get(), &contextFactory));
494 TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
495 DamageAccumulator damageAccumulator;
496 info.damageAccumulator = &damageAccumulator;
497 info.observer = nullptr;
498 renderNode->prepareTree(info);
499
500 //create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection
501 ZReorderCanvas canvas(100, 100);
502 RenderNodeDrawable drawable(renderNode.get(), &canvas, false);
503 canvas.drawDrawable(&drawable);
504 return canvas.getIndex();
505}
506}
507
508RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectedInMiddle) {
509 /* R is backward projected on B
510 A
511 / \
512 B C
513 |
514 R
515 */
516 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
517 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
518 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
519 props.setProjectionReceiver(true);
520 } ); //nodeB
521 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
522 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
523 props.setProjectBackwards(true);
524 props.setClipToBounds(false);
525 } ); //nodeR
526 } ); //nodeC
527 }); //nodeA
528 EXPECT_EQ(3, drawNode(renderThread, nodeA));
529}
530
531RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectLast) {
532 /* R is backward projected on E
533 A
534 / | \
535 / | \
536 B C E
537 |
538 R
539 */
540 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
541 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
542 drawOrderedNode(&canvas, 0, nullptr); //nodeB
543 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
544 drawOrderedNode(&canvas, 3, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //drawn as 2
545 props.setProjectBackwards(true);
546 props.setClipToBounds(false);
547 } ); //nodeR
548 } ); //nodeC
549 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //drawn as 3
550 props.setProjectionReceiver(true);
551 } ); //nodeE
552 }); //nodeA
553 EXPECT_EQ(4, drawNode(renderThread, nodeA));
554}
555
556RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderNoReceivable) {
557 /* R is backward projected without receiver
558 A
559 / \
560 B C
561 |
562 R
563 */
564 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
565 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
566 drawOrderedNode(&canvas, 0, nullptr); //nodeB
567 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
568 drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
569 //not having a projection receiver is an undefined behavior
570 props.setProjectBackwards(true);
571 props.setClipToBounds(false);
572 } ); //nodeR
573 } ); //nodeC
574 }); //nodeA
575 EXPECT_EQ(2, drawNode(renderThread, nodeA));
576}
577
578RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderParentReceivable) {
579 /* R is backward projected on C
580 A
581 / \
582 B C
583 |
584 R
585 */
586 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
587 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
588 drawOrderedNode(&canvas, 0, nullptr); //nodeB
589 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
590 props.setProjectionReceiver(true);
591 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
592 props.setProjectBackwards(true);
593 props.setClipToBounds(false);
594 } ); //nodeR
595 } ); //nodeC
596 }); //nodeA
597 EXPECT_EQ(3, drawNode(renderThread, nodeA));
598}
599
600RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderSameNodeReceivable) {
601 /* R is backward projected on R
602 A
603 / \
604 B C
605 |
606 R
607 */
608 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
609 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
610 drawOrderedNode(&canvas, 0, nullptr); //nodeB
611 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
612 drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
613 //having a node that is projected on itself is an undefined/unexpected behavior
614 props.setProjectionReceiver(true);
615 props.setProjectBackwards(true);
616 props.setClipToBounds(false);
617 } ); //nodeR
618 } ); //nodeC
619 }); //nodeA
620 EXPECT_EQ(2, drawNode(renderThread, nodeA));
621}
622
623//Note: the outcome for this test is different in HWUI
624RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectedSibling) {
625 /* R is set to project on B, but R is not drawn because projecting on a sibling is not allowed.
626 A
627 /|\
628 / | \
629 B C R
630 */
631 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
632 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
633 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
634 props.setProjectionReceiver(true);
635 } ); //nodeB
636 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
637 } ); //nodeC
638 drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
639 props.setProjectBackwards(true);
640 props.setClipToBounds(false);
641 } ); //nodeR
642 }); //nodeA
643 EXPECT_EQ(2, drawNode(renderThread, nodeA));
644}
645
646RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectedSibling2) {
647 /* R is set to project on B, but R is not drawn because projecting on a sibling is not allowed.
648 A
649 |
650 G
651 /|\
652 / | \
653 B C R
654 */
655 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
656 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
657 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
658 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
659 props.setProjectionReceiver(true);
660 } ); //nodeB
661 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
662 } ); //nodeC
663 drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
664 props.setProjectBackwards(true);
665 props.setClipToBounds(false);
666 } ); //nodeR
667 } ); //nodeG
668 }); //nodeA
669 EXPECT_EQ(3, drawNode(renderThread, nodeA));
670}
671
672RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderGrandparentReceivable) {
673 /* R is backward projected on B
674 A
675 |
676 B
677 |
678 C
679 |
680 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 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
687 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
688 props.setProjectBackwards(true);
689 props.setClipToBounds(false);
690 } ); //nodeR
691 } ); //nodeC
692 } ); //nodeB
693 }); //nodeA
694 EXPECT_EQ(3, drawNode(renderThread, nodeA));
695}
696
697RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderTwoReceivables) {
698 /* B and G are receivables, R is backward projected
699 A
700 / \
701 B C
702 / \
703 G R
704 */
705 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
706 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
707 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //B
708 props.setProjectionReceiver(true);
709 } ); //nodeB
710 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //C
711 drawOrderedNode(&canvas, 3, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //G
712 props.setProjectionReceiver(true);
713 } ); //nodeG
714 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //R
715 props.setProjectBackwards(true);
716 props.setClipToBounds(false);
717 } ); //nodeR
718 } ); //nodeC
719 }); //nodeA
720 EXPECT_EQ(4, drawNode(renderThread, nodeA));
721}
722
723RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderTwoReceivablesLikelyScenario) {
724 /* B and G are receivables, G is backward projected
725 A
726 / \
727 B C
728 / \
729 G R
730 */
731 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
732 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
733 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //B
734 props.setProjectionReceiver(true);
735 } ); //nodeB
736 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //C
737 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //G
738 props.setProjectionReceiver(true);
739 props.setProjectBackwards(true);
740 props.setClipToBounds(false);
741 } ); //nodeG
742 drawOrderedNode(&canvas, 3, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //R
743 } ); //nodeR
744 } ); //nodeC
745 }); //nodeA
746 EXPECT_EQ(4, drawNode(renderThread, nodeA));
747}
748
749RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderTwoReceivablesDeeper) {
750 /* B and G are receivables, R is backward projected
751 A
752 / \
753 B C
754 / \
755 G D
756 |
757 R
758 */
759 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
760 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
761 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //B
762 props.setProjectionReceiver(true);
763 } ); //nodeB
764 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //C
765 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //G
766 props.setProjectionReceiver(true);
767 } ); //nodeG
768 drawOrderedNode(&canvas, 4, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //D
769 drawOrderedNode(&canvas, 3, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //R
770 props.setProjectBackwards(true);
771 props.setClipToBounds(false);
772 } ); //nodeR
773 } ); //nodeD
774 } ); //nodeC
775 }); //nodeA
776 EXPECT_EQ(5, drawNode(renderThread, nodeA));
Stan Iliev021693b2016-10-17 16:26:15 -0400777}