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