RecordingCanvasTests.cpp revision caa24184735a607e87077c73262a42acdea7b8fb
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 <RecordedOp.h>
20#include <RecordingCanvas.h>
21#include <tests/common/TestUtils.h>
22
23namespace android {
24namespace uirenderer {
25
26static void playbackOps(const DisplayList& displayList,
27        std::function<void(const RecordedOp&)> opReceiver) {
28    for (auto& chunk : displayList.getChunks()) {
29        for (size_t opIndex = chunk.beginOpIndex; opIndex < chunk.endOpIndex; opIndex++) {
30            RecordedOp* op = displayList.getOps()[opIndex];
31            opReceiver(*op);
32        }
33    }
34}
35
36TEST(RecordingCanvas, emptyPlayback) {
37    auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
38        canvas.save(SaveFlags::MatrixClip);
39        canvas.restore();
40    });
41    playbackOps(*dl, [](const RecordedOp& op) { ADD_FAILURE(); });
42}
43
44TEST(RecordingCanvas, clipRect) {
45    auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [](RecordingCanvas& canvas) {
46        canvas.save(SaveFlags::MatrixClip);
47        canvas.clipRect(0, 0, 100, 100, SkRegion::kIntersect_Op);
48        canvas.drawRect(0, 0, 50, 50, SkPaint());
49        canvas.drawRect(50, 50, 100, 100, SkPaint());
50        canvas.restore();
51    });
52
53    ASSERT_EQ(2u, dl->getOps().size()) << "Must be exactly two ops";
54    EXPECT_CLIP_RECT(Rect(100, 100), dl->getOps()[0]->localClip);
55    EXPECT_CLIP_RECT(Rect(100, 100), dl->getOps()[1]->localClip);
56    EXPECT_EQ(dl->getOps()[0]->localClip, dl->getOps()[1]->localClip)
57            << "Clip should be serialized once";
58}
59
60TEST(RecordingCanvas, drawArc) {
61    auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
62        canvas.drawArc(0, 0, 200, 200, 0, 180, true, SkPaint());
63        canvas.drawArc(0, 0, 100, 100, 0, 360, true, SkPaint());
64    });
65
66    auto&& ops = dl->getOps();
67    ASSERT_EQ(2u, ops.size()) << "Must be exactly two ops";
68    EXPECT_EQ(RecordedOpId::ArcOp, ops[0]->opId);
69    EXPECT_EQ(Rect(200, 200), ops[0]->unmappedBounds);
70
71    EXPECT_EQ(RecordedOpId::OvalOp, ops[1]->opId)
72            << "Circular arcs should be converted to ovals";
73    EXPECT_EQ(Rect(100, 100), ops[1]->unmappedBounds);
74}
75
76TEST(RecordingCanvas, drawLines) {
77    auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
78        SkPaint paint;
79        paint.setStrokeWidth(20); // doesn't affect recorded bounds - would be resolved at bake time
80        float points[] = { 0, 0, 20, 10, 30, 40, 90 }; // NB: only 1 valid line
81        canvas.drawLines(&points[0], 7, paint);
82    });
83
84    ASSERT_EQ(1u, dl->getOps().size()) << "Must be exactly one op";
85    auto op = dl->getOps()[0];
86    ASSERT_EQ(RecordedOpId::LinesOp, op->opId);
87    EXPECT_EQ(4, ((LinesOp*)op)->floatCount)
88            << "float count must be rounded down to closest multiple of 4";
89    EXPECT_EQ(Rect(20, 10), op->unmappedBounds)
90            << "unmapped bounds must be size of line, and not outset for stroke width";
91}
92
93TEST(RecordingCanvas, drawRect) {
94    auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
95        canvas.drawRect(10, 20, 90, 180, SkPaint());
96    });
97
98    ASSERT_EQ(1u, dl->getOps().size()) << "Must be exactly one op";
99    auto op = *(dl->getOps()[0]);
100    ASSERT_EQ(RecordedOpId::RectOp, op.opId);
101    EXPECT_EQ(nullptr, op.localClip);
102    EXPECT_EQ(Rect(10, 20, 90, 180), op.unmappedBounds);
103}
104
105TEST(RecordingCanvas, drawText) {
106    auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
107        SkPaint paint;
108        paint.setAntiAlias(true);
109        paint.setTextSize(20);
110        paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
111        TestUtils::drawTextToCanvas(&canvas, "test text", paint, 25, 25);
112    });
113
114    int count = 0;
115    playbackOps(*dl, [&count](const RecordedOp& op) {
116        count++;
117        ASSERT_EQ(RecordedOpId::TextOp, op.opId);
118        EXPECT_EQ(nullptr, op.localClip);
119        EXPECT_TRUE(op.localMatrix.isIdentity());
120        EXPECT_TRUE(op.unmappedBounds.contains(25, 15, 50, 25))
121                << "Op expected to be 25+ pixels wide, 10+ pixels tall";
122    });
123    ASSERT_EQ(1, count);
124}
125
126TEST(RecordingCanvas, drawText_strikeThruAndUnderline) {
127    auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
128        SkPaint paint;
129        paint.setAntiAlias(true);
130        paint.setTextSize(20);
131        paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
132        for (int i = 0; i < 2; i++) {
133            for (int j = 0; j < 2; j++) {
134                paint.setUnderlineText(i != 0);
135                paint.setStrikeThruText(j != 0);
136                TestUtils::drawTextToCanvas(&canvas, "test text", paint, 25, 25);
137            }
138        }
139    });
140
141    auto ops = dl->getOps();
142    ASSERT_EQ(8u, ops.size());
143
144    int index = 0;
145    EXPECT_EQ(RecordedOpId::TextOp, ops[index++]->opId); // no underline or strikethrough
146
147    EXPECT_EQ(RecordedOpId::TextOp, ops[index++]->opId);
148    EXPECT_EQ(RecordedOpId::RectOp, ops[index++]->opId); // strikethrough only
149
150    EXPECT_EQ(RecordedOpId::TextOp, ops[index++]->opId);
151    EXPECT_EQ(RecordedOpId::RectOp, ops[index++]->opId); // underline only
152
153    EXPECT_EQ(RecordedOpId::TextOp, ops[index++]->opId);
154    EXPECT_EQ(RecordedOpId::RectOp, ops[index++]->opId); // underline
155    EXPECT_EQ(RecordedOpId::RectOp, ops[index++]->opId); // strikethrough
156}
157
158TEST(RecordingCanvas, drawText_forceAlignLeft) {
159    auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
160        SkPaint paint;
161        paint.setAntiAlias(true);
162        paint.setTextSize(20);
163        paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
164        paint.setTextAlign(SkPaint::kLeft_Align);
165        TestUtils::drawTextToCanvas(&canvas, "test text", paint, 25, 25);
166        paint.setTextAlign(SkPaint::kCenter_Align);
167        TestUtils::drawTextToCanvas(&canvas, "test text", paint, 25, 25);
168        paint.setTextAlign(SkPaint::kRight_Align);
169        TestUtils::drawTextToCanvas(&canvas, "test text", paint, 25, 25);
170    });
171
172    int count = 0;
173    float lastX = FLT_MAX;
174    playbackOps(*dl, [&count, &lastX](const RecordedOp& op) {
175        count++;
176        ASSERT_EQ(RecordedOpId::TextOp, op.opId);
177        EXPECT_EQ(SkPaint::kLeft_Align, op.paint->getTextAlign())
178                << "recorded drawText commands must force kLeft_Align on their paint";
179
180        // verify TestUtils alignment offsetting (TODO: move asserts to Canvas base class)
181        EXPECT_GT(lastX, ((const TextOp&)op).x)
182                << "x coordinate should reduce across each of the draw commands, from alignment";
183        lastX = ((const TextOp&)op).x;
184    });
185    ASSERT_EQ(3, count);
186}
187
188TEST(RecordingCanvas, backgroundAndImage) {
189    auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
190        SkBitmap bitmap;
191        bitmap.setInfo(SkImageInfo::MakeUnknown(25, 25));
192        SkPaint paint;
193        paint.setColor(SK_ColorBLUE);
194
195        canvas.save(SaveFlags::MatrixClip);
196        {
197            // a background!
198            canvas.save(SaveFlags::MatrixClip);
199            canvas.drawRect(0, 0, 100, 200, paint);
200            canvas.restore();
201        }
202        {
203            // an image!
204            canvas.save(SaveFlags::MatrixClip);
205            canvas.translate(25, 25);
206            canvas.scale(2, 2);
207            canvas.drawBitmap(bitmap, 0, 0, nullptr);
208            canvas.restore();
209        }
210        canvas.restore();
211    });
212
213    int count = 0;
214    playbackOps(*dl, [&count](const RecordedOp& op) {
215        if (count == 0) {
216            ASSERT_EQ(RecordedOpId::RectOp, op.opId);
217            ASSERT_NE(nullptr, op.paint);
218            EXPECT_EQ(SK_ColorBLUE, op.paint->getColor());
219            EXPECT_EQ(Rect(100, 200), op.unmappedBounds);
220            EXPECT_EQ(nullptr, op.localClip);
221
222            Matrix4 expectedMatrix;
223            expectedMatrix.loadIdentity();
224            EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix);
225        } else {
226            ASSERT_EQ(RecordedOpId::BitmapOp, op.opId);
227            EXPECT_EQ(nullptr, op.paint);
228            EXPECT_EQ(Rect(25, 25), op.unmappedBounds);
229            EXPECT_EQ(nullptr, op.localClip);
230
231            Matrix4 expectedMatrix;
232            expectedMatrix.loadTranslate(25, 25, 0);
233            expectedMatrix.scale(2, 2, 1);
234            EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix);
235        }
236        count++;
237    });
238    ASSERT_EQ(2, count);
239}
240
241TEST(RecordingCanvas, saveLayer_simple) {
242    auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
243        canvas.saveLayerAlpha(10, 20, 190, 180, 128, SaveFlags::ClipToLayer);
244        canvas.drawRect(10, 20, 190, 180, SkPaint());
245        canvas.restore();
246    });
247    int count = 0;
248    playbackOps(*dl, [&count](const RecordedOp& op) {
249        Matrix4 expectedMatrix;
250        switch(count++) {
251        case 0:
252            EXPECT_EQ(RecordedOpId::BeginLayerOp, op.opId);
253            EXPECT_EQ(Rect(10, 20, 190, 180), op.unmappedBounds);
254            EXPECT_EQ(nullptr, op.localClip);
255            EXPECT_TRUE(op.localMatrix.isIdentity());
256            break;
257        case 1:
258            EXPECT_EQ(RecordedOpId::RectOp, op.opId);
259            EXPECT_CLIP_RECT(Rect(180, 160), op.localClip);
260            EXPECT_EQ(Rect(10, 20, 190, 180), op.unmappedBounds);
261            expectedMatrix.loadTranslate(-10, -20, 0);
262            EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix);
263            break;
264        case 2:
265            EXPECT_EQ(RecordedOpId::EndLayerOp, op.opId);
266            // Don't bother asserting recording state data - it's not used
267            break;
268        default:
269            ADD_FAILURE();
270        }
271    });
272    EXPECT_EQ(3, count);
273}
274
275TEST(RecordingCanvas, saveLayer_missingRestore) {
276    auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
277        canvas.saveLayerAlpha(0, 0, 200, 200, 128, SaveFlags::ClipToLayer);
278        canvas.drawRect(0, 0, 200, 200, SkPaint());
279        // Note: restore omitted, shouldn't result in unmatched save
280    });
281    int count = 0;
282    playbackOps(*dl, [&count](const RecordedOp& op) {
283        if (count++ == 2) {
284            EXPECT_EQ(RecordedOpId::EndLayerOp, op.opId);
285        }
286    });
287    EXPECT_EQ(3, count) << "Missing a restore shouldn't result in an unmatched saveLayer";
288}
289
290TEST(RecordingCanvas, saveLayer_simpleUnclipped) {
291    auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
292        canvas.saveLayerAlpha(10, 20, 190, 180, 128, (SaveFlags::Flags)0); // unclipped
293        canvas.drawRect(10, 20, 190, 180, SkPaint());
294        canvas.restore();
295    });
296    int count = 0;
297    playbackOps(*dl, [&count](const RecordedOp& op) {
298        switch(count++) {
299        case 0:
300            EXPECT_EQ(RecordedOpId::BeginUnclippedLayerOp, op.opId);
301            EXPECT_EQ(Rect(10, 20, 190, 180), op.unmappedBounds);
302            EXPECT_EQ(nullptr, op.localClip);
303            EXPECT_TRUE(op.localMatrix.isIdentity());
304            break;
305        case 1:
306            EXPECT_EQ(RecordedOpId::RectOp, op.opId);
307            EXPECT_EQ(nullptr, op.localClip);
308            EXPECT_EQ(Rect(10, 20, 190, 180), op.unmappedBounds);
309            EXPECT_TRUE(op.localMatrix.isIdentity());
310            break;
311        case 2:
312            EXPECT_EQ(RecordedOpId::EndUnclippedLayerOp, op.opId);
313            // Don't bother asserting recording state data - it's not used
314            break;
315        default:
316            ADD_FAILURE();
317        }
318    });
319    EXPECT_EQ(3, count);
320}
321
322TEST(RecordingCanvas, saveLayer_addClipFlag) {
323    auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
324        canvas.save(SaveFlags::MatrixClip);
325        canvas.clipRect(10, 20, 190, 180, SkRegion::kIntersect_Op);
326        canvas.saveLayerAlpha(10, 20, 190, 180, 128, (SaveFlags::Flags)0); // unclipped
327        canvas.drawRect(10, 20, 190, 180, SkPaint());
328        canvas.restore();
329        canvas.restore();
330    });
331    int count = 0;
332    playbackOps(*dl, [&count](const RecordedOp& op) {
333        if (count++ == 0) {
334            EXPECT_EQ(RecordedOpId::BeginLayerOp, op.opId)
335                    << "Clip + unclipped saveLayer should result in a clipped layer";
336        }
337    });
338    EXPECT_EQ(3, count);
339}
340
341TEST(RecordingCanvas, saveLayer_viewportCrop) {
342    auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
343        // shouldn't matter, since saveLayer will clip to its bounds
344        canvas.clipRect(-1000, -1000, 1000, 1000, SkRegion::kReplace_Op);
345
346        canvas.saveLayerAlpha(100, 100, 300, 300, 128, SaveFlags::ClipToLayer);
347        canvas.drawRect(0, 0, 400, 400, SkPaint());
348        canvas.restore();
349    });
350    int count = 0;
351    playbackOps(*dl, [&count](const RecordedOp& op) {
352        if (count++ == 1) {
353            Matrix4 expectedMatrix;
354            EXPECT_EQ(RecordedOpId::RectOp, op.opId);
355            EXPECT_CLIP_RECT(Rect(100, 100), op.localClip) // Recorded clip rect should be
356            // intersection of viewport and saveLayer bounds, in layer space;
357            EXPECT_EQ(Rect(400, 400), op.unmappedBounds);
358            expectedMatrix.loadTranslate(-100, -100, 0);
359            EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix);
360        }
361    });
362    EXPECT_EQ(3, count);
363}
364
365TEST(RecordingCanvas, saveLayer_rotateUnclipped) {
366    auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
367        canvas.save(SaveFlags::MatrixClip);
368        canvas.translate(100, 100);
369        canvas.rotate(45);
370        canvas.translate(-50, -50);
371
372        canvas.saveLayerAlpha(0, 0, 100, 100, 128, SaveFlags::ClipToLayer);
373        canvas.drawRect(0, 0, 100, 100, SkPaint());
374        canvas.restore();
375
376        canvas.restore();
377    });
378    int count = 0;
379    playbackOps(*dl, [&count](const RecordedOp& op) {
380        if (count++ == 1) {
381            EXPECT_EQ(RecordedOpId::RectOp, op.opId);
382            EXPECT_CLIP_RECT(Rect(100, 100), op.localClip);
383            EXPECT_EQ(Rect(100, 100), op.unmappedBounds);
384            EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), op.localMatrix)
385                    << "Recorded op shouldn't see any canvas transform before the saveLayer";
386        }
387    });
388    EXPECT_EQ(3, count);
389}
390
391TEST(RecordingCanvas, saveLayer_rotateClipped) {
392    auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
393        canvas.save(SaveFlags::MatrixClip);
394        canvas.translate(100, 100);
395        canvas.rotate(45);
396        canvas.translate(-200, -200);
397
398        // area of saveLayer will be clipped to parent viewport, so we ask for 400x400...
399        canvas.saveLayerAlpha(0, 0, 400, 400, 128, SaveFlags::ClipToLayer);
400        canvas.drawRect(0, 0, 400, 400, SkPaint());
401        canvas.restore();
402
403        canvas.restore();
404    });
405    int count = 0;
406    playbackOps(*dl, [&count](const RecordedOp& op) {
407        if (count++ == 1) {
408            Matrix4 expectedMatrix;
409            EXPECT_EQ(RecordedOpId::RectOp, op.opId);
410
411            // ...and get about 58.6, 58.6, 341.4 341.4, because the bounds are clipped by
412            // the parent 200x200 viewport, but prior to rotation
413            ASSERT_NE(nullptr, op.localClip);
414            ASSERT_EQ(ClipMode::Rectangle, op.localClip->mode);
415            // NOTE: this check relies on saveLayer altering the clip post-viewport init. This
416            // causes the clip to be recorded by contained draw commands, though it's not necessary
417            // since the same clip will be computed at draw time. If such a change is made, this
418            // check could be done at record time by querying the clip, or the clip could be altered
419            // slightly so that it is serialized.
420            EXPECT_RECT_APPROX_EQ(Rect(58.57864, 58.57864, 341.42136, 341.42136),
421                    (reinterpret_cast<const ClipRect*>(op.localClip))->rect);
422
423            EXPECT_EQ(Rect(400, 400), op.unmappedBounds);
424            expectedMatrix.loadIdentity();
425            EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix);
426        }
427    });
428    EXPECT_EQ(3, count);
429}
430
431TEST(RecordingCanvas, drawRenderNode_projection) {
432    sp<RenderNode> background = TestUtils::createNode(50, 50, 150, 150,
433            [](RenderProperties& props, RecordingCanvas& canvas) {
434        SkPaint paint;
435        paint.setColor(SK_ColorWHITE);
436        canvas.drawRect(0, 0, 100, 100, paint);
437    });
438    {
439        background->mutateStagingProperties().setProjectionReceiver(false);
440
441        // NO RECEIVER PRESENT
442        auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200,
443                    [&background](RecordingCanvas& canvas) {
444            canvas.drawRect(0, 0, 100, 100, SkPaint());
445            canvas.drawRenderNode(background.get());
446            canvas.drawRect(0, 0, 100, 100, SkPaint());
447        });
448        EXPECT_EQ(-1, dl->projectionReceiveIndex)
449                << "no projection receiver should have been observed";
450    }
451    {
452        background->mutateStagingProperties().setProjectionReceiver(true);
453
454        // RECEIVER PRESENT
455        auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200,
456                    [&background](RecordingCanvas& canvas) {
457            canvas.drawRect(0, 0, 100, 100, SkPaint());
458            canvas.drawRenderNode(background.get());
459            canvas.drawRect(0, 0, 100, 100, SkPaint());
460        });
461
462        ASSERT_EQ(3u, dl->getOps().size()) << "Must be three ops";
463        auto op = dl->getOps()[1];
464        EXPECT_EQ(RecordedOpId::RenderNodeOp, op->opId);
465        EXPECT_EQ(1, dl->projectionReceiveIndex)
466                << "correct projection receiver not identified";
467
468        // verify the behavior works even though projection receiver hasn't been sync'd yet
469        EXPECT_TRUE(background->stagingProperties().isProjectionReceiver());
470        EXPECT_FALSE(background->properties().isProjectionReceiver());
471    }
472}
473
474TEST(RecordingCanvas, firstClipWillReplace) {
475    auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
476        canvas.save(SaveFlags::MatrixClip);
477        // since no explicit clip set on canvas, this should be the one observed on op:
478        canvas.clipRect(-100, -100, 300, 300, SkRegion::kIntersect_Op);
479
480        SkPaint paint;
481        paint.setColor(SK_ColorWHITE);
482        canvas.drawRect(0, 0, 100, 100, paint);
483
484        canvas.restore();
485    });
486    ASSERT_EQ(1u, dl->getOps().size()) << "Must have one op";
487    // first clip must be preserved, even if it extends beyond canvas bounds
488    EXPECT_CLIP_RECT(Rect(-100, -100, 300, 300), dl->getOps()[0]->localClip);
489}
490
491TEST(RecordingCanvas, insertReorderBarrier) {
492    auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
493        canvas.drawRect(0, 0, 400, 400, SkPaint());
494        canvas.insertReorderBarrier(true);
495        canvas.insertReorderBarrier(false);
496        canvas.insertReorderBarrier(false);
497        canvas.insertReorderBarrier(true);
498        canvas.drawRect(0, 0, 400, 400, SkPaint());
499        canvas.insertReorderBarrier(false);
500    });
501
502    auto chunks = dl->getChunks();
503    EXPECT_EQ(0u, chunks[0].beginOpIndex);
504    EXPECT_EQ(1u, chunks[0].endOpIndex);
505    EXPECT_FALSE(chunks[0].reorderChildren);
506
507    EXPECT_EQ(1u, chunks[1].beginOpIndex);
508    EXPECT_EQ(2u, chunks[1].endOpIndex);
509    EXPECT_TRUE(chunks[1].reorderChildren);
510}
511
512TEST(RecordingCanvas, refPaint) {
513    SkPaint paint;
514    paint.setAntiAlias(true);
515    paint.setTextSize(20);
516    paint.setTextAlign(SkPaint::kLeft_Align);
517    paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
518
519    auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [&paint](RecordingCanvas& canvas) {
520        paint.setColor(SK_ColorBLUE);
521        // first three should use same paint
522        canvas.drawRect(0, 0, 200, 10, paint);
523        SkPaint paintCopy(paint);
524        canvas.drawRect(0, 10, 200, 20, paintCopy);
525        TestUtils::drawTextToCanvas(&canvas, "helloworld", paint, 50, 25);
526
527        // only here do we use different paint ptr
528        paint.setColor(SK_ColorRED);
529        canvas.drawRect(0, 20, 200, 30, paint);
530    });
531    auto ops = dl->getOps();
532    ASSERT_EQ(4u, ops.size());
533
534    // first three are the same
535    EXPECT_NE(nullptr, ops[0]->paint);
536    EXPECT_NE(&paint, ops[0]->paint);
537    EXPECT_EQ(ops[0]->paint, ops[1]->paint);
538    EXPECT_EQ(ops[0]->paint, ops[2]->paint);
539
540    // last is different, but still copied / non-null
541    EXPECT_NE(nullptr, ops[3]->paint);
542    EXPECT_NE(ops[0]->paint, ops[3]->paint);
543    EXPECT_NE(&paint, ops[3]->paint);
544}
545
546} // namespace uirenderer
547} // namespace android
548