• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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 
19 #include <DeferredLayerUpdater.h>
20 #include <RecordedOp.h>
21 #include <RecordingCanvas.h>
22 #include <hwui/Paint.h>
23 #include <minikin/Layout.h>
24 #include <tests/common/TestUtils.h>
25 #include <utils/Color.h>
26 
27 #include <SkGradientShader.h>
28 #include <SkImagePriv.h>
29 #include <SkShader.h>
30 
31 namespace android {
32 namespace uirenderer {
33 
playbackOps(const DisplayList & displayList,std::function<void (const RecordedOp &)> opReceiver)34 static void playbackOps(const DisplayList& displayList,
35         std::function<void(const RecordedOp&)> opReceiver) {
36     for (auto& chunk : displayList.getChunks()) {
37         for (size_t opIndex = chunk.beginOpIndex; opIndex < chunk.endOpIndex; opIndex++) {
38             RecordedOp* op = displayList.getOps()[opIndex];
39             opReceiver(*op);
40         }
41     }
42 }
43 
validateSingleOp(std::unique_ptr<DisplayList> & dl,std::function<void (const RecordedOp & op)> opValidator)44 static void validateSingleOp(std::unique_ptr<DisplayList>& dl,
45         std::function<void(const RecordedOp& op)> opValidator) {
46     ASSERT_EQ(1u, dl->getOps().size()) << "Must be exactly one op";
47     opValidator(*(dl->getOps()[0]));
48 }
49 
50 // The RecordingCanvas is only ever used by the OpenGL RenderPipeline and never when Skia is in use.
51 // Thus, even though many of these test are not directly dependent on the current RenderPipeline, we
52 // set them all to be OPENGL_PIPELINE_TESTs in case the underlying code in RecordingCanvas ever
53 // changes to require the use of the OPENGL_PIPELINE. Currently the textureLayer test is the only
54 // test that requires being an OPENGL_PIPELINE_TEST.
55 
OPENGL_PIPELINE_TEST(RecordingCanvas,emptyPlayback)56 OPENGL_PIPELINE_TEST(RecordingCanvas, emptyPlayback) {
57     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
58         canvas.save(SaveFlags::MatrixClip);
59         canvas.restore();
60     });
61     playbackOps(*dl, [](const RecordedOp& op) { ADD_FAILURE(); });
62 }
63 
OPENGL_PIPELINE_TEST(RecordingCanvas,clipRect)64 OPENGL_PIPELINE_TEST(RecordingCanvas, clipRect) {
65     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [](RecordingCanvas& canvas) {
66         canvas.save(SaveFlags::MatrixClip);
67         canvas.clipRect(0, 0, 100, 100, SkClipOp::kIntersect);
68         canvas.drawRect(0, 0, 50, 50, SkPaint());
69         canvas.drawRect(50, 50, 100, 100, SkPaint());
70         canvas.restore();
71     });
72 
73     ASSERT_EQ(2u, dl->getOps().size()) << "Must be exactly two ops";
74     EXPECT_CLIP_RECT(Rect(100, 100), dl->getOps()[0]->localClip);
75     EXPECT_CLIP_RECT(Rect(100, 100), dl->getOps()[1]->localClip);
76     EXPECT_EQ(dl->getOps()[0]->localClip, dl->getOps()[1]->localClip)
77             << "Clip should be serialized once";
78 }
79 
OPENGL_PIPELINE_TEST(RecordingCanvas,emptyClipRect)80 OPENGL_PIPELINE_TEST(RecordingCanvas, emptyClipRect) {
81     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
82         canvas.save(SaveFlags::MatrixClip);
83         canvas.clipRect(0, 0, 100, 100, SkClipOp::kIntersect);
84         canvas.clipRect(100, 100, 200, 200, SkClipOp::kIntersect);
85         canvas.drawRect(0, 0, 50, 50, SkPaint()); // rejected at record time
86         canvas.restore();
87     });
88     ASSERT_EQ(0u, dl->getOps().size()) << "Must be zero ops. Rect should be rejected.";
89 }
90 
OPENGL_PIPELINE_TEST(RecordingCanvas,emptyPaintRejection)91 OPENGL_PIPELINE_TEST(RecordingCanvas, emptyPaintRejection) {
92     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
93         SkPaint emptyPaint;
94         emptyPaint.setColor(Color::Transparent);
95 
96         float points[] = {0, 0, 200, 200};
97         canvas.drawPoints(points, 4, emptyPaint);
98         canvas.drawLines(points, 4, emptyPaint);
99         canvas.drawRect(0, 0, 200, 200, emptyPaint);
100         canvas.drawRegion(SkRegion(SkIRect::MakeWH(200, 200)), emptyPaint);
101         canvas.drawRoundRect(0, 0, 200, 200, 10, 10, emptyPaint);
102         canvas.drawCircle(100, 100, 100, emptyPaint);
103         canvas.drawOval(0, 0, 200, 200, emptyPaint);
104         canvas.drawArc(0, 0, 200, 200, 0, 360, true, emptyPaint);
105         SkPath path;
106         path.addRect(0, 0, 200, 200);
107         canvas.drawPath(path, emptyPaint);
108     });
109     EXPECT_EQ(0u, dl->getOps().size()) << "Op should be rejected";
110 }
111 
OPENGL_PIPELINE_TEST(RecordingCanvas,drawArc)112 OPENGL_PIPELINE_TEST(RecordingCanvas, drawArc) {
113     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
114         canvas.drawArc(0, 0, 200, 200, 0, 180, true, SkPaint());
115         canvas.drawArc(0, 0, 100, 100, 0, 360, true, SkPaint());
116     });
117 
118     auto&& ops = dl->getOps();
119     ASSERT_EQ(2u, ops.size()) << "Must be exactly two ops";
120     EXPECT_EQ(RecordedOpId::ArcOp, ops[0]->opId);
121     EXPECT_EQ(Rect(200, 200), ops[0]->unmappedBounds);
122 
123     EXPECT_EQ(RecordedOpId::OvalOp, ops[1]->opId)
124             << "Circular arcs should be converted to ovals";
125     EXPECT_EQ(Rect(100, 100), ops[1]->unmappedBounds);
126 }
127 
OPENGL_PIPELINE_TEST(RecordingCanvas,drawLines)128 OPENGL_PIPELINE_TEST(RecordingCanvas, drawLines) {
129     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
130         SkPaint paint;
131         paint.setStrokeWidth(20); // doesn't affect recorded bounds - would be resolved at bake time
132         float points[] = { 0, 0, 20, 10, 30, 40, 90 }; // NB: only 1 valid line
133         canvas.drawLines(&points[0], 7, paint);
134     });
135 
136     ASSERT_EQ(1u, dl->getOps().size()) << "Must be exactly one op";
137     auto op = dl->getOps()[0];
138     ASSERT_EQ(RecordedOpId::LinesOp, op->opId);
139     EXPECT_EQ(4, ((LinesOp*)op)->floatCount)
140             << "float count must be rounded down to closest multiple of 4";
141     EXPECT_EQ(Rect(20, 10), op->unmappedBounds)
142             << "unmapped bounds must be size of line, and not outset for stroke width";
143 }
144 
OPENGL_PIPELINE_TEST(RecordingCanvas,drawRect)145 OPENGL_PIPELINE_TEST(RecordingCanvas, drawRect) {
146     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
147         canvas.drawRect(10, 20, 90, 180, SkPaint());
148     });
149 
150     ASSERT_EQ(1u, dl->getOps().size()) << "Must be exactly one op";
151     auto op = *(dl->getOps()[0]);
152     ASSERT_EQ(RecordedOpId::RectOp, op.opId);
153     EXPECT_EQ(nullptr, op.localClip);
154     EXPECT_EQ(Rect(10, 20, 90, 180), op.unmappedBounds);
155 }
156 
OPENGL_PIPELINE_TEST(RecordingCanvas,drawRoundRect)157 OPENGL_PIPELINE_TEST(RecordingCanvas, drawRoundRect) {
158     // Round case - stays rounded
159     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
160         canvas.drawRoundRect(0, 0, 100, 100, 10, 10, SkPaint());
161     });
162     ASSERT_EQ(1u, dl->getOps().size()) << "Must be exactly one op";
163     ASSERT_EQ(RecordedOpId::RoundRectOp, dl->getOps()[0]->opId);
164 
165     // Non-rounded case - turned into drawRect
166     dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
167         canvas.drawRoundRect(0, 0, 100, 100, 0, -1, SkPaint());
168     });
169     ASSERT_EQ(1u, dl->getOps().size()) << "Must be exactly one op";
170     ASSERT_EQ(RecordedOpId::RectOp, dl->getOps()[0]->opId)
171         << "Non-rounded rects should be converted";
172 }
173 
OPENGL_PIPELINE_TEST(RecordingCanvas,drawGlyphs)174 OPENGL_PIPELINE_TEST(RecordingCanvas, drawGlyphs) {
175     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
176         SkPaint paint;
177         paint.setAntiAlias(true);
178         paint.setTextSize(20);
179         paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
180         TestUtils::drawUtf8ToCanvas(&canvas, "test text", paint, 25, 25);
181     });
182 
183     int count = 0;
184     playbackOps(*dl, [&count](const RecordedOp& op) {
185         count++;
186         ASSERT_EQ(RecordedOpId::TextOp, op.opId);
187         EXPECT_EQ(nullptr, op.localClip);
188         EXPECT_TRUE(op.localMatrix.isIdentity());
189         EXPECT_TRUE(op.unmappedBounds.contains(25, 15, 50, 25))
190                 << "Op expected to be 25+ pixels wide, 10+ pixels tall";
191     });
192     ASSERT_EQ(1, count);
193 }
194 
OPENGL_PIPELINE_TEST(RecordingCanvas,drawGlyphs_strikeThruAndUnderline)195 OPENGL_PIPELINE_TEST(RecordingCanvas, drawGlyphs_strikeThruAndUnderline) {
196     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
197         SkPaint paint;
198         paint.setAntiAlias(true);
199         paint.setTextSize(20);
200         paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
201         for (int i = 0; i < 2; i++) {
202             for (int j = 0; j < 2; j++) {
203                 uint32_t flags = paint.getFlags();
204                 if (i != 0) {
205                     flags |= SkPaint::kUnderlineText_ReserveFlag;
206                 } else {
207                     flags &= ~SkPaint::kUnderlineText_ReserveFlag;
208                 }
209                 if (j != 0) {
210                     flags |= SkPaint::kStrikeThruText_ReserveFlag;
211                 } else {
212                     flags &= ~SkPaint::kStrikeThruText_ReserveFlag;
213                 }
214                 paint.setFlags(flags);
215                 TestUtils::drawUtf8ToCanvas(&canvas, "test text", paint, 25, 25);
216             }
217         }
218     });
219 
220     auto ops = dl->getOps();
221     ASSERT_EQ(8u, ops.size());
222 
223     int index = 0;
224     EXPECT_EQ(RecordedOpId::TextOp, ops[index++]->opId); // no underline or strikethrough
225 
226     EXPECT_EQ(RecordedOpId::TextOp, ops[index++]->opId);
227     EXPECT_EQ(RecordedOpId::RectOp, ops[index++]->opId); // strikethrough only
228 
229     EXPECT_EQ(RecordedOpId::TextOp, ops[index++]->opId);
230     EXPECT_EQ(RecordedOpId::RectOp, ops[index++]->opId); // underline only
231 
232     EXPECT_EQ(RecordedOpId::TextOp, ops[index++]->opId);
233     EXPECT_EQ(RecordedOpId::RectOp, ops[index++]->opId); // underline
234     EXPECT_EQ(RecordedOpId::RectOp, ops[index++]->opId); // strikethrough
235 }
236 
OPENGL_PIPELINE_TEST(RecordingCanvas,drawGlyphs_forceAlignLeft)237 OPENGL_PIPELINE_TEST(RecordingCanvas, drawGlyphs_forceAlignLeft) {
238     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
239         SkPaint paint;
240         paint.setAntiAlias(true);
241         paint.setTextSize(20);
242         paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
243         paint.setTextAlign(SkPaint::kLeft_Align);
244         TestUtils::drawUtf8ToCanvas(&canvas, "test text", paint, 25, 25);
245         paint.setTextAlign(SkPaint::kCenter_Align);
246         TestUtils::drawUtf8ToCanvas(&canvas, "test text", paint, 25, 25);
247         paint.setTextAlign(SkPaint::kRight_Align);
248         TestUtils::drawUtf8ToCanvas(&canvas, "test text", paint, 25, 25);
249     });
250 
251     int count = 0;
252     float lastX = FLT_MAX;
253     playbackOps(*dl, [&count, &lastX](const RecordedOp& op) {
254         count++;
255         ASSERT_EQ(RecordedOpId::TextOp, op.opId);
256         EXPECT_EQ(SkPaint::kLeft_Align, op.paint->getTextAlign())
257                 << "recorded drawText commands must force kLeft_Align on their paint";
258 
259         // verify TestUtils alignment offsetting (TODO: move asserts to Canvas base class)
260         EXPECT_GT(lastX, ((const TextOp&)op).x)
261                 << "x coordinate should reduce across each of the draw commands, from alignment";
262         lastX = ((const TextOp&)op).x;
263     });
264     ASSERT_EQ(3, count);
265 }
266 
OPENGL_PIPELINE_TEST(RecordingCanvas,drawColor)267 OPENGL_PIPELINE_TEST(RecordingCanvas, drawColor) {
268     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
269         canvas.drawColor(Color::Black, SkBlendMode::kSrcOver);
270     });
271 
272     ASSERT_EQ(1u, dl->getOps().size()) << "Must be exactly one op";
273     auto op = *(dl->getOps()[0]);
274     EXPECT_EQ(RecordedOpId::ColorOp, op.opId);
275     EXPECT_EQ(nullptr, op.localClip);
276     EXPECT_TRUE(op.unmappedBounds.isEmpty()) << "Expect undefined recorded bounds";
277 }
278 
OPENGL_PIPELINE_TEST(RecordingCanvas,backgroundAndImage)279 OPENGL_PIPELINE_TEST(RecordingCanvas, backgroundAndImage) {
280     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
281         sk_sp<Bitmap> bitmap(TestUtils::createBitmap(25, 25));
282         SkPaint paint;
283         paint.setColor(SK_ColorBLUE);
284 
285         canvas.save(SaveFlags::MatrixClip);
286         {
287             // a background!
288             canvas.save(SaveFlags::MatrixClip);
289             canvas.drawRect(0, 0, 100, 200, paint);
290             canvas.restore();
291         }
292         {
293             // an image!
294             canvas.save(SaveFlags::MatrixClip);
295             canvas.translate(25, 25);
296             canvas.scale(2, 2);
297             canvas.drawBitmap(*bitmap, 0, 0, nullptr);
298             canvas.restore();
299         }
300         canvas.restore();
301     });
302 
303     int count = 0;
304     playbackOps(*dl, [&count](const RecordedOp& op) {
305         if (count == 0) {
306             ASSERT_EQ(RecordedOpId::RectOp, op.opId);
307             ASSERT_NE(nullptr, op.paint);
308             EXPECT_EQ(SK_ColorBLUE, op.paint->getColor());
309             EXPECT_EQ(Rect(100, 200), op.unmappedBounds);
310             EXPECT_EQ(nullptr, op.localClip);
311 
312             Matrix4 expectedMatrix;
313             expectedMatrix.loadIdentity();
314             EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix);
315         } else {
316             ASSERT_EQ(RecordedOpId::BitmapOp, op.opId);
317             EXPECT_EQ(nullptr, op.paint);
318             EXPECT_EQ(Rect(25, 25), op.unmappedBounds);
319             EXPECT_EQ(nullptr, op.localClip);
320 
321             Matrix4 expectedMatrix;
322             expectedMatrix.loadTranslate(25, 25, 0);
323             expectedMatrix.scale(2, 2, 1);
324             EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix);
325         }
326         count++;
327     });
328     ASSERT_EQ(2, count);
329 }
330 
RENDERTHREAD_OPENGL_PIPELINE_TEST(RecordingCanvas,textureLayer)331 RENDERTHREAD_OPENGL_PIPELINE_TEST(RecordingCanvas, textureLayer) {
332     auto layerUpdater = TestUtils::createTextureLayerUpdater(renderThread, 100, 100,
333             SkMatrix::MakeTrans(5, 5));
334 
335     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200,
336             [&layerUpdater](RecordingCanvas& canvas) {
337         canvas.drawLayer(layerUpdater.get());
338     });
339 
340     validateSingleOp(dl, [] (const RecordedOp& op) {
341         ASSERT_EQ(RecordedOpId::TextureLayerOp, op.opId);
342         ASSERT_TRUE(op.localMatrix.isIdentity()) << "Op must not apply matrix at record time.";
343     });
344 }
345 
OPENGL_PIPELINE_TEST(RecordingCanvas,saveLayer_simple)346 OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_simple) {
347     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
348         canvas.saveLayerAlpha(10, 20, 190, 180, 128, SaveFlags::ClipToLayer);
349         canvas.drawRect(10, 20, 190, 180, SkPaint());
350         canvas.restore();
351     });
352     int count = 0;
353     playbackOps(*dl, [&count](const RecordedOp& op) {
354         Matrix4 expectedMatrix;
355         switch(count++) {
356         case 0:
357             EXPECT_EQ(RecordedOpId::BeginLayerOp, op.opId);
358             EXPECT_EQ(Rect(10, 20, 190, 180), op.unmappedBounds);
359             EXPECT_EQ(nullptr, op.localClip);
360             EXPECT_TRUE(op.localMatrix.isIdentity());
361             break;
362         case 1:
363             EXPECT_EQ(RecordedOpId::RectOp, op.opId);
364             EXPECT_CLIP_RECT(Rect(180, 160), op.localClip);
365             EXPECT_EQ(Rect(10, 20, 190, 180), op.unmappedBounds);
366             expectedMatrix.loadTranslate(-10, -20, 0);
367             EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix);
368             break;
369         case 2:
370             EXPECT_EQ(RecordedOpId::EndLayerOp, op.opId);
371             // Don't bother asserting recording state data - it's not used
372             break;
373         default:
374             ADD_FAILURE();
375         }
376     });
377     EXPECT_EQ(3, count);
378 }
379 
OPENGL_PIPELINE_TEST(RecordingCanvas,saveLayer_rounding)380 OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_rounding) {
381     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [](RecordingCanvas& canvas) {
382             canvas.saveLayerAlpha(10.25f, 10.75f, 89.25f, 89.75f, 128, SaveFlags::ClipToLayer);
383             canvas.drawRect(20, 20, 80, 80, SkPaint());
384             canvas.restore();
385         });
386         int count = 0;
387         playbackOps(*dl, [&count](const RecordedOp& op) {
388             Matrix4 expectedMatrix;
389             switch(count++) {
390             case 0:
391                 EXPECT_EQ(RecordedOpId::BeginLayerOp, op.opId);
392                 EXPECT_EQ(Rect(10, 10, 90, 90), op.unmappedBounds) << "Expect bounds rounded out";
393                 break;
394             case 1:
395                 EXPECT_EQ(RecordedOpId::RectOp, op.opId);
396                 expectedMatrix.loadTranslate(-10, -10, 0);
397                 EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix) << "Expect rounded offset";
398                 break;
399             case 2:
400                 EXPECT_EQ(RecordedOpId::EndLayerOp, op.opId);
401                 // Don't bother asserting recording state data - it's not used
402                 break;
403             default:
404                 ADD_FAILURE();
405             }
406         });
407         EXPECT_EQ(3, count);
408 }
409 
OPENGL_PIPELINE_TEST(RecordingCanvas,saveLayer_missingRestore)410 OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_missingRestore) {
411     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
412         canvas.saveLayerAlpha(0, 0, 200, 200, 128, SaveFlags::ClipToLayer);
413         canvas.drawRect(0, 0, 200, 200, SkPaint());
414         // Note: restore omitted, shouldn't result in unmatched save
415     });
416     int count = 0;
417     playbackOps(*dl, [&count](const RecordedOp& op) {
418         if (count++ == 2) {
419             EXPECT_EQ(RecordedOpId::EndLayerOp, op.opId);
420         }
421     });
422     EXPECT_EQ(3, count) << "Missing a restore shouldn't result in an unmatched saveLayer";
423 }
424 
OPENGL_PIPELINE_TEST(RecordingCanvas,saveLayer_simpleUnclipped)425 OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_simpleUnclipped) {
426     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
427         canvas.saveLayerAlpha(10, 20, 190, 180, 128, (SaveFlags::Flags)0); // unclipped
428         canvas.drawRect(10, 20, 190, 180, SkPaint());
429         canvas.restore();
430     });
431     int count = 0;
432     playbackOps(*dl, [&count](const RecordedOp& op) {
433         switch(count++) {
434         case 0:
435             EXPECT_EQ(RecordedOpId::BeginUnclippedLayerOp, op.opId);
436             EXPECT_EQ(Rect(10, 20, 190, 180), op.unmappedBounds);
437             EXPECT_EQ(nullptr, op.localClip);
438             EXPECT_TRUE(op.localMatrix.isIdentity());
439             break;
440         case 1:
441             EXPECT_EQ(RecordedOpId::RectOp, op.opId);
442             EXPECT_EQ(nullptr, op.localClip);
443             EXPECT_EQ(Rect(10, 20, 190, 180), op.unmappedBounds);
444             EXPECT_TRUE(op.localMatrix.isIdentity());
445             break;
446         case 2:
447             EXPECT_EQ(RecordedOpId::EndUnclippedLayerOp, op.opId);
448             // Don't bother asserting recording state data - it's not used
449             break;
450         default:
451             ADD_FAILURE();
452         }
453     });
454     EXPECT_EQ(3, count);
455 }
456 
OPENGL_PIPELINE_TEST(RecordingCanvas,saveLayer_addClipFlag)457 OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_addClipFlag) {
458     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
459         canvas.save(SaveFlags::MatrixClip);
460         canvas.clipRect(10, 20, 190, 180, SkClipOp::kIntersect);
461         canvas.saveLayerAlpha(10, 20, 190, 180, 128, (SaveFlags::Flags)0); // unclipped
462         canvas.drawRect(10, 20, 190, 180, SkPaint());
463         canvas.restore();
464         canvas.restore();
465     });
466     int count = 0;
467     playbackOps(*dl, [&count](const RecordedOp& op) {
468         if (count++ == 0) {
469             EXPECT_EQ(RecordedOpId::BeginLayerOp, op.opId)
470                     << "Clip + unclipped saveLayer should result in a clipped layer";
471         }
472     });
473     EXPECT_EQ(3, count);
474 }
475 
OPENGL_PIPELINE_TEST(RecordingCanvas,saveLayer_viewportCrop)476 OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_viewportCrop) {
477     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
478         // shouldn't matter, since saveLayer will clip to its bounds
479         canvas.clipRect(-1000, -1000, 1000, 1000, SkClipOp::kReplace_deprecated);
480 
481         canvas.saveLayerAlpha(100, 100, 300, 300, 128, SaveFlags::ClipToLayer);
482         canvas.drawRect(0, 0, 400, 400, SkPaint());
483         canvas.restore();
484     });
485     int count = 0;
486     playbackOps(*dl, [&count](const RecordedOp& op) {
487         if (count++ == 1) {
488             Matrix4 expectedMatrix;
489             EXPECT_EQ(RecordedOpId::RectOp, op.opId);
490             EXPECT_CLIP_RECT(Rect(100, 100), op.localClip) // Recorded clip rect should be
491             // intersection of viewport and saveLayer bounds, in layer space;
492             EXPECT_EQ(Rect(400, 400), op.unmappedBounds);
493             expectedMatrix.loadTranslate(-100, -100, 0);
494             EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix);
495         }
496     });
497     EXPECT_EQ(3, count);
498 }
499 
OPENGL_PIPELINE_TEST(RecordingCanvas,saveLayer_rotateUnclipped)500 OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_rotateUnclipped) {
501     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
502         canvas.save(SaveFlags::MatrixClip);
503         canvas.translate(100, 100);
504         canvas.rotate(45);
505         canvas.translate(-50, -50);
506 
507         canvas.saveLayerAlpha(0, 0, 100, 100, 128, SaveFlags::ClipToLayer);
508         canvas.drawRect(0, 0, 100, 100, SkPaint());
509         canvas.restore();
510 
511         canvas.restore();
512     });
513     int count = 0;
514     playbackOps(*dl, [&count](const RecordedOp& op) {
515         if (count++ == 1) {
516             EXPECT_EQ(RecordedOpId::RectOp, op.opId);
517             EXPECT_CLIP_RECT(Rect(100, 100), op.localClip);
518             EXPECT_EQ(Rect(100, 100), op.unmappedBounds);
519             EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), op.localMatrix)
520                     << "Recorded op shouldn't see any canvas transform before the saveLayer";
521         }
522     });
523     EXPECT_EQ(3, count);
524 }
525 
OPENGL_PIPELINE_TEST(RecordingCanvas,saveLayer_rotateClipped)526 OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_rotateClipped) {
527     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
528         canvas.save(SaveFlags::MatrixClip);
529         canvas.translate(100, 100);
530         canvas.rotate(45);
531         canvas.translate(-200, -200);
532 
533         // area of saveLayer will be clipped to parent viewport, so we ask for 400x400...
534         canvas.saveLayerAlpha(0, 0, 400, 400, 128, SaveFlags::ClipToLayer);
535         canvas.drawRect(0, 0, 400, 400, SkPaint());
536         canvas.restore();
537 
538         canvas.restore();
539     });
540     int count = 0;
541     playbackOps(*dl, [&count](const RecordedOp& op) {
542         if (count++ == 1) {
543             Matrix4 expectedMatrix;
544             EXPECT_EQ(RecordedOpId::RectOp, op.opId);
545 
546             // ...and get about 58.6, 58.6, 341.4 341.4, because the bounds are clipped by
547             // the parent 200x200 viewport, but prior to rotation
548             ASSERT_NE(nullptr, op.localClip);
549             ASSERT_EQ(ClipMode::Rectangle, op.localClip->mode);
550             // NOTE: this check relies on saveLayer altering the clip post-viewport init. This
551             // causes the clip to be recorded by contained draw commands, though it's not necessary
552             // since the same clip will be computed at draw time. If such a change is made, this
553             // check could be done at record time by querying the clip, or the clip could be altered
554             // slightly so that it is serialized.
555             EXPECT_EQ(Rect(59, 59, 341, 341), op.localClip->rect);
556             EXPECT_EQ(Rect(400, 400), op.unmappedBounds);
557             expectedMatrix.loadIdentity();
558             EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix);
559         }
560     });
561     EXPECT_EQ(3, count);
562 }
563 
OPENGL_PIPELINE_TEST(RecordingCanvas,saveLayer_rejectBegin)564 OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_rejectBegin) {
565     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
566         canvas.save(SaveFlags::MatrixClip);
567         canvas.translate(0, -20); // avoid identity case
568         // empty clip rect should force layer + contents to be rejected
569         canvas.clipRect(0, -20, 200, -20, SkClipOp::kIntersect);
570         canvas.saveLayerAlpha(0, 0, 200, 200, 128, SaveFlags::ClipToLayer);
571         canvas.drawRect(0, 0, 200, 200, SkPaint());
572         canvas.restore();
573         canvas.restore();
574     });
575 
576     ASSERT_EQ(0u, dl->getOps().size()) << "Begin/Rect/End should all be rejected.";
577 }
578 
OPENGL_PIPELINE_TEST(RecordingCanvas,drawRenderNode_rejection)579 OPENGL_PIPELINE_TEST(RecordingCanvas, drawRenderNode_rejection) {
580     auto child = TestUtils::createNode(50, 50, 150, 150,
581             [](RenderProperties& props, Canvas& canvas) {
582         SkPaint paint;
583         paint.setColor(SK_ColorWHITE);
584         canvas.drawRect(0, 0, 100, 100, paint);
585     });
586 
587     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [&child](RecordingCanvas& canvas) {
588         canvas.clipRect(0, 0, 0, 0, SkClipOp::kIntersect); // empty clip, reject node
589         canvas.drawRenderNode(child.get()); // shouldn't crash when rejecting node...
590     });
591     ASSERT_TRUE(dl->isEmpty());
592 }
593 
OPENGL_PIPELINE_TEST(RecordingCanvas,drawRenderNode_projection)594 OPENGL_PIPELINE_TEST(RecordingCanvas, drawRenderNode_projection) {
595     sp<RenderNode> background = TestUtils::createNode(50, 50, 150, 150,
596             [](RenderProperties& props, Canvas& canvas) {
597         SkPaint paint;
598         paint.setColor(SK_ColorWHITE);
599         canvas.drawRect(0, 0, 100, 100, paint);
600     });
601     {
602         background->mutateStagingProperties().setProjectionReceiver(false);
603 
604         // NO RECEIVER PRESENT
605         auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200,
606                     [&background](RecordingCanvas& canvas) {
607             canvas.drawRect(0, 0, 100, 100, SkPaint());
608             canvas.drawRenderNode(background.get());
609             canvas.drawRect(0, 0, 100, 100, SkPaint());
610         });
611         EXPECT_EQ(-1, dl->projectionReceiveIndex)
612                 << "no projection receiver should have been observed";
613     }
614     {
615         background->mutateStagingProperties().setProjectionReceiver(true);
616 
617         // RECEIVER PRESENT
618         auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200,
619                     [&background](RecordingCanvas& canvas) {
620             canvas.drawRect(0, 0, 100, 100, SkPaint());
621             canvas.drawRenderNode(background.get());
622             canvas.drawRect(0, 0, 100, 100, SkPaint());
623         });
624 
625         ASSERT_EQ(3u, dl->getOps().size()) << "Must be three ops";
626         auto op = dl->getOps()[1];
627         EXPECT_EQ(RecordedOpId::RenderNodeOp, op->opId);
628         EXPECT_EQ(1, dl->projectionReceiveIndex)
629                 << "correct projection receiver not identified";
630 
631         // verify the behavior works even though projection receiver hasn't been sync'd yet
632         EXPECT_TRUE(background->stagingProperties().isProjectionReceiver());
633         EXPECT_FALSE(background->properties().isProjectionReceiver());
634     }
635 }
636 
OPENGL_PIPELINE_TEST(RecordingCanvas,firstClipWillReplace)637 OPENGL_PIPELINE_TEST(RecordingCanvas, firstClipWillReplace) {
638     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
639         canvas.save(SaveFlags::MatrixClip);
640         // since no explicit clip set on canvas, this should be the one observed on op:
641         canvas.clipRect(-100, -100, 300, 300, SkClipOp::kIntersect);
642 
643         SkPaint paint;
644         paint.setColor(SK_ColorWHITE);
645         canvas.drawRect(0, 0, 100, 100, paint);
646 
647         canvas.restore();
648     });
649     ASSERT_EQ(1u, dl->getOps().size()) << "Must have one op";
650     // first clip must be preserved, even if it extends beyond canvas bounds
651     EXPECT_CLIP_RECT(Rect(-100, -100, 300, 300), dl->getOps()[0]->localClip);
652 }
653 
OPENGL_PIPELINE_TEST(RecordingCanvas,replaceClipIntersectWithRoot)654 OPENGL_PIPELINE_TEST(RecordingCanvas, replaceClipIntersectWithRoot) {
655     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [](RecordingCanvas& canvas) {
656         canvas.save(SaveFlags::MatrixClip);
657         canvas.clipRect(-10, -10, 110, 110, SkClipOp::kReplace_deprecated);
658         canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
659         canvas.restore();
660     });
661     ASSERT_EQ(1u, dl->getOps().size()) << "Must have one op";
662     // first clip must be preserved, even if it extends beyond canvas bounds
663     EXPECT_CLIP_RECT(Rect(-10, -10, 110, 110), dl->getOps()[0]->localClip);
664     EXPECT_TRUE(dl->getOps()[0]->localClip->intersectWithRoot);
665 }
666 
OPENGL_PIPELINE_TEST(RecordingCanvas,insertReorderBarrier)667 OPENGL_PIPELINE_TEST(RecordingCanvas, insertReorderBarrier) {
668     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
669         canvas.drawRect(0, 0, 400, 400, SkPaint());
670         canvas.insertReorderBarrier(true);
671         canvas.insertReorderBarrier(false);
672         canvas.insertReorderBarrier(false);
673         canvas.insertReorderBarrier(true);
674         canvas.drawRect(0, 0, 400, 400, SkPaint());
675         canvas.insertReorderBarrier(false);
676     });
677 
678     auto chunks = dl->getChunks();
679     EXPECT_EQ(0u, chunks[0].beginOpIndex);
680     EXPECT_EQ(1u, chunks[0].endOpIndex);
681     EXPECT_FALSE(chunks[0].reorderChildren);
682 
683     EXPECT_EQ(1u, chunks[1].beginOpIndex);
684     EXPECT_EQ(2u, chunks[1].endOpIndex);
685     EXPECT_TRUE(chunks[1].reorderChildren);
686 }
687 
OPENGL_PIPELINE_TEST(RecordingCanvas,insertReorderBarrier_clip)688 OPENGL_PIPELINE_TEST(RecordingCanvas, insertReorderBarrier_clip) {
689     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
690         // first chunk: no recorded clip
691         canvas.insertReorderBarrier(true);
692         canvas.drawRect(0, 0, 400, 400, SkPaint());
693 
694         // second chunk: no recorded clip, since inorder region
695         canvas.clipRect(0, 0, 200, 200, SkClipOp::kIntersect);
696         canvas.insertReorderBarrier(false);
697         canvas.drawRect(0, 0, 400, 400, SkPaint());
698 
699         // third chunk: recorded clip
700         canvas.insertReorderBarrier(true);
701         canvas.drawRect(0, 0, 400, 400, SkPaint());
702     });
703 
704     auto chunks = dl->getChunks();
705     ASSERT_EQ(3u, chunks.size());
706 
707     EXPECT_TRUE(chunks[0].reorderChildren);
708     EXPECT_EQ(nullptr, chunks[0].reorderClip);
709 
710     EXPECT_FALSE(chunks[1].reorderChildren);
711     EXPECT_EQ(nullptr, chunks[1].reorderClip);
712 
713     EXPECT_TRUE(chunks[2].reorderChildren);
714     ASSERT_NE(nullptr, chunks[2].reorderClip);
715     EXPECT_EQ(Rect(200, 200), chunks[2].reorderClip->rect);
716 }
717 
OPENGL_PIPELINE_TEST(RecordingCanvas,refPaint)718 OPENGL_PIPELINE_TEST(RecordingCanvas, refPaint) {
719     SkPaint paint;
720 
721     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [&paint](RecordingCanvas& canvas) {
722         paint.setColor(SK_ColorBLUE);
723         // first two should use same paint
724         canvas.drawRect(0, 0, 200, 10, paint);
725         SkPaint paintCopy(paint);
726         canvas.drawRect(0, 10, 200, 20, paintCopy);
727 
728         // only here do we use different paint ptr
729         paint.setColor(SK_ColorRED);
730         canvas.drawRect(0, 20, 200, 30, paint);
731     });
732     auto ops = dl->getOps();
733     ASSERT_EQ(3u, ops.size());
734 
735     // first two are the same
736     EXPECT_NE(nullptr, ops[0]->paint);
737     EXPECT_NE(&paint, ops[0]->paint);
738     EXPECT_EQ(ops[0]->paint, ops[1]->paint);
739 
740     // last is different, but still copied / non-null
741     EXPECT_NE(nullptr, ops[2]->paint);
742     EXPECT_NE(ops[0]->paint, ops[2]->paint);
743     EXPECT_NE(&paint, ops[2]->paint);
744 }
745 
OPENGL_PIPELINE_TEST(RecordingCanvas,refBitmap)746 OPENGL_PIPELINE_TEST(RecordingCanvas, refBitmap) {
747     sk_sp<Bitmap> bitmap(TestUtils::createBitmap(100, 100));
748     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [&bitmap](RecordingCanvas& canvas) {
749         canvas.drawBitmap(*bitmap, 0, 0, nullptr);
750     });
751     auto& bitmaps = dl->getBitmapResources();
752     EXPECT_EQ(1u, bitmaps.size());
753 }
754 
OPENGL_PIPELINE_TEST(RecordingCanvas,refBitmapInShader_bitmapShader)755 OPENGL_PIPELINE_TEST(RecordingCanvas, refBitmapInShader_bitmapShader) {
756     sk_sp<Bitmap> bitmap = TestUtils::createBitmap(100, 100);
757     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [&bitmap](RecordingCanvas& canvas) {
758         SkPaint paint;
759         SkBitmap skBitmap;
760         bitmap->getSkBitmap(&skBitmap);
761         sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(skBitmap, kNever_SkCopyPixelsMode);
762         sk_sp<SkShader> shader = image->makeShader(
763                 SkShader::TileMode::kClamp_TileMode,
764                 SkShader::TileMode::kClamp_TileMode,
765                 nullptr);
766         paint.setShader(std::move(shader));
767         canvas.drawRoundRect(0, 0, 100, 100, 20.0f, 20.0f, paint);
768     });
769     auto& bitmaps = dl->getBitmapResources();
770     EXPECT_EQ(1u, bitmaps.size());
771 }
772 
OPENGL_PIPELINE_TEST(RecordingCanvas,refBitmapInShader_composeShader)773 OPENGL_PIPELINE_TEST(RecordingCanvas, refBitmapInShader_composeShader) {
774     sk_sp<Bitmap> bitmap = TestUtils::createBitmap(100, 100);
775     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [&bitmap](RecordingCanvas& canvas) {
776         SkPaint paint;
777         SkBitmap skBitmap;
778         bitmap->getSkBitmap(&skBitmap);
779         sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(skBitmap, kNever_SkCopyPixelsMode);
780         sk_sp<SkShader> shader1 = image->makeShader(
781                 SkShader::TileMode::kClamp_TileMode,
782                 SkShader::TileMode::kClamp_TileMode,
783                 nullptr);
784 
785         SkPoint center;
786         center.set(50, 50);
787         SkColor colors[2];
788         colors[0] = Color::Black;
789         colors[1] = Color::White;
790         sk_sp<SkShader> shader2 = SkGradientShader::MakeRadial(center, 50, colors, nullptr, 2,
791                 SkShader::TileMode::kRepeat_TileMode);
792 
793         sk_sp<SkShader> composeShader = SkShader::MakeComposeShader(std::move(shader1), std::move(shader2),
794                 SkBlendMode::kMultiply);
795         paint.setShader(std::move(composeShader));
796         canvas.drawRoundRect(0, 0, 100, 100, 20.0f, 20.0f, paint);
797     });
798     auto& bitmaps = dl->getBitmapResources();
799     EXPECT_EQ(1u, bitmaps.size());
800 }
801 
OPENGL_PIPELINE_TEST(RecordingCanvas,drawText)802 OPENGL_PIPELINE_TEST(RecordingCanvas, drawText) {
803     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
804         Paint paint;
805         paint.setAntiAlias(true);
806         paint.setTextSize(20);
807         paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
808         std::unique_ptr<uint16_t[]> dst = TestUtils::asciiToUtf16("HELLO");
809         canvas.drawText(dst.get(), 0, 5, 5, 25, 25, minikin::kBidi_Force_LTR, paint, NULL);
810     });
811 
812     int count = 0;
813     playbackOps(*dl, [&count](const RecordedOp& op) {
814         count++;
815         ASSERT_EQ(RecordedOpId::TextOp, op.opId);
816         EXPECT_EQ(nullptr, op.localClip);
817         EXPECT_TRUE(op.localMatrix.isIdentity());
818         EXPECT_TRUE(op.unmappedBounds.getHeight() >= 10);
819         EXPECT_TRUE(op.unmappedBounds.getWidth() >= 25);
820     });
821     ASSERT_EQ(1, count);
822 }
823 
OPENGL_PIPELINE_TEST(RecordingCanvas,drawTextInHighContrast)824 OPENGL_PIPELINE_TEST(RecordingCanvas, drawTextInHighContrast) {
825     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
826         canvas.setHighContrastText(true);
827         Paint paint;
828         paint.setColor(SK_ColorWHITE);
829         paint.setAntiAlias(true);
830         paint.setTextSize(20);
831         paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
832         std::unique_ptr<uint16_t[]> dst = TestUtils::asciiToUtf16("HELLO");
833         canvas.drawText(dst.get(), 0, 5, 5, 25, 25, minikin::kBidi_Force_LTR, paint, NULL);
834     });
835 
836     int count = 0;
837     playbackOps(*dl, [&count](const RecordedOp& op) {
838         ASSERT_EQ(RecordedOpId::TextOp, op.opId);
839         if (count++ == 0) {
840             EXPECT_EQ(SK_ColorBLACK, op.paint->getColor());
841             EXPECT_EQ(SkPaint::kStrokeAndFill_Style, op.paint->getStyle());
842         } else {
843             EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
844             EXPECT_EQ(SkPaint::kFill_Style, op.paint->getStyle());
845         }
846 
847     });
848     ASSERT_EQ(2, count);
849 }
850 
851 } // namespace uirenderer
852 } // namespace android
853