• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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 <canvas/CanvasFrontend.h>
20 #include <canvas/CanvasOpBuffer.h>
21 #include <canvas/CanvasOps.h>
22 #include <canvas/CanvasOpRasterizer.h>
23 
24 #include <tests/common/CallCountingCanvas.h>
25 
26 #include "SkPictureRecorder.h"
27 #include "SkColor.h"
28 #include "SkLatticeIter.h"
29 #include "pipeline/skia/AnimatedDrawables.h"
30 #include <SkNoDrawCanvas.h>
31 
32 using namespace android;
33 using namespace android::uirenderer;
34 using namespace android::uirenderer::skiapipeline;
35 using namespace android::uirenderer::test;
36 
37 // We lazy
38 using Op = CanvasOpType;
39 
40 class CanvasOpCountingReceiver {
41 public:
42     template <CanvasOpType T>
push_container(CanvasOpContainer<T> && op)43     void push_container(CanvasOpContainer<T>&& op) {
44         mOpCounts[static_cast<size_t>(T)] += 1;
45     }
46 
operator [](CanvasOpType op) const47     int operator[](CanvasOpType op) const {
48         return mOpCounts[static_cast<size_t>(op)];
49     }
50 
51 private:
52     std::array<int, static_cast<size_t>(CanvasOpType::COUNT)> mOpCounts;
53 };
54 
55 template<typename T>
countItems(const T & t)56 static int countItems(const T& t) {
57     int count = 0;
58     t.for_each([&](auto i) {
59         count++;
60     });
61     return count;
62 }
63 
TEST(CanvasOp,verifyConst)64 TEST(CanvasOp, verifyConst) {
65     CanvasOpBuffer buffer;
66     buffer.push<Op::DrawColor>({
67         .color = SkColors::kBlack,
68         .mode = SkBlendMode::kSrcOver,
69     });
70     buffer.for_each([](auto op) {
71         static_assert(std::is_const_v<std::remove_reference_t<decltype(*op)>>,
72                 "Expected container to be const");
73         static_assert(std::is_const_v<std::remove_reference_t<decltype(op->op())>>,
74                 "Expected op to be const");
75     });
76 }
77 
TEST(CanvasOp,simplePush)78 TEST(CanvasOp, simplePush) {
79     CanvasOpBuffer buffer;
80     EXPECT_EQ(buffer.size(), 0);
81     buffer.push<Op::Save>({});
82     buffer.push<Op::Save>({});
83     buffer.push<Op::Restore>({});
84     EXPECT_GT(buffer.size(), 0);
85 
86     int saveCount = 0;
87     int restoreCount = 0;
88     int otherCount = 0;
89 
90     buffer.for_each([&](auto op) {
91         switch (op->type()) {
92             case Op::Save:
93                 saveCount++;
94                 break;
95             case Op::Restore:
96                 restoreCount++;
97                 break;
98             default:
99                 otherCount++;
100                 break;
101         }
102     });
103 
104     EXPECT_EQ(saveCount, 2);
105     EXPECT_EQ(restoreCount, 1);
106     EXPECT_EQ(otherCount, 0);
107 
108     buffer.clear();
109     int itemCount = 0;
110     buffer.for_each([&](auto op) {
111         itemCount++;
112     });
113     EXPECT_EQ(itemCount, 0);
114     buffer.resize(0);
115     EXPECT_EQ(buffer.size(), 0);
116 }
117 
TEST(CanvasOp,simpleDrawPaint)118 TEST(CanvasOp, simpleDrawPaint) {
119     CanvasOpBuffer buffer;
120     EXPECT_EQ(buffer.size(), 0);
121     buffer.push<Op::DrawColor> ({
122         .color = SkColor4f{1, 1, 1, 1},
123         .mode = SkBlendMode::kSrcIn
124     });
125 
126     CallCountingCanvas canvas;
127     EXPECT_EQ(0, canvas.sumTotalDrawCalls());
128     rasterizeCanvasBuffer(buffer, &canvas);
129     EXPECT_EQ(1, canvas.drawPaintCount);
130     EXPECT_EQ(1, canvas.sumTotalDrawCalls());
131 }
132 
TEST(CanvasOp,simpleDrawPoint)133 TEST(CanvasOp, simpleDrawPoint) {
134     CanvasOpBuffer buffer;
135     EXPECT_EQ(buffer.size(), 0);
136     buffer.push<Op::DrawPoint> ({
137         .x = 12,
138         .y = 42,
139         .paint = SkPaint{}
140     });
141 
142     CallCountingCanvas canvas;
143     EXPECT_EQ(0, canvas.sumTotalDrawCalls());
144     rasterizeCanvasBuffer(buffer, &canvas);
145     EXPECT_EQ(1, canvas.drawPoints);
146     EXPECT_EQ(1, canvas.sumTotalDrawCalls());
147 }
148 
TEST(CanvasOp,simpleDrawPoints)149 TEST(CanvasOp, simpleDrawPoints) {
150     CanvasOpBuffer buffer;
151     EXPECT_EQ(buffer.size(), 0);
152     size_t numPts = 3;
153     auto pts = sk_sp<Points>(
154           new Points({
155               {32, 16},
156               {48, 48},
157               {16, 32}
158           })
159     );
160 
161     buffer.push(CanvasOp<Op::DrawPoints> {
162         .count = numPts,
163         .paint = SkPaint{},
164         .points = pts
165     });
166 
167     CallCountingCanvas canvas;
168     EXPECT_EQ(0, canvas.sumTotalDrawCalls());
169     rasterizeCanvasBuffer(buffer, &canvas);
170     EXPECT_EQ(1, canvas.drawPoints);
171     EXPECT_EQ(1, canvas.sumTotalDrawCalls());
172 }
173 
TEST(CanvasOp,simpleDrawLine)174 TEST(CanvasOp, simpleDrawLine) {
175     CanvasOpBuffer buffer;
176     EXPECT_EQ(buffer.size(), 0);
177     buffer.push<Op::DrawLine> ({
178         .startX = 16,
179         .startY = 28,
180         .endX = 12,
181         .endY = 30,
182         .paint = SkPaint{}
183     });
184 
185     CallCountingCanvas canvas;
186     EXPECT_EQ(0, canvas.sumTotalDrawCalls());
187     rasterizeCanvasBuffer(buffer, &canvas);
188     EXPECT_EQ(1, canvas.drawPoints);
189     EXPECT_EQ(1, canvas.sumTotalDrawCalls());
190 }
191 
TEST(CanvasOp,simpleDrawLines)192 TEST(CanvasOp, simpleDrawLines) {
193     CanvasOpBuffer buffer;
194     EXPECT_EQ(buffer.size(), 0);
195     size_t numPts = 3;
196     auto pts = sk_sp<Points>(
197         new Points({
198                {32, 16},
199                {48, 48},
200                {16, 32}
201           })
202         );
203     buffer.push(CanvasOp<Op::DrawLines> {
204         .count = numPts,
205         .paint = SkPaint{},
206         .points = pts
207     });
208 
209     CallCountingCanvas canvas;
210     EXPECT_EQ(0, canvas.sumTotalDrawCalls());
211     rasterizeCanvasBuffer(buffer, &canvas);
212     EXPECT_EQ(1, canvas.drawPoints);
213     EXPECT_EQ(1, canvas.sumTotalDrawCalls());
214 }
215 
TEST(CanvasOp,simpleDrawRect)216 TEST(CanvasOp, simpleDrawRect) {
217     CanvasOpBuffer buffer;
218     EXPECT_EQ(buffer.size(), 0);
219     buffer.push<Op::DrawRect> ({
220         .paint = SkPaint{},
221         .rect = SkRect::MakeEmpty()
222     });
223 
224     CallCountingCanvas canvas;
225     EXPECT_EQ(0, canvas.sumTotalDrawCalls());
226     rasterizeCanvasBuffer(buffer, &canvas);
227     EXPECT_EQ(1, canvas.drawRectCount);
228     EXPECT_EQ(1, canvas.sumTotalDrawCalls());
229 }
230 
TEST(CanvasOp,simpleDrawRegionRect)231 TEST(CanvasOp, simpleDrawRegionRect) {
232     CanvasOpBuffer buffer;
233     EXPECT_EQ(buffer.size(), 0);
234     SkRegion region;
235     region.setRect(SkIRect::MakeWH(12, 50));
236     buffer.push<Op::DrawRegion> ({
237         .paint = SkPaint{},
238         .region = region
239     });
240 
241     CallCountingCanvas canvas;
242     EXPECT_EQ(0, canvas.sumTotalDrawCalls());
243     rasterizeCanvasBuffer(buffer, &canvas);
244     // If the region is a rectangle, drawRegion calls into drawRect as a fast path
245     EXPECT_EQ(1, canvas.drawRectCount);
246     EXPECT_EQ(1, canvas.sumTotalDrawCalls());
247 }
248 
TEST(CanvasOp,simpleDrawRegionPath)249 TEST(CanvasOp, simpleDrawRegionPath) {
250     CanvasOpBuffer buffer;
251     EXPECT_EQ(buffer.size(), 0);
252     SkPath path;
253     path.addCircle(50, 50, 50);
254     SkRegion clip;
255     clip.setRect(SkIRect::MakeWH(100, 100));
256     SkRegion region;
257     region.setPath(path, clip);
258     buffer.push<Op::DrawRegion> ({
259         .paint = SkPaint{},
260         .region = region
261     });
262 
263     CallCountingCanvas canvas;
264     EXPECT_EQ(0, canvas.sumTotalDrawCalls());
265     rasterizeCanvasBuffer(buffer, &canvas);
266     EXPECT_EQ(1, canvas.drawRegionCount);
267     EXPECT_EQ(1, canvas.sumTotalDrawCalls());
268 }
269 
TEST(CanvasOp,simpleDrawRoundRect)270 TEST(CanvasOp, simpleDrawRoundRect) {
271     CanvasOpBuffer buffer;
272     EXPECT_EQ(buffer.size(), 0);
273     buffer.push<Op::DrawRoundRect> ({
274         .paint = SkPaint{},
275         .rect = SkRect::MakeEmpty(),
276         .rx = 10,
277         .ry = 10
278     });
279 
280     CallCountingCanvas canvas;
281     EXPECT_EQ(0, canvas.sumTotalDrawCalls());
282     rasterizeCanvasBuffer(buffer, &canvas);
283     EXPECT_EQ(1, canvas.drawRRectCount);
284     EXPECT_EQ(1, canvas.sumTotalDrawCalls());
285 }
286 
TEST(CanvasOp,simpleDrawDoubleRoundRect)287 TEST(CanvasOp, simpleDrawDoubleRoundRect) {
288     CanvasOpBuffer buffer;
289     EXPECT_EQ(buffer.size(), 0);
290     SkRect outer = SkRect::MakeLTRB(0, 0, 100, 100);
291     SkRect inner = SkRect::MakeLTRB(20, 20, 80, 80);
292 
293     const int numPts = 4;
294     SkRRect outerRRect;
295 
296     auto outerPts = std::make_unique<SkVector[]>(numPts);
297     outerPts[0].set(32, 16);
298     outerPts[1].set(48, 48);
299     outerPts[2].set(16, 32);
300     outerPts[3].set(20, 20);
301     outerRRect.setRectRadii(outer, outerPts.get());
302     outerRRect.setRect(outer);
303 
304     SkRRect innerRRect;
305     auto innerPts = std::make_unique<SkVector[]>(numPts);
306     innerPts[0].set(16, 8);
307     innerPts[1].set(24, 24);
308     innerPts[2].set(8, 16);
309     innerPts[3].set(10, 10);
310     innerRRect.setRectRadii(inner, innerPts.get());
311 
312     buffer.push<Op::DrawDoubleRoundRect> ({
313         .outer = outerRRect,
314         .inner = innerRRect,
315         .paint = SkPaint{}
316     });
317 
318     CallCountingCanvas canvas;
319     EXPECT_EQ(0, canvas.sumTotalDrawCalls());
320     rasterizeCanvasBuffer(buffer, &canvas);
321     EXPECT_EQ(1, canvas.drawDRRectCount);
322     EXPECT_EQ(1, canvas.sumTotalDrawCalls());
323 }
324 
TEST(CanvasOp,simpleDrawCircle)325 TEST(CanvasOp, simpleDrawCircle) {
326     CanvasOpBuffer buffer;
327     EXPECT_EQ(buffer.size(), 0);
328     buffer.push<Op::DrawCircle>({
329         .cx = 5,
330         .cy = 7,
331         .radius = 10,
332         .paint = SkPaint{}
333     });
334 
335     CallCountingCanvas canvas;
336     EXPECT_EQ(0, canvas.sumTotalDrawCalls());
337     rasterizeCanvasBuffer(buffer, &canvas);
338     EXPECT_EQ(1, canvas.drawOvalCount);
339     EXPECT_EQ(1, canvas.sumTotalDrawCalls());
340 }
341 
TEST(CanvasOp,simpleDrawOval)342 TEST(CanvasOp, simpleDrawOval) {
343     CanvasOpBuffer buffer;
344     EXPECT_EQ(buffer.size(), 0);
345     buffer.push<Op::DrawOval> ({
346         .oval = SkRect::MakeEmpty(),
347         .paint = SkPaint{}
348     });
349 
350     CallCountingCanvas canvas;
351     EXPECT_EQ(0, canvas.sumTotalDrawCalls());
352     rasterizeCanvasBuffer(buffer, &canvas);
353     EXPECT_EQ(1, canvas.drawOvalCount);
354     EXPECT_EQ(1, canvas.sumTotalDrawCalls());
355 }
356 
TEST(CanvasOp,simpleDrawArc)357 TEST(CanvasOp, simpleDrawArc) {
358     CanvasOpBuffer buffer;
359     EXPECT_EQ(buffer.size(), 0);
360     buffer.push<Op::DrawArc>({
361         .oval = SkRect::MakeWH(100, 100),
362         .startAngle = 120,
363         .sweepAngle = 70,
364         .useCenter = true,
365         .paint = SkPaint{}
366     });
367 
368     CallCountingCanvas canvas;
369     EXPECT_EQ(0, canvas.sumTotalDrawCalls());
370     rasterizeCanvasBuffer(buffer, &canvas);
371     EXPECT_EQ(1, canvas.drawArcCount);
372     EXPECT_EQ(1, canvas.sumTotalDrawCalls());
373 }
374 
TEST(CanvasOp,simpleDrawPath)375 TEST(CanvasOp, simpleDrawPath) {
376     CanvasOpBuffer buffer;
377     EXPECT_EQ(buffer.size(), 0);
378     SkPath path;
379     path.addCircle(50, 50, 30);
380     buffer.push<Op::DrawPath> ({
381         .path = path,
382         .paint = SkPaint{}
383     });
384 
385     CallCountingCanvas canvas;
386     EXPECT_EQ(0, canvas.sumTotalDrawCalls());
387     rasterizeCanvasBuffer(buffer, &canvas);
388     EXPECT_EQ(1, canvas.drawPathCount);
389     EXPECT_EQ(1, canvas.sumTotalDrawCalls());
390 }
391 
TEST(CanvasOp,simpleDrawRoundRectProperty)392 TEST(CanvasOp, simpleDrawRoundRectProperty) {
393     CanvasOpBuffer buffer;
394     EXPECT_EQ(buffer.size(), 0);
395 
396     auto left = sp<CanvasPropertyPrimitive>(new uirenderer::CanvasPropertyPrimitive(1));
397     auto top = sp<CanvasPropertyPrimitive>(new uirenderer::CanvasPropertyPrimitive(2));
398     auto right = sp<CanvasPropertyPrimitive>(new uirenderer::CanvasPropertyPrimitive(3));
399     auto bottom = sp<CanvasPropertyPrimitive>(new uirenderer::CanvasPropertyPrimitive(4));
400     auto radiusX = sp<CanvasPropertyPrimitive>(new uirenderer::CanvasPropertyPrimitive(5));
401     auto radiusY = sp<CanvasPropertyPrimitive>(new uirenderer::CanvasPropertyPrimitive(6));
402     auto propertyPaint =
403             sp<uirenderer::CanvasPropertyPaint>(new uirenderer::CanvasPropertyPaint(SkPaint{}));
404 
405     buffer.push<Op::DrawRoundRectProperty> ({
406         .left = left,
407         .top = top,
408         .right = right,
409         .bottom = bottom,
410         .rx = radiusX,
411         .ry = radiusY,
412         .paint = propertyPaint
413     });
414 
415     CallCountingCanvas canvas;
416     EXPECT_EQ(0, canvas.sumTotalDrawCalls());
417     rasterizeCanvasBuffer(buffer, &canvas);
418     EXPECT_EQ(1, canvas.drawRRectCount);
419     EXPECT_EQ(1, canvas.sumTotalDrawCalls());
420 }
421 
TEST(CanvasOp,simpleDrawCircleProperty)422 TEST(CanvasOp, simpleDrawCircleProperty) {
423     CanvasOpBuffer buffer;
424     EXPECT_EQ(buffer.size(), 0);
425 
426     auto x = sp<CanvasPropertyPrimitive>(new uirenderer::CanvasPropertyPrimitive(1));
427     auto y = sp<CanvasPropertyPrimitive>(new uirenderer::CanvasPropertyPrimitive(2));
428     auto radius = sp<CanvasPropertyPrimitive>(new uirenderer::CanvasPropertyPrimitive(5));
429     auto propertyPaint =
430             sp<uirenderer::CanvasPropertyPaint>(new uirenderer::CanvasPropertyPaint(SkPaint{}));
431 
432     buffer.push<Op::DrawCircleProperty> ({
433         .x = x,
434         .y = y,
435         .radius = radius,
436         .paint = propertyPaint
437     });
438 
439     CallCountingCanvas canvas;
440     EXPECT_EQ(0, canvas.sumTotalDrawCalls());
441     rasterizeCanvasBuffer(buffer, &canvas);
442     EXPECT_EQ(1, canvas.drawOvalCount);
443     EXPECT_EQ(1, canvas.sumTotalDrawCalls());
444 }
445 
TEST(CanvasOp,simpleDrawVertices)446 TEST(CanvasOp, simpleDrawVertices) {
447     CanvasOpBuffer buffer;
448     EXPECT_EQ(buffer.size(), 0);
449 
450     SkPoint pts[3] = {{64, 32}, {0, 224}, {128, 224}};
451     SkColor colors[3] = {SK_ColorRED, SK_ColorBLUE, SK_ColorGREEN};
452     sk_sp<SkVertices> vertices = SkVertices::MakeCopy(SkVertices::kTriangles_VertexMode, 3, pts,
453             nullptr, colors);
454     buffer.push<Op::DrawVertices> ({
455         .vertices = vertices,
456         .mode = SkBlendMode::kSrcOver,
457         .paint = SkPaint{}
458     });
459 
460     CallCountingCanvas canvas;
461     EXPECT_EQ(0, canvas.sumTotalDrawCalls());
462     rasterizeCanvasBuffer(buffer, &canvas);
463     EXPECT_EQ(1, canvas.drawVerticesCount);
464     EXPECT_EQ(1, canvas.sumTotalDrawCalls());
465 }
466 
TEST(CanvasOp,simpleDrawImage)467 TEST(CanvasOp, simpleDrawImage) {
468     CanvasOpBuffer buffer;
469     EXPECT_EQ(buffer.size(), 0);
470 
471     SkImageInfo info =SkImageInfo::Make(5, 1,
472         kGray_8_SkColorType, kOpaque_SkAlphaType);
473     sk_sp<Bitmap> bitmap = Bitmap::allocateHeapBitmap(info);
474     buffer.push<Op::DrawImage> ({
475             bitmap,
476             7,
477             19,
478             SkFilterMode::kNearest,
479             SkPaint{}
480         }
481     );
482 
483     CallCountingCanvas canvas;
484     EXPECT_EQ(0, canvas.sumTotalDrawCalls());
485     rasterizeCanvasBuffer(buffer, &canvas);
486     EXPECT_EQ(1, canvas.drawImageCount);
487     EXPECT_EQ(1, canvas.sumTotalDrawCalls());
488 }
489 
TEST(CanvasOp,simpleDrawImageRect)490 TEST(CanvasOp, simpleDrawImageRect) {
491     CanvasOpBuffer buffer;
492     EXPECT_EQ(buffer.size(), 0);
493 
494     SkImageInfo info = SkImageInfo::Make(5, 1,
495         kGray_8_SkColorType, kOpaque_SkAlphaType);
496 
497     sk_sp<Bitmap> bitmap = Bitmap::allocateHeapBitmap(info);
498     buffer.push<Op::DrawImageRect> ({
499           bitmap, SkRect::MakeWH(100, 100),
500           SkRect::MakeLTRB(120, 110, 220, 210),
501           SkFilterMode::kNearest, SkPaint{}
502         }
503     );
504 
505     CallCountingCanvas canvas;
506     EXPECT_EQ(0, canvas.sumTotalDrawCalls());
507     rasterizeCanvasBuffer(buffer, &canvas);
508     EXPECT_EQ(1, canvas.drawImageRectCount);
509     EXPECT_EQ(1, canvas.sumTotalDrawCalls());
510 }
511 
TEST(CanvasOp,simpleDrawImageLattice)512 TEST(CanvasOp, simpleDrawImageLattice) {
513     CanvasOpBuffer buffer;
514     EXPECT_EQ(buffer.size(), 0);
515 
516     SkBitmap skBitmap;
517     skBitmap.allocPixels(SkImageInfo::MakeN32Premul(60, 60));
518 
519     const int xDivs[] = { 20, 50 };
520     const int yDivs[] = { 10, 40 };
521     SkCanvas::Lattice::RectType fillTypes[3][3];
522     memset(fillTypes, 0, sizeof(fillTypes));
523     fillTypes[1][1] = SkCanvas::Lattice::kTransparent;
524     SkColor colors[9];
525     SkCanvas::Lattice lattice = { xDivs, yDivs, fillTypes[0], 2,
526          2, nullptr, colors };
527     sk_sp<Bitmap> bitmap = Bitmap::allocateHeapBitmap(&skBitmap);
528     buffer.push<Op::DrawImageLattice>(
529         {
530             bitmap,
531             SkRect::MakeWH(5, 1),
532             lattice,
533             SkFilterMode::kNearest,
534             SkPaint{}
535         }
536     );
537 
538     CallCountingCanvas canvas;
539     EXPECT_EQ(0, canvas.sumTotalDrawCalls());
540     rasterizeCanvasBuffer(buffer, &canvas);
541     EXPECT_EQ(1, canvas.drawImageLatticeCount);
542     EXPECT_EQ(1, canvas.sumTotalDrawCalls());
543 }
544 
TEST(CanvasOp,simpleDrawPicture)545 TEST(CanvasOp, simpleDrawPicture) {
546     CanvasOpBuffer buffer;
547     EXPECT_EQ(buffer.size(), 0);
548 
549     SkPictureRecorder recorder;
550     SkCanvas* pictureCanvas = recorder.beginRecording({64, 64, 192, 192});
551     SkPaint paint;
552     pictureCanvas->drawRect(SkRect::MakeWH(200, 200), paint);
553     paint.setColor(SK_ColorWHITE);
554     pictureCanvas->drawRect(SkRect::MakeLTRB(20, 20, 180, 180), paint);
555     sk_sp<SkPicture> picture = recorder.finishRecordingAsPicture();
556     buffer.push<Op::DrawPicture> ({
557         .picture = picture
558     });
559 
560     CallCountingCanvas canvas;
561     EXPECT_EQ(0, canvas.sumTotalDrawCalls());
562     rasterizeCanvasBuffer(buffer, &canvas);
563     // Note because we are explicitly issuing 2 drawRect calls
564     // in the picture recorder above, when it is played back into
565     // CallCountingCanvas we will see 2 calls to drawRect instead of 1
566     // call to drawPicture.
567     // This is because SkiaCanvas::drawPicture uses picture.playback(canvas)
568     // instead of canvas->drawPicture.
569     EXPECT_EQ(2, canvas.drawRectCount);
570     EXPECT_EQ(2, canvas.sumTotalDrawCalls());
571 }
572 
TEST(CanvasOp,simpleDrawRipple)573 TEST(CanvasOp, simpleDrawRipple) {
574     CanvasOpBuffer buffer;
575     EXPECT_EQ(buffer.size(), 0);
576 
577     const char* sksl =
578             "half4 main(float2 coord) {"
579             "  return half4(1.);"
580             "}";
581     auto [effect, error] = SkRuntimeEffect::MakeForShader(SkString(sksl));
582     auto params = RippleDrawableParams{
583             .x = sp<CanvasPropertyPrimitive>(new CanvasPropertyPrimitive(100)),
584             .y = sp<CanvasPropertyPrimitive>(new CanvasPropertyPrimitive(200)),
585             .radius = sp<CanvasPropertyPrimitive>(new CanvasPropertyPrimitive(50)),
586             .progress = sp<CanvasPropertyPrimitive>(new CanvasPropertyPrimitive(0.5)),
587             .turbulencePhase = sp<CanvasPropertyPrimitive>(new CanvasPropertyPrimitive(1)),
588             .color = 0xff00ff,
589             .paint = sp<CanvasPropertyPaint>(new CanvasPropertyPaint(SkPaint{})),
590             .effectBuilder = SkRuntimeShaderBuilder(effect)};
591     buffer.push<Op::DrawRippleDrawable>({.params = params});
592 
593     CallCountingCanvas canvas;
594     EXPECT_EQ(0, canvas.sumTotalDrawCalls());
595     rasterizeCanvasBuffer(buffer, &canvas);
596     EXPECT_EQ(1, canvas.drawOvalCount);
597     EXPECT_EQ(1, canvas.sumTotalDrawCalls());
598 }
599 
TEST(CanvasOp,immediateRendering)600 TEST(CanvasOp, immediateRendering) {
601     auto canvas = std::make_shared<CallCountingCanvas>();
602 
603     EXPECT_EQ(0, canvas->sumTotalDrawCalls());
604     ImmediateModeRasterizer rasterizer{canvas};
605     auto op = CanvasOp<Op::DrawRect> {
606         .paint = SkPaint{},
607         .rect = SkRect::MakeEmpty()
608     };
609     EXPECT_TRUE(CanvasOpTraits::can_draw<decltype(op)>);
610     rasterizer.draw(op);
611     EXPECT_EQ(1, canvas->drawRectCount);
612     EXPECT_EQ(1, canvas->sumTotalDrawCalls());
613 }
614 
TEST(CanvasOp,frontendSaveCount)615 TEST(CanvasOp, frontendSaveCount) {
616     SkNoDrawCanvas skiaCanvas(100, 100);
617     CanvasFrontend<CanvasOpCountingReceiver> opCanvas(100, 100);
618     const auto& receiver = opCanvas.receiver();
619 
620     EXPECT_EQ(1, skiaCanvas.getSaveCount());
621     EXPECT_EQ(1, opCanvas.saveCount());
622 
623     skiaCanvas.save();
624     opCanvas.save(SaveFlags::MatrixClip);
625     EXPECT_EQ(2, skiaCanvas.getSaveCount());
626     EXPECT_EQ(2, opCanvas.saveCount());
627 
628     skiaCanvas.restore();
629     opCanvas.restore();
630     EXPECT_EQ(1, skiaCanvas.getSaveCount());
631     EXPECT_EQ(1, opCanvas.saveCount());
632 
633     skiaCanvas.restore();
634     opCanvas.restore();
635     EXPECT_EQ(1, skiaCanvas.getSaveCount());
636     EXPECT_EQ(1, opCanvas.saveCount());
637 
638     EXPECT_EQ(1, receiver[Op::Save]);
639     EXPECT_EQ(1, receiver[Op::Restore]);
640 }
641