1 /*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <VectorDrawable.h>
18 #include <gtest/gtest.h>
19
20 #include <SkClipStack.h>
21 #include <SkLiteRecorder.h>
22 #include <SkSurface_Base.h>
23 #include <string.h>
24 #include "AnimationContext.h"
25 #include "DamageAccumulator.h"
26 #include "FatalTestCanvas.h"
27 #include "IContextFactory.h"
28 #include "SkiaCanvas.h"
29 #include "pipeline/skia/SkiaDisplayList.h"
30 #include "pipeline/skia/SkiaOpenGLPipeline.h"
31 #include "pipeline/skia/SkiaPipeline.h"
32 #include "pipeline/skia/SkiaRecordingCanvas.h"
33 #include "renderthread/CanvasContext.h"
34 #include "tests/common/TestUtils.h"
35
36 using namespace android;
37 using namespace android::uirenderer;
38 using namespace android::uirenderer::renderthread;
39 using namespace android::uirenderer::skiapipeline;
40
TEST(RenderNodeDrawable,create)41 TEST(RenderNodeDrawable, create) {
42 auto rootNode =
43 TestUtils::createNode(0, 0, 200, 400, [](RenderProperties& props, Canvas& canvas) {
44 canvas.drawColor(Color::Red_500, SkBlendMode::kSrcOver);
45 });
46
47 SkLiteDL skLiteDL;
48 SkLiteRecorder canvas;
49 canvas.reset(&skLiteDL, SkIRect::MakeWH(1, 1));
50 canvas.translate(100, 100);
51 RenderNodeDrawable drawable(rootNode.get(), &canvas);
52
53 ASSERT_EQ(drawable.getRenderNode(), rootNode.get());
54 ASSERT_EQ(&drawable.getNodeProperties(), &rootNode->properties());
55 ASSERT_EQ(drawable.getRecordedMatrix(), canvas.getTotalMatrix());
56 }
57
58 namespace {
59
drawOrderedRect(Canvas * canvas,uint8_t expectedDrawOrder)60 static void drawOrderedRect(Canvas* canvas, uint8_t expectedDrawOrder) {
61 SkPaint paint;
62 // order put in blue channel, transparent so overlapped content doesn't get rejected
63 paint.setColor(SkColorSetARGB(1, 0, 0, expectedDrawOrder));
64 canvas->drawRect(0, 0, 100, 100, paint);
65 }
66
drawOrderedNode(Canvas * canvas,uint8_t expectedDrawOrder,float z)67 static void drawOrderedNode(Canvas* canvas, uint8_t expectedDrawOrder, float z) {
68 auto node = TestUtils::createSkiaNode(
69 0, 0, 100, 100,
70 [expectedDrawOrder, z](RenderProperties& props, SkiaRecordingCanvas& canvas) {
71 drawOrderedRect(&canvas, expectedDrawOrder);
72 props.setTranslationZ(z);
73 });
74 canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership
75 }
76
drawOrderedNode(Canvas * canvas,uint8_t expectedDrawOrder,std::function<void (RenderProperties & props,SkiaRecordingCanvas & canvas)> setup)77 static void drawOrderedNode(
78 Canvas* canvas, uint8_t expectedDrawOrder,
79 std::function<void(RenderProperties& props, SkiaRecordingCanvas& canvas)> setup) {
80 auto node = TestUtils::createSkiaNode(
81 0, 0, 100, 100,
82 [expectedDrawOrder, setup](RenderProperties& props, SkiaRecordingCanvas& canvas) {
83 drawOrderedRect(&canvas, expectedDrawOrder);
84 if (setup) {
85 setup(props, canvas);
86 }
87 });
88 canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership
89 }
90
91 class ZReorderCanvas : public SkCanvas {
92 public:
ZReorderCanvas(int width,int height)93 ZReorderCanvas(int width, int height) : SkCanvas(width, height) {}
onDrawRect(const SkRect & rect,const SkPaint & paint)94 void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
95 int expectedOrder = SkColorGetB(paint.getColor()); // extract order from blue channel
96 EXPECT_EQ(expectedOrder, mDrawCounter++) << "An op was drawn out of order";
97 }
getIndex()98 int getIndex() { return mDrawCounter; }
99
100 protected:
101 int mDrawCounter = 0;
102 };
103
104 } // end anonymous namespace
105
TEST(RenderNodeDrawable,zReorder)106 TEST(RenderNodeDrawable, zReorder) {
107 auto parent = TestUtils::createSkiaNode(0, 0, 100, 100, [](RenderProperties& props,
108 SkiaRecordingCanvas& canvas) {
109 canvas.insertReorderBarrier(true);
110 canvas.insertReorderBarrier(false);
111 drawOrderedNode(&canvas, 0, 10.0f); // in reorder=false at this point, so played inorder
112 drawOrderedRect(&canvas, 1);
113 canvas.insertReorderBarrier(true);
114 drawOrderedNode(&canvas, 6, 2.0f);
115 drawOrderedRect(&canvas, 3);
116 drawOrderedNode(&canvas, 4, 0.0f);
117 drawOrderedRect(&canvas, 5);
118 drawOrderedNode(&canvas, 2, -2.0f);
119 drawOrderedNode(&canvas, 7, 2.0f);
120 canvas.insertReorderBarrier(false);
121 drawOrderedRect(&canvas, 8);
122 drawOrderedNode(&canvas, 9, -10.0f); // in reorder=false at this point, so played inorder
123 canvas.insertReorderBarrier(true); // reorder a node ahead of drawrect op
124 drawOrderedRect(&canvas, 11);
125 drawOrderedNode(&canvas, 10, -1.0f);
126 canvas.insertReorderBarrier(false);
127 canvas.insertReorderBarrier(true); // test with two empty reorder sections
128 canvas.insertReorderBarrier(true);
129 canvas.insertReorderBarrier(false);
130 drawOrderedRect(&canvas, 12);
131 });
132
133 // create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection
134 ZReorderCanvas canvas(100, 100);
135 RenderNodeDrawable drawable(parent.get(), &canvas, false);
136 canvas.drawDrawable(&drawable);
137 EXPECT_EQ(13, canvas.getIndex());
138 }
139
TEST(RenderNodeDrawable,composeOnLayer)140 TEST(RenderNodeDrawable, composeOnLayer) {
141 auto surface = SkSurface::MakeRasterN32Premul(1, 1);
142 SkCanvas& canvas = *surface->getCanvas();
143 canvas.drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
144 ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
145
146 auto rootNode = TestUtils::createSkiaNode(
147 0, 0, 1, 1, [](RenderProperties& props, SkiaRecordingCanvas& recorder) {
148 recorder.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);
149 });
150
151 // attach a layer to the render node
152 auto surfaceLayer = SkSurface::MakeRasterN32Premul(1, 1);
153 auto canvas2 = surfaceLayer->getCanvas();
154 canvas2->drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
155 rootNode->setLayerSurface(surfaceLayer);
156
157 RenderNodeDrawable drawable1(rootNode.get(), &canvas, false);
158 canvas.drawDrawable(&drawable1);
159 ASSERT_EQ(SK_ColorRED, TestUtils::getColor(surface, 0, 0));
160
161 RenderNodeDrawable drawable2(rootNode.get(), &canvas, true);
162 canvas.drawDrawable(&drawable2);
163 ASSERT_EQ(SK_ColorWHITE, TestUtils::getColor(surface, 0, 0));
164
165 RenderNodeDrawable drawable3(rootNode.get(), &canvas, false);
166 canvas.drawDrawable(&drawable3);
167 ASSERT_EQ(SK_ColorRED, TestUtils::getColor(surface, 0, 0));
168
169 rootNode->setLayerSurface(sk_sp<SkSurface>());
170 }
171
172 namespace {
getRecorderClipBounds(const SkiaRecordingCanvas & recorder)173 static SkRect getRecorderClipBounds(const SkiaRecordingCanvas& recorder) {
174 SkRect clipBounds;
175 recorder.getClipBounds(&clipBounds);
176 return clipBounds;
177 }
178
getRecorderMatrix(const SkiaRecordingCanvas & recorder)179 static SkMatrix getRecorderMatrix(const SkiaRecordingCanvas& recorder) {
180 SkMatrix matrix;
181 recorder.getMatrix(&matrix);
182 return matrix;
183 }
184 }
185
TEST(RenderNodeDrawable,saveLayerClipAndMatrixRestore)186 TEST(RenderNodeDrawable, saveLayerClipAndMatrixRestore) {
187 auto surface = SkSurface::MakeRasterN32Premul(400, 800);
188 SkCanvas& canvas = *surface->getCanvas();
189 canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
190 ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorWHITE);
191
192 auto rootNode = TestUtils::createSkiaNode(
193 0, 0, 400, 800, [](RenderProperties& props, SkiaRecordingCanvas& recorder) {
194 SkPaint layerPaint;
195 ASSERT_EQ(SkRect::MakeLTRB(0, 0, 400, 800), getRecorderClipBounds(recorder));
196 EXPECT_TRUE(getRecorderMatrix(recorder).isIdentity());
197
198 // note we don't pass SaveFlags::MatrixClip, but matrix and clip will be saved
199 recorder.saveLayer(0, 0, 400, 400, &layerPaint, SaveFlags::ClipToLayer);
200 ASSERT_EQ(SkRect::MakeLTRB(0, 0, 400, 400), getRecorderClipBounds(recorder));
201 EXPECT_TRUE(getRecorderMatrix(recorder).isIdentity());
202
203 recorder.clipRect(50, 50, 350, 350, SkClipOp::kIntersect);
204 ASSERT_EQ(SkRect::MakeLTRB(50, 50, 350, 350), getRecorderClipBounds(recorder));
205
206 recorder.translate(300.0f, 400.0f);
207 EXPECT_EQ(SkMatrix::MakeTrans(300.0f, 400.0f), getRecorderMatrix(recorder));
208
209 recorder.restore();
210 ASSERT_EQ(SkRect::MakeLTRB(0, 0, 400, 800), getRecorderClipBounds(recorder));
211 EXPECT_TRUE(getRecorderMatrix(recorder).isIdentity());
212
213 SkPaint paint;
214 paint.setAntiAlias(true);
215 paint.setColor(SK_ColorGREEN);
216 recorder.drawRect(0.0f, 400.0f, 400.0f, 800.0f, paint);
217 });
218
219 RenderNodeDrawable drawable(rootNode.get(), &canvas, true);
220 canvas.drawDrawable(&drawable);
221 ASSERT_EQ(SK_ColorGREEN, TestUtils::getColor(surface, 200, 600));
222 }
223
224 namespace {
225 class ContextFactory : public IContextFactory {
226 public:
createAnimationContext(renderthread::TimeLord & clock)227 virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) override {
228 return new AnimationContext(clock);
229 }
230 };
231 } // end anonymous namespace
232
RENDERTHREAD_TEST(RenderNodeDrawable,projectionReorder)233 RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorder) {
234 static const int SCROLL_X = 5;
235 static const int SCROLL_Y = 10;
236 class ProjectionTestCanvas : public SkCanvas {
237 public:
238 ProjectionTestCanvas(int width, int height) : SkCanvas(width, height) {}
239 void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
240 const int index = mDrawCounter++;
241 SkMatrix expectedMatrix;
242 ;
243 switch (index) {
244 case 0: // this is node "B"
245 EXPECT_EQ(SkRect::MakeWH(100, 100), rect);
246 EXPECT_EQ(SK_ColorWHITE, paint.getColor());
247 expectedMatrix.reset();
248 EXPECT_EQ(SkRect::MakeLTRB(0, 0, 100, 100), TestUtils::getClipBounds(this));
249 break;
250 case 1: // this is node "P"
251 EXPECT_EQ(SkRect::MakeLTRB(-10, -10, 60, 60), rect);
252 EXPECT_EQ(SK_ColorDKGRAY, paint.getColor());
253 expectedMatrix.setTranslate(50 - SCROLL_X, 50 - SCROLL_Y);
254 EXPECT_EQ(SkRect::MakeLTRB(-35, -30, 45, 50),
255 TestUtils::getLocalClipBounds(this));
256 break;
257 case 2: // this is node "C"
258 EXPECT_EQ(SkRect::MakeWH(100, 50), rect);
259 EXPECT_EQ(SK_ColorBLUE, paint.getColor());
260 expectedMatrix.setTranslate(-SCROLL_X, 50 - SCROLL_Y);
261 EXPECT_EQ(SkRect::MakeLTRB(0, 40, 95, 90), TestUtils::getClipBounds(this));
262 break;
263 default:
264 ADD_FAILURE();
265 }
266 EXPECT_EQ(expectedMatrix, getTotalMatrix());
267 }
268
269 int getIndex() { return mDrawCounter; }
270
271 protected:
272 int mDrawCounter = 0;
273 };
274
275 /**
276 * Construct a tree of nodes, where the root (A) has a receiver background (B), and a child (C)
277 * with a projecting child (P) of its own. P would normally draw between B and C's "background"
278 * draw, but because it is projected backwards, it's drawn in between B and C.
279 *
280 * The parent is scrolled by SCROLL_X/SCROLL_Y, but this does not affect the background
281 * (which isn't affected by scroll).
282 */
283 auto receiverBackground = TestUtils::createSkiaNode(
284 0, 0, 100, 100,
285 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
286 properties.setProjectionReceiver(true);
287 // scroll doesn't apply to background, so undone via translationX/Y
288 // NOTE: translationX/Y only! no other transform properties may be set for a proj
289 // receiver!
290 properties.setTranslationX(SCROLL_X);
291 properties.setTranslationY(SCROLL_Y);
292
293 SkPaint paint;
294 paint.setColor(SK_ColorWHITE);
295 canvas.drawRect(0, 0, 100, 100, paint);
296 },
297 "B");
298
299 auto projectingRipple = TestUtils::createSkiaNode(
300 50, 0, 100, 50,
301 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
302 properties.setProjectBackwards(true);
303 properties.setClipToBounds(false);
304 SkPaint paint;
305 paint.setColor(SK_ColorDKGRAY);
306 canvas.drawRect(-10, -10, 60, 60, paint);
307 },
308 "P");
309 auto child = TestUtils::createSkiaNode(
310 0, 50, 100, 100,
311 [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
312 SkPaint paint;
313 paint.setColor(SK_ColorBLUE);
314 canvas.drawRect(0, 0, 100, 50, paint);
315 canvas.drawRenderNode(projectingRipple.get());
316 },
317 "C");
318 auto parent = TestUtils::createSkiaNode(
319 0, 0, 100, 100,
320 [&receiverBackground, &child](RenderProperties& properties,
321 SkiaRecordingCanvas& canvas) {
322 // Set a rect outline for the projecting ripple to be masked against.
323 properties.mutableOutline().setRoundRect(10, 10, 90, 90, 5, 1.0f);
324
325 canvas.save(SaveFlags::MatrixClip);
326 canvas.translate(-SCROLL_X,
327 -SCROLL_Y); // Apply scroll (note: bg undoes this internally)
328 canvas.drawRenderNode(receiverBackground.get());
329 canvas.drawRenderNode(child.get());
330 canvas.restore();
331 },
332 "A");
333 ContextFactory contextFactory;
334 std::unique_ptr<CanvasContext> canvasContext(
335 CanvasContext::create(renderThread, false, parent.get(), &contextFactory));
336 TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
337 DamageAccumulator damageAccumulator;
338 info.damageAccumulator = &damageAccumulator;
339 parent->prepareTree(info);
340
341 // parent(A) -> (receiverBackground, child)
342 // child(C) -> (rect[0, 0, 100, 50], projectingRipple)
343 // projectingRipple(P) -> (rect[-10, -10, 60, 60]) -> projects backwards
344 // receiverBackground(B) -> (rect[0, 0, 100, 100]) -> projection receiver
345
346 // create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection
347 ProjectionTestCanvas canvas(100, 100);
348 RenderNodeDrawable drawable(parent.get(), &canvas, true);
349 canvas.drawDrawable(&drawable);
350 EXPECT_EQ(3, canvas.getIndex());
351 }
352
RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable,emptyReceiver)353 RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, emptyReceiver) {
354 class ProjectionTestCanvas : public SkCanvas {
355 public:
356 ProjectionTestCanvas(int width, int height) : SkCanvas(width, height) {}
357 void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
358 mDrawCounter++;
359 }
360
361 int getDrawCounter() { return mDrawCounter; }
362
363 private:
364 int mDrawCounter = 0;
365 };
366
367 auto receiverBackground = TestUtils::createSkiaNode(
368 0, 0, 100, 100,
369 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
370 properties.setProjectionReceiver(true);
371 },
372 "B"); // a receiver with an empty display list
373
374 auto projectingRipple = TestUtils::createSkiaNode(
375 0, 0, 100, 100,
376 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
377 properties.setProjectBackwards(true);
378 properties.setClipToBounds(false);
379 SkPaint paint;
380 canvas.drawRect(0, 0, 100, 100, paint);
381 },
382 "P");
383 auto child = TestUtils::createSkiaNode(
384 0, 0, 100, 100,
385 [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
386 SkPaint paint;
387 canvas.drawRect(0, 0, 100, 100, paint);
388 canvas.drawRenderNode(projectingRipple.get());
389 },
390 "C");
391 auto parent = TestUtils::createSkiaNode(
392 0, 0, 100, 100,
393 [&receiverBackground, &child](RenderProperties& properties,
394 SkiaRecordingCanvas& canvas) {
395 canvas.drawRenderNode(receiverBackground.get());
396 canvas.drawRenderNode(child.get());
397 },
398 "A");
399 ContextFactory contextFactory;
400 std::unique_ptr<CanvasContext> canvasContext(
401 CanvasContext::create(renderThread, false, parent.get(), &contextFactory));
402 TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
403 DamageAccumulator damageAccumulator;
404 info.damageAccumulator = &damageAccumulator;
405 parent->prepareTree(info);
406
407 // parent(A) -> (receiverBackground, child)
408 // child(C) -> (rect[0, 0, 100, 100], projectingRipple)
409 // projectingRipple(P) -> (rect[0, 0, 100, 100]) -> projects backwards
410 // receiverBackground(B) -> (empty) -> projection receiver
411
412 // create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection
413 ProjectionTestCanvas canvas(100, 100);
414 RenderNodeDrawable drawable(parent.get(), &canvas, true);
415 canvas.drawDrawable(&drawable);
416 EXPECT_EQ(2, canvas.getDrawCounter());
417 }
418
RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable,projectionHwLayer)419 RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, projectionHwLayer) {
420 /* R is backward projected on B and C is a layer.
421 A
422 / \
423 B C
424 |
425 R
426 */
427 static const int SCROLL_X = 5;
428 static const int SCROLL_Y = 10;
429 static const int CANVAS_WIDTH = 400;
430 static const int CANVAS_HEIGHT = 400;
431 static const int LAYER_WIDTH = 200;
432 static const int LAYER_HEIGHT = 200;
433 class ProjectionTestCanvas : public SkCanvas {
434 public:
435 ProjectionTestCanvas(int* drawCounter)
436 : SkCanvas(CANVAS_WIDTH, CANVAS_HEIGHT), mDrawCounter(drawCounter) {}
437 void onDrawArc(const SkRect&, SkScalar startAngle, SkScalar sweepAngle, bool useCenter,
438 const SkPaint&) override {
439 EXPECT_EQ(0, (*mDrawCounter)++); // part of painting the layer
440 EXPECT_EQ(SkRect::MakeLTRB(0, 0, LAYER_WIDTH, LAYER_HEIGHT),
441 TestUtils::getClipBounds(this));
442 }
443 void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
444 EXPECT_EQ(1, (*mDrawCounter)++);
445 EXPECT_EQ(SkRect::MakeLTRB(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT),
446 TestUtils::getClipBounds(this));
447 }
448 void onDrawOval(const SkRect&, const SkPaint&) override {
449 EXPECT_EQ(2, (*mDrawCounter)++);
450 SkMatrix expectedMatrix;
451 expectedMatrix.setTranslate(100 - SCROLL_X, 100 - SCROLL_Y);
452 EXPECT_EQ(expectedMatrix, getTotalMatrix());
453 EXPECT_EQ(SkRect::MakeLTRB(-85, -80, 295, 300), TestUtils::getLocalClipBounds(this));
454 }
455 int* mDrawCounter;
456 };
457
458 class ProjectionLayer : public SkSurface_Base {
459 public:
460 ProjectionLayer(int* drawCounter)
461 : SkSurface_Base(SkImageInfo::MakeN32Premul(LAYER_WIDTH, LAYER_HEIGHT), nullptr)
462 , mDrawCounter(drawCounter) {}
463 virtual sk_sp<SkImage> onNewImageSnapshot() override {
464 EXPECT_EQ(3, (*mDrawCounter)++);
465 EXPECT_EQ(SkRect::MakeLTRB(100 - SCROLL_X, 100 - SCROLL_Y, 300 - SCROLL_X,
466 300 - SCROLL_Y),
467 TestUtils::getClipBounds(this->getCanvas()));
468 return nullptr;
469 }
470 SkCanvas* onNewCanvas() override { return new ProjectionTestCanvas(mDrawCounter); }
471 sk_sp<SkSurface> onNewSurface(const SkImageInfo&) override { return nullptr; }
472 void onCopyOnWrite(ContentChangeMode) override {}
473 int* mDrawCounter;
474 void onWritePixels(const SkPixmap&, int x, int y) {}
475 };
476
477 auto receiverBackground = TestUtils::createSkiaNode(
478 0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
479 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
480 properties.setProjectionReceiver(true);
481 // scroll doesn't apply to background, so undone via translationX/Y
482 // NOTE: translationX/Y only! no other transform properties may be set for a proj
483 // receiver!
484 properties.setTranslationX(SCROLL_X);
485 properties.setTranslationY(SCROLL_Y);
486
487 canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, SkPaint());
488 },
489 "B"); // B
490 auto projectingRipple = TestUtils::createSkiaNode(
491 0, 0, LAYER_WIDTH, LAYER_HEIGHT,
492 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
493 properties.setProjectBackwards(true);
494 properties.setClipToBounds(false);
495 canvas.drawOval(100, 100, 300, 300, SkPaint()); // drawn mostly out of layer bounds
496 },
497 "R"); // R
498 auto child = TestUtils::createSkiaNode(
499 100, 100, 300, 300,
500 [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
501 canvas.drawRenderNode(projectingRipple.get());
502 canvas.drawArc(0, 0, LAYER_WIDTH, LAYER_HEIGHT, 0.0f, 280.0f, true, SkPaint());
503 },
504 "C"); // C
505 auto parent = TestUtils::createSkiaNode(
506 0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
507 [&receiverBackground, &child](RenderProperties& properties,
508 SkiaRecordingCanvas& canvas) {
509 // Set a rect outline for the projecting ripple to be masked against.
510 properties.mutableOutline().setRoundRect(10, 10, 390, 390, 0, 1.0f);
511 canvas.translate(-SCROLL_X,
512 -SCROLL_Y); // Apply scroll (note: bg undoes this internally)
513 canvas.drawRenderNode(receiverBackground.get());
514 canvas.drawRenderNode(child.get());
515 },
516 "A"); // A
517
518 // prepareTree is required to find, which receivers have backward projected nodes
519 ContextFactory contextFactory;
520 std::unique_ptr<CanvasContext> canvasContext(
521 CanvasContext::create(renderThread, false, parent.get(), &contextFactory));
522 TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
523 DamageAccumulator damageAccumulator;
524 info.damageAccumulator = &damageAccumulator;
525 parent->prepareTree(info);
526
527 int drawCounter = 0;
528 // set a layer after prepareTree to avoid layer logic there
529 child->animatorProperties().mutateLayerProperties().setType(LayerType::RenderLayer);
530 sk_sp<SkSurface> surfaceLayer1(new ProjectionLayer(&drawCounter));
531 child->setLayerSurface(surfaceLayer1);
532 Matrix4 windowTransform;
533 windowTransform.loadTranslate(100, 100, 0);
534 child->getSkiaLayer()->inverseTransformInWindow.loadInverse(windowTransform);
535
536 LayerUpdateQueue layerUpdateQueue;
537 layerUpdateQueue.enqueueLayerWithDamage(child.get(),
538 android::uirenderer::Rect(LAYER_WIDTH, LAYER_HEIGHT));
539 auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
540 pipeline->renderLayersImpl(layerUpdateQueue, true, false);
541 EXPECT_EQ(1, drawCounter); // assert index 0 is drawn on the layer
542
543 RenderNodeDrawable drawable(parent.get(), surfaceLayer1->getCanvas(), true);
544 surfaceLayer1->getCanvas()->drawDrawable(&drawable);
545 EXPECT_EQ(4, drawCounter);
546
547 // clean up layer pointer, so we can safely destruct RenderNode
548 child->setLayerSurface(nullptr);
549 }
550
RENDERTHREAD_TEST(RenderNodeDrawable,projectionChildScroll)551 RENDERTHREAD_TEST(RenderNodeDrawable, projectionChildScroll) {
552 /* R is backward projected on B.
553 A
554 / \
555 B C
556 |
557 R
558 */
559 static const int SCROLL_X = 500000;
560 static const int SCROLL_Y = 0;
561 static const int CANVAS_WIDTH = 400;
562 static const int CANVAS_HEIGHT = 400;
563 class ProjectionChildScrollTestCanvas : public SkCanvas {
564 public:
565 ProjectionChildScrollTestCanvas() : SkCanvas(CANVAS_WIDTH, CANVAS_HEIGHT) {}
566 void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
567 EXPECT_EQ(0, mDrawCounter++);
568 EXPECT_TRUE(getTotalMatrix().isIdentity());
569 }
570 void onDrawOval(const SkRect&, const SkPaint&) override {
571 EXPECT_EQ(1, mDrawCounter++);
572 EXPECT_EQ(SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT), TestUtils::getClipBounds(this));
573 EXPECT_TRUE(getTotalMatrix().isIdentity());
574 }
575 int mDrawCounter = 0;
576 };
577
578 auto receiverBackground = TestUtils::createSkiaNode(
579 0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
580 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
581 properties.setProjectionReceiver(true);
582 canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, SkPaint());
583 },
584 "B"); // B
585 auto projectingRipple = TestUtils::createSkiaNode(
586 0, 0, 200, 200,
587 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
588 // scroll doesn't apply to background, so undone via translationX/Y
589 // NOTE: translationX/Y only! no other transform properties may be set for a proj
590 // receiver!
591 properties.setTranslationX(SCROLL_X);
592 properties.setTranslationY(SCROLL_Y);
593 properties.setProjectBackwards(true);
594 properties.setClipToBounds(false);
595 canvas.drawOval(0, 0, 200, 200, SkPaint());
596 },
597 "R"); // R
598 auto child = TestUtils::createSkiaNode(
599 0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
600 [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
601 // Record time clip will be ignored by projectee
602 canvas.clipRect(100, 100, 300, 300, SkClipOp::kIntersect);
603
604 canvas.translate(-SCROLL_X,
605 -SCROLL_Y); // Apply scroll (note: bg undoes this internally)
606 canvas.drawRenderNode(projectingRipple.get());
607 },
608 "C"); // C
609 auto parent =
610 TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
611 [&receiverBackground, &child](RenderProperties& properties,
612 SkiaRecordingCanvas& canvas) {
613 canvas.drawRenderNode(receiverBackground.get());
614 canvas.drawRenderNode(child.get());
615 },
616 "A"); // A
617
618 // prepareTree is required to find, which receivers have backward projected nodes
619 ContextFactory contextFactory;
620 std::unique_ptr<CanvasContext> canvasContext(
621 CanvasContext::create(renderThread, false, parent.get(), &contextFactory));
622 TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
623 DamageAccumulator damageAccumulator;
624 info.damageAccumulator = &damageAccumulator;
625 parent->prepareTree(info);
626
627 std::unique_ptr<ProjectionChildScrollTestCanvas> canvas(new ProjectionChildScrollTestCanvas());
628 RenderNodeDrawable drawable(parent.get(), canvas.get(), true);
629 canvas->drawDrawable(&drawable);
630 EXPECT_EQ(2, canvas->mDrawCounter);
631 }
632
633 namespace {
drawNode(RenderThread & renderThread,const sp<RenderNode> & renderNode)634 static int drawNode(RenderThread& renderThread, const sp<RenderNode>& renderNode) {
635 ContextFactory contextFactory;
636 std::unique_ptr<CanvasContext> canvasContext(
637 CanvasContext::create(renderThread, false, renderNode.get(), &contextFactory));
638 TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
639 DamageAccumulator damageAccumulator;
640 info.damageAccumulator = &damageAccumulator;
641 renderNode->prepareTree(info);
642
643 // create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection
644 ZReorderCanvas canvas(100, 100);
645 RenderNodeDrawable drawable(renderNode.get(), &canvas, false);
646 canvas.drawDrawable(&drawable);
647 return canvas.getIndex();
648 }
649 }
650
RENDERTHREAD_TEST(RenderNodeDrawable,projectionReorderProjectedInMiddle)651 RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectedInMiddle) {
652 /* R is backward projected on B
653 A
654 / \
655 B C
656 |
657 R
658 */
659 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, [](RenderProperties& props,
660 SkiaRecordingCanvas& canvas) {
661 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
662 props.setProjectionReceiver(true);
663 }); // nodeB
664 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
665 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
666 props.setProjectBackwards(true);
667 props.setClipToBounds(false);
668 }); // nodeR
669 }); // nodeC
670 }); // nodeA
671 EXPECT_EQ(3, drawNode(renderThread, nodeA));
672 }
673
RENDERTHREAD_TEST(RenderNodeDrawable,projectionReorderProjectLast)674 RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectLast) {
675 /* R is backward projected on E
676 A
677 / | \
678 / | \
679 B C E
680 |
681 R
682 */
683 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, [](RenderProperties& props,
684 SkiaRecordingCanvas& canvas) {
685 drawOrderedNode(&canvas, 0, nullptr); // nodeB
686 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
687 drawOrderedNode(&canvas, 3, [](RenderProperties& props,
688 SkiaRecordingCanvas& canvas) { // drawn as 2
689 props.setProjectBackwards(true);
690 props.setClipToBounds(false);
691 }); // nodeR
692 }); // nodeC
693 drawOrderedNode(&canvas, 2,
694 [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // drawn as 3
695 props.setProjectionReceiver(true);
696 }); // nodeE
697 }); // nodeA
698 EXPECT_EQ(4, drawNode(renderThread, nodeA));
699 }
700
RENDERTHREAD_TEST(RenderNodeDrawable,projectionReorderNoReceivable)701 RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderNoReceivable) {
702 /* R is backward projected without receiver
703 A
704 / \
705 B C
706 |
707 R
708 */
709 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, [](RenderProperties& props,
710 SkiaRecordingCanvas& canvas) {
711 drawOrderedNode(&canvas, 0, nullptr); // nodeB
712 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
713 drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
714 // not having a projection receiver is an undefined behavior
715 props.setProjectBackwards(true);
716 props.setClipToBounds(false);
717 }); // nodeR
718 }); // nodeC
719 }); // nodeA
720 EXPECT_EQ(2, drawNode(renderThread, nodeA));
721 }
722
RENDERTHREAD_TEST(RenderNodeDrawable,projectionReorderParentReceivable)723 RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderParentReceivable) {
724 /* R is backward projected on C
725 A
726 / \
727 B C
728 |
729 R
730 */
731 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, [](RenderProperties& props,
732 SkiaRecordingCanvas& canvas) {
733 drawOrderedNode(&canvas, 0, nullptr); // nodeB
734 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
735 props.setProjectionReceiver(true);
736 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
737 props.setProjectBackwards(true);
738 props.setClipToBounds(false);
739 }); // nodeR
740 }); // nodeC
741 }); // nodeA
742 EXPECT_EQ(3, drawNode(renderThread, nodeA));
743 }
744
RENDERTHREAD_TEST(RenderNodeDrawable,projectionReorderSameNodeReceivable)745 RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderSameNodeReceivable) {
746 /* R is backward projected on R
747 A
748 / \
749 B C
750 |
751 R
752 */
753 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, [](RenderProperties& props,
754 SkiaRecordingCanvas& canvas) {
755 drawOrderedNode(&canvas, 0, nullptr); // nodeB
756 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
757 drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
758 // having a node that is projected on itself is an undefined/unexpected behavior
759 props.setProjectionReceiver(true);
760 props.setProjectBackwards(true);
761 props.setClipToBounds(false);
762 }); // nodeR
763 }); // nodeC
764 }); // nodeA
765 EXPECT_EQ(2, drawNode(renderThread, nodeA));
766 }
767
768 // Note: the outcome for this test is different in HWUI
RENDERTHREAD_TEST(RenderNodeDrawable,projectionReorderProjectedSibling)769 RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectedSibling) {
770 /* R is set to project on B, but R is not drawn because projecting on a sibling is not allowed.
771 A
772 /|\
773 / | \
774 B C R
775 */
776 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, [](RenderProperties& props,
777 SkiaRecordingCanvas& canvas) {
778 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
779 props.setProjectionReceiver(true);
780 }); // nodeB
781 drawOrderedNode(&canvas, 1,
782 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {}); // nodeC
783 drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
784 props.setProjectBackwards(true);
785 props.setClipToBounds(false);
786 }); // nodeR
787 }); // nodeA
788 EXPECT_EQ(2, drawNode(renderThread, nodeA));
789 }
790
RENDERTHREAD_TEST(RenderNodeDrawable,projectionReorderProjectedSibling2)791 RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectedSibling2) {
792 /* R is set to project on B, but R is not drawn because projecting on a sibling is not allowed.
793 A
794 |
795 G
796 /|\
797 / | \
798 B C R
799 */
800 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, [](RenderProperties& props,
801 SkiaRecordingCanvas& canvas) {
802 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
803 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
804 props.setProjectionReceiver(true);
805 }); // nodeB
806 drawOrderedNode(&canvas, 2,
807 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {}); // nodeC
808 drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
809 props.setProjectBackwards(true);
810 props.setClipToBounds(false);
811 }); // nodeR
812 }); // nodeG
813 }); // nodeA
814 EXPECT_EQ(3, drawNode(renderThread, nodeA));
815 }
816
RENDERTHREAD_TEST(RenderNodeDrawable,projectionReorderGrandparentReceivable)817 RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderGrandparentReceivable) {
818 /* R is backward projected on B
819 A
820 |
821 B
822 |
823 C
824 |
825 R
826 */
827 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, [](RenderProperties& props,
828 SkiaRecordingCanvas& canvas) {
829 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
830 props.setProjectionReceiver(true);
831 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
832 drawOrderedNode(&canvas, 2,
833 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
834 props.setProjectBackwards(true);
835 props.setClipToBounds(false);
836 }); // nodeR
837 }); // nodeC
838 }); // nodeB
839 }); // nodeA
840 EXPECT_EQ(3, drawNode(renderThread, nodeA));
841 }
842
RENDERTHREAD_TEST(RenderNodeDrawable,projectionReorderTwoReceivables)843 RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderTwoReceivables) {
844 /* B and G are receivables, R is backward projected
845 A
846 / \
847 B C
848 / \
849 G R
850 */
851 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, [](RenderProperties& props,
852 SkiaRecordingCanvas& canvas) {
853 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // B
854 props.setProjectionReceiver(true);
855 }); // nodeB
856 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // C
857 drawOrderedNode(&canvas, 3,
858 [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // G
859 props.setProjectionReceiver(true);
860 }); // nodeG
861 drawOrderedNode(&canvas, 1,
862 [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // R
863 props.setProjectBackwards(true);
864 props.setClipToBounds(false);
865 }); // nodeR
866 }); // nodeC
867 }); // nodeA
868 EXPECT_EQ(4, drawNode(renderThread, nodeA));
869 }
870
RENDERTHREAD_TEST(RenderNodeDrawable,projectionReorderTwoReceivablesLikelyScenario)871 RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderTwoReceivablesLikelyScenario) {
872 /* B and G are receivables, G is backward projected
873 A
874 / \
875 B C
876 / \
877 G R
878 */
879 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, [](RenderProperties& props,
880 SkiaRecordingCanvas& canvas) {
881 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // B
882 props.setProjectionReceiver(true);
883 }); // nodeB
884 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // C
885 drawOrderedNode(&canvas, 1,
886 [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // G
887 props.setProjectionReceiver(true);
888 props.setProjectBackwards(true);
889 props.setClipToBounds(false);
890 }); // nodeG
891 drawOrderedNode(&canvas, 3,
892 [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // R
893 }); // nodeR
894 }); // nodeC
895 }); // nodeA
896 EXPECT_EQ(4, drawNode(renderThread, nodeA));
897 }
898
RENDERTHREAD_TEST(RenderNodeDrawable,projectionReorderTwoReceivablesDeeper)899 RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderTwoReceivablesDeeper) {
900 /* B and G are receivables, R is backward projected
901 A
902 / \
903 B C
904 / \
905 G D
906 |
907 R
908 */
909 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, [](RenderProperties& props,
910 SkiaRecordingCanvas& canvas) {
911 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // B
912 props.setProjectionReceiver(true);
913 }); // nodeB
914 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // C
915 drawOrderedNode(&canvas, 2,
916 [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // G
917 props.setProjectionReceiver(true);
918 }); // nodeG
919 drawOrderedNode(&canvas, 4,
920 [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // D
921 drawOrderedNode(&canvas, 3, [](RenderProperties& props,
922 SkiaRecordingCanvas& canvas) { // R
923 props.setProjectBackwards(true);
924 props.setClipToBounds(false);
925 }); // nodeR
926 }); // nodeD
927 }); // nodeC
928 }); // nodeA
929 EXPECT_EQ(5, drawNode(renderThread, nodeA));
930 }
931
RENDERTHREAD_TEST(RenderNodeDrawable,simple)932 RENDERTHREAD_TEST(RenderNodeDrawable, simple) {
933 static const int CANVAS_WIDTH = 100;
934 static const int CANVAS_HEIGHT = 200;
935 class SimpleTestCanvas : public TestCanvasBase {
936 public:
937 SimpleTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {}
938 void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
939 EXPECT_EQ(0, mDrawCounter++);
940 }
941 void onDrawImage(const SkImage*, SkScalar dx, SkScalar dy, const SkPaint*) override {
942 EXPECT_EQ(1, mDrawCounter++);
943 }
944 };
945
946 auto node = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
947 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
948 sk_sp<Bitmap> bitmap(TestUtils::createBitmap(25, 25));
949 canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
950 SkPaint());
951 canvas.drawBitmap(*bitmap, 10, 10, nullptr);
952 });
953
954 SimpleTestCanvas canvas;
955 RenderNodeDrawable drawable(node.get(), &canvas, true);
956 canvas.drawDrawable(&drawable);
957 EXPECT_EQ(2, canvas.mDrawCounter);
958 }
959
RENDERTHREAD_TEST(RenderNodeDrawable,colorOp_unbounded)960 RENDERTHREAD_TEST(RenderNodeDrawable, colorOp_unbounded) {
961 static const int CANVAS_WIDTH = 200;
962 static const int CANVAS_HEIGHT = 200;
963 class ColorTestCanvas : public TestCanvasBase {
964 public:
965 ColorTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {}
966 void onDrawPaint(const SkPaint&) {
967 switch (mDrawCounter++) {
968 case 0:
969 EXPECT_EQ(SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT),
970 TestUtils::getClipBounds(this));
971 break;
972 case 1:
973 EXPECT_EQ(SkRect::MakeWH(10, 10), TestUtils::getClipBounds(this));
974 break;
975 default:
976 ADD_FAILURE();
977 }
978 }
979 };
980
981 auto unclippedColorView = TestUtils::createSkiaNode(
982 0, 0, 10, 10, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
983 props.setClipToBounds(false);
984 canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
985 });
986
987 auto clippedColorView = TestUtils::createSkiaNode(
988 0, 0, 10, 10, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
989 canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
990 });
991
992 ColorTestCanvas canvas;
993 RenderNodeDrawable drawable(unclippedColorView.get(), &canvas, true);
994 canvas.drawDrawable(&drawable);
995 EXPECT_EQ(1, canvas.mDrawCounter);
996 RenderNodeDrawable drawable2(clippedColorView.get(), &canvas, true);
997 canvas.drawDrawable(&drawable2);
998 EXPECT_EQ(2, canvas.mDrawCounter);
999 }
1000
TEST(RenderNodeDrawable,renderNode)1001 TEST(RenderNodeDrawable, renderNode) {
1002 static const int CANVAS_WIDTH = 200;
1003 static const int CANVAS_HEIGHT = 200;
1004 class RenderNodeTestCanvas : public TestCanvasBase {
1005 public:
1006 RenderNodeTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {}
1007 void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
1008 switch (mDrawCounter++) {
1009 case 0:
1010 EXPECT_EQ(SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT),
1011 TestUtils::getClipBounds(this));
1012 EXPECT_EQ(SK_ColorDKGRAY, paint.getColor());
1013 break;
1014 case 1:
1015 EXPECT_EQ(SkRect::MakeLTRB(50, 50, 150, 150), TestUtils::getClipBounds(this));
1016 EXPECT_EQ(SK_ColorWHITE, paint.getColor());
1017 break;
1018 default:
1019 ADD_FAILURE();
1020 }
1021 }
1022 };
1023
1024 auto child = TestUtils::createSkiaNode(
1025 10, 10, 110, 110, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
1026 SkPaint paint;
1027 paint.setColor(SK_ColorWHITE);
1028 canvas.drawRect(0, 0, 100, 100, paint);
1029 });
1030
1031 auto parent = TestUtils::createSkiaNode(
1032 0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
1033 [&child](RenderProperties& props, SkiaRecordingCanvas& canvas) {
1034 SkPaint paint;
1035 paint.setColor(SK_ColorDKGRAY);
1036 canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, paint);
1037
1038 canvas.save(SaveFlags::MatrixClip);
1039 canvas.translate(40, 40);
1040 canvas.drawRenderNode(child.get());
1041 canvas.restore();
1042 });
1043
1044 RenderNodeTestCanvas canvas;
1045 RenderNodeDrawable drawable(parent.get(), &canvas, true);
1046 canvas.drawDrawable(&drawable);
1047 EXPECT_EQ(2, canvas.mDrawCounter);
1048 }
1049
1050 // Verify that layers are composed with kLow_SkFilterQuality filter quality.
RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable,layerComposeQuality)1051 RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, layerComposeQuality) {
1052 static const int CANVAS_WIDTH = 1;
1053 static const int CANVAS_HEIGHT = 1;
1054 static const int LAYER_WIDTH = 1;
1055 static const int LAYER_HEIGHT = 1;
1056 class FrameTestCanvas : public TestCanvasBase {
1057 public:
1058 FrameTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {}
1059 void onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
1060 const SkPaint* paint, SrcRectConstraint constraint) override {
1061 mDrawCounter++;
1062 EXPECT_EQ(kLow_SkFilterQuality, paint->getFilterQuality());
1063 }
1064 };
1065
1066 auto layerNode = TestUtils::createSkiaNode(
1067 0, 0, LAYER_WIDTH, LAYER_HEIGHT,
1068 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
1069 canvas.drawPaint(SkPaint());
1070 });
1071
1072 layerNode->animatorProperties().mutateLayerProperties().setType(LayerType::RenderLayer);
1073 layerNode->setLayerSurface(SkSurface::MakeRasterN32Premul(LAYER_WIDTH, LAYER_HEIGHT));
1074
1075 FrameTestCanvas canvas;
1076 RenderNodeDrawable drawable(layerNode.get(), &canvas, true);
1077 canvas.drawDrawable(&drawable);
1078 EXPECT_EQ(1, canvas.mDrawCounter); //make sure the layer was composed
1079
1080 // clean up layer pointer, so we can safely destruct RenderNode
1081 layerNode->setLayerSurface(nullptr);
1082 }
1083
TEST(ReorderBarrierDrawable,testShadowMatrix)1084 TEST(ReorderBarrierDrawable, testShadowMatrix) {
1085 static const int CANVAS_WIDTH = 100;
1086 static const int CANVAS_HEIGHT = 100;
1087 static const float TRANSLATE_X = 11.0f;
1088 static const float TRANSLATE_Y = 22.0f;
1089 static const float CASTER_X = 40.0f;
1090 static const float CASTER_Y = 40.0f;
1091 static const float CASTER_WIDTH = 20.0f;
1092 static const float CASTER_HEIGHT = 20.0f;
1093
1094 class ShadowTestCanvas : public SkCanvas {
1095 public:
1096 ShadowTestCanvas(int width, int height) : SkCanvas(width, height) {}
1097 int getIndex() { return mDrawCounter; }
1098
1099 virtual void onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) override {
1100 // expect to draw 2 RenderNodeDrawable, 1 StartReorderBarrierDrawable,
1101 // 1 EndReorderBarrierDrawable
1102 mDrawCounter++;
1103 SkCanvas::onDrawDrawable(drawable, matrix);
1104 }
1105
1106 virtual void didTranslate(SkScalar dx, SkScalar dy) override {
1107 mDrawCounter++;
1108 EXPECT_EQ(dx, TRANSLATE_X);
1109 EXPECT_EQ(dy, TRANSLATE_Y);
1110 }
1111
1112 virtual void didConcat(const SkMatrix& matrix) override {
1113 // This function is invoked by EndReorderBarrierDrawable::drawShadow to apply shadow
1114 // matrix.
1115 mDrawCounter++;
1116 EXPECT_EQ(SkMatrix::MakeTrans(CASTER_X, CASTER_Y), matrix);
1117 EXPECT_EQ(SkMatrix::MakeTrans(CASTER_X + TRANSLATE_X, CASTER_Y + TRANSLATE_Y),
1118 getTotalMatrix());
1119 }
1120
1121 protected:
1122 int mDrawCounter = 0;
1123 };
1124
1125 auto parent = TestUtils::createSkiaNode(
1126 0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
1127 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
1128 canvas.translate(TRANSLATE_X, TRANSLATE_Y);
1129 canvas.insertReorderBarrier(true);
1130
1131 auto node = TestUtils::createSkiaNode(
1132 CASTER_X, CASTER_Y, CASTER_X + CASTER_WIDTH, CASTER_Y + CASTER_HEIGHT,
1133 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
1134 props.setElevation(42);
1135 props.mutableOutline().setRoundRect(0, 0, 20, 20, 5, 1);
1136 props.mutableOutline().setShouldClip(true);
1137 });
1138 canvas.drawRenderNode(node.get());
1139 canvas.insertReorderBarrier(false);
1140 });
1141
1142 // create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection
1143 ShadowTestCanvas canvas(CANVAS_WIDTH, CANVAS_HEIGHT);
1144 RenderNodeDrawable drawable(parent.get(), &canvas, false);
1145 canvas.drawDrawable(&drawable);
1146 EXPECT_EQ(6, canvas.getIndex());
1147 }
1148
1149 // Draw a vector drawable twice but with different bounds and verify correct bounds are used.
RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaRecordingCanvas,drawVectorDrawable)1150 RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaRecordingCanvas, drawVectorDrawable) {
1151 static const int CANVAS_WIDTH = 100;
1152 static const int CANVAS_HEIGHT = 200;
1153 class VectorDrawableTestCanvas : public TestCanvasBase {
1154 public:
1155 VectorDrawableTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {}
1156 void onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
1157 const SkPaint* paint, SrcRectConstraint constraint) override {
1158 const int index = mDrawCounter++;
1159 switch (index) {
1160 case 0:
1161 EXPECT_EQ(dst, SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT));
1162 break;
1163 case 1:
1164 EXPECT_EQ(dst, SkRect::MakeWH(CANVAS_WIDTH/2, CANVAS_HEIGHT));
1165 break;
1166 default:
1167 ADD_FAILURE();
1168 }
1169 }
1170 };
1171
1172 VectorDrawable::Group* group = new VectorDrawable::Group();
1173 sp<VectorDrawableRoot> vectorDrawable(new VectorDrawableRoot(group));
1174 vectorDrawable->mutateStagingProperties()->setScaledSize(CANVAS_WIDTH/10, CANVAS_HEIGHT/10);
1175
1176 auto node = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
1177 [&](RenderProperties& props, SkiaRecordingCanvas& canvas) {
1178 vectorDrawable->mutateStagingProperties()->setBounds(SkRect::MakeWH(CANVAS_WIDTH,
1179 CANVAS_HEIGHT));
1180 canvas.drawVectorDrawable(vectorDrawable.get());
1181 vectorDrawable->mutateStagingProperties()->setBounds(SkRect::MakeWH(CANVAS_WIDTH/2,
1182 CANVAS_HEIGHT));
1183 canvas.drawVectorDrawable(vectorDrawable.get());
1184 });
1185
1186 VectorDrawableTestCanvas canvas;
1187 RenderNodeDrawable drawable(node.get(), &canvas, true);
1188 canvas.drawDrawable(&drawable);
1189 EXPECT_EQ(2, canvas.mDrawCounter);
1190 }
1191