blob: 4b29100c55a62e7b1c327a3444f19c1d503fabd6 [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
Kevin Lubick4e8ce462022-12-01 20:29:16 +000017#include <SkBlendMode.h>
John Reck1bcacfd2017-11-03 10:12:19 -070018#include <SkClipStack.h>
John Reck1bcacfd2017-11-03 10:12:19 -070019#include <SkSurface_Base.h>
Dongya Jiange2b99382022-02-28 21:35:57 +080020#include <VectorDrawable.h>
21#include <gtest/gtest.h>
22#include <include/effects/SkImageFilters.h>
John Reck1bcacfd2017-11-03 10:12:19 -070023#include <string.h>
Dongya Jiange2b99382022-02-28 21:35:57 +080024
Stan Iliev021693b2016-10-17 16:26:15 -040025#include "AnimationContext.h"
26#include "DamageAccumulator.h"
John Reck1bcacfd2017-11-03 10:12:19 -070027#include "FatalTestCanvas.h"
Stan Iliev021693b2016-10-17 16:26:15 -040028#include "IContextFactory.h"
John Reck8f45d4a2018-08-15 10:17:12 -070029#include "RecordingCanvas.h"
John Reck1bcacfd2017-11-03 10:12:19 -070030#include "SkiaCanvas.h"
Dongya Jiange2b99382022-02-28 21:35:57 +080031#include "hwui/Paint.h"
32#include "pipeline/skia/BackdropFilterDrawable.h"
Stan Iliev021693b2016-10-17 16:26:15 -040033#include "pipeline/skia/SkiaDisplayList.h"
Stan Ilieve9d00122017-09-19 12:07:10 -040034#include "pipeline/skia/SkiaOpenGLPipeline.h"
John Reck1bcacfd2017-11-03 10:12:19 -070035#include "pipeline/skia/SkiaPipeline.h"
Stan Iliev021693b2016-10-17 16:26:15 -040036#include "pipeline/skia/SkiaRecordingCanvas.h"
37#include "renderthread/CanvasContext.h"
38#include "tests/common/TestUtils.h"
Derek Sollenberger72903272018-09-20 14:56:27 -040039#include "utils/Color.h"
Stan Iliev021693b2016-10-17 16:26:15 -040040
41using namespace android;
42using namespace android::uirenderer;
43using namespace android::uirenderer::renderthread;
44using namespace android::uirenderer::skiapipeline;
45
Stan Iliev021693b2016-10-17 16:26:15 -040046TEST(RenderNodeDrawable, create) {
John Reck1bcacfd2017-11-03 10:12:19 -070047 auto rootNode =
48 TestUtils::createNode(0, 0, 200, 400, [](RenderProperties& props, Canvas& canvas) {
Stan Iliev021693b2016-10-17 16:26:15 -040049 canvas.drawColor(Color::Red_500, SkBlendMode::kSrcOver);
50 });
51
John Reck8f45d4a2018-08-15 10:17:12 -070052 DisplayListData skLiteDL;
53 RecordingCanvas canvas;
Derek Sollenbergerea1fe9b2017-03-01 13:02:43 -050054 canvas.reset(&skLiteDL, SkIRect::MakeWH(1, 1));
Stan Iliev021693b2016-10-17 16:26:15 -040055 canvas.translate(100, 100);
56 RenderNodeDrawable drawable(rootNode.get(), &canvas);
57
58 ASSERT_EQ(drawable.getRenderNode(), rootNode.get());
59 ASSERT_EQ(&drawable.getNodeProperties(), &rootNode->properties());
60 ASSERT_EQ(drawable.getRecordedMatrix(), canvas.getTotalMatrix());
61}
62
Stan Ilievdb45a4b2016-11-08 14:18:31 -050063namespace {
64
Stan Iliev2f06e8a2016-11-02 15:29:03 -040065static void drawOrderedRect(Canvas* canvas, uint8_t expectedDrawOrder) {
Mike Reedc2dbc032019-07-25 12:28:29 -040066 Paint paint;
Stan Iliev2f06e8a2016-11-02 15:29:03 -040067 // order put in blue channel, transparent so overlapped content doesn't get rejected
68 paint.setColor(SkColorSetARGB(1, 0, 0, expectedDrawOrder));
69 canvas->drawRect(0, 0, 100, 100, paint);
Stan Iliev021693b2016-10-17 16:26:15 -040070}
71
Stan Iliev2f06e8a2016-11-02 15:29:03 -040072static void drawOrderedNode(Canvas* canvas, uint8_t expectedDrawOrder, float z) {
John Reck1bcacfd2017-11-03 10:12:19 -070073 auto node = TestUtils::createSkiaNode(
74 0, 0, 100, 100,
Stan Iliev2f06e8a2016-11-02 15:29:03 -040075 [expectedDrawOrder, z](RenderProperties& props, SkiaRecordingCanvas& canvas) {
John Reck1bcacfd2017-11-03 10:12:19 -070076 drawOrderedRect(&canvas, expectedDrawOrder);
77 props.setTranslationZ(z);
78 });
79 canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership
Stan Iliev2f06e8a2016-11-02 15:29:03 -040080}
Stan Iliev021693b2016-10-17 16:26:15 -040081
John Reck1bcacfd2017-11-03 10:12:19 -070082static void drawOrderedNode(
83 Canvas* canvas, uint8_t expectedDrawOrder,
Stan Ilievdb45a4b2016-11-08 14:18:31 -050084 std::function<void(RenderProperties& props, SkiaRecordingCanvas& canvas)> setup) {
John Reck1bcacfd2017-11-03 10:12:19 -070085 auto node = TestUtils::createSkiaNode(
86 0, 0, 100, 100,
Stan Ilievdb45a4b2016-11-08 14:18:31 -050087 [expectedDrawOrder, setup](RenderProperties& props, SkiaRecordingCanvas& canvas) {
John Reck1bcacfd2017-11-03 10:12:19 -070088 drawOrderedRect(&canvas, expectedDrawOrder);
89 if (setup) {
90 setup(props, canvas);
91 }
92 });
93 canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership
Stan Ilievdb45a4b2016-11-08 14:18:31 -050094}
95
96class ZReorderCanvas : public SkCanvas {
97public:
98 ZReorderCanvas(int width, int height) : SkCanvas(width, height) {}
99 void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
John Reck1bcacfd2017-11-03 10:12:19 -0700100 int expectedOrder = SkColorGetB(paint.getColor()); // extract order from blue channel
Stan Iliev52771272016-11-17 09:54:38 -0500101 EXPECT_EQ(expectedOrder, mDrawCounter++) << "An op was drawn out of order";
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500102 }
Stan Iliev52771272016-11-17 09:54:38 -0500103 int getIndex() { return mDrawCounter; }
John Reck1bcacfd2017-11-03 10:12:19 -0700104
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500105protected:
Stan Iliev52771272016-11-17 09:54:38 -0500106 int mDrawCounter = 0;
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500107};
108
John Reck1bcacfd2017-11-03 10:12:19 -0700109} // end anonymous namespace
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500110
111TEST(RenderNodeDrawable, zReorder) {
John Reck1bcacfd2017-11-03 10:12:19 -0700112 auto parent = TestUtils::createSkiaNode(0, 0, 100, 100, [](RenderProperties& props,
113 SkiaRecordingCanvas& canvas) {
Leon Scroggins III0f53e102020-05-05 15:53:29 -0400114 canvas.enableZ(true);
115 canvas.enableZ(false);
John Reck1bcacfd2017-11-03 10:12:19 -0700116 drawOrderedNode(&canvas, 0, 10.0f); // in reorder=false at this point, so played inorder
Stan Iliev2f06e8a2016-11-02 15:29:03 -0400117 drawOrderedRect(&canvas, 1);
Leon Scroggins III0f53e102020-05-05 15:53:29 -0400118 canvas.enableZ(true);
Stan Iliev2f06e8a2016-11-02 15:29:03 -0400119 drawOrderedNode(&canvas, 6, 2.0f);
120 drawOrderedRect(&canvas, 3);
121 drawOrderedNode(&canvas, 4, 0.0f);
122 drawOrderedRect(&canvas, 5);
123 drawOrderedNode(&canvas, 2, -2.0f);
124 drawOrderedNode(&canvas, 7, 2.0f);
Leon Scroggins III0f53e102020-05-05 15:53:29 -0400125 canvas.enableZ(false);
Stan Iliev2f06e8a2016-11-02 15:29:03 -0400126 drawOrderedRect(&canvas, 8);
John Reck1bcacfd2017-11-03 10:12:19 -0700127 drawOrderedNode(&canvas, 9, -10.0f); // in reorder=false at this point, so played inorder
Leon Scroggins III0f53e102020-05-05 15:53:29 -0400128 canvas.enableZ(true); // reorder a node ahead of drawrect op
Stan Iliev88e08912016-11-22 18:19:29 -0500129 drawOrderedRect(&canvas, 11);
130 drawOrderedNode(&canvas, 10, -1.0f);
Leon Scroggins III0f53e102020-05-05 15:53:29 -0400131 canvas.enableZ(false);
132 canvas.enableZ(true); // test with two empty reorder sections
133 canvas.enableZ(true);
134 canvas.enableZ(false);
Stan Iliev88e08912016-11-22 18:19:29 -0500135 drawOrderedRect(&canvas, 12);
Stan Iliev2f06e8a2016-11-02 15:29:03 -0400136 });
Stan Iliev021693b2016-10-17 16:26:15 -0400137
John Reck1bcacfd2017-11-03 10:12:19 -0700138 // create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection
Stan Iliev2f06e8a2016-11-02 15:29:03 -0400139 ZReorderCanvas canvas(100, 100);
140 RenderNodeDrawable drawable(parent.get(), &canvas, false);
141 canvas.drawDrawable(&drawable);
Stan Iliev88e08912016-11-22 18:19:29 -0500142 EXPECT_EQ(13, canvas.getIndex());
Stan Iliev021693b2016-10-17 16:26:15 -0400143}
144
John Reck1bcacfd2017-11-03 10:12:19 -0700145TEST(RenderNodeDrawable, composeOnLayer) {
Kevin Lubick6817fc42023-05-12 19:27:20 +0000146 auto surface = SkSurfaces::Raster(SkImageInfo::MakeN32Premul(1, 1));
Stan Iliev021693b2016-10-17 16:26:15 -0400147 SkCanvas& canvas = *surface->getCanvas();
148 canvas.drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
149 ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
150
John Reck1bcacfd2017-11-03 10:12:19 -0700151 auto rootNode = TestUtils::createSkiaNode(
152 0, 0, 1, 1, [](RenderProperties& props, SkiaRecordingCanvas& recorder) {
153 recorder.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);
154 });
Stan Iliev021693b2016-10-17 16:26:15 -0400155
John Reck1bcacfd2017-11-03 10:12:19 -0700156 // attach a layer to the render node
Kevin Lubick6817fc42023-05-12 19:27:20 +0000157 auto surfaceLayer = SkSurfaces::Raster(SkImageInfo::MakeN32Premul(1, 1));
Stan Iliev021693b2016-10-17 16:26:15 -0400158 auto canvas2 = surfaceLayer->getCanvas();
159 canvas2->drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
Stan Iliev500a0c32016-10-26 10:30:09 -0400160 rootNode->setLayerSurface(surfaceLayer);
Stan Iliev021693b2016-10-17 16:26:15 -0400161
162 RenderNodeDrawable drawable1(rootNode.get(), &canvas, false);
163 canvas.drawDrawable(&drawable1);
164 ASSERT_EQ(SK_ColorRED, TestUtils::getColor(surface, 0, 0));
165
166 RenderNodeDrawable drawable2(rootNode.get(), &canvas, true);
167 canvas.drawDrawable(&drawable2);
168 ASSERT_EQ(SK_ColorWHITE, TestUtils::getColor(surface, 0, 0));
169
170 RenderNodeDrawable drawable3(rootNode.get(), &canvas, false);
171 canvas.drawDrawable(&drawable3);
172 ASSERT_EQ(SK_ColorRED, TestUtils::getColor(surface, 0, 0));
173
Stan Iliev500a0c32016-10-26 10:30:09 -0400174 rootNode->setLayerSurface(sk_sp<SkSurface>());
Stan Iliev021693b2016-10-17 16:26:15 -0400175}
176
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500177namespace {
Stan Iliev68885e32016-12-14 11:18:34 -0500178static SkRect getRecorderClipBounds(const SkiaRecordingCanvas& recorder) {
179 SkRect clipBounds;
180 recorder.getClipBounds(&clipBounds);
181 return clipBounds;
182}
183
184static SkMatrix getRecorderMatrix(const SkiaRecordingCanvas& recorder) {
185 SkMatrix matrix;
186 recorder.getMatrix(&matrix);
187 return matrix;
188}
189}
190
John Reck1bcacfd2017-11-03 10:12:19 -0700191TEST(RenderNodeDrawable, saveLayerClipAndMatrixRestore) {
Kevin Lubick6817fc42023-05-12 19:27:20 +0000192 auto surface = SkSurfaces::Raster(SkImageInfo::MakeN32Premul(400, 800));
Stan Iliev68885e32016-12-14 11:18:34 -0500193 SkCanvas& canvas = *surface->getCanvas();
194 canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
195 ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorWHITE);
196
John Reck1bcacfd2017-11-03 10:12:19 -0700197 auto rootNode = TestUtils::createSkiaNode(
198 0, 0, 400, 800, [](RenderProperties& props, SkiaRecordingCanvas& recorder) {
199 SkPaint layerPaint;
200 ASSERT_EQ(SkRect::MakeLTRB(0, 0, 400, 800), getRecorderClipBounds(recorder));
201 EXPECT_TRUE(getRecorderMatrix(recorder).isIdentity());
Stan Iliev68885e32016-12-14 11:18:34 -0500202
John Reck1bcacfd2017-11-03 10:12:19 -0700203 // note we don't pass SaveFlags::MatrixClip, but matrix and clip will be saved
John Recka00eef212020-11-16 12:45:55 -0500204 recorder.saveLayer(0, 0, 400, 400, &layerPaint);
John Reck1bcacfd2017-11-03 10:12:19 -0700205 ASSERT_EQ(SkRect::MakeLTRB(0, 0, 400, 400), getRecorderClipBounds(recorder));
206 EXPECT_TRUE(getRecorderMatrix(recorder).isIdentity());
Stan Iliev68885e32016-12-14 11:18:34 -0500207
John Reck1bcacfd2017-11-03 10:12:19 -0700208 recorder.clipRect(50, 50, 350, 350, SkClipOp::kIntersect);
209 ASSERT_EQ(SkRect::MakeLTRB(50, 50, 350, 350), getRecorderClipBounds(recorder));
Stan Iliev68885e32016-12-14 11:18:34 -0500210
John Reck1bcacfd2017-11-03 10:12:19 -0700211 recorder.translate(300.0f, 400.0f);
Mike Reedc65b1d52020-11-24 12:14:20 -0500212 EXPECT_EQ(SkMatrix::Translate(300.0f, 400.0f), getRecorderMatrix(recorder));
Stan Iliev68885e32016-12-14 11:18:34 -0500213
John Reck1bcacfd2017-11-03 10:12:19 -0700214 recorder.restore();
215 ASSERT_EQ(SkRect::MakeLTRB(0, 0, 400, 800), getRecorderClipBounds(recorder));
216 EXPECT_TRUE(getRecorderMatrix(recorder).isIdentity());
Stan Iliev68885e32016-12-14 11:18:34 -0500217
Mike Reedc2dbc032019-07-25 12:28:29 -0400218 Paint paint;
John Reck1bcacfd2017-11-03 10:12:19 -0700219 paint.setAntiAlias(true);
220 paint.setColor(SK_ColorGREEN);
221 recorder.drawRect(0.0f, 400.0f, 400.0f, 800.0f, paint);
222 });
Stan Iliev68885e32016-12-14 11:18:34 -0500223
224 RenderNodeDrawable drawable(rootNode.get(), &canvas, true);
225 canvas.drawDrawable(&drawable);
226 ASSERT_EQ(SK_ColorGREEN, TestUtils::getColor(surface, 200, 600));
227}
228
229namespace {
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500230class ContextFactory : public IContextFactory {
231public:
232 virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) override {
233 return new AnimationContext(clock);
234 }
235};
John Reck1bcacfd2017-11-03 10:12:19 -0700236} // end anonymous namespace
Stan Iliev021693b2016-10-17 16:26:15 -0400237
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500238RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorder) {
239 static const int SCROLL_X = 5;
240 static const int SCROLL_Y = 10;
241 class ProjectionTestCanvas : public SkCanvas {
242 public:
243 ProjectionTestCanvas(int width, int height) : SkCanvas(width, height) {}
244 void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
Stan Iliev52771272016-11-17 09:54:38 -0500245 const int index = mDrawCounter++;
John Reck1bcacfd2017-11-03 10:12:19 -0700246 SkMatrix expectedMatrix;
247 ;
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500248 switch (index) {
John Reck1bcacfd2017-11-03 10:12:19 -0700249 case 0: // this is node "B"
250 EXPECT_EQ(SkRect::MakeWH(100, 100), rect);
251 EXPECT_EQ(SK_ColorWHITE, paint.getColor());
252 expectedMatrix.reset();
253 EXPECT_EQ(SkRect::MakeLTRB(0, 0, 100, 100), TestUtils::getClipBounds(this));
254 break;
255 case 1: // this is node "P"
256 EXPECT_EQ(SkRect::MakeLTRB(-10, -10, 60, 60), rect);
257 EXPECT_EQ(SK_ColorDKGRAY, paint.getColor());
258 expectedMatrix.setTranslate(50 - SCROLL_X, 50 - SCROLL_Y);
259 EXPECT_EQ(SkRect::MakeLTRB(-35, -30, 45, 50),
260 TestUtils::getLocalClipBounds(this));
261 break;
262 case 2: // this is node "C"
263 EXPECT_EQ(SkRect::MakeWH(100, 50), rect);
264 EXPECT_EQ(SK_ColorBLUE, paint.getColor());
265 expectedMatrix.setTranslate(-SCROLL_X, 50 - SCROLL_Y);
266 EXPECT_EQ(SkRect::MakeLTRB(0, 40, 95, 90), TestUtils::getClipBounds(this));
267 break;
268 default:
269 ADD_FAILURE();
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500270 }
271 EXPECT_EQ(expectedMatrix, getTotalMatrix());
272 }
Stan Iliev021693b2016-10-17 16:26:15 -0400273
Stan Iliev52771272016-11-17 09:54:38 -0500274 int getIndex() { return mDrawCounter; }
John Reck1bcacfd2017-11-03 10:12:19 -0700275
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500276 protected:
Stan Iliev52771272016-11-17 09:54:38 -0500277 int mDrawCounter = 0;
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500278 };
Stan Iliev021693b2016-10-17 16:26:15 -0400279
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500280 /**
281 * Construct a tree of nodes, where the root (A) has a receiver background (B), and a child (C)
282 * with a projecting child (P) of its own. P would normally draw between B and C's "background"
283 * draw, but because it is projected backwards, it's drawn in between B and C.
284 *
285 * The parent is scrolled by SCROLL_X/SCROLL_Y, but this does not affect the background
286 * (which isn't affected by scroll).
287 */
John Reck1bcacfd2017-11-03 10:12:19 -0700288 auto receiverBackground = TestUtils::createSkiaNode(
289 0, 0, 100, 100,
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500290 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
John Reck1bcacfd2017-11-03 10:12:19 -0700291 properties.setProjectionReceiver(true);
292 // scroll doesn't apply to background, so undone via translationX/Y
293 // NOTE: translationX/Y only! no other transform properties may be set for a proj
294 // receiver!
295 properties.setTranslationX(SCROLL_X);
296 properties.setTranslationY(SCROLL_Y);
Stan Iliev021693b2016-10-17 16:26:15 -0400297
Mike Reedc2dbc032019-07-25 12:28:29 -0400298 Paint paint;
John Reck1bcacfd2017-11-03 10:12:19 -0700299 paint.setColor(SK_ColorWHITE);
300 canvas.drawRect(0, 0, 100, 100, paint);
301 },
302 "B");
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500303
John Reck1bcacfd2017-11-03 10:12:19 -0700304 auto projectingRipple = TestUtils::createSkiaNode(
305 50, 0, 100, 50,
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500306 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
John Reck1bcacfd2017-11-03 10:12:19 -0700307 properties.setProjectBackwards(true);
308 properties.setClipToBounds(false);
Mike Reedc2dbc032019-07-25 12:28:29 -0400309 Paint paint;
John Reck1bcacfd2017-11-03 10:12:19 -0700310 paint.setColor(SK_ColorDKGRAY);
311 canvas.drawRect(-10, -10, 60, 60, paint);
312 },
313 "P");
314 auto child = TestUtils::createSkiaNode(
315 0, 50, 100, 100,
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500316 [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
Mike Reedc2dbc032019-07-25 12:28:29 -0400317 Paint paint;
John Reck1bcacfd2017-11-03 10:12:19 -0700318 paint.setColor(SK_ColorBLUE);
319 canvas.drawRect(0, 0, 100, 50, paint);
320 canvas.drawRenderNode(projectingRipple.get());
321 },
322 "C");
323 auto parent = TestUtils::createSkiaNode(
324 0, 0, 100, 100,
325 [&receiverBackground, &child](RenderProperties& properties,
326 SkiaRecordingCanvas& canvas) {
327 // Set a rect outline for the projecting ripple to be masked against.
328 properties.mutableOutline().setRoundRect(10, 10, 90, 90, 5, 1.0f);
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500329
John Reck1bcacfd2017-11-03 10:12:19 -0700330 canvas.save(SaveFlags::MatrixClip);
331 canvas.translate(-SCROLL_X,
332 -SCROLL_Y); // Apply scroll (note: bg undoes this internally)
333 canvas.drawRenderNode(receiverBackground.get());
334 canvas.drawRenderNode(child.get());
335 canvas.restore();
336 },
337 "A");
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500338 ContextFactory contextFactory;
John Reck1bcacfd2017-11-03 10:12:19 -0700339 std::unique_ptr<CanvasContext> canvasContext(
Matt Buckleye9023cf2022-11-23 22:39:25 +0000340 CanvasContext::create(renderThread, false, parent.get(), &contextFactory, 0, 0));
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500341 TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
342 DamageAccumulator damageAccumulator;
343 info.damageAccumulator = &damageAccumulator;
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500344 parent->prepareTree(info);
345
John Reck1bcacfd2017-11-03 10:12:19 -0700346 // parent(A) -> (receiverBackground, child)
347 // child(C) -> (rect[0, 0, 100, 50], projectingRipple)
348 // projectingRipple(P) -> (rect[-10, -10, 60, 60]) -> projects backwards
349 // receiverBackground(B) -> (rect[0, 0, 100, 100]) -> projection receiver
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500350
John Reck1bcacfd2017-11-03 10:12:19 -0700351 // create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500352 ProjectionTestCanvas canvas(100, 100);
353 RenderNodeDrawable drawable(parent.get(), &canvas, true);
354 canvas.drawDrawable(&drawable);
355 EXPECT_EQ(3, canvas.getIndex());
356}
357
John Reck1efef7b2023-07-17 23:29:30 -0400358RENDERTHREAD_TEST(RenderNodeDrawable, emptyReceiver) {
Yuqian Li70910fd2017-11-29 13:38:40 -0500359 class ProjectionTestCanvas : public SkCanvas {
360 public:
361 ProjectionTestCanvas(int width, int height) : SkCanvas(width, height) {}
John Reck283bb462018-12-13 16:40:14 -0800362 void onDrawRect(const SkRect& rect, const SkPaint& paint) override { mDrawCounter++; }
Yuqian Li70910fd2017-11-29 13:38:40 -0500363
364 int getDrawCounter() { return mDrawCounter; }
365
366 private:
367 int mDrawCounter = 0;
368 };
369
370 auto receiverBackground = TestUtils::createSkiaNode(
371 0, 0, 100, 100,
372 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
373 properties.setProjectionReceiver(true);
374 },
John Reck283bb462018-12-13 16:40:14 -0800375 "B"); // a receiver with an empty display list
Yuqian Li70910fd2017-11-29 13:38:40 -0500376
377 auto projectingRipple = TestUtils::createSkiaNode(
378 0, 0, 100, 100,
379 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
380 properties.setProjectBackwards(true);
381 properties.setClipToBounds(false);
Mike Reedc2dbc032019-07-25 12:28:29 -0400382 Paint paint;
Yuqian Li70910fd2017-11-29 13:38:40 -0500383 canvas.drawRect(0, 0, 100, 100, paint);
384 },
385 "P");
386 auto child = TestUtils::createSkiaNode(
387 0, 0, 100, 100,
388 [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
Mike Reedc2dbc032019-07-25 12:28:29 -0400389 Paint paint;
Yuqian Li70910fd2017-11-29 13:38:40 -0500390 canvas.drawRect(0, 0, 100, 100, paint);
391 canvas.drawRenderNode(projectingRipple.get());
392 },
393 "C");
John Reck283bb462018-12-13 16:40:14 -0800394 auto parent =
395 TestUtils::createSkiaNode(0, 0, 100, 100,
396 [&receiverBackground, &child](RenderProperties& properties,
397 SkiaRecordingCanvas& canvas) {
398 canvas.drawRenderNode(receiverBackground.get());
399 canvas.drawRenderNode(child.get());
400 },
401 "A");
Yuqian Li70910fd2017-11-29 13:38:40 -0500402 ContextFactory contextFactory;
403 std::unique_ptr<CanvasContext> canvasContext(
Matt Buckleye9023cf2022-11-23 22:39:25 +0000404 CanvasContext::create(renderThread, false, parent.get(), &contextFactory, 0, 0));
Yuqian Li70910fd2017-11-29 13:38:40 -0500405 TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
406 DamageAccumulator damageAccumulator;
407 info.damageAccumulator = &damageAccumulator;
408 parent->prepareTree(info);
409
410 // parent(A) -> (receiverBackground, child)
411 // child(C) -> (rect[0, 0, 100, 100], projectingRipple)
412 // projectingRipple(P) -> (rect[0, 0, 100, 100]) -> projects backwards
413 // receiverBackground(B) -> (empty) -> projection receiver
414
415 // create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection
416 ProjectionTestCanvas canvas(100, 100);
417 RenderNodeDrawable drawable(parent.get(), &canvas, true);
418 canvas.drawDrawable(&drawable);
419 EXPECT_EQ(2, canvas.getDrawCounter());
420}
421
John Reck1efef7b2023-07-17 23:29:30 -0400422RENDERTHREAD_TEST(RenderNodeDrawable, projectionHwLayer) {
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500423 /* R is backward projected on B and C is a layer.
424 A
425 / \
426 B C
427 |
428 R
429 */
430 static const int SCROLL_X = 5;
431 static const int SCROLL_Y = 10;
432 static const int CANVAS_WIDTH = 400;
433 static const int CANVAS_HEIGHT = 400;
434 static const int LAYER_WIDTH = 200;
435 static const int LAYER_HEIGHT = 200;
436 class ProjectionTestCanvas : public SkCanvas {
437 public:
Mike Reed6acfe162016-11-18 17:21:09 -0500438 ProjectionTestCanvas(int* drawCounter)
John Reck1bcacfd2017-11-03 10:12:19 -0700439 : SkCanvas(CANVAS_WIDTH, CANVAS_HEIGHT), mDrawCounter(drawCounter) {}
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500440 void onDrawArc(const SkRect&, SkScalar startAngle, SkScalar sweepAngle, bool useCenter,
John Reck1bcacfd2017-11-03 10:12:19 -0700441 const SkPaint&) override {
442 EXPECT_EQ(0, (*mDrawCounter)++); // part of painting the layer
443 EXPECT_EQ(SkRect::MakeLTRB(0, 0, LAYER_WIDTH, LAYER_HEIGHT),
444 TestUtils::getClipBounds(this));
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500445 }
446 void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
Mike Reed6acfe162016-11-18 17:21:09 -0500447 EXPECT_EQ(1, (*mDrawCounter)++);
John Reck1bcacfd2017-11-03 10:12:19 -0700448 EXPECT_EQ(SkRect::MakeLTRB(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT),
449 TestUtils::getClipBounds(this));
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500450 }
451 void onDrawOval(const SkRect&, const SkPaint&) override {
Mike Reed6acfe162016-11-18 17:21:09 -0500452 EXPECT_EQ(2, (*mDrawCounter)++);
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500453 SkMatrix expectedMatrix;
454 expectedMatrix.setTranslate(100 - SCROLL_X, 100 - SCROLL_Y);
455 EXPECT_EQ(expectedMatrix, getTotalMatrix());
Stan Iliev52771272016-11-17 09:54:38 -0500456 EXPECT_EQ(SkRect::MakeLTRB(-85, -80, 295, 300), TestUtils::getLocalClipBounds(this));
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500457 }
Mike Reed6acfe162016-11-18 17:21:09 -0500458 int* mDrawCounter;
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500459 };
460
461 class ProjectionLayer : public SkSurface_Base {
462 public:
Mike Reed6acfe162016-11-18 17:21:09 -0500463 ProjectionLayer(int* drawCounter)
John Reck1bcacfd2017-11-03 10:12:19 -0700464 : SkSurface_Base(SkImageInfo::MakeN32Premul(LAYER_WIDTH, LAYER_HEIGHT), nullptr)
465 , mDrawCounter(drawCounter) {}
Derek Sollenbergerdbb4bc82018-11-21 08:47:31 -0500466 virtual sk_sp<SkImage> onNewImageSnapshot(const SkIRect* bounds) override {
Mike Reed6acfe162016-11-18 17:21:09 -0500467 EXPECT_EQ(3, (*mDrawCounter)++);
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500468 EXPECT_EQ(SkRect::MakeLTRB(100 - SCROLL_X, 100 - SCROLL_Y, 300 - SCROLL_X,
John Reck1bcacfd2017-11-03 10:12:19 -0700469 300 - SCROLL_Y),
470 TestUtils::getClipBounds(this->getCanvas()));
Derek Sollenberger03e6cff72017-12-04 15:07:08 -0500471 return nullptr;
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500472 }
John Reck1bcacfd2017-11-03 10:12:19 -0700473 SkCanvas* onNewCanvas() override { return new ProjectionTestCanvas(mDrawCounter); }
474 sk_sp<SkSurface> onNewSurface(const SkImageInfo&) override { return nullptr; }
Ben Wagner9b875562021-10-28 21:42:35 +0000475 bool onCopyOnWrite(ContentChangeMode) override { return true; }
Mike Reed6acfe162016-11-18 17:21:09 -0500476 int* mDrawCounter;
Leon Scroggins III71195ab2018-02-08 17:14:28 -0500477 void onWritePixels(const SkPixmap&, int x, int y) {}
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500478 };
479
John Reck1bcacfd2017-11-03 10:12:19 -0700480 auto receiverBackground = TestUtils::createSkiaNode(
481 0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500482 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
John Reck1bcacfd2017-11-03 10:12:19 -0700483 properties.setProjectionReceiver(true);
484 // scroll doesn't apply to background, so undone via translationX/Y
485 // NOTE: translationX/Y only! no other transform properties may be set for a proj
486 // receiver!
487 properties.setTranslationX(SCROLL_X);
488 properties.setTranslationY(SCROLL_Y);
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500489
Mike Reedc2dbc032019-07-25 12:28:29 -0400490 canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, Paint());
John Reck1bcacfd2017-11-03 10:12:19 -0700491 },
492 "B"); // B
493 auto projectingRipple = TestUtils::createSkiaNode(
494 0, 0, LAYER_WIDTH, LAYER_HEIGHT,
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500495 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
John Reck1bcacfd2017-11-03 10:12:19 -0700496 properties.setProjectBackwards(true);
497 properties.setClipToBounds(false);
Mike Reedc2dbc032019-07-25 12:28:29 -0400498 canvas.drawOval(100, 100, 300, 300, Paint()); // drawn mostly out of layer bounds
John Reck1bcacfd2017-11-03 10:12:19 -0700499 },
500 "R"); // R
501 auto child = TestUtils::createSkiaNode(
502 100, 100, 300, 300,
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500503 [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
John Reck1bcacfd2017-11-03 10:12:19 -0700504 canvas.drawRenderNode(projectingRipple.get());
Mike Reedc2dbc032019-07-25 12:28:29 -0400505 canvas.drawArc(0, 0, LAYER_WIDTH, LAYER_HEIGHT, 0.0f, 280.0f, true, Paint());
John Reck1bcacfd2017-11-03 10:12:19 -0700506 },
507 "C"); // C
508 auto parent = TestUtils::createSkiaNode(
509 0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500510 [&receiverBackground, &child](RenderProperties& properties,
John Reck1bcacfd2017-11-03 10:12:19 -0700511 SkiaRecordingCanvas& canvas) {
512 // Set a rect outline for the projecting ripple to be masked against.
513 properties.mutableOutline().setRoundRect(10, 10, 390, 390, 0, 1.0f);
514 canvas.translate(-SCROLL_X,
515 -SCROLL_Y); // Apply scroll (note: bg undoes this internally)
516 canvas.drawRenderNode(receiverBackground.get());
517 canvas.drawRenderNode(child.get());
518 },
519 "A"); // A
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500520
John Reck1bcacfd2017-11-03 10:12:19 -0700521 // prepareTree is required to find, which receivers have backward projected nodes
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500522 ContextFactory contextFactory;
John Reck1bcacfd2017-11-03 10:12:19 -0700523 std::unique_ptr<CanvasContext> canvasContext(
Matt Buckleye9023cf2022-11-23 22:39:25 +0000524 CanvasContext::create(renderThread, false, parent.get(), &contextFactory, 0, 0));
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500525 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 int drawCounter = 0;
John Reck1bcacfd2017-11-03 10:12:19 -0700531 // set a layer after prepareTree to avoid layer logic there
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500532 child->animatorProperties().mutateLayerProperties().setType(LayerType::RenderLayer);
Mike Reed6acfe162016-11-18 17:21:09 -0500533 sk_sp<SkSurface> surfaceLayer1(new ProjectionLayer(&drawCounter));
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500534 child->setLayerSurface(surfaceLayer1);
535 Matrix4 windowTransform;
536 windowTransform.loadTranslate(100, 100, 0);
537 child->getSkiaLayer()->inverseTransformInWindow.loadInverse(windowTransform);
538
539 LayerUpdateQueue layerUpdateQueue;
540 layerUpdateQueue.enqueueLayerWithDamage(child.get(),
John Reck1bcacfd2017-11-03 10:12:19 -0700541 android::uirenderer::Rect(LAYER_WIDTH, LAYER_HEIGHT));
Stan Ilieve9d00122017-09-19 12:07:10 -0400542 auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
Peiyong Lin1f6aa122018-09-10 16:28:08 -0700543 pipeline->renderLayersImpl(layerUpdateQueue, true);
John Reck1bcacfd2017-11-03 10:12:19 -0700544 EXPECT_EQ(1, drawCounter); // assert index 0 is drawn on the layer
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500545
Mike Reed6acfe162016-11-18 17:21:09 -0500546 RenderNodeDrawable drawable(parent.get(), surfaceLayer1->getCanvas(), true);
547 surfaceLayer1->getCanvas()->drawDrawable(&drawable);
548 EXPECT_EQ(4, drawCounter);
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500549
550 // clean up layer pointer, so we can safely destruct RenderNode
551 child->setLayerSurface(nullptr);
552}
553
554RENDERTHREAD_TEST(RenderNodeDrawable, projectionChildScroll) {
555 /* R is backward projected on B.
556 A
557 / \
558 B C
559 |
560 R
561 */
562 static const int SCROLL_X = 500000;
563 static const int SCROLL_Y = 0;
564 static const int CANVAS_WIDTH = 400;
565 static const int CANVAS_HEIGHT = 400;
566 class ProjectionChildScrollTestCanvas : public SkCanvas {
567 public:
568 ProjectionChildScrollTestCanvas() : SkCanvas(CANVAS_WIDTH, CANVAS_HEIGHT) {}
569 void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
Stan Iliev52771272016-11-17 09:54:38 -0500570 EXPECT_EQ(0, mDrawCounter++);
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500571 EXPECT_TRUE(getTotalMatrix().isIdentity());
572 }
573 void onDrawOval(const SkRect&, const SkPaint&) override {
Stan Iliev52771272016-11-17 09:54:38 -0500574 EXPECT_EQ(1, mDrawCounter++);
575 EXPECT_EQ(SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT), TestUtils::getClipBounds(this));
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500576 EXPECT_TRUE(getTotalMatrix().isIdentity());
577 }
Stan Iliev52771272016-11-17 09:54:38 -0500578 int mDrawCounter = 0;
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500579 };
580
John Reck1bcacfd2017-11-03 10:12:19 -0700581 auto receiverBackground = TestUtils::createSkiaNode(
582 0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500583 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
John Reck1bcacfd2017-11-03 10:12:19 -0700584 properties.setProjectionReceiver(true);
Mike Reedc2dbc032019-07-25 12:28:29 -0400585 canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, Paint());
John Reck1bcacfd2017-11-03 10:12:19 -0700586 },
587 "B"); // B
588 auto projectingRipple = TestUtils::createSkiaNode(
589 0, 0, 200, 200,
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500590 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
John Reck1bcacfd2017-11-03 10:12:19 -0700591 // scroll doesn't apply to background, so undone via translationX/Y
592 // NOTE: translationX/Y only! no other transform properties may be set for a proj
593 // receiver!
594 properties.setTranslationX(SCROLL_X);
595 properties.setTranslationY(SCROLL_Y);
596 properties.setProjectBackwards(true);
597 properties.setClipToBounds(false);
Mike Reedc2dbc032019-07-25 12:28:29 -0400598 canvas.drawOval(0, 0, 200, 200, Paint());
John Reck1bcacfd2017-11-03 10:12:19 -0700599 },
600 "R"); // R
601 auto child = TestUtils::createSkiaNode(
602 0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500603 [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
John Reck1bcacfd2017-11-03 10:12:19 -0700604 // Record time clip will be ignored by projectee
605 canvas.clipRect(100, 100, 300, 300, SkClipOp::kIntersect);
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500606
John Reck1bcacfd2017-11-03 10:12:19 -0700607 canvas.translate(-SCROLL_X,
608 -SCROLL_Y); // Apply scroll (note: bg undoes this internally)
609 canvas.drawRenderNode(projectingRipple.get());
610 },
611 "C"); // C
612 auto parent =
613 TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
614 [&receiverBackground, &child](RenderProperties& properties,
615 SkiaRecordingCanvas& canvas) {
616 canvas.drawRenderNode(receiverBackground.get());
617 canvas.drawRenderNode(child.get());
618 },
619 "A"); // A
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500620
John Reck1bcacfd2017-11-03 10:12:19 -0700621 // prepareTree is required to find, which receivers have backward projected nodes
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500622 ContextFactory contextFactory;
John Reck1bcacfd2017-11-03 10:12:19 -0700623 std::unique_ptr<CanvasContext> canvasContext(
Matt Buckleye9023cf2022-11-23 22:39:25 +0000624 CanvasContext::create(renderThread, false, parent.get(), &contextFactory, 0, 0));
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500625 TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
626 DamageAccumulator damageAccumulator;
627 info.damageAccumulator = &damageAccumulator;
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500628 parent->prepareTree(info);
629
Mike Reed6acfe162016-11-18 17:21:09 -0500630 std::unique_ptr<ProjectionChildScrollTestCanvas> canvas(new ProjectionChildScrollTestCanvas());
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500631 RenderNodeDrawable drawable(parent.get(), canvas.get(), true);
632 canvas->drawDrawable(&drawable);
Stan Iliev52771272016-11-17 09:54:38 -0500633 EXPECT_EQ(2, canvas->mDrawCounter);
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500634}
635
636namespace {
John Reck1bcacfd2017-11-03 10:12:19 -0700637static int drawNode(RenderThread& renderThread, const sp<RenderNode>& renderNode) {
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500638 ContextFactory contextFactory;
John Reck1bcacfd2017-11-03 10:12:19 -0700639 std::unique_ptr<CanvasContext> canvasContext(
Matt Buckleye9023cf2022-11-23 22:39:25 +0000640 CanvasContext::create(renderThread, false, renderNode.get(), &contextFactory, 0, 0));
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500641 TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
642 DamageAccumulator damageAccumulator;
643 info.damageAccumulator = &damageAccumulator;
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500644 renderNode->prepareTree(info);
645
John Reck1bcacfd2017-11-03 10:12:19 -0700646 // create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500647 ZReorderCanvas canvas(100, 100);
648 RenderNodeDrawable drawable(renderNode.get(), &canvas, false);
649 canvas.drawDrawable(&drawable);
650 return canvas.getIndex();
651}
652}
653
654RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectedInMiddle) {
655 /* R is backward projected on B
656 A
657 / \
658 B C
659 |
660 R
661 */
John Reck1bcacfd2017-11-03 10:12:19 -0700662 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, [](RenderProperties& props,
663 SkiaRecordingCanvas& canvas) {
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500664 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
665 props.setProjectionReceiver(true);
John Reck1bcacfd2017-11-03 10:12:19 -0700666 }); // nodeB
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500667 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
668 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
669 props.setProjectBackwards(true);
670 props.setClipToBounds(false);
John Reck1bcacfd2017-11-03 10:12:19 -0700671 }); // nodeR
672 }); // nodeC
673 }); // nodeA
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500674 EXPECT_EQ(3, drawNode(renderThread, nodeA));
675}
676
677RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectLast) {
678 /* R is backward projected on E
679 A
680 / | \
681 / | \
682 B C E
683 |
684 R
685 */
John Reck1bcacfd2017-11-03 10:12:19 -0700686 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, [](RenderProperties& props,
687 SkiaRecordingCanvas& canvas) {
688 drawOrderedNode(&canvas, 0, nullptr); // nodeB
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500689 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
John Reck1bcacfd2017-11-03 10:12:19 -0700690 drawOrderedNode(&canvas, 3, [](RenderProperties& props,
691 SkiaRecordingCanvas& canvas) { // drawn as 2
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500692 props.setProjectBackwards(true);
693 props.setClipToBounds(false);
John Reck1bcacfd2017-11-03 10:12:19 -0700694 }); // nodeR
695 }); // nodeC
696 drawOrderedNode(&canvas, 2,
697 [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // drawn as 3
698 props.setProjectionReceiver(true);
699 }); // nodeE
700 }); // nodeA
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500701 EXPECT_EQ(4, drawNode(renderThread, nodeA));
702}
703
704RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderNoReceivable) {
705 /* R is backward projected without receiver
706 A
707 / \
708 B C
709 |
710 R
711 */
John Reck1bcacfd2017-11-03 10:12:19 -0700712 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, [](RenderProperties& props,
713 SkiaRecordingCanvas& canvas) {
714 drawOrderedNode(&canvas, 0, nullptr); // nodeB
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500715 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
716 drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
John Reck1bcacfd2017-11-03 10:12:19 -0700717 // not having a projection receiver is an undefined behavior
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500718 props.setProjectBackwards(true);
719 props.setClipToBounds(false);
John Reck1bcacfd2017-11-03 10:12:19 -0700720 }); // nodeR
721 }); // nodeC
722 }); // nodeA
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500723 EXPECT_EQ(2, drawNode(renderThread, nodeA));
724}
725
726RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderParentReceivable) {
727 /* R is backward projected on C
728 A
729 / \
730 B C
731 |
732 R
733 */
John Reck1bcacfd2017-11-03 10:12:19 -0700734 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, [](RenderProperties& props,
735 SkiaRecordingCanvas& canvas) {
736 drawOrderedNode(&canvas, 0, nullptr); // nodeB
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500737 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
738 props.setProjectionReceiver(true);
739 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
740 props.setProjectBackwards(true);
741 props.setClipToBounds(false);
John Reck1bcacfd2017-11-03 10:12:19 -0700742 }); // nodeR
743 }); // nodeC
744 }); // nodeA
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500745 EXPECT_EQ(3, drawNode(renderThread, nodeA));
746}
747
748RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderSameNodeReceivable) {
749 /* R is backward projected on R
750 A
751 / \
752 B C
753 |
754 R
755 */
John Reck1bcacfd2017-11-03 10:12:19 -0700756 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, [](RenderProperties& props,
757 SkiaRecordingCanvas& canvas) {
758 drawOrderedNode(&canvas, 0, nullptr); // nodeB
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500759 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
760 drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
John Reck1bcacfd2017-11-03 10:12:19 -0700761 // having a node that is projected on itself is an undefined/unexpected behavior
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500762 props.setProjectionReceiver(true);
763 props.setProjectBackwards(true);
764 props.setClipToBounds(false);
John Reck1bcacfd2017-11-03 10:12:19 -0700765 }); // nodeR
766 }); // nodeC
767 }); // nodeA
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500768 EXPECT_EQ(2, drawNode(renderThread, nodeA));
769}
770
John Reck1bcacfd2017-11-03 10:12:19 -0700771// Note: the outcome for this test is different in HWUI
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500772RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectedSibling) {
773 /* R is set to project on B, but R is not drawn because projecting on a sibling is not allowed.
774 A
775 /|\
776 / | \
777 B C R
778 */
John Reck1bcacfd2017-11-03 10:12:19 -0700779 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, [](RenderProperties& props,
780 SkiaRecordingCanvas& canvas) {
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500781 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
782 props.setProjectionReceiver(true);
John Reck1bcacfd2017-11-03 10:12:19 -0700783 }); // nodeB
784 drawOrderedNode(&canvas, 1,
785 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {}); // nodeC
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500786 drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
787 props.setProjectBackwards(true);
788 props.setClipToBounds(false);
John Reck1bcacfd2017-11-03 10:12:19 -0700789 }); // nodeR
790 }); // nodeA
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500791 EXPECT_EQ(2, drawNode(renderThread, nodeA));
792}
793
794RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectedSibling2) {
795 /* R is set to project on B, but R is not drawn because projecting on a sibling is not allowed.
796 A
797 |
798 G
799 /|\
800 / | \
801 B C R
802 */
John Reck1bcacfd2017-11-03 10:12:19 -0700803 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, [](RenderProperties& props,
804 SkiaRecordingCanvas& canvas) {
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500805 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
806 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
807 props.setProjectionReceiver(true);
John Reck1bcacfd2017-11-03 10:12:19 -0700808 }); // nodeB
809 drawOrderedNode(&canvas, 2,
810 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {}); // nodeC
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500811 drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
812 props.setProjectBackwards(true);
813 props.setClipToBounds(false);
John Reck1bcacfd2017-11-03 10:12:19 -0700814 }); // nodeR
815 }); // nodeG
816 }); // nodeA
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500817 EXPECT_EQ(3, drawNode(renderThread, nodeA));
818}
819
820RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderGrandparentReceivable) {
821 /* R is backward projected on B
822 A
823 |
824 B
825 |
826 C
827 |
828 R
829 */
John Reck1bcacfd2017-11-03 10:12:19 -0700830 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, [](RenderProperties& props,
831 SkiaRecordingCanvas& canvas) {
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500832 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
833 props.setProjectionReceiver(true);
834 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
John Reck1bcacfd2017-11-03 10:12:19 -0700835 drawOrderedNode(&canvas, 2,
836 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
837 props.setProjectBackwards(true);
838 props.setClipToBounds(false);
839 }); // nodeR
840 }); // nodeC
841 }); // nodeB
842 }); // nodeA
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500843 EXPECT_EQ(3, drawNode(renderThread, nodeA));
844}
845
846RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderTwoReceivables) {
847 /* B and G are receivables, R is backward projected
848 A
849 / \
850 B C
851 / \
852 G R
853 */
John Reck1bcacfd2017-11-03 10:12:19 -0700854 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, [](RenderProperties& props,
855 SkiaRecordingCanvas& canvas) {
856 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // B
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500857 props.setProjectionReceiver(true);
John Reck1bcacfd2017-11-03 10:12:19 -0700858 }); // nodeB
859 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // C
860 drawOrderedNode(&canvas, 3,
861 [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // G
862 props.setProjectionReceiver(true);
863 }); // nodeG
864 drawOrderedNode(&canvas, 1,
865 [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // R
866 props.setProjectBackwards(true);
867 props.setClipToBounds(false);
868 }); // nodeR
869 }); // nodeC
870 }); // nodeA
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500871 EXPECT_EQ(4, drawNode(renderThread, nodeA));
872}
873
874RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderTwoReceivablesLikelyScenario) {
875 /* B and G are receivables, G is backward projected
876 A
877 / \
878 B C
879 / \
880 G R
881 */
John Reck1bcacfd2017-11-03 10:12:19 -0700882 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, [](RenderProperties& props,
883 SkiaRecordingCanvas& canvas) {
884 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // B
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500885 props.setProjectionReceiver(true);
John Reck1bcacfd2017-11-03 10:12:19 -0700886 }); // nodeB
887 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // C
888 drawOrderedNode(&canvas, 1,
889 [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // G
890 props.setProjectionReceiver(true);
891 props.setProjectBackwards(true);
892 props.setClipToBounds(false);
893 }); // nodeG
894 drawOrderedNode(&canvas, 3,
895 [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // R
896 }); // nodeR
897 }); // nodeC
898 }); // nodeA
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500899 EXPECT_EQ(4, drawNode(renderThread, nodeA));
900}
901
902RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderTwoReceivablesDeeper) {
903 /* B and G are receivables, R is backward projected
904 A
905 / \
906 B C
907 / \
908 G D
909 |
910 R
911 */
John Reck1bcacfd2017-11-03 10:12:19 -0700912 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, [](RenderProperties& props,
913 SkiaRecordingCanvas& canvas) {
914 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // B
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500915 props.setProjectionReceiver(true);
John Reck1bcacfd2017-11-03 10:12:19 -0700916 }); // nodeB
917 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // C
918 drawOrderedNode(&canvas, 2,
919 [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // G
920 props.setProjectionReceiver(true);
921 }); // nodeG
922 drawOrderedNode(&canvas, 4,
923 [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // D
924 drawOrderedNode(&canvas, 3, [](RenderProperties& props,
925 SkiaRecordingCanvas& canvas) { // R
926 props.setProjectBackwards(true);
927 props.setClipToBounds(false);
928 }); // nodeR
929 }); // nodeD
930 }); // nodeC
931 }); // nodeA
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500932 EXPECT_EQ(5, drawNode(renderThread, nodeA));
Stan Iliev021693b2016-10-17 16:26:15 -0400933}
Stan Iliev52771272016-11-17 09:54:38 -0500934
935RENDERTHREAD_TEST(RenderNodeDrawable, simple) {
936 static const int CANVAS_WIDTH = 100;
937 static const int CANVAS_HEIGHT = 200;
938 class SimpleTestCanvas : public TestCanvasBase {
939 public:
John Reck1bcacfd2017-11-03 10:12:19 -0700940 SimpleTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {}
Stan Iliev52771272016-11-17 09:54:38 -0500941 void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
942 EXPECT_EQ(0, mDrawCounter++);
943 }
Michael Ludwig1544ba92024-01-02 15:26:37 +0000944 void onDrawImageRect2(const SkImage*, const SkRect&, const SkRect&,
945 const SkSamplingOptions&, const SkPaint*,
946 SrcRectConstraint) override {
Stan Iliev52771272016-11-17 09:54:38 -0500947 EXPECT_EQ(1, mDrawCounter++);
948 }
949 };
950
951 auto node = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
John Reck1bcacfd2017-11-03 10:12:19 -0700952 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
953 sk_sp<Bitmap> bitmap(TestUtils::createBitmap(25, 25));
954 canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
Mike Reedc2dbc032019-07-25 12:28:29 -0400955 Paint());
John Reck1bcacfd2017-11-03 10:12:19 -0700956 canvas.drawBitmap(*bitmap, 10, 10, nullptr);
957 });
Stan Iliev52771272016-11-17 09:54:38 -0500958
959 SimpleTestCanvas canvas;
960 RenderNodeDrawable drawable(node.get(), &canvas, true);
961 canvas.drawDrawable(&drawable);
962 EXPECT_EQ(2, canvas.mDrawCounter);
963}
964
965RENDERTHREAD_TEST(RenderNodeDrawable, colorOp_unbounded) {
966 static const int CANVAS_WIDTH = 200;
967 static const int CANVAS_HEIGHT = 200;
968 class ColorTestCanvas : public TestCanvasBase {
969 public:
John Reck1bcacfd2017-11-03 10:12:19 -0700970 ColorTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {}
Stan Iliev52771272016-11-17 09:54:38 -0500971 void onDrawPaint(const SkPaint&) {
972 switch (mDrawCounter++) {
John Reck1bcacfd2017-11-03 10:12:19 -0700973 case 0:
974 EXPECT_EQ(SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT),
975 TestUtils::getClipBounds(this));
976 break;
977 case 1:
978 EXPECT_EQ(SkRect::MakeWH(10, 10), TestUtils::getClipBounds(this));
979 break;
980 default:
981 ADD_FAILURE();
Stan Iliev52771272016-11-17 09:54:38 -0500982 }
983 }
984 };
985
John Reck1bcacfd2017-11-03 10:12:19 -0700986 auto unclippedColorView = TestUtils::createSkiaNode(
987 0, 0, 10, 10, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
988 props.setClipToBounds(false);
989 canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
990 });
Stan Iliev52771272016-11-17 09:54:38 -0500991
John Reck1bcacfd2017-11-03 10:12:19 -0700992 auto clippedColorView = TestUtils::createSkiaNode(
993 0, 0, 10, 10, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
994 canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
995 });
Stan Iliev52771272016-11-17 09:54:38 -0500996
997 ColorTestCanvas canvas;
998 RenderNodeDrawable drawable(unclippedColorView.get(), &canvas, true);
999 canvas.drawDrawable(&drawable);
1000 EXPECT_EQ(1, canvas.mDrawCounter);
1001 RenderNodeDrawable drawable2(clippedColorView.get(), &canvas, true);
1002 canvas.drawDrawable(&drawable2);
1003 EXPECT_EQ(2, canvas.mDrawCounter);
1004}
1005
1006TEST(RenderNodeDrawable, renderNode) {
1007 static const int CANVAS_WIDTH = 200;
1008 static const int CANVAS_HEIGHT = 200;
1009 class RenderNodeTestCanvas : public TestCanvasBase {
1010 public:
John Reck1bcacfd2017-11-03 10:12:19 -07001011 RenderNodeTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {}
Stan Iliev52771272016-11-17 09:54:38 -05001012 void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
John Reck1bcacfd2017-11-03 10:12:19 -07001013 switch (mDrawCounter++) {
1014 case 0:
1015 EXPECT_EQ(SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT),
1016 TestUtils::getClipBounds(this));
1017 EXPECT_EQ(SK_ColorDKGRAY, paint.getColor());
1018 break;
1019 case 1:
1020 EXPECT_EQ(SkRect::MakeLTRB(50, 50, 150, 150), TestUtils::getClipBounds(this));
1021 EXPECT_EQ(SK_ColorWHITE, paint.getColor());
1022 break;
1023 default:
1024 ADD_FAILURE();
Stan Iliev52771272016-11-17 09:54:38 -05001025 }
1026 }
1027 };
1028
John Reck1bcacfd2017-11-03 10:12:19 -07001029 auto child = TestUtils::createSkiaNode(
1030 10, 10, 110, 110, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
Mike Reedc2dbc032019-07-25 12:28:29 -04001031 Paint paint;
John Reck1bcacfd2017-11-03 10:12:19 -07001032 paint.setColor(SK_ColorWHITE);
1033 canvas.drawRect(0, 0, 100, 100, paint);
1034 });
Stan Iliev52771272016-11-17 09:54:38 -05001035
John Reck1bcacfd2017-11-03 10:12:19 -07001036 auto parent = TestUtils::createSkiaNode(
1037 0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
Stan Iliev52771272016-11-17 09:54:38 -05001038 [&child](RenderProperties& props, SkiaRecordingCanvas& canvas) {
Mike Reedc2dbc032019-07-25 12:28:29 -04001039 Paint paint;
John Reck1bcacfd2017-11-03 10:12:19 -07001040 paint.setColor(SK_ColorDKGRAY);
1041 canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, paint);
Stan Iliev52771272016-11-17 09:54:38 -05001042
John Reck1bcacfd2017-11-03 10:12:19 -07001043 canvas.save(SaveFlags::MatrixClip);
1044 canvas.translate(40, 40);
1045 canvas.drawRenderNode(child.get());
1046 canvas.restore();
1047 });
Stan Iliev52771272016-11-17 09:54:38 -05001048
1049 RenderNodeTestCanvas canvas;
1050 RenderNodeDrawable drawable(parent.get(), &canvas, true);
1051 canvas.drawDrawable(&drawable);
1052 EXPECT_EQ(2, canvas.mDrawCounter);
1053}
1054
Mike Reed2d1279f2020-12-30 09:57:25 -05001055// Verify that layers are composed with linear filtering.
John Reck1efef7b2023-07-17 23:29:30 -04001056RENDERTHREAD_TEST(RenderNodeDrawable, layerComposeQuality) {
Stan Iliev7e100e92018-01-22 10:36:33 -05001057 static const int CANVAS_WIDTH = 1;
1058 static const int CANVAS_HEIGHT = 1;
1059 static const int LAYER_WIDTH = 1;
1060 static const int LAYER_HEIGHT = 1;
1061 class FrameTestCanvas : public TestCanvasBase {
1062 public:
1063 FrameTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {}
Mike Reed2d1279f2020-12-30 09:57:25 -05001064 void onDrawImageRect2(const SkImage* image, const SkRect& src, const SkRect& dst,
1065 const SkSamplingOptions& sampling, const SkPaint* paint,
1066 SrcRectConstraint constraint) override {
Stan Iliev7e100e92018-01-22 10:36:33 -05001067 mDrawCounter++;
Mike Reed2d1279f2020-12-30 09:57:25 -05001068 EXPECT_FALSE(sampling.useCubic);
1069 EXPECT_EQ(SkFilterMode::kLinear, sampling.filter);
Stan Iliev7e100e92018-01-22 10:36:33 -05001070 }
1071 };
1072
1073 auto layerNode = TestUtils::createSkiaNode(
1074 0, 0, LAYER_WIDTH, LAYER_HEIGHT,
1075 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
Mike Reedc2dbc032019-07-25 12:28:29 -04001076 canvas.drawPaint(Paint());
Stan Iliev7e100e92018-01-22 10:36:33 -05001077 });
1078
1079 layerNode->animatorProperties().mutateLayerProperties().setType(LayerType::RenderLayer);
Kevin Lubick6817fc42023-05-12 19:27:20 +00001080 layerNode->setLayerSurface(SkSurfaces::Raster(SkImageInfo::MakeN32Premul(LAYER_WIDTH,
1081 LAYER_HEIGHT)));
Stan Iliev7e100e92018-01-22 10:36:33 -05001082
1083 FrameTestCanvas canvas;
1084 RenderNodeDrawable drawable(layerNode.get(), &canvas, true);
1085 canvas.drawDrawable(&drawable);
John Reck283bb462018-12-13 16:40:14 -08001086 EXPECT_EQ(1, canvas.mDrawCounter); // make sure the layer was composed
Stan Iliev7e100e92018-01-22 10:36:33 -05001087
1088 // clean up layer pointer, so we can safely destruct RenderNode
1089 layerNode->setLayerSurface(nullptr);
1090}
1091
Stan Ilievd7410f72017-04-04 15:23:54 -04001092TEST(ReorderBarrierDrawable, testShadowMatrix) {
1093 static const int CANVAS_WIDTH = 100;
1094 static const int CANVAS_HEIGHT = 100;
1095 static const float TRANSLATE_X = 11.0f;
1096 static const float TRANSLATE_Y = 22.0f;
1097 static const float CASTER_X = 40.0f;
1098 static const float CASTER_Y = 40.0f;
1099 static const float CASTER_WIDTH = 20.0f;
1100 static const float CASTER_HEIGHT = 20.0f;
1101
Stan Ilievd7410f72017-04-04 15:23:54 -04001102 class ShadowTestCanvas : public SkCanvas {
1103 public:
1104 ShadowTestCanvas(int width, int height) : SkCanvas(width, height) {}
Stan Iliev54d70322018-06-14 18:00:10 -04001105 int getDrawCounter() { return mDrawCounter; }
Stan Ilievd7410f72017-04-04 15:23:54 -04001106
1107 virtual void onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) override {
Nathaniel Nifong31fee3a2019-07-11 16:27:14 -04001108 // Do not expect this to be called. See RecordingCanvas.cpp DrawDrawable for context.
1109 EXPECT_TRUE(false);
Stan Ilievd7410f72017-04-04 15:23:54 -04001110 }
1111
1112 virtual void didTranslate(SkScalar dx, SkScalar dy) override {
1113 mDrawCounter++;
1114 EXPECT_EQ(dx, TRANSLATE_X);
1115 EXPECT_EQ(dy, TRANSLATE_Y);
1116 }
1117
Mike Reed65257632020-11-25 21:31:08 -05001118 virtual void didSetM44(const SkM44& matrix) override {
Stan Ilievd7410f72017-04-04 15:23:54 -04001119 mDrawCounter++;
Stan Iliev54d70322018-06-14 18:00:10 -04001120 // First invocation is EndReorderBarrierDrawable::drawShadow to apply shadow matrix.
1121 // Second invocation is preparing the matrix for an elevated RenderNodeDrawable.
Mike Reed65257632020-11-25 21:31:08 -05001122 EXPECT_TRUE(matrix == SkM44());
Stan Iliev54d70322018-06-14 18:00:10 -04001123 EXPECT_TRUE(getTotalMatrix().isIdentity());
1124 }
1125
Mike Reed65257632020-11-25 21:31:08 -05001126 virtual void didConcat44(const SkM44& matrix) override {
Stan Iliev54d70322018-06-14 18:00:10 -04001127 mDrawCounter++;
1128 if (mFirstDidConcat) {
1129 // First invocation is EndReorderBarrierDrawable::drawShadow to apply shadow matrix.
1130 mFirstDidConcat = false;
Mike Reed65257632020-11-25 21:31:08 -05001131 EXPECT_EQ(SkM44::Translate(CASTER_X + TRANSLATE_X, CASTER_Y + TRANSLATE_Y),
Stan Iliev54d70322018-06-14 18:00:10 -04001132 matrix);
Mike Reedc65b1d52020-11-24 12:14:20 -05001133 EXPECT_EQ(SkMatrix::Translate(CASTER_X + TRANSLATE_X, CASTER_Y + TRANSLATE_Y),
Stan Iliev54d70322018-06-14 18:00:10 -04001134 getTotalMatrix());
1135 } else {
1136 // Second invocation is preparing the matrix for an elevated RenderNodeDrawable.
Mike Reed65257632020-11-25 21:31:08 -05001137 EXPECT_EQ(SkM44::Translate(TRANSLATE_X, TRANSLATE_Y), matrix);
Mike Reedc65b1d52020-11-24 12:14:20 -05001138 EXPECT_EQ(SkMatrix::Translate(TRANSLATE_X, TRANSLATE_Y), getTotalMatrix());
Stan Iliev54d70322018-06-14 18:00:10 -04001139 }
Stan Ilievd7410f72017-04-04 15:23:54 -04001140 }
John Reck1bcacfd2017-11-03 10:12:19 -07001141
Stan Ilievd7410f72017-04-04 15:23:54 -04001142 protected:
1143 int mDrawCounter = 0;
John Reck283bb462018-12-13 16:40:14 -08001144
Stan Iliev54d70322018-06-14 18:00:10 -04001145 private:
1146 bool mFirstDidConcat = true;
Stan Ilievd7410f72017-04-04 15:23:54 -04001147 };
1148
John Reck1bcacfd2017-11-03 10:12:19 -07001149 auto parent = TestUtils::createSkiaNode(
1150 0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
Stan Ilievd7410f72017-04-04 15:23:54 -04001151 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
John Reck1bcacfd2017-11-03 10:12:19 -07001152 canvas.translate(TRANSLATE_X, TRANSLATE_Y);
Leon Scroggins III0f53e102020-05-05 15:53:29 -04001153 canvas.enableZ(true);
Stan Ilievd7410f72017-04-04 15:23:54 -04001154
John Reck1bcacfd2017-11-03 10:12:19 -07001155 auto node = TestUtils::createSkiaNode(
1156 CASTER_X, CASTER_Y, CASTER_X + CASTER_WIDTH, CASTER_Y + CASTER_HEIGHT,
1157 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
1158 props.setElevation(42);
1159 props.mutableOutline().setRoundRect(0, 0, 20, 20, 5, 1);
1160 props.mutableOutline().setShouldClip(true);
1161 });
1162 canvas.drawRenderNode(node.get());
Leon Scroggins III0f53e102020-05-05 15:53:29 -04001163 canvas.enableZ(false);
John Reck1bcacfd2017-11-03 10:12:19 -07001164 });
Stan Ilievd7410f72017-04-04 15:23:54 -04001165
John Reck1bcacfd2017-11-03 10:12:19 -07001166 // create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection
Stan Ilievd7410f72017-04-04 15:23:54 -04001167 ShadowTestCanvas canvas(CANVAS_WIDTH, CANVAS_HEIGHT);
1168 RenderNodeDrawable drawable(parent.get(), &canvas, false);
Nathaniel Nifong31fee3a2019-07-11 16:27:14 -04001169 drawable.draw(&canvas);
1170 EXPECT_EQ(5, canvas.getDrawCounter());
Stan Iliev65e678f2018-02-07 14:07:30 -05001171}
1172
1173// Draw a vector drawable twice but with different bounds and verify correct bounds are used.
John Reck1efef7b2023-07-17 23:29:30 -04001174RENDERTHREAD_TEST(SkiaRecordingCanvas, drawVectorDrawable) {
Stan Iliev65e678f2018-02-07 14:07:30 -05001175 static const int CANVAS_WIDTH = 100;
1176 static const int CANVAS_HEIGHT = 200;
1177 class VectorDrawableTestCanvas : public TestCanvasBase {
1178 public:
1179 VectorDrawableTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {}
Mike Reed2d1279f2020-12-30 09:57:25 -05001180 void onDrawImageRect2(const SkImage*, const SkRect& src, const SkRect& dst,
1181 const SkSamplingOptions&, const SkPaint* paint,
1182 SrcRectConstraint constraint) override {
Mike Reedf2d08ac2020-03-11 09:19:03 -04001183 const int index = mDrawCounter++;
1184 switch (index) {
1185 case 0:
1186 EXPECT_EQ(dst, SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT));
1187 break;
1188 case 1:
1189 EXPECT_EQ(dst, SkRect::MakeWH(CANVAS_WIDTH / 2, CANVAS_HEIGHT));
1190 break;
1191 default:
1192 ADD_FAILURE();
1193 }
1194 }
Stan Iliev65e678f2018-02-07 14:07:30 -05001195 };
1196
1197 VectorDrawable::Group* group = new VectorDrawable::Group();
1198 sp<VectorDrawableRoot> vectorDrawable(new VectorDrawableRoot(group));
John Reck283bb462018-12-13 16:40:14 -08001199 vectorDrawable->mutateStagingProperties()->setScaledSize(CANVAS_WIDTH / 10, CANVAS_HEIGHT / 10);
Stan Iliev65e678f2018-02-07 14:07:30 -05001200
John Reck283bb462018-12-13 16:40:14 -08001201 auto node =
1202 TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
1203 [&](RenderProperties& props, SkiaRecordingCanvas& canvas) {
1204 vectorDrawable->mutateStagingProperties()->setBounds(
1205 SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT));
1206 canvas.drawVectorDrawable(vectorDrawable.get());
1207 vectorDrawable->mutateStagingProperties()->setBounds(
1208 SkRect::MakeWH(CANVAS_WIDTH / 2, CANVAS_HEIGHT));
1209 canvas.drawVectorDrawable(vectorDrawable.get());
1210 });
Stan Iliev65e678f2018-02-07 14:07:30 -05001211
1212 VectorDrawableTestCanvas canvas;
1213 RenderNodeDrawable drawable(node.get(), &canvas, true);
1214 canvas.drawDrawable(&drawable);
1215 EXPECT_EQ(2, canvas.mDrawCounter);
1216}
Dongya Jiange2b99382022-02-28 21:35:57 +08001217
1218// Verify drawing logics for BackdropFilterDrawable
1219RENDERTHREAD_TEST(BackdropFilterDrawable, drawing) {
1220 static const int CANVAS_WIDTH = 100;
1221 static const int CANVAS_HEIGHT = 200;
1222 class SimpleTestCanvas : public TestCanvasBase {
1223 public:
1224 SkRect mDstBounds;
1225 SimpleTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {}
1226 void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
1227 // did nothing.
1228 }
1229
1230 // called when BackdropFilterDrawable is drawn.
1231 void onDrawImageRect2(const SkImage*, const SkRect& src, const SkRect& dst,
1232 const SkSamplingOptions&, const SkPaint*,
1233 SrcRectConstraint) override {
1234 mDrawCounter++;
1235 mDstBounds = dst;
1236 }
1237 };
1238 class SimpleLayer : public SkSurface_Base {
1239 public:
1240 SimpleLayer()
1241 : SkSurface_Base(SkImageInfo::MakeN32Premul(CANVAS_WIDTH, CANVAS_HEIGHT), nullptr) {
1242 }
1243 virtual sk_sp<SkImage> onNewImageSnapshot(const SkIRect* bounds) override {
1244 SkBitmap bitmap;
1245 bitmap.allocN32Pixels(CANVAS_WIDTH, CANVAS_HEIGHT);
1246 bitmap.setImmutable();
1247 return bitmap.asImage();
1248 }
1249 SkCanvas* onNewCanvas() override { return new SimpleTestCanvas(); }
1250 sk_sp<SkSurface> onNewSurface(const SkImageInfo&) override { return nullptr; }
1251 bool onCopyOnWrite(ContentChangeMode) override { return true; }
1252 void onWritePixels(const SkPixmap&, int x, int y) {}
1253 };
1254
1255 auto node = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
1256 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
1257 canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
1258 Paint());
1259 });
1260
1261 sk_sp<SkSurface> surface(new SimpleLayer());
1262 auto* canvas = reinterpret_cast<SimpleTestCanvas*>(surface->getCanvas());
1263 RenderNodeDrawable drawable(node.get(), canvas, true);
1264 BackdropFilterDrawable backdropDrawable(node.get(), canvas);
1265 canvas->drawDrawable(&drawable);
1266 canvas->drawDrawable(&backdropDrawable);
1267 // no backdrop filter, skip drawing.
1268 EXPECT_EQ(0, canvas->mDrawCounter);
1269
1270 sk_sp<SkImageFilter> filter(SkImageFilters::Blur(3, 3, nullptr));
1271 node->animatorProperties().mutateLayerProperties().setBackdropImageFilter(filter.get());
1272 canvas->drawDrawable(&drawable);
1273 canvas->drawDrawable(&backdropDrawable);
1274 // backdrop filter is set, ok to draw.
1275 EXPECT_EQ(1, canvas->mDrawCounter);
1276 EXPECT_EQ(SkRect::MakeLTRB(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT), canvas->mDstBounds);
1277
1278 canvas->translate(30, 30);
1279 canvas->drawDrawable(&drawable);
1280 canvas->drawDrawable(&backdropDrawable);
1281 // the drawable is still visible, ok to draw.
1282 EXPECT_EQ(2, canvas->mDrawCounter);
John Reckbdadc6d2024-10-04 16:35:27 -04001283 EXPECT_EQ(SkRect::MakeLTRB(30, 30, CANVAS_WIDTH, CANVAS_HEIGHT), canvas->mDstBounds);
Dongya Jiange2b99382022-02-28 21:35:57 +08001284
1285 canvas->translate(CANVAS_WIDTH, CANVAS_HEIGHT);
1286 canvas->drawDrawable(&drawable);
1287 canvas->drawDrawable(&backdropDrawable);
1288 // the drawable is invisible, skip drawing.
1289 EXPECT_EQ(2, canvas->mDrawCounter);
1290}