blob: d182d78da00ad7e5afa11ffe07e5702e3d71bbde [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 Ilieve9d00122017-09-19 12:07:10 -040025#include "pipeline/skia/SkiaOpenGLPipeline.h"
Stan Iliev021693b2016-10-17 16:26:15 -040026#include "pipeline/skia/SkiaRecordingCanvas.h"
27#include "renderthread/CanvasContext.h"
28#include "tests/common/TestUtils.h"
29#include "SkiaCanvas.h"
Stan Ilievdb45a4b2016-11-08 14:18:31 -050030#include <SkSurface_Base.h>
Stan Iliev021693b2016-10-17 16:26:15 -040031#include <SkLiteRecorder.h>
Stan Ilievdb45a4b2016-11-08 14:18:31 -050032#include <SkClipStack.h>
Stan Iliev52771272016-11-17 09:54:38 -050033#include "FatalTestCanvas.h"
Stan Iliev021693b2016-10-17 16:26:15 -040034#include <string.h>
35
36
37using namespace android;
38using namespace android::uirenderer;
39using namespace android::uirenderer::renderthread;
40using namespace android::uirenderer::skiapipeline;
41
Stan Iliev021693b2016-10-17 16:26:15 -040042TEST(RenderNodeDrawable, create) {
43 auto rootNode = TestUtils::createNode(0, 0, 200, 400,
44 [](RenderProperties& props, Canvas& canvas) {
45 canvas.drawColor(Color::Red_500, SkBlendMode::kSrcOver);
46 });
47
Derek Sollenbergerea1fe9b2017-03-01 13:02:43 -050048 SkLiteDL skLiteDL;
Stan Iliev021693b2016-10-17 16:26:15 -040049 SkLiteRecorder canvas;
Derek Sollenbergerea1fe9b2017-03-01 13:02:43 -050050 canvas.reset(&skLiteDL, SkIRect::MakeWH(1, 1));
Stan Iliev021693b2016-10-17 16:26:15 -040051 canvas.translate(100, 100);
52 RenderNodeDrawable drawable(rootNode.get(), &canvas);
53
54 ASSERT_EQ(drawable.getRenderNode(), rootNode.get());
55 ASSERT_EQ(&drawable.getNodeProperties(), &rootNode->properties());
56 ASSERT_EQ(drawable.getRecordedMatrix(), canvas.getTotalMatrix());
57}
58
Stan Ilievdb45a4b2016-11-08 14:18:31 -050059namespace {
60
Stan Iliev2f06e8a2016-11-02 15:29:03 -040061static void drawOrderedRect(Canvas* canvas, uint8_t expectedDrawOrder) {
62 SkPaint paint;
63 // order put in blue channel, transparent so overlapped content doesn't get rejected
64 paint.setColor(SkColorSetARGB(1, 0, 0, expectedDrawOrder));
65 canvas->drawRect(0, 0, 100, 100, paint);
Stan Iliev021693b2016-10-17 16:26:15 -040066}
67
Stan Iliev2f06e8a2016-11-02 15:29:03 -040068static void drawOrderedNode(Canvas* canvas, uint8_t expectedDrawOrder, float z) {
69 auto node = TestUtils::createSkiaNode(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}
Stan Iliev021693b2016-10-17 16:26:15 -040076
Stan Ilievdb45a4b2016-11-08 14:18:31 -050077static void drawOrderedNode(Canvas* canvas, uint8_t expectedDrawOrder,
78 std::function<void(RenderProperties& props, SkiaRecordingCanvas& canvas)> setup) {
79 auto node = TestUtils::createSkiaNode(0, 0, 100, 100,
80 [expectedDrawOrder, setup](RenderProperties& props, SkiaRecordingCanvas& canvas) {
81 drawOrderedRect(&canvas, expectedDrawOrder);
82 if (setup) {
83 setup(props, canvas);
Stan Iliev2f06e8a2016-11-02 15:29:03 -040084 }
Stan Ilievdb45a4b2016-11-08 14:18:31 -050085 });
86 canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership
87}
88
89class ZReorderCanvas : public SkCanvas {
90public:
91 ZReorderCanvas(int width, int height) : SkCanvas(width, height) {}
92 void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
93 int expectedOrder = SkColorGetB(paint.getColor()); // extract order from blue channel
Stan Iliev52771272016-11-17 09:54:38 -050094 EXPECT_EQ(expectedOrder, mDrawCounter++) << "An op was drawn out of order";
Stan Ilievdb45a4b2016-11-08 14:18:31 -050095 }
Stan Iliev52771272016-11-17 09:54:38 -050096 int getIndex() { return mDrawCounter; }
Stan Ilievdb45a4b2016-11-08 14:18:31 -050097protected:
Stan Iliev52771272016-11-17 09:54:38 -050098 int mDrawCounter = 0;
Stan Ilievdb45a4b2016-11-08 14:18:31 -050099};
100
101} // end anonymous namespace
102
103TEST(RenderNodeDrawable, zReorder) {
Stan Iliev021693b2016-10-17 16:26:15 -0400104
Stan Iliev2f06e8a2016-11-02 15:29:03 -0400105 auto parent = TestUtils::createSkiaNode(0, 0, 100, 100,
106 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
Stan Iliev347691f2016-12-01 12:25:07 -0500107 canvas.insertReorderBarrier(true);
108 canvas.insertReorderBarrier(false);
Stan Iliev2f06e8a2016-11-02 15:29:03 -0400109 drawOrderedNode(&canvas, 0, 10.0f); // in reorder=false at this point, so played inorder
110 drawOrderedRect(&canvas, 1);
111 canvas.insertReorderBarrier(true);
112 drawOrderedNode(&canvas, 6, 2.0f);
113 drawOrderedRect(&canvas, 3);
114 drawOrderedNode(&canvas, 4, 0.0f);
115 drawOrderedRect(&canvas, 5);
116 drawOrderedNode(&canvas, 2, -2.0f);
117 drawOrderedNode(&canvas, 7, 2.0f);
118 canvas.insertReorderBarrier(false);
119 drawOrderedRect(&canvas, 8);
120 drawOrderedNode(&canvas, 9, -10.0f); // in reorder=false at this point, so played inorder
Stan Iliev88e08912016-11-22 18:19:29 -0500121 canvas.insertReorderBarrier(true); //reorder a node ahead of drawrect op
122 drawOrderedRect(&canvas, 11);
123 drawOrderedNode(&canvas, 10, -1.0f);
124 canvas.insertReorderBarrier(false);
125 canvas.insertReorderBarrier(true); //test with two empty reorder sections
126 canvas.insertReorderBarrier(true);
127 canvas.insertReorderBarrier(false);
128 drawOrderedRect(&canvas, 12);
Stan Iliev2f06e8a2016-11-02 15:29:03 -0400129 });
Stan Iliev021693b2016-10-17 16:26:15 -0400130
Stan Iliev2f06e8a2016-11-02 15:29:03 -0400131 //create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection
132 ZReorderCanvas canvas(100, 100);
133 RenderNodeDrawable drawable(parent.get(), &canvas, false);
134 canvas.drawDrawable(&drawable);
Stan Iliev88e08912016-11-22 18:19:29 -0500135 EXPECT_EQ(13, canvas.getIndex());
Stan Iliev021693b2016-10-17 16:26:15 -0400136}
137
138TEST(RenderNodeDrawable, composeOnLayer)
139{
140 auto surface = SkSurface::MakeRasterN32Premul(1, 1);
141 SkCanvas& canvas = *surface->getCanvas();
142 canvas.drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
143 ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
144
Stan Iliev500a0c32016-10-26 10:30:09 -0400145 auto rootNode = TestUtils::createSkiaNode(0, 0, 1, 1,
Stan Iliev021693b2016-10-17 16:26:15 -0400146 [](RenderProperties& props, SkiaRecordingCanvas& recorder) {
147 recorder.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);
148 });
149
150 //attach a layer to the render node
151 auto surfaceLayer = SkSurface::MakeRasterN32Premul(1, 1);
152 auto canvas2 = surfaceLayer->getCanvas();
153 canvas2->drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
Stan Iliev500a0c32016-10-26 10:30:09 -0400154 rootNode->setLayerSurface(surfaceLayer);
Stan Iliev021693b2016-10-17 16:26:15 -0400155
156 RenderNodeDrawable drawable1(rootNode.get(), &canvas, false);
157 canvas.drawDrawable(&drawable1);
158 ASSERT_EQ(SK_ColorRED, TestUtils::getColor(surface, 0, 0));
159
160 RenderNodeDrawable drawable2(rootNode.get(), &canvas, true);
161 canvas.drawDrawable(&drawable2);
162 ASSERT_EQ(SK_ColorWHITE, TestUtils::getColor(surface, 0, 0));
163
164 RenderNodeDrawable drawable3(rootNode.get(), &canvas, false);
165 canvas.drawDrawable(&drawable3);
166 ASSERT_EQ(SK_ColorRED, TestUtils::getColor(surface, 0, 0));
167
Stan Iliev500a0c32016-10-26 10:30:09 -0400168 rootNode->setLayerSurface(sk_sp<SkSurface>());
Stan Iliev021693b2016-10-17 16:26:15 -0400169}
170
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500171namespace {
Stan Iliev68885e32016-12-14 11:18:34 -0500172static SkRect getRecorderClipBounds(const SkiaRecordingCanvas& recorder) {
173 SkRect clipBounds;
174 recorder.getClipBounds(&clipBounds);
175 return clipBounds;
176}
177
178static SkMatrix getRecorderMatrix(const SkiaRecordingCanvas& recorder) {
179 SkMatrix matrix;
180 recorder.getMatrix(&matrix);
181 return matrix;
182}
183}
184
185TEST(RenderNodeDrawable, saveLayerClipAndMatrixRestore)
186{
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(0, 0, 400, 800,
193 [](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
224namespace {
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500225class ContextFactory : public IContextFactory {
226public:
227 virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) override {
228 return new AnimationContext(clock);
229 }
230};
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500231} // end anonymous namespace
Stan Iliev021693b2016-10-17 16:26:15 -0400232
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500233RENDERTHREAD_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 {
Stan Iliev52771272016-11-17 09:54:38 -0500240 const int index = mDrawCounter++;
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500241 SkMatrix expectedMatrix;;
242 switch (index) {
243 case 0: //this is node "B"
244 EXPECT_EQ(SkRect::MakeWH(100, 100), rect);
245 EXPECT_EQ(SK_ColorWHITE, paint.getColor());
246 expectedMatrix.reset();
Stan Iliev52771272016-11-17 09:54:38 -0500247 EXPECT_EQ(SkRect::MakeLTRB(0, 0, 100, 100), TestUtils::getClipBounds(this));
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500248 break;
249 case 1: //this is node "P"
250 EXPECT_EQ(SkRect::MakeLTRB(-10, -10, 60, 60), rect);
251 EXPECT_EQ(SK_ColorDKGRAY, paint.getColor());
252 expectedMatrix.setTranslate(50 - SCROLL_X, 50 - SCROLL_Y);
Stan Iliev52771272016-11-17 09:54:38 -0500253 EXPECT_EQ(SkRect::MakeLTRB(-35, -30, 45, 50), TestUtils::getLocalClipBounds(this));
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500254 break;
255 case 2: //this is node "C"
256 EXPECT_EQ(SkRect::MakeWH(100, 50), rect);
257 EXPECT_EQ(SK_ColorBLUE, paint.getColor());
258 expectedMatrix.setTranslate(-SCROLL_X, 50 - SCROLL_Y);
Stan Iliev52771272016-11-17 09:54:38 -0500259 EXPECT_EQ(SkRect::MakeLTRB(0, 40, 95, 90), TestUtils::getClipBounds(this));
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500260 break;
261 default:
262 ADD_FAILURE();
263 }
264 EXPECT_EQ(expectedMatrix, getTotalMatrix());
265 }
Stan Iliev021693b2016-10-17 16:26:15 -0400266
Stan Iliev52771272016-11-17 09:54:38 -0500267 int getIndex() { return mDrawCounter; }
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500268 protected:
Stan Iliev52771272016-11-17 09:54:38 -0500269 int mDrawCounter = 0;
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500270 };
Stan Iliev021693b2016-10-17 16:26:15 -0400271
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500272 /**
273 * Construct a tree of nodes, where the root (A) has a receiver background (B), and a child (C)
274 * with a projecting child (P) of its own. P would normally draw between B and C's "background"
275 * draw, but because it is projected backwards, it's drawn in between B and C.
276 *
277 * The parent is scrolled by SCROLL_X/SCROLL_Y, but this does not affect the background
278 * (which isn't affected by scroll).
279 */
280 auto receiverBackground = TestUtils::createSkiaNode(0, 0, 100, 100,
281 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
282 properties.setProjectionReceiver(true);
283 // scroll doesn't apply to background, so undone via translationX/Y
284 // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
285 properties.setTranslationX(SCROLL_X);
286 properties.setTranslationY(SCROLL_Y);
Stan Iliev021693b2016-10-17 16:26:15 -0400287
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500288 SkPaint paint;
289 paint.setColor(SK_ColorWHITE);
290 canvas.drawRect(0, 0, 100, 100, paint);
291 }, "B");
292
293 auto projectingRipple = TestUtils::createSkiaNode(50, 0, 100, 50,
294 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
295 properties.setProjectBackwards(true);
296 properties.setClipToBounds(false);
297 SkPaint paint;
298 paint.setColor(SK_ColorDKGRAY);
299 canvas.drawRect(-10, -10, 60, 60, paint);
300 }, "P");
301 auto child = TestUtils::createSkiaNode(0, 50, 100, 100,
302 [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
303 SkPaint paint;
304 paint.setColor(SK_ColorBLUE);
305 canvas.drawRect(0, 0, 100, 50, paint);
306 canvas.drawRenderNode(projectingRipple.get());
307 }, "C");
308 auto parent = TestUtils::createSkiaNode(0, 0, 100, 100,
309 [&receiverBackground, &child](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
310 // Set a rect outline for the projecting ripple to be masked against.
311 properties.mutableOutline().setRoundRect(10, 10, 90, 90, 5, 1.0f);
312
313 canvas.save(SaveFlags::MatrixClip);
314 canvas.translate(-SCROLL_X, -SCROLL_Y); // Apply scroll (note: bg undoes this internally)
315 canvas.drawRenderNode(receiverBackground.get());
316 canvas.drawRenderNode(child.get());
317 canvas.restore();
318 }, "A");
319 ContextFactory contextFactory;
320 std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create(
321 renderThread, false, parent.get(), &contextFactory));
322 TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
323 DamageAccumulator damageAccumulator;
324 info.damageAccumulator = &damageAccumulator;
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500325 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
Stan Ilieve9d00122017-09-19 12:07:10 -0400339RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, projectionHwLayer) {
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500340 /* 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 {
Robert Phillips3c01a072017-03-23 11:24:40 -0400393 return nullptr;
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500394 }
Robert Phillips3c01a072017-03-23 11:24:40 -0400395 sk_sp<SkImage> onNewImageSnapshot() override {
396 return nullptr;
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500397 }
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;
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500440 parent->prepareTree(info);
441
Mike Reed6acfe162016-11-18 17:21:09 -0500442 int drawCounter = 0;
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500443 //set a layer after prepareTree to avoid layer logic there
444 child->animatorProperties().mutateLayerProperties().setType(LayerType::RenderLayer);
Mike Reed6acfe162016-11-18 17:21:09 -0500445 sk_sp<SkSurface> surfaceLayer1(new ProjectionLayer(&drawCounter));
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500446 child->setLayerSurface(surfaceLayer1);
447 Matrix4 windowTransform;
448 windowTransform.loadTranslate(100, 100, 0);
449 child->getSkiaLayer()->inverseTransformInWindow.loadInverse(windowTransform);
450
451 LayerUpdateQueue layerUpdateQueue;
452 layerUpdateQueue.enqueueLayerWithDamage(child.get(),
453 android::uirenderer::Rect(LAYER_WIDTH, LAYER_HEIGHT));
Stan Ilieve9d00122017-09-19 12:07:10 -0400454 auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
455 pipeline->renderLayersImpl(layerUpdateQueue, true, false);
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;
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500530 parent->prepareTree(info);
531
Mike Reed6acfe162016-11-18 17:21:09 -0500532 std::unique_ptr<ProjectionChildScrollTestCanvas> canvas(new ProjectionChildScrollTestCanvas());
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500533 RenderNodeDrawable drawable(parent.get(), canvas.get(), true);
534 canvas->drawDrawable(&drawable);
Stan Iliev52771272016-11-17 09:54:38 -0500535 EXPECT_EQ(2, canvas->mDrawCounter);
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500536}
537
538namespace {
539static int drawNode(RenderThread& renderThread, const sp<RenderNode>& renderNode)
540{
541 ContextFactory contextFactory;
542 std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create(
543 renderThread, false, renderNode.get(), &contextFactory));
544 TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
545 DamageAccumulator damageAccumulator;
546 info.damageAccumulator = &damageAccumulator;
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500547 renderNode->prepareTree(info);
548
549 //create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection
550 ZReorderCanvas canvas(100, 100);
551 RenderNodeDrawable drawable(renderNode.get(), &canvas, false);
552 canvas.drawDrawable(&drawable);
553 return canvas.getIndex();
554}
555}
556
557RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectedInMiddle) {
558 /* R is backward projected on B
559 A
560 / \
561 B C
562 |
563 R
564 */
565 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
566 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
567 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
568 props.setProjectionReceiver(true);
569 } ); //nodeB
570 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
571 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
572 props.setProjectBackwards(true);
573 props.setClipToBounds(false);
574 } ); //nodeR
575 } ); //nodeC
576 }); //nodeA
577 EXPECT_EQ(3, drawNode(renderThread, nodeA));
578}
579
580RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectLast) {
581 /* R is backward projected on E
582 A
583 / | \
584 / | \
585 B C E
586 |
587 R
588 */
589 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
590 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
591 drawOrderedNode(&canvas, 0, nullptr); //nodeB
592 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
593 drawOrderedNode(&canvas, 3, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //drawn as 2
594 props.setProjectBackwards(true);
595 props.setClipToBounds(false);
596 } ); //nodeR
597 } ); //nodeC
598 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //drawn as 3
599 props.setProjectionReceiver(true);
600 } ); //nodeE
601 }); //nodeA
602 EXPECT_EQ(4, drawNode(renderThread, nodeA));
603}
604
605RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderNoReceivable) {
606 /* R is backward projected without receiver
607 A
608 / \
609 B C
610 |
611 R
612 */
613 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
614 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
615 drawOrderedNode(&canvas, 0, nullptr); //nodeB
616 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
617 drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
618 //not having a projection receiver is an undefined behavior
619 props.setProjectBackwards(true);
620 props.setClipToBounds(false);
621 } ); //nodeR
622 } ); //nodeC
623 }); //nodeA
624 EXPECT_EQ(2, drawNode(renderThread, nodeA));
625}
626
627RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderParentReceivable) {
628 /* R is backward projected on C
629 A
630 / \
631 B C
632 |
633 R
634 */
635 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
636 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
637 drawOrderedNode(&canvas, 0, nullptr); //nodeB
638 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
639 props.setProjectionReceiver(true);
640 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
641 props.setProjectBackwards(true);
642 props.setClipToBounds(false);
643 } ); //nodeR
644 } ); //nodeC
645 }); //nodeA
646 EXPECT_EQ(3, drawNode(renderThread, nodeA));
647}
648
649RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderSameNodeReceivable) {
650 /* R is backward projected on R
651 A
652 / \
653 B C
654 |
655 R
656 */
657 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
658 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
659 drawOrderedNode(&canvas, 0, nullptr); //nodeB
660 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
661 drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
662 //having a node that is projected on itself is an undefined/unexpected behavior
663 props.setProjectionReceiver(true);
664 props.setProjectBackwards(true);
665 props.setClipToBounds(false);
666 } ); //nodeR
667 } ); //nodeC
668 }); //nodeA
669 EXPECT_EQ(2, drawNode(renderThread, nodeA));
670}
671
672//Note: the outcome for this test is different in HWUI
673RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectedSibling) {
674 /* R is set to project on B, but R is not drawn because projecting on a sibling is not allowed.
675 A
676 /|\
677 / | \
678 B C R
679 */
680 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
681 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
682 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
683 props.setProjectionReceiver(true);
684 } ); //nodeB
685 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
686 } ); //nodeC
687 drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
688 props.setProjectBackwards(true);
689 props.setClipToBounds(false);
690 } ); //nodeR
691 }); //nodeA
692 EXPECT_EQ(2, drawNode(renderThread, nodeA));
693}
694
695RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectedSibling2) {
696 /* R is set to project on B, but R is not drawn because projecting on a sibling is not allowed.
697 A
698 |
699 G
700 /|\
701 / | \
702 B C R
703 */
704 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
705 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
706 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
707 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
708 props.setProjectionReceiver(true);
709 } ); //nodeB
710 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
711 } ); //nodeC
712 drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
713 props.setProjectBackwards(true);
714 props.setClipToBounds(false);
715 } ); //nodeR
716 } ); //nodeG
717 }); //nodeA
718 EXPECT_EQ(3, drawNode(renderThread, nodeA));
719}
720
721RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderGrandparentReceivable) {
722 /* R is backward projected on B
723 A
724 |
725 B
726 |
727 C
728 |
729 R
730 */
731 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
732 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
733 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
734 props.setProjectionReceiver(true);
735 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
736 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
737 props.setProjectBackwards(true);
738 props.setClipToBounds(false);
739 } ); //nodeR
740 } ); //nodeC
741 } ); //nodeB
742 }); //nodeA
743 EXPECT_EQ(3, drawNode(renderThread, nodeA));
744}
745
746RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderTwoReceivables) {
747 /* B and G are receivables, R is backward projected
748 A
749 / \
750 B C
751 / \
752 G R
753 */
754 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
755 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
756 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //B
757 props.setProjectionReceiver(true);
758 } ); //nodeB
759 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //C
760 drawOrderedNode(&canvas, 3, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //G
761 props.setProjectionReceiver(true);
762 } ); //nodeG
763 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //R
764 props.setProjectBackwards(true);
765 props.setClipToBounds(false);
766 } ); //nodeR
767 } ); //nodeC
768 }); //nodeA
769 EXPECT_EQ(4, drawNode(renderThread, nodeA));
770}
771
772RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderTwoReceivablesLikelyScenario) {
773 /* B and G are receivables, G is backward projected
774 A
775 / \
776 B C
777 / \
778 G R
779 */
780 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
781 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
782 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //B
783 props.setProjectionReceiver(true);
784 } ); //nodeB
785 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //C
786 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //G
787 props.setProjectionReceiver(true);
788 props.setProjectBackwards(true);
789 props.setClipToBounds(false);
790 } ); //nodeG
791 drawOrderedNode(&canvas, 3, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //R
792 } ); //nodeR
793 } ); //nodeC
794 }); //nodeA
795 EXPECT_EQ(4, drawNode(renderThread, nodeA));
796}
797
798RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderTwoReceivablesDeeper) {
799 /* B and G are receivables, R is backward projected
800 A
801 / \
802 B C
803 / \
804 G D
805 |
806 R
807 */
808 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
809 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
810 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //B
811 props.setProjectionReceiver(true);
812 } ); //nodeB
813 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //C
814 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //G
815 props.setProjectionReceiver(true);
816 } ); //nodeG
817 drawOrderedNode(&canvas, 4, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //D
818 drawOrderedNode(&canvas, 3, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //R
819 props.setProjectBackwards(true);
820 props.setClipToBounds(false);
821 } ); //nodeR
822 } ); //nodeD
823 } ); //nodeC
824 }); //nodeA
825 EXPECT_EQ(5, drawNode(renderThread, nodeA));
Stan Iliev021693b2016-10-17 16:26:15 -0400826}
Stan Iliev52771272016-11-17 09:54:38 -0500827
828RENDERTHREAD_TEST(RenderNodeDrawable, simple) {
829 static const int CANVAS_WIDTH = 100;
830 static const int CANVAS_HEIGHT = 200;
831 class SimpleTestCanvas : public TestCanvasBase {
832 public:
833 SimpleTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {
834 }
835 void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
836 EXPECT_EQ(0, mDrawCounter++);
837 }
838 void onDrawImage(const SkImage*, SkScalar dx, SkScalar dy, const SkPaint*) override {
839 EXPECT_EQ(1, mDrawCounter++);
840 }
841 };
842
843 auto node = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
844 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
845 sk_sp<Bitmap> bitmap(TestUtils::createBitmap(25, 25));
846 canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, SkPaint());
847 canvas.drawBitmap(*bitmap, 10, 10, nullptr);
848 });
849
850 SimpleTestCanvas canvas;
851 RenderNodeDrawable drawable(node.get(), &canvas, true);
852 canvas.drawDrawable(&drawable);
853 EXPECT_EQ(2, canvas.mDrawCounter);
854}
855
856RENDERTHREAD_TEST(RenderNodeDrawable, colorOp_unbounded) {
857 static const int CANVAS_WIDTH = 200;
858 static const int CANVAS_HEIGHT = 200;
859 class ColorTestCanvas : public TestCanvasBase {
860 public:
861 ColorTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {
862 }
863 void onDrawPaint(const SkPaint&) {
864 switch (mDrawCounter++) {
865 case 0:
Stan Ilievc1db0e02017-01-23 13:47:35 -0500866 EXPECT_EQ(SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT),
867 TestUtils::getClipBounds(this));
Stan Iliev52771272016-11-17 09:54:38 -0500868 break;
869 case 1:
870 EXPECT_EQ(SkRect::MakeWH(10, 10), TestUtils::getClipBounds(this));
871 break;
872 default:
873 ADD_FAILURE();
874 }
875 }
876 };
877
878 auto unclippedColorView = TestUtils::createSkiaNode(0, 0, 10, 10,
879 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
880 props.setClipToBounds(false);
881 canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
882 });
883
884 auto clippedColorView = TestUtils::createSkiaNode(0, 0, 10, 10,
885 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
886 canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
887 });
888
889 ColorTestCanvas canvas;
890 RenderNodeDrawable drawable(unclippedColorView.get(), &canvas, true);
891 canvas.drawDrawable(&drawable);
892 EXPECT_EQ(1, canvas.mDrawCounter);
893 RenderNodeDrawable drawable2(clippedColorView.get(), &canvas, true);
894 canvas.drawDrawable(&drawable2);
895 EXPECT_EQ(2, canvas.mDrawCounter);
896}
897
898TEST(RenderNodeDrawable, renderNode) {
899 static const int CANVAS_WIDTH = 200;
900 static const int CANVAS_HEIGHT = 200;
901 class RenderNodeTestCanvas : public TestCanvasBase {
902 public:
903 RenderNodeTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {
904 }
905 void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
906 switch(mDrawCounter++) {
907 case 0:
908 EXPECT_EQ(SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT), TestUtils::getClipBounds(this));
909 EXPECT_EQ(SK_ColorDKGRAY, paint.getColor());
910 break;
911 case 1:
912 EXPECT_EQ(SkRect::MakeLTRB(50, 50, 150, 150), TestUtils::getClipBounds(this));
913 EXPECT_EQ(SK_ColorWHITE, paint.getColor());
914 break;
915 default:
916 ADD_FAILURE();
917 }
918 }
919 };
920
921 auto child = TestUtils::createSkiaNode(10, 10, 110, 110,
922 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
923 SkPaint paint;
924 paint.setColor(SK_ColorWHITE);
925 canvas.drawRect(0, 0, 100, 100, paint);
926 });
927
928 auto parent = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
929 [&child](RenderProperties& props, SkiaRecordingCanvas& canvas) {
930 SkPaint paint;
931 paint.setColor(SK_ColorDKGRAY);
932 canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, paint);
933
934 canvas.save(SaveFlags::MatrixClip);
935 canvas.translate(40, 40);
936 canvas.drawRenderNode(child.get());
937 canvas.restore();
938 });
939
940 RenderNodeTestCanvas canvas;
941 RenderNodeDrawable drawable(parent.get(), &canvas, true);
942 canvas.drawDrawable(&drawable);
943 EXPECT_EQ(2, canvas.mDrawCounter);
944}
945
Stan Ilievd7410f72017-04-04 15:23:54 -0400946
947TEST(ReorderBarrierDrawable, testShadowMatrix) {
948 static const int CANVAS_WIDTH = 100;
949 static const int CANVAS_HEIGHT = 100;
950 static const float TRANSLATE_X = 11.0f;
951 static const float TRANSLATE_Y = 22.0f;
952 static const float CASTER_X = 40.0f;
953 static const float CASTER_Y = 40.0f;
954 static const float CASTER_WIDTH = 20.0f;
955 static const float CASTER_HEIGHT = 20.0f;
956
957
958 class ShadowTestCanvas : public SkCanvas {
959 public:
960 ShadowTestCanvas(int width, int height) : SkCanvas(width, height) {}
961 int getIndex() { return mDrawCounter; }
962
963 virtual void onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) override {
964 // expect to draw 2 RenderNodeDrawable, 1 StartReorderBarrierDrawable,
965 // 1 EndReorderBarrierDrawable
966 mDrawCounter++;
967 SkCanvas::onDrawDrawable(drawable, matrix);
968 }
969
970 virtual void didTranslate(SkScalar dx, SkScalar dy) override {
971 mDrawCounter++;
972 EXPECT_EQ(dx, TRANSLATE_X);
973 EXPECT_EQ(dy, TRANSLATE_Y);
974 }
975
976 virtual void didConcat(const SkMatrix& matrix) override {
977 // This function is invoked by EndReorderBarrierDrawable::drawShadow to apply shadow
978 // matrix.
979 mDrawCounter++;
980 EXPECT_EQ(SkMatrix::MakeTrans(CASTER_X, CASTER_Y), matrix);
981 EXPECT_EQ(SkMatrix::MakeTrans(CASTER_X+TRANSLATE_X, CASTER_Y+TRANSLATE_Y),
982 getTotalMatrix());
983 }
984 protected:
985 int mDrawCounter = 0;
986 };
987
988 auto parent = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
989 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
990 canvas.translate(TRANSLATE_X, TRANSLATE_Y);
991 canvas.insertReorderBarrier(true);
992
993 auto node = TestUtils::createSkiaNode(CASTER_X, CASTER_Y, CASTER_X + CASTER_WIDTH,
994 CASTER_Y + CASTER_HEIGHT,
995 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
996 props.setElevation(42);
997 props.mutableOutline().setRoundRect(0, 0, 20, 20, 5, 1);
998 props.mutableOutline().setShouldClip(true);
999 });
1000 canvas.drawRenderNode(node.get());
1001 canvas.insertReorderBarrier(false);
1002 });
1003
1004 //create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection
1005 ShadowTestCanvas canvas(CANVAS_WIDTH, CANVAS_HEIGHT);
1006 RenderNodeDrawable drawable(parent.get(), &canvas, false);
1007 canvas.drawDrawable(&drawable);
1008 EXPECT_EQ(6, canvas.getIndex());
1009}