blob: cbea501e3df352585fb2df24d9911563109ac7bb [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
Derek Sollenbergerea1fe9b2017-03-01 13:02:43 -050047 SkLiteDL skLiteDL;
Stan Iliev021693b2016-10-17 16:26:15 -040048 SkLiteRecorder canvas;
Derek Sollenbergerea1fe9b2017-03-01 13:02:43 -050049 canvas.reset(&skLiteDL, SkIRect::MakeWH(1, 1));
Stan Iliev021693b2016-10-17 16:26:15 -040050 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;
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500324 parent->prepareTree(info);
325
326 //parent(A) -> (receiverBackground, child)
327 //child(C) -> (rect[0, 0, 100, 50], projectingRipple)
328 //projectingRipple(P) -> (rect[-10, -10, 60, 60]) -> projects backwards
329 //receiverBackground(B) -> (rect[0, 0, 100, 100]) -> projection receiver
330
331 //create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection
332 ProjectionTestCanvas canvas(100, 100);
333 RenderNodeDrawable drawable(parent.get(), &canvas, true);
334 canvas.drawDrawable(&drawable);
335 EXPECT_EQ(3, canvas.getIndex());
336}
337
338RENDERTHREAD_TEST(RenderNodeDrawable, projectionHwLayer) {
339 /* R is backward projected on B and C is a layer.
340 A
341 / \
342 B C
343 |
344 R
345 */
346 static const int SCROLL_X = 5;
347 static const int SCROLL_Y = 10;
348 static const int CANVAS_WIDTH = 400;
349 static const int CANVAS_HEIGHT = 400;
350 static const int LAYER_WIDTH = 200;
351 static const int LAYER_HEIGHT = 200;
352 class ProjectionTestCanvas : public SkCanvas {
353 public:
Mike Reed6acfe162016-11-18 17:21:09 -0500354 ProjectionTestCanvas(int* drawCounter)
355 : SkCanvas(CANVAS_WIDTH, CANVAS_HEIGHT)
Stan Iliev52771272016-11-17 09:54:38 -0500356 , mDrawCounter(drawCounter)
Mike Reed6acfe162016-11-18 17:21:09 -0500357 {}
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500358 void onDrawArc(const SkRect&, SkScalar startAngle, SkScalar sweepAngle, bool useCenter,
359 const SkPaint&) override {
Mike Reed6acfe162016-11-18 17:21:09 -0500360 EXPECT_EQ(0, (*mDrawCounter)++); //part of painting the layer
Stan Iliev52771272016-11-17 09:54:38 -0500361 EXPECT_EQ(SkRect::MakeLTRB(0, 0, LAYER_WIDTH, LAYER_HEIGHT), TestUtils::getClipBounds(this));
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500362 }
363 void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
Mike Reed6acfe162016-11-18 17:21:09 -0500364 EXPECT_EQ(1, (*mDrawCounter)++);
Stan Iliev52771272016-11-17 09:54:38 -0500365 EXPECT_EQ(SkRect::MakeLTRB(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT), TestUtils::getClipBounds(this));
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500366 }
367 void onDrawOval(const SkRect&, const SkPaint&) override {
Mike Reed6acfe162016-11-18 17:21:09 -0500368 EXPECT_EQ(2, (*mDrawCounter)++);
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500369 SkMatrix expectedMatrix;
370 expectedMatrix.setTranslate(100 - SCROLL_X, 100 - SCROLL_Y);
371 EXPECT_EQ(expectedMatrix, getTotalMatrix());
Stan Iliev52771272016-11-17 09:54:38 -0500372 EXPECT_EQ(SkRect::MakeLTRB(-85, -80, 295, 300), TestUtils::getLocalClipBounds(this));
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500373 }
Mike Reed6acfe162016-11-18 17:21:09 -0500374 int* mDrawCounter;
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500375 };
376
377 class ProjectionLayer : public SkSurface_Base {
378 public:
Mike Reed6acfe162016-11-18 17:21:09 -0500379 ProjectionLayer(int* drawCounter)
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500380 : SkSurface_Base(SkImageInfo::MakeN32Premul(LAYER_WIDTH, LAYER_HEIGHT), nullptr)
Mike Reed6acfe162016-11-18 17:21:09 -0500381 , mDrawCounter(drawCounter) {
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500382 }
383 void onDraw(SkCanvas*, SkScalar x, SkScalar y, const SkPaint*) override {
Mike Reed6acfe162016-11-18 17:21:09 -0500384 EXPECT_EQ(3, (*mDrawCounter)++);
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500385 EXPECT_EQ(SkRect::MakeLTRB(100 - SCROLL_X, 100 - SCROLL_Y, 300 - SCROLL_X,
Stan Iliev52771272016-11-17 09:54:38 -0500386 300 - SCROLL_Y), TestUtils::getClipBounds(this->getCanvas()));
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500387 }
388 SkCanvas* onNewCanvas() override {
Mike Reed6acfe162016-11-18 17:21:09 -0500389 return new ProjectionTestCanvas(mDrawCounter);
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500390 }
391 sk_sp<SkSurface> onNewSurface(const SkImageInfo&) override {
392 return sk_sp<SkSurface>();
393 }
Derek Sollenberger624ad832017-01-11 11:09:48 -0500394 sk_sp<SkImage> onNewImageSnapshot(SkBudgeted) override {
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500395 return sk_sp<SkImage>();
396 }
397 void onCopyOnWrite(ContentChangeMode) override {}
Mike Reed6acfe162016-11-18 17:21:09 -0500398 int* mDrawCounter;
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500399 };
400
401 auto receiverBackground = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
402 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
403 properties.setProjectionReceiver(true);
404 // scroll doesn't apply to background, so undone via translationX/Y
405 // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
406 properties.setTranslationX(SCROLL_X);
407 properties.setTranslationY(SCROLL_Y);
408
409 canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, SkPaint());
410 }, "B"); //B
411 auto projectingRipple = TestUtils::createSkiaNode(0, 0, LAYER_WIDTH, LAYER_HEIGHT,
412 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
413 properties.setProjectBackwards(true);
414 properties.setClipToBounds(false);
415 canvas.drawOval(100, 100, 300, 300, SkPaint()); // drawn mostly out of layer bounds
416 }, "R"); //R
417 auto child = TestUtils::createSkiaNode(100, 100, 300, 300,
418 [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
419 canvas.drawRenderNode(projectingRipple.get());
420 canvas.drawArc(0, 0, LAYER_WIDTH, LAYER_HEIGHT, 0.0f, 280.0f, true, SkPaint());
421 }, "C"); //C
422 auto parent = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
423 [&receiverBackground, &child](RenderProperties& properties,
424 SkiaRecordingCanvas& canvas) {
425 // Set a rect outline for the projecting ripple to be masked against.
426 properties.mutableOutline().setRoundRect(10, 10, 390, 390, 0, 1.0f);
427 canvas.translate(-SCROLL_X, -SCROLL_Y); // Apply scroll (note: bg undoes this internally)
428 canvas.drawRenderNode(receiverBackground.get());
429 canvas.drawRenderNode(child.get());
430 }, "A"); //A
431
432 //prepareTree is required to find, which receivers have backward projected nodes
433 ContextFactory contextFactory;
434 std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create(
435 renderThread, false, parent.get(), &contextFactory));
436 TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
437 DamageAccumulator damageAccumulator;
438 info.damageAccumulator = &damageAccumulator;
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500439 parent->prepareTree(info);
440
Mike Reed6acfe162016-11-18 17:21:09 -0500441 int drawCounter = 0;
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500442 //set a layer after prepareTree to avoid layer logic there
443 child->animatorProperties().mutateLayerProperties().setType(LayerType::RenderLayer);
Mike Reed6acfe162016-11-18 17:21:09 -0500444 sk_sp<SkSurface> surfaceLayer1(new ProjectionLayer(&drawCounter));
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500445 child->setLayerSurface(surfaceLayer1);
446 Matrix4 windowTransform;
447 windowTransform.loadTranslate(100, 100, 0);
448 child->getSkiaLayer()->inverseTransformInWindow.loadInverse(windowTransform);
449
450 LayerUpdateQueue layerUpdateQueue;
451 layerUpdateQueue.enqueueLayerWithDamage(child.get(),
452 android::uirenderer::Rect(LAYER_WIDTH, LAYER_HEIGHT));
453 SkiaPipeline::renderLayersImpl(layerUpdateQueue, true);
Mike Reed6acfe162016-11-18 17:21:09 -0500454 EXPECT_EQ(1, drawCounter); //assert index 0 is drawn on the layer
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500455
Mike Reed6acfe162016-11-18 17:21:09 -0500456 RenderNodeDrawable drawable(parent.get(), surfaceLayer1->getCanvas(), true);
457 surfaceLayer1->getCanvas()->drawDrawable(&drawable);
458 EXPECT_EQ(4, drawCounter);
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500459
460 // clean up layer pointer, so we can safely destruct RenderNode
461 child->setLayerSurface(nullptr);
462}
463
464RENDERTHREAD_TEST(RenderNodeDrawable, projectionChildScroll) {
465 /* R is backward projected on B.
466 A
467 / \
468 B C
469 |
470 R
471 */
472 static const int SCROLL_X = 500000;
473 static const int SCROLL_Y = 0;
474 static const int CANVAS_WIDTH = 400;
475 static const int CANVAS_HEIGHT = 400;
476 class ProjectionChildScrollTestCanvas : public SkCanvas {
477 public:
478 ProjectionChildScrollTestCanvas() : SkCanvas(CANVAS_WIDTH, CANVAS_HEIGHT) {}
479 void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
Stan Iliev52771272016-11-17 09:54:38 -0500480 EXPECT_EQ(0, mDrawCounter++);
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500481 EXPECT_TRUE(getTotalMatrix().isIdentity());
482 }
483 void onDrawOval(const SkRect&, const SkPaint&) override {
Stan Iliev52771272016-11-17 09:54:38 -0500484 EXPECT_EQ(1, mDrawCounter++);
485 EXPECT_EQ(SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT), TestUtils::getClipBounds(this));
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500486 EXPECT_TRUE(getTotalMatrix().isIdentity());
487 }
Stan Iliev52771272016-11-17 09:54:38 -0500488 int mDrawCounter = 0;
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500489 };
490
491 auto receiverBackground = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
492 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
493 properties.setProjectionReceiver(true);
494 canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, SkPaint());
495 }, "B"); //B
496 auto projectingRipple = TestUtils::createSkiaNode(0, 0, 200, 200,
497 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
498 // scroll doesn't apply to background, so undone via translationX/Y
499 // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
500 properties.setTranslationX(SCROLL_X);
501 properties.setTranslationY(SCROLL_Y);
502 properties.setProjectBackwards(true);
503 properties.setClipToBounds(false);
504 canvas.drawOval(0, 0, 200, 200, SkPaint());
505 }, "R"); //R
506 auto child = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
507 [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
508 // Record time clip will be ignored by projectee
Mike Reed6c67f1d2016-12-14 10:29:54 -0500509 canvas.clipRect(100, 100, 300, 300, SkClipOp::kIntersect);
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500510
511 canvas.translate(-SCROLL_X, -SCROLL_Y); // Apply scroll (note: bg undoes this internally)
512 canvas.drawRenderNode(projectingRipple.get());
513 }, "C"); //C
514 auto parent = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
515 [&receiverBackground, &child](RenderProperties& properties,
516 SkiaRecordingCanvas& canvas) {
517 canvas.drawRenderNode(receiverBackground.get());
518 canvas.drawRenderNode(child.get());
519 }, "A"); //A
520
521 //prepareTree is required to find, which receivers have backward projected nodes
522 ContextFactory contextFactory;
523 std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create(
524 renderThread, false, parent.get(), &contextFactory));
525 TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
526 DamageAccumulator damageAccumulator;
527 info.damageAccumulator = &damageAccumulator;
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500528 parent->prepareTree(info);
529
Mike Reed6acfe162016-11-18 17:21:09 -0500530 std::unique_ptr<ProjectionChildScrollTestCanvas> canvas(new ProjectionChildScrollTestCanvas());
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500531 RenderNodeDrawable drawable(parent.get(), canvas.get(), true);
532 canvas->drawDrawable(&drawable);
Stan Iliev52771272016-11-17 09:54:38 -0500533 EXPECT_EQ(2, canvas->mDrawCounter);
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500534}
535
536namespace {
537static int drawNode(RenderThread& renderThread, const sp<RenderNode>& renderNode)
538{
539 ContextFactory contextFactory;
540 std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create(
541 renderThread, false, renderNode.get(), &contextFactory));
542 TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
543 DamageAccumulator damageAccumulator;
544 info.damageAccumulator = &damageAccumulator;
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500545 renderNode->prepareTree(info);
546
547 //create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection
548 ZReorderCanvas canvas(100, 100);
549 RenderNodeDrawable drawable(renderNode.get(), &canvas, false);
550 canvas.drawDrawable(&drawable);
551 return canvas.getIndex();
552}
553}
554
555RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectedInMiddle) {
556 /* R is backward projected on B
557 A
558 / \
559 B C
560 |
561 R
562 */
563 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
564 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
565 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
566 props.setProjectionReceiver(true);
567 } ); //nodeB
568 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
569 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
570 props.setProjectBackwards(true);
571 props.setClipToBounds(false);
572 } ); //nodeR
573 } ); //nodeC
574 }); //nodeA
575 EXPECT_EQ(3, drawNode(renderThread, nodeA));
576}
577
578RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectLast) {
579 /* R is backward projected on E
580 A
581 / | \
582 / | \
583 B C E
584 |
585 R
586 */
587 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
588 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
589 drawOrderedNode(&canvas, 0, nullptr); //nodeB
590 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
591 drawOrderedNode(&canvas, 3, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //drawn as 2
592 props.setProjectBackwards(true);
593 props.setClipToBounds(false);
594 } ); //nodeR
595 } ); //nodeC
596 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //drawn as 3
597 props.setProjectionReceiver(true);
598 } ); //nodeE
599 }); //nodeA
600 EXPECT_EQ(4, drawNode(renderThread, nodeA));
601}
602
603RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderNoReceivable) {
604 /* R is backward projected without receiver
605 A
606 / \
607 B C
608 |
609 R
610 */
611 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
612 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
613 drawOrderedNode(&canvas, 0, nullptr); //nodeB
614 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
615 drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
616 //not having a projection receiver is an undefined behavior
617 props.setProjectBackwards(true);
618 props.setClipToBounds(false);
619 } ); //nodeR
620 } ); //nodeC
621 }); //nodeA
622 EXPECT_EQ(2, drawNode(renderThread, nodeA));
623}
624
625RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderParentReceivable) {
626 /* R is backward projected on C
627 A
628 / \
629 B C
630 |
631 R
632 */
633 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
634 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
635 drawOrderedNode(&canvas, 0, nullptr); //nodeB
636 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
637 props.setProjectionReceiver(true);
638 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
639 props.setProjectBackwards(true);
640 props.setClipToBounds(false);
641 } ); //nodeR
642 } ); //nodeC
643 }); //nodeA
644 EXPECT_EQ(3, drawNode(renderThread, nodeA));
645}
646
647RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderSameNodeReceivable) {
648 /* R is backward projected on R
649 A
650 / \
651 B C
652 |
653 R
654 */
655 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
656 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
657 drawOrderedNode(&canvas, 0, nullptr); //nodeB
658 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
659 drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
660 //having a node that is projected on itself is an undefined/unexpected behavior
661 props.setProjectionReceiver(true);
662 props.setProjectBackwards(true);
663 props.setClipToBounds(false);
664 } ); //nodeR
665 } ); //nodeC
666 }); //nodeA
667 EXPECT_EQ(2, drawNode(renderThread, nodeA));
668}
669
670//Note: the outcome for this test is different in HWUI
671RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectedSibling) {
672 /* R is set to project on B, but R is not drawn because projecting on a sibling is not allowed.
673 A
674 /|\
675 / | \
676 B C R
677 */
678 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
679 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
680 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
681 props.setProjectionReceiver(true);
682 } ); //nodeB
683 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
684 } ); //nodeC
685 drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
686 props.setProjectBackwards(true);
687 props.setClipToBounds(false);
688 } ); //nodeR
689 }); //nodeA
690 EXPECT_EQ(2, drawNode(renderThread, nodeA));
691}
692
693RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectedSibling2) {
694 /* R is set to project on B, but R is not drawn because projecting on a sibling is not allowed.
695 A
696 |
697 G
698 /|\
699 / | \
700 B C R
701 */
702 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
703 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
704 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
705 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
706 props.setProjectionReceiver(true);
707 } ); //nodeB
708 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
709 } ); //nodeC
710 drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
711 props.setProjectBackwards(true);
712 props.setClipToBounds(false);
713 } ); //nodeR
714 } ); //nodeG
715 }); //nodeA
716 EXPECT_EQ(3, drawNode(renderThread, nodeA));
717}
718
719RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderGrandparentReceivable) {
720 /* R is backward projected on B
721 A
722 |
723 B
724 |
725 C
726 |
727 R
728 */
729 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
730 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
731 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
732 props.setProjectionReceiver(true);
733 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
734 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
735 props.setProjectBackwards(true);
736 props.setClipToBounds(false);
737 } ); //nodeR
738 } ); //nodeC
739 } ); //nodeB
740 }); //nodeA
741 EXPECT_EQ(3, drawNode(renderThread, nodeA));
742}
743
744RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderTwoReceivables) {
745 /* B and G are receivables, R is backward projected
746 A
747 / \
748 B C
749 / \
750 G R
751 */
752 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
753 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
754 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //B
755 props.setProjectionReceiver(true);
756 } ); //nodeB
757 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //C
758 drawOrderedNode(&canvas, 3, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //G
759 props.setProjectionReceiver(true);
760 } ); //nodeG
761 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //R
762 props.setProjectBackwards(true);
763 props.setClipToBounds(false);
764 } ); //nodeR
765 } ); //nodeC
766 }); //nodeA
767 EXPECT_EQ(4, drawNode(renderThread, nodeA));
768}
769
770RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderTwoReceivablesLikelyScenario) {
771 /* B and G are receivables, G is backward projected
772 A
773 / \
774 B C
775 / \
776 G R
777 */
778 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
779 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
780 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //B
781 props.setProjectionReceiver(true);
782 } ); //nodeB
783 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //C
784 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //G
785 props.setProjectionReceiver(true);
786 props.setProjectBackwards(true);
787 props.setClipToBounds(false);
788 } ); //nodeG
789 drawOrderedNode(&canvas, 3, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //R
790 } ); //nodeR
791 } ); //nodeC
792 }); //nodeA
793 EXPECT_EQ(4, drawNode(renderThread, nodeA));
794}
795
796RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderTwoReceivablesDeeper) {
797 /* B and G are receivables, R is backward projected
798 A
799 / \
800 B C
801 / \
802 G D
803 |
804 R
805 */
806 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
807 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
808 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //B
809 props.setProjectionReceiver(true);
810 } ); //nodeB
811 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //C
812 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //G
813 props.setProjectionReceiver(true);
814 } ); //nodeG
815 drawOrderedNode(&canvas, 4, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //D
816 drawOrderedNode(&canvas, 3, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //R
817 props.setProjectBackwards(true);
818 props.setClipToBounds(false);
819 } ); //nodeR
820 } ); //nodeD
821 } ); //nodeC
822 }); //nodeA
823 EXPECT_EQ(5, drawNode(renderThread, nodeA));
Stan Iliev021693b2016-10-17 16:26:15 -0400824}
Stan Iliev52771272016-11-17 09:54:38 -0500825
826RENDERTHREAD_TEST(RenderNodeDrawable, simple) {
827 static const int CANVAS_WIDTH = 100;
828 static const int CANVAS_HEIGHT = 200;
829 class SimpleTestCanvas : public TestCanvasBase {
830 public:
831 SimpleTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {
832 }
833 void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
834 EXPECT_EQ(0, mDrawCounter++);
835 }
836 void onDrawImage(const SkImage*, SkScalar dx, SkScalar dy, const SkPaint*) override {
837 EXPECT_EQ(1, mDrawCounter++);
838 }
839 };
840
841 auto node = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
842 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
843 sk_sp<Bitmap> bitmap(TestUtils::createBitmap(25, 25));
844 canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, SkPaint());
845 canvas.drawBitmap(*bitmap, 10, 10, nullptr);
846 });
847
848 SimpleTestCanvas canvas;
849 RenderNodeDrawable drawable(node.get(), &canvas, true);
850 canvas.drawDrawable(&drawable);
851 EXPECT_EQ(2, canvas.mDrawCounter);
852}
853
854RENDERTHREAD_TEST(RenderNodeDrawable, colorOp_unbounded) {
855 static const int CANVAS_WIDTH = 200;
856 static const int CANVAS_HEIGHT = 200;
857 class ColorTestCanvas : public TestCanvasBase {
858 public:
859 ColorTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {
860 }
861 void onDrawPaint(const SkPaint&) {
862 switch (mDrawCounter++) {
863 case 0:
Stan Ilievc1db0e02017-01-23 13:47:35 -0500864 EXPECT_EQ(SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT),
865 TestUtils::getClipBounds(this));
Stan Iliev52771272016-11-17 09:54:38 -0500866 break;
867 case 1:
868 EXPECT_EQ(SkRect::MakeWH(10, 10), TestUtils::getClipBounds(this));
869 break;
870 default:
871 ADD_FAILURE();
872 }
873 }
874 };
875
876 auto unclippedColorView = TestUtils::createSkiaNode(0, 0, 10, 10,
877 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
878 props.setClipToBounds(false);
879 canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
880 });
881
882 auto clippedColorView = TestUtils::createSkiaNode(0, 0, 10, 10,
883 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
884 canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
885 });
886
887 ColorTestCanvas canvas;
888 RenderNodeDrawable drawable(unclippedColorView.get(), &canvas, true);
889 canvas.drawDrawable(&drawable);
890 EXPECT_EQ(1, canvas.mDrawCounter);
891 RenderNodeDrawable drawable2(clippedColorView.get(), &canvas, true);
892 canvas.drawDrawable(&drawable2);
893 EXPECT_EQ(2, canvas.mDrawCounter);
894}
895
896TEST(RenderNodeDrawable, renderNode) {
897 static const int CANVAS_WIDTH = 200;
898 static const int CANVAS_HEIGHT = 200;
899 class RenderNodeTestCanvas : public TestCanvasBase {
900 public:
901 RenderNodeTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {
902 }
903 void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
904 switch(mDrawCounter++) {
905 case 0:
906 EXPECT_EQ(SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT), TestUtils::getClipBounds(this));
907 EXPECT_EQ(SK_ColorDKGRAY, paint.getColor());
908 break;
909 case 1:
910 EXPECT_EQ(SkRect::MakeLTRB(50, 50, 150, 150), TestUtils::getClipBounds(this));
911 EXPECT_EQ(SK_ColorWHITE, paint.getColor());
912 break;
913 default:
914 ADD_FAILURE();
915 }
916 }
917 };
918
919 auto child = TestUtils::createSkiaNode(10, 10, 110, 110,
920 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
921 SkPaint paint;
922 paint.setColor(SK_ColorWHITE);
923 canvas.drawRect(0, 0, 100, 100, paint);
924 });
925
926 auto parent = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
927 [&child](RenderProperties& props, SkiaRecordingCanvas& canvas) {
928 SkPaint paint;
929 paint.setColor(SK_ColorDKGRAY);
930 canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, paint);
931
932 canvas.save(SaveFlags::MatrixClip);
933 canvas.translate(40, 40);
934 canvas.drawRenderNode(child.get());
935 canvas.restore();
936 });
937
938 RenderNodeTestCanvas canvas;
939 RenderNodeDrawable drawable(parent.get(), &canvas, true);
940 canvas.drawDrawable(&drawable);
941 EXPECT_EQ(2, canvas.mDrawCounter);
942}
943