blob: 0906cc84ab7ad360a1d08186edae759a8371260c [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 {
Stan Iliev68885e32016-12-14 11:18:34 -0500171static SkRect getRecorderClipBounds(const SkiaRecordingCanvas& recorder) {
172 SkRect clipBounds;
173 recorder.getClipBounds(&clipBounds);
174 return clipBounds;
175}
176
177static SkMatrix getRecorderMatrix(const SkiaRecordingCanvas& recorder) {
178 SkMatrix matrix;
179 recorder.getMatrix(&matrix);
180 return matrix;
181}
182}
183
184TEST(RenderNodeDrawable, saveLayerClipAndMatrixRestore)
185{
186 auto surface = SkSurface::MakeRasterN32Premul(400, 800);
187 SkCanvas& canvas = *surface->getCanvas();
188 canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
189 ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorWHITE);
190
191 auto rootNode = TestUtils::createSkiaNode(0, 0, 400, 800,
192 [](RenderProperties& props, SkiaRecordingCanvas& recorder) {
193 SkPaint layerPaint;
194 ASSERT_EQ(SkRect::MakeLTRB(0, 0, 400, 800), getRecorderClipBounds(recorder));
195 EXPECT_TRUE(getRecorderMatrix(recorder).isIdentity());
196
197 //note we don't pass SaveFlags::MatrixClip, but matrix and clip will be saved
198 recorder.saveLayer(0, 0, 400, 400, &layerPaint, SaveFlags::ClipToLayer);
199 ASSERT_EQ(SkRect::MakeLTRB(0, 0, 400, 400), getRecorderClipBounds(recorder));
200 EXPECT_TRUE(getRecorderMatrix(recorder).isIdentity());
201
202 recorder.clipRect(50, 50, 350, 350, SkClipOp::kIntersect);
203 ASSERT_EQ(SkRect::MakeLTRB(50, 50, 350, 350), getRecorderClipBounds(recorder));
204
205 recorder.translate(300.0f, 400.0f);
206 EXPECT_EQ(SkMatrix::MakeTrans(300.0f, 400.0f), getRecorderMatrix(recorder));
207
208 recorder.restore();
209 ASSERT_EQ(SkRect::MakeLTRB(0, 0, 400, 800), getRecorderClipBounds(recorder));
210 EXPECT_TRUE(getRecorderMatrix(recorder).isIdentity());
211
212 SkPaint paint;
213 paint.setAntiAlias(true);
214 paint.setColor(SK_ColorGREEN);
215 recorder.drawRect(0.0f, 400.0f, 400.0f, 800.0f, paint);
216 });
217
218 RenderNodeDrawable drawable(rootNode.get(), &canvas, true);
219 canvas.drawDrawable(&drawable);
220 ASSERT_EQ(SK_ColorGREEN, TestUtils::getColor(surface, 200, 600));
221}
222
223namespace {
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500224class ContextFactory : public IContextFactory {
225public:
226 virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) override {
227 return new AnimationContext(clock);
228 }
229};
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500230} // end anonymous namespace
Stan Iliev021693b2016-10-17 16:26:15 -0400231
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500232RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorder) {
233 static const int SCROLL_X = 5;
234 static const int SCROLL_Y = 10;
235 class ProjectionTestCanvas : public SkCanvas {
236 public:
237 ProjectionTestCanvas(int width, int height) : SkCanvas(width, height) {}
238 void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
Stan Iliev52771272016-11-17 09:54:38 -0500239 const int index = mDrawCounter++;
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500240 SkMatrix expectedMatrix;;
241 switch (index) {
242 case 0: //this is node "B"
243 EXPECT_EQ(SkRect::MakeWH(100, 100), rect);
244 EXPECT_EQ(SK_ColorWHITE, paint.getColor());
245 expectedMatrix.reset();
Stan Iliev52771272016-11-17 09:54:38 -0500246 EXPECT_EQ(SkRect::MakeLTRB(0, 0, 100, 100), TestUtils::getClipBounds(this));
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500247 break;
248 case 1: //this is node "P"
249 EXPECT_EQ(SkRect::MakeLTRB(-10, -10, 60, 60), rect);
250 EXPECT_EQ(SK_ColorDKGRAY, paint.getColor());
251 expectedMatrix.setTranslate(50 - SCROLL_X, 50 - SCROLL_Y);
Stan Iliev52771272016-11-17 09:54:38 -0500252 EXPECT_EQ(SkRect::MakeLTRB(-35, -30, 45, 50), TestUtils::getLocalClipBounds(this));
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500253 break;
254 case 2: //this is node "C"
255 EXPECT_EQ(SkRect::MakeWH(100, 50), rect);
256 EXPECT_EQ(SK_ColorBLUE, paint.getColor());
257 expectedMatrix.setTranslate(-SCROLL_X, 50 - SCROLL_Y);
Stan Iliev52771272016-11-17 09:54:38 -0500258 EXPECT_EQ(SkRect::MakeLTRB(0, 40, 95, 90), TestUtils::getClipBounds(this));
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500259 break;
260 default:
261 ADD_FAILURE();
262 }
263 EXPECT_EQ(expectedMatrix, getTotalMatrix());
264 }
Stan Iliev021693b2016-10-17 16:26:15 -0400265
Stan Iliev52771272016-11-17 09:54:38 -0500266 int getIndex() { return mDrawCounter; }
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500267 protected:
Stan Iliev52771272016-11-17 09:54:38 -0500268 int mDrawCounter = 0;
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500269 };
Stan Iliev021693b2016-10-17 16:26:15 -0400270
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500271 /**
272 * Construct a tree of nodes, where the root (A) has a receiver background (B), and a child (C)
273 * with a projecting child (P) of its own. P would normally draw between B and C's "background"
274 * draw, but because it is projected backwards, it's drawn in between B and C.
275 *
276 * The parent is scrolled by SCROLL_X/SCROLL_Y, but this does not affect the background
277 * (which isn't affected by scroll).
278 */
279 auto receiverBackground = TestUtils::createSkiaNode(0, 0, 100, 100,
280 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
281 properties.setProjectionReceiver(true);
282 // scroll doesn't apply to background, so undone via translationX/Y
283 // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
284 properties.setTranslationX(SCROLL_X);
285 properties.setTranslationY(SCROLL_Y);
Stan Iliev021693b2016-10-17 16:26:15 -0400286
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500287 SkPaint paint;
288 paint.setColor(SK_ColorWHITE);
289 canvas.drawRect(0, 0, 100, 100, paint);
290 }, "B");
291
292 auto projectingRipple = TestUtils::createSkiaNode(50, 0, 100, 50,
293 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
294 properties.setProjectBackwards(true);
295 properties.setClipToBounds(false);
296 SkPaint paint;
297 paint.setColor(SK_ColorDKGRAY);
298 canvas.drawRect(-10, -10, 60, 60, paint);
299 }, "P");
300 auto child = TestUtils::createSkiaNode(0, 50, 100, 100,
301 [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
302 SkPaint paint;
303 paint.setColor(SK_ColorBLUE);
304 canvas.drawRect(0, 0, 100, 50, paint);
305 canvas.drawRenderNode(projectingRipple.get());
306 }, "C");
307 auto parent = TestUtils::createSkiaNode(0, 0, 100, 100,
308 [&receiverBackground, &child](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
309 // Set a rect outline for the projecting ripple to be masked against.
310 properties.mutableOutline().setRoundRect(10, 10, 90, 90, 5, 1.0f);
311
312 canvas.save(SaveFlags::MatrixClip);
313 canvas.translate(-SCROLL_X, -SCROLL_Y); // Apply scroll (note: bg undoes this internally)
314 canvas.drawRenderNode(receiverBackground.get());
315 canvas.drawRenderNode(child.get());
316 canvas.restore();
317 }, "A");
318 ContextFactory contextFactory;
319 std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create(
320 renderThread, false, parent.get(), &contextFactory));
321 TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
322 DamageAccumulator damageAccumulator;
323 info.damageAccumulator = &damageAccumulator;
324 info.observer = nullptr;
325 parent->prepareTree(info);
326
327 //parent(A) -> (receiverBackground, child)
328 //child(C) -> (rect[0, 0, 100, 50], projectingRipple)
329 //projectingRipple(P) -> (rect[-10, -10, 60, 60]) -> projects backwards
330 //receiverBackground(B) -> (rect[0, 0, 100, 100]) -> projection receiver
331
332 //create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection
333 ProjectionTestCanvas canvas(100, 100);
334 RenderNodeDrawable drawable(parent.get(), &canvas, true);
335 canvas.drawDrawable(&drawable);
336 EXPECT_EQ(3, canvas.getIndex());
337}
338
339RENDERTHREAD_TEST(RenderNodeDrawable, projectionHwLayer) {
340 /* R is backward projected on B and C is a layer.
341 A
342 / \
343 B C
344 |
345 R
346 */
347 static const int SCROLL_X = 5;
348 static const int SCROLL_Y = 10;
349 static const int CANVAS_WIDTH = 400;
350 static const int CANVAS_HEIGHT = 400;
351 static const int LAYER_WIDTH = 200;
352 static const int LAYER_HEIGHT = 200;
353 class ProjectionTestCanvas : public SkCanvas {
354 public:
Mike Reed6acfe162016-11-18 17:21:09 -0500355 ProjectionTestCanvas(int* drawCounter)
356 : SkCanvas(CANVAS_WIDTH, CANVAS_HEIGHT)
Stan Iliev52771272016-11-17 09:54:38 -0500357 , mDrawCounter(drawCounter)
Mike Reed6acfe162016-11-18 17:21:09 -0500358 {}
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500359 void onDrawArc(const SkRect&, SkScalar startAngle, SkScalar sweepAngle, bool useCenter,
360 const SkPaint&) override {
Mike Reed6acfe162016-11-18 17:21:09 -0500361 EXPECT_EQ(0, (*mDrawCounter)++); //part of painting the layer
Stan Iliev52771272016-11-17 09:54:38 -0500362 EXPECT_EQ(SkRect::MakeLTRB(0, 0, LAYER_WIDTH, LAYER_HEIGHT), TestUtils::getClipBounds(this));
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500363 }
364 void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
Mike Reed6acfe162016-11-18 17:21:09 -0500365 EXPECT_EQ(1, (*mDrawCounter)++);
Stan Iliev52771272016-11-17 09:54:38 -0500366 EXPECT_EQ(SkRect::MakeLTRB(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT), TestUtils::getClipBounds(this));
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500367 }
368 void onDrawOval(const SkRect&, const SkPaint&) override {
Mike Reed6acfe162016-11-18 17:21:09 -0500369 EXPECT_EQ(2, (*mDrawCounter)++);
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500370 SkMatrix expectedMatrix;
371 expectedMatrix.setTranslate(100 - SCROLL_X, 100 - SCROLL_Y);
372 EXPECT_EQ(expectedMatrix, getTotalMatrix());
Stan Iliev52771272016-11-17 09:54:38 -0500373 EXPECT_EQ(SkRect::MakeLTRB(-85, -80, 295, 300), TestUtils::getLocalClipBounds(this));
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500374 }
Mike Reed6acfe162016-11-18 17:21:09 -0500375 int* mDrawCounter;
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500376 };
377
378 class ProjectionLayer : public SkSurface_Base {
379 public:
Mike Reed6acfe162016-11-18 17:21:09 -0500380 ProjectionLayer(int* drawCounter)
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500381 : SkSurface_Base(SkImageInfo::MakeN32Premul(LAYER_WIDTH, LAYER_HEIGHT), nullptr)
Mike Reed6acfe162016-11-18 17:21:09 -0500382 , mDrawCounter(drawCounter) {
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500383 }
384 void onDraw(SkCanvas*, SkScalar x, SkScalar y, const SkPaint*) override {
Mike Reed6acfe162016-11-18 17:21:09 -0500385 EXPECT_EQ(3, (*mDrawCounter)++);
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500386 EXPECT_EQ(SkRect::MakeLTRB(100 - SCROLL_X, 100 - SCROLL_Y, 300 - SCROLL_X,
Stan Iliev52771272016-11-17 09:54:38 -0500387 300 - SCROLL_Y), TestUtils::getClipBounds(this->getCanvas()));
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500388 }
389 SkCanvas* onNewCanvas() override {
Mike Reed6acfe162016-11-18 17:21:09 -0500390 return new ProjectionTestCanvas(mDrawCounter);
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500391 }
392 sk_sp<SkSurface> onNewSurface(const SkImageInfo&) override {
393 return sk_sp<SkSurface>();
394 }
395 sk_sp<SkImage> onNewImageSnapshot(SkBudgeted, SkCopyPixelsMode) override {
396 return sk_sp<SkImage>();
397 }
398 void onCopyOnWrite(ContentChangeMode) override {}
Mike Reed6acfe162016-11-18 17:21:09 -0500399 int* mDrawCounter;
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500400 };
401
402 auto receiverBackground = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
403 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
404 properties.setProjectionReceiver(true);
405 // scroll doesn't apply to background, so undone via translationX/Y
406 // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
407 properties.setTranslationX(SCROLL_X);
408 properties.setTranslationY(SCROLL_Y);
409
410 canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, SkPaint());
411 }, "B"); //B
412 auto projectingRipple = TestUtils::createSkiaNode(0, 0, LAYER_WIDTH, LAYER_HEIGHT,
413 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
414 properties.setProjectBackwards(true);
415 properties.setClipToBounds(false);
416 canvas.drawOval(100, 100, 300, 300, SkPaint()); // drawn mostly out of layer bounds
417 }, "R"); //R
418 auto child = TestUtils::createSkiaNode(100, 100, 300, 300,
419 [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
420 canvas.drawRenderNode(projectingRipple.get());
421 canvas.drawArc(0, 0, LAYER_WIDTH, LAYER_HEIGHT, 0.0f, 280.0f, true, SkPaint());
422 }, "C"); //C
423 auto parent = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
424 [&receiverBackground, &child](RenderProperties& properties,
425 SkiaRecordingCanvas& canvas) {
426 // Set a rect outline for the projecting ripple to be masked against.
427 properties.mutableOutline().setRoundRect(10, 10, 390, 390, 0, 1.0f);
428 canvas.translate(-SCROLL_X, -SCROLL_Y); // Apply scroll (note: bg undoes this internally)
429 canvas.drawRenderNode(receiverBackground.get());
430 canvas.drawRenderNode(child.get());
431 }, "A"); //A
432
433 //prepareTree is required to find, which receivers have backward projected nodes
434 ContextFactory contextFactory;
435 std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create(
436 renderThread, false, parent.get(), &contextFactory));
437 TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
438 DamageAccumulator damageAccumulator;
439 info.damageAccumulator = &damageAccumulator;
440 info.observer = nullptr;
441 parent->prepareTree(info);
442
Mike Reed6acfe162016-11-18 17:21:09 -0500443 int drawCounter = 0;
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500444 //set a layer after prepareTree to avoid layer logic there
445 child->animatorProperties().mutateLayerProperties().setType(LayerType::RenderLayer);
Mike Reed6acfe162016-11-18 17:21:09 -0500446 sk_sp<SkSurface> surfaceLayer1(new ProjectionLayer(&drawCounter));
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500447 child->setLayerSurface(surfaceLayer1);
448 Matrix4 windowTransform;
449 windowTransform.loadTranslate(100, 100, 0);
450 child->getSkiaLayer()->inverseTransformInWindow.loadInverse(windowTransform);
451
452 LayerUpdateQueue layerUpdateQueue;
453 layerUpdateQueue.enqueueLayerWithDamage(child.get(),
454 android::uirenderer::Rect(LAYER_WIDTH, LAYER_HEIGHT));
455 SkiaPipeline::renderLayersImpl(layerUpdateQueue, true);
Mike Reed6acfe162016-11-18 17:21:09 -0500456 EXPECT_EQ(1, drawCounter); //assert index 0 is drawn on the layer
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500457
Mike Reed6acfe162016-11-18 17:21:09 -0500458 RenderNodeDrawable drawable(parent.get(), surfaceLayer1->getCanvas(), true);
459 surfaceLayer1->getCanvas()->drawDrawable(&drawable);
460 EXPECT_EQ(4, drawCounter);
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500461
462 // clean up layer pointer, so we can safely destruct RenderNode
463 child->setLayerSurface(nullptr);
464}
465
466RENDERTHREAD_TEST(RenderNodeDrawable, projectionChildScroll) {
467 /* R is backward projected on B.
468 A
469 / \
470 B C
471 |
472 R
473 */
474 static const int SCROLL_X = 500000;
475 static const int SCROLL_Y = 0;
476 static const int CANVAS_WIDTH = 400;
477 static const int CANVAS_HEIGHT = 400;
478 class ProjectionChildScrollTestCanvas : public SkCanvas {
479 public:
480 ProjectionChildScrollTestCanvas() : SkCanvas(CANVAS_WIDTH, CANVAS_HEIGHT) {}
481 void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
Stan Iliev52771272016-11-17 09:54:38 -0500482 EXPECT_EQ(0, mDrawCounter++);
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500483 EXPECT_TRUE(getTotalMatrix().isIdentity());
484 }
485 void onDrawOval(const SkRect&, const SkPaint&) override {
Stan Iliev52771272016-11-17 09:54:38 -0500486 EXPECT_EQ(1, mDrawCounter++);
487 EXPECT_EQ(SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT), TestUtils::getClipBounds(this));
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500488 EXPECT_TRUE(getTotalMatrix().isIdentity());
489 }
Stan Iliev52771272016-11-17 09:54:38 -0500490 int mDrawCounter = 0;
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500491 };
492
493 auto receiverBackground = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
494 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
495 properties.setProjectionReceiver(true);
496 canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, SkPaint());
497 }, "B"); //B
498 auto projectingRipple = TestUtils::createSkiaNode(0, 0, 200, 200,
499 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
500 // scroll doesn't apply to background, so undone via translationX/Y
501 // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
502 properties.setTranslationX(SCROLL_X);
503 properties.setTranslationY(SCROLL_Y);
504 properties.setProjectBackwards(true);
505 properties.setClipToBounds(false);
506 canvas.drawOval(0, 0, 200, 200, SkPaint());
507 }, "R"); //R
508 auto child = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
509 [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
510 // Record time clip will be ignored by projectee
Mike Reed6c67f1d2016-12-14 10:29:54 -0500511 canvas.clipRect(100, 100, 300, 300, SkClipOp::kIntersect);
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500512
513 canvas.translate(-SCROLL_X, -SCROLL_Y); // Apply scroll (note: bg undoes this internally)
514 canvas.drawRenderNode(projectingRipple.get());
515 }, "C"); //C
516 auto parent = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
517 [&receiverBackground, &child](RenderProperties& properties,
518 SkiaRecordingCanvas& canvas) {
519 canvas.drawRenderNode(receiverBackground.get());
520 canvas.drawRenderNode(child.get());
521 }, "A"); //A
522
523 //prepareTree is required to find, which receivers have backward projected nodes
524 ContextFactory contextFactory;
525 std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create(
526 renderThread, false, parent.get(), &contextFactory));
527 TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
528 DamageAccumulator damageAccumulator;
529 info.damageAccumulator = &damageAccumulator;
530 info.observer = nullptr;
531 parent->prepareTree(info);
532
Mike Reed6acfe162016-11-18 17:21:09 -0500533 std::unique_ptr<ProjectionChildScrollTestCanvas> canvas(new ProjectionChildScrollTestCanvas());
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500534 RenderNodeDrawable drawable(parent.get(), canvas.get(), true);
535 canvas->drawDrawable(&drawable);
Stan Iliev52771272016-11-17 09:54:38 -0500536 EXPECT_EQ(2, canvas->mDrawCounter);
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500537}
538
539namespace {
540static int drawNode(RenderThread& renderThread, const sp<RenderNode>& renderNode)
541{
542 ContextFactory contextFactory;
543 std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create(
544 renderThread, false, renderNode.get(), &contextFactory));
545 TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
546 DamageAccumulator damageAccumulator;
547 info.damageAccumulator = &damageAccumulator;
548 info.observer = nullptr;
549 renderNode->prepareTree(info);
550
551 //create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection
552 ZReorderCanvas canvas(100, 100);
553 RenderNodeDrawable drawable(renderNode.get(), &canvas, false);
554 canvas.drawDrawable(&drawable);
555 return canvas.getIndex();
556}
557}
558
559RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectedInMiddle) {
560 /* R is backward projected on B
561 A
562 / \
563 B C
564 |
565 R
566 */
567 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
568 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
569 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
570 props.setProjectionReceiver(true);
571 } ); //nodeB
572 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
573 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
574 props.setProjectBackwards(true);
575 props.setClipToBounds(false);
576 } ); //nodeR
577 } ); //nodeC
578 }); //nodeA
579 EXPECT_EQ(3, drawNode(renderThread, nodeA));
580}
581
582RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectLast) {
583 /* R is backward projected on E
584 A
585 / | \
586 / | \
587 B C E
588 |
589 R
590 */
591 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
592 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
593 drawOrderedNode(&canvas, 0, nullptr); //nodeB
594 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
595 drawOrderedNode(&canvas, 3, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //drawn as 2
596 props.setProjectBackwards(true);
597 props.setClipToBounds(false);
598 } ); //nodeR
599 } ); //nodeC
600 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //drawn as 3
601 props.setProjectionReceiver(true);
602 } ); //nodeE
603 }); //nodeA
604 EXPECT_EQ(4, drawNode(renderThread, nodeA));
605}
606
607RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderNoReceivable) {
608 /* R is backward projected without receiver
609 A
610 / \
611 B C
612 |
613 R
614 */
615 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
616 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
617 drawOrderedNode(&canvas, 0, nullptr); //nodeB
618 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
619 drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
620 //not having a projection receiver is an undefined behavior
621 props.setProjectBackwards(true);
622 props.setClipToBounds(false);
623 } ); //nodeR
624 } ); //nodeC
625 }); //nodeA
626 EXPECT_EQ(2, drawNode(renderThread, nodeA));
627}
628
629RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderParentReceivable) {
630 /* R is backward projected on C
631 A
632 / \
633 B C
634 |
635 R
636 */
637 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
638 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
639 drawOrderedNode(&canvas, 0, nullptr); //nodeB
640 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
641 props.setProjectionReceiver(true);
642 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
643 props.setProjectBackwards(true);
644 props.setClipToBounds(false);
645 } ); //nodeR
646 } ); //nodeC
647 }); //nodeA
648 EXPECT_EQ(3, drawNode(renderThread, nodeA));
649}
650
651RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderSameNodeReceivable) {
652 /* R is backward projected on R
653 A
654 / \
655 B C
656 |
657 R
658 */
659 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
660 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
661 drawOrderedNode(&canvas, 0, nullptr); //nodeB
662 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
663 drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
664 //having a node that is projected on itself is an undefined/unexpected behavior
665 props.setProjectionReceiver(true);
666 props.setProjectBackwards(true);
667 props.setClipToBounds(false);
668 } ); //nodeR
669 } ); //nodeC
670 }); //nodeA
671 EXPECT_EQ(2, drawNode(renderThread, nodeA));
672}
673
674//Note: the outcome for this test is different in HWUI
675RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectedSibling) {
676 /* R is set to project on B, but R is not drawn because projecting on a sibling is not allowed.
677 A
678 /|\
679 / | \
680 B C R
681 */
682 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
683 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
684 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
685 props.setProjectionReceiver(true);
686 } ); //nodeB
687 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
688 } ); //nodeC
689 drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
690 props.setProjectBackwards(true);
691 props.setClipToBounds(false);
692 } ); //nodeR
693 }); //nodeA
694 EXPECT_EQ(2, drawNode(renderThread, nodeA));
695}
696
697RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectedSibling2) {
698 /* R is set to project on B, but R is not drawn because projecting on a sibling is not allowed.
699 A
700 |
701 G
702 /|\
703 / | \
704 B C R
705 */
706 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
707 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
708 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
709 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
710 props.setProjectionReceiver(true);
711 } ); //nodeB
712 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
713 } ); //nodeC
714 drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
715 props.setProjectBackwards(true);
716 props.setClipToBounds(false);
717 } ); //nodeR
718 } ); //nodeG
719 }); //nodeA
720 EXPECT_EQ(3, drawNode(renderThread, nodeA));
721}
722
723RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderGrandparentReceivable) {
724 /* R is backward projected on B
725 A
726 |
727 B
728 |
729 C
730 |
731 R
732 */
733 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
734 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
735 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
736 props.setProjectionReceiver(true);
737 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
738 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
739 props.setProjectBackwards(true);
740 props.setClipToBounds(false);
741 } ); //nodeR
742 } ); //nodeC
743 } ); //nodeB
744 }); //nodeA
745 EXPECT_EQ(3, drawNode(renderThread, nodeA));
746}
747
748RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderTwoReceivables) {
749 /* B and G are receivables, R is backward projected
750 A
751 / \
752 B C
753 / \
754 G R
755 */
756 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
757 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
758 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //B
759 props.setProjectionReceiver(true);
760 } ); //nodeB
761 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //C
762 drawOrderedNode(&canvas, 3, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //G
763 props.setProjectionReceiver(true);
764 } ); //nodeG
765 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //R
766 props.setProjectBackwards(true);
767 props.setClipToBounds(false);
768 } ); //nodeR
769 } ); //nodeC
770 }); //nodeA
771 EXPECT_EQ(4, drawNode(renderThread, nodeA));
772}
773
774RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderTwoReceivablesLikelyScenario) {
775 /* B and G are receivables, G is backward projected
776 A
777 / \
778 B C
779 / \
780 G R
781 */
782 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
783 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
784 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //B
785 props.setProjectionReceiver(true);
786 } ); //nodeB
787 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //C
788 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //G
789 props.setProjectionReceiver(true);
790 props.setProjectBackwards(true);
791 props.setClipToBounds(false);
792 } ); //nodeG
793 drawOrderedNode(&canvas, 3, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //R
794 } ); //nodeR
795 } ); //nodeC
796 }); //nodeA
797 EXPECT_EQ(4, drawNode(renderThread, nodeA));
798}
799
800RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderTwoReceivablesDeeper) {
801 /* B and G are receivables, R is backward projected
802 A
803 / \
804 B C
805 / \
806 G D
807 |
808 R
809 */
810 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
811 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
812 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //B
813 props.setProjectionReceiver(true);
814 } ); //nodeB
815 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //C
816 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //G
817 props.setProjectionReceiver(true);
818 } ); //nodeG
819 drawOrderedNode(&canvas, 4, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //D
820 drawOrderedNode(&canvas, 3, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //R
821 props.setProjectBackwards(true);
822 props.setClipToBounds(false);
823 } ); //nodeR
824 } ); //nodeD
825 } ); //nodeC
826 }); //nodeA
827 EXPECT_EQ(5, drawNode(renderThread, nodeA));
Stan Iliev021693b2016-10-17 16:26:15 -0400828}
Stan Iliev52771272016-11-17 09:54:38 -0500829
830RENDERTHREAD_TEST(RenderNodeDrawable, simple) {
831 static const int CANVAS_WIDTH = 100;
832 static const int CANVAS_HEIGHT = 200;
833 class SimpleTestCanvas : public TestCanvasBase {
834 public:
835 SimpleTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {
836 }
837 void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
838 EXPECT_EQ(0, mDrawCounter++);
839 }
840 void onDrawImage(const SkImage*, SkScalar dx, SkScalar dy, const SkPaint*) override {
841 EXPECT_EQ(1, mDrawCounter++);
842 }
843 };
844
845 auto node = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
846 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
847 sk_sp<Bitmap> bitmap(TestUtils::createBitmap(25, 25));
848 canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, SkPaint());
849 canvas.drawBitmap(*bitmap, 10, 10, nullptr);
850 });
851
852 SimpleTestCanvas canvas;
853 RenderNodeDrawable drawable(node.get(), &canvas, true);
854 canvas.drawDrawable(&drawable);
855 EXPECT_EQ(2, canvas.mDrawCounter);
856}
857
858RENDERTHREAD_TEST(RenderNodeDrawable, colorOp_unbounded) {
859 static const int CANVAS_WIDTH = 200;
860 static const int CANVAS_HEIGHT = 200;
861 class ColorTestCanvas : public TestCanvasBase {
862 public:
863 ColorTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {
864 }
865 void onDrawPaint(const SkPaint&) {
866 switch (mDrawCounter++) {
867 case 0:
868 // While this mirrors FrameBuilder::colorOp_unbounded, this value is different
869 // because there is no root (root is clipped in SkiaPipeline::renderFrame).
870 // SkiaPipeline.clipped and clip_replace verify the root clip.
871 EXPECT_TRUE(TestUtils::getClipBounds(this).isEmpty());
872 break;
873 case 1:
874 EXPECT_EQ(SkRect::MakeWH(10, 10), TestUtils::getClipBounds(this));
875 break;
876 default:
877 ADD_FAILURE();
878 }
879 }
880 };
881
882 auto unclippedColorView = TestUtils::createSkiaNode(0, 0, 10, 10,
883 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
884 props.setClipToBounds(false);
885 canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
886 });
887
888 auto clippedColorView = TestUtils::createSkiaNode(0, 0, 10, 10,
889 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
890 canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
891 });
892
893 ColorTestCanvas canvas;
894 RenderNodeDrawable drawable(unclippedColorView.get(), &canvas, true);
895 canvas.drawDrawable(&drawable);
896 EXPECT_EQ(1, canvas.mDrawCounter);
897 RenderNodeDrawable drawable2(clippedColorView.get(), &canvas, true);
898 canvas.drawDrawable(&drawable2);
899 EXPECT_EQ(2, canvas.mDrawCounter);
900}
901
902TEST(RenderNodeDrawable, renderNode) {
903 static const int CANVAS_WIDTH = 200;
904 static const int CANVAS_HEIGHT = 200;
905 class RenderNodeTestCanvas : public TestCanvasBase {
906 public:
907 RenderNodeTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {
908 }
909 void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
910 switch(mDrawCounter++) {
911 case 0:
912 EXPECT_EQ(SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT), TestUtils::getClipBounds(this));
913 EXPECT_EQ(SK_ColorDKGRAY, paint.getColor());
914 break;
915 case 1:
916 EXPECT_EQ(SkRect::MakeLTRB(50, 50, 150, 150), TestUtils::getClipBounds(this));
917 EXPECT_EQ(SK_ColorWHITE, paint.getColor());
918 break;
919 default:
920 ADD_FAILURE();
921 }
922 }
923 };
924
925 auto child = TestUtils::createSkiaNode(10, 10, 110, 110,
926 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
927 SkPaint paint;
928 paint.setColor(SK_ColorWHITE);
929 canvas.drawRect(0, 0, 100, 100, paint);
930 });
931
932 auto parent = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
933 [&child](RenderProperties& props, SkiaRecordingCanvas& canvas) {
934 SkPaint paint;
935 paint.setColor(SK_ColorDKGRAY);
936 canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, paint);
937
938 canvas.save(SaveFlags::MatrixClip);
939 canvas.translate(40, 40);
940 canvas.drawRenderNode(child.get());
941 canvas.restore();
942 });
943
944 RenderNodeTestCanvas canvas;
945 RenderNodeDrawable drawable(parent.get(), &canvas, true);
946 canvas.drawDrawable(&drawable);
947 EXPECT_EQ(2, canvas.mDrawCounter);
948}
949