1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4// FLUTTER_NOLINT
5
6#include "flutter/testing/mock_canvas.h"
7
8#include "flutter/fml/logging.h"
9#include "third_party/skia/include/core/SkImageInfo.h"
10#include "third_party/skia/include/core/SkPicture.h"
11#include "third_party/skia/include/core/SkSerialProcs.h"
12#include "third_party/skia/include/core/SkSize.h"
13#include "third_party/skia/include/core/SkTextBlob.h"
14
15namespace flutter {
16namespace testing {
17
18constexpr SkISize kSize = SkISize::Make(64, 64);
19
20MockCanvas::MockCanvas()
21 : SkCanvasVirtualEnforcer<SkCanvas>(kSize.fWidth, kSize.fHeight),
22 internal_canvas_(imageInfo().width(), imageInfo().height()),
23 current_layer_(0) {
24 internal_canvas_.addCanvas(this);
25}
26
27MockCanvas::~MockCanvas() {
28 EXPECT_EQ(current_layer_, 0);
29}
30
31void MockCanvas::willSave() {
32 draw_calls_.emplace_back(
33 DrawCall{current_layer_, SaveData{current_layer_ + 1}});
34 current_layer_++; // Must go here; func params order of eval is undefined
35}
36
37SkCanvas::SaveLayerStrategy MockCanvas::getSaveLayerStrategy(
38 const SaveLayerRec& rec) {
39 // saveLayer calls this prior to running, so we use it to track saveLayer
40 // calls
41 draw_calls_.emplace_back(DrawCall{
42 current_layer_,
43 SaveLayerData{rec.fBounds ? *rec.fBounds : SkRect(),
44 rec.fPaint ? *rec.fPaint : SkPaint(),
45 rec.fBackdrop ? sk_ref_sp<SkImageFilter>(rec.fBackdrop)
46 : sk_sp<SkImageFilter>(),
47 current_layer_ + 1}});
48 current_layer_++; // Must go here; func params order of eval is undefined
49 return kNoLayer_SaveLayerStrategy;
50}
51
52void MockCanvas::willRestore() {
53 FML_DCHECK(current_layer_ > 0);
54
55 draw_calls_.emplace_back(
56 DrawCall{current_layer_, RestoreData{current_layer_ - 1}});
57 current_layer_--; // Must go here; func params order of eval is undefined
58}
59
60void MockCanvas::didConcat(const SkMatrix& matrix) {
61 draw_calls_.emplace_back(DrawCall{current_layer_, ConcatMatrixData{matrix}});
62}
63
64void MockCanvas::didConcat44(const SkM44& matrix) {
65 draw_calls_.emplace_back(
66 DrawCall{current_layer_, ConcatMatrix44Data{matrix}});
67}
68
69void MockCanvas::didScale(SkScalar x, SkScalar y) {
70 SkMatrix m;
71 m.setScale(x, y);
72 this->didConcat(m);
73}
74
75void MockCanvas::didTranslate(SkScalar x, SkScalar y) {
76 SkMatrix m;
77 m.setTranslate(x, y);
78 this->didConcat(m);
79}
80
81void MockCanvas::didSetMatrix(const SkMatrix& matrix) {
82 draw_calls_.emplace_back(DrawCall{current_layer_, SetMatrixData{matrix}});
83}
84
85void MockCanvas::onDrawTextBlob(const SkTextBlob* text,
86 SkScalar x,
87 SkScalar y,
88 const SkPaint& paint) {
89 // This duplicates existing logic in SkCanvas::onDrawPicture
90 // that should probably be split out so it doesn't need to be here as well.
91 SkRect storage;
92 const SkRect* bounds = nullptr;
93 if (paint.canComputeFastBounds()) {
94 storage = text->bounds().makeOffset(x, y);
95 SkRect tmp;
96 if (this->quickReject(paint.computeFastBounds(storage, &tmp))) {
97 return;
98 }
99 bounds = &storage;
100 }
101
102 draw_calls_.emplace_back(DrawCall{
103 current_layer_, DrawTextData{text ? text->serialize(SkSerialProcs{})
104 : SkData::MakeUninitialized(0),
105 paint, SkPoint::Make(x, y)}});
106}
107
108void MockCanvas::onDrawRect(const SkRect& rect, const SkPaint& paint) {
109 draw_calls_.emplace_back(DrawCall{current_layer_, DrawRectData{rect, paint}});
110}
111
112void MockCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
113 draw_calls_.emplace_back(DrawCall{current_layer_, DrawPathData{path, paint}});
114}
115
116void MockCanvas::onDrawShadowRec(const SkPath& path,
117 const SkDrawShadowRec& rec) {
118 (void)rec; // Can't use b/c Skia keeps this type anonymous.
119 draw_calls_.emplace_back(DrawCall{current_layer_, DrawShadowData{path}});
120}
121
122void MockCanvas::onDrawPicture(const SkPicture* picture,
123 const SkMatrix* matrix,
124 const SkPaint* paint) {
125 // This duplicates existing logic in SkCanvas::onDrawPicture
126 // that should probably be split out so it doesn't need to be here as well.
127 if (!paint || paint->canComputeFastBounds()) {
128 SkRect bounds = picture->cullRect();
129 if (paint) {
130 paint->computeFastBounds(bounds, &bounds);
131 }
132 if (matrix) {
133 matrix->mapRect(&bounds);
134 }
135 if (this->quickReject(bounds)) {
136 return;
137 }
138 }
139
140 draw_calls_.emplace_back(DrawCall{
141 current_layer_,
142 DrawPictureData{
143 picture ? picture->serialize() : SkData::MakeUninitialized(0),
144 paint ? *paint : SkPaint(), matrix ? *matrix : SkMatrix()}});
145}
146
147void MockCanvas::onClipRect(const SkRect& rect,
148 SkClipOp op,
149 ClipEdgeStyle style) {
150 draw_calls_.emplace_back(
151 DrawCall{current_layer_, ClipRectData{rect, op, style}});
152}
153
154void MockCanvas::onClipRRect(const SkRRect& rrect,
155 SkClipOp op,
156 ClipEdgeStyle style) {
157 draw_calls_.emplace_back(
158 DrawCall{current_layer_, ClipRRectData{rrect, op, style}});
159}
160
161void MockCanvas::onClipPath(const SkPath& path,
162 SkClipOp op,
163 ClipEdgeStyle style) {
164 draw_calls_.emplace_back(
165 DrawCall{current_layer_, ClipPathData{path, op, style}});
166}
167
168bool MockCanvas::onDoSaveBehind(const SkRect*) {
169 FML_DCHECK(false);
170 return false;
171}
172
173void MockCanvas::onDrawAnnotation(const SkRect&, const char[], SkData*) {
174 FML_DCHECK(false);
175}
176
177void MockCanvas::onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) {
178 FML_DCHECK(false);
179}
180
181void MockCanvas::onDrawDrawable(SkDrawable*, const SkMatrix*) {
182 FML_DCHECK(false);
183}
184
185void MockCanvas::onDrawPatch(const SkPoint[12],
186 const SkColor[4],
187 const SkPoint[4],
188 SkBlendMode,
189 const SkPaint&) {
190 FML_DCHECK(false);
191}
192
193void MockCanvas::onDrawPaint(const SkPaint&) {
194 FML_DCHECK(false);
195}
196
197void MockCanvas::onDrawBehind(const SkPaint&) {
198 FML_DCHECK(false);
199}
200
201void MockCanvas::onDrawPoints(PointMode,
202 size_t,
203 const SkPoint[],
204 const SkPaint&) {
205 FML_DCHECK(false);
206}
207
208void MockCanvas::onDrawRegion(const SkRegion&, const SkPaint&) {
209 FML_DCHECK(false);
210}
211
212void MockCanvas::onDrawOval(const SkRect&, const SkPaint&) {
213 FML_DCHECK(false);
214}
215
216void MockCanvas::onDrawArc(const SkRect&,
217 SkScalar,
218 SkScalar,
219 bool,
220 const SkPaint&) {
221 FML_DCHECK(false);
222}
223
224void MockCanvas::onDrawRRect(const SkRRect&, const SkPaint&) {
225 FML_DCHECK(false);
226}
227
228void MockCanvas::onDrawImage(const SkImage*,
229 SkScalar,
230 SkScalar,
231 const SkPaint*) {
232 FML_DCHECK(false);
233}
234
235void MockCanvas::onDrawImageRect(const SkImage*,
236 const SkRect*,
237 const SkRect&,
238 const SkPaint*,
239 SrcRectConstraint) {
240 FML_DCHECK(false);
241}
242
243void MockCanvas::onDrawImageNine(const SkImage*,
244 const SkIRect&,
245 const SkRect&,
246 const SkPaint*) {
247 FML_DCHECK(false);
248}
249
250void MockCanvas::onDrawImageLattice(const SkImage*,
251 const Lattice&,
252 const SkRect&,
253 const SkPaint*) {
254 FML_DCHECK(false);
255}
256
257void MockCanvas::onDrawVerticesObject(const SkVertices*,
258 SkBlendMode,
259 const SkPaint&) {
260 FML_DCHECK(false);
261}
262
263void MockCanvas::onDrawAtlas(const SkImage*,
264 const SkRSXform[],
265 const SkRect[],
266 const SkColor[],
267 int,
268 SkBlendMode,
269 const SkRect*,
270 const SkPaint*) {
271 FML_DCHECK(false);
272}
273
274void MockCanvas::onDrawEdgeAAQuad(const SkRect&,
275 const SkPoint[4],
276 QuadAAFlags,
277 const SkColor4f&,
278 SkBlendMode) {
279 FML_DCHECK(false);
280}
281
282void MockCanvas::onDrawEdgeAAImageSet(const ImageSetEntry[],
283 int,
284 const SkPoint[],
285 const SkMatrix[],
286 const SkPaint*,
287 SrcRectConstraint) {
288 FML_DCHECK(false);
289}
290
291void MockCanvas::onClipRegion(const SkRegion&, SkClipOp) {
292 FML_DCHECK(false);
293}
294
295bool operator==(const MockCanvas::SaveData& a, const MockCanvas::SaveData& b) {
296 return a.save_to_layer == b.save_to_layer;
297}
298
299std::ostream& operator<<(std::ostream& os, const MockCanvas::SaveData& data) {
300 return os << data.save_to_layer;
301}
302
303bool operator==(const MockCanvas::SaveLayerData& a,
304 const MockCanvas::SaveLayerData& b) {
305 return a.save_bounds == b.save_bounds && a.restore_paint == b.restore_paint &&
306 a.backdrop_filter == b.backdrop_filter &&
307 a.save_to_layer == b.save_to_layer;
308}
309
310std::ostream& operator<<(std::ostream& os,
311 const MockCanvas::SaveLayerData& data) {
312 return os << data.save_bounds << " " << data.restore_paint << " "
313 << data.backdrop_filter << " " << data.save_to_layer;
314}
315
316bool operator==(const MockCanvas::RestoreData& a,
317 const MockCanvas::RestoreData& b) {
318 return a.restore_to_layer == b.restore_to_layer;
319}
320
321std::ostream& operator<<(std::ostream& os,
322 const MockCanvas::RestoreData& data) {
323 return os << data.restore_to_layer;
324}
325
326bool operator==(const MockCanvas::ConcatMatrixData& a,
327 const MockCanvas::ConcatMatrixData& b) {
328 return a.matrix == b.matrix;
329}
330
331std::ostream& operator<<(std::ostream& os,
332 const MockCanvas::ConcatMatrixData& data) {
333 return os << data.matrix;
334}
335
336bool operator==(const MockCanvas::ConcatMatrix44Data& a,
337 const MockCanvas::ConcatMatrix44Data& b) {
338 return a.matrix == b.matrix;
339}
340
341std::ostream& operator<<(std::ostream& os,
342 const MockCanvas::ConcatMatrix44Data& data) {
343 return os << data.matrix;
344}
345
346bool operator==(const MockCanvas::SetMatrixData& a,
347 const MockCanvas::SetMatrixData& b) {
348 return a.matrix == b.matrix;
349}
350
351std::ostream& operator<<(std::ostream& os,
352 const MockCanvas::SetMatrixData& data) {
353 return os << data.matrix;
354}
355
356bool operator==(const MockCanvas::DrawRectData& a,
357 const MockCanvas::DrawRectData& b) {
358 return a.rect == b.rect && a.paint == b.paint;
359}
360
361std::ostream& operator<<(std::ostream& os,
362 const MockCanvas::DrawRectData& data) {
363 return os << data.rect << " " << data.paint;
364}
365
366bool operator==(const MockCanvas::DrawPathData& a,
367 const MockCanvas::DrawPathData& b) {
368 return a.path == b.path && a.paint == b.paint;
369}
370
371std::ostream& operator<<(std::ostream& os,
372 const MockCanvas::DrawPathData& data) {
373 return os << data.path << " " << data.paint;
374}
375
376bool operator==(const MockCanvas::DrawTextData& a,
377 const MockCanvas::DrawTextData& b) {
378 return a.serialized_text->equals(b.serialized_text.get()) &&
379 a.paint == b.paint && a.offset == b.offset;
380}
381
382std::ostream& operator<<(std::ostream& os,
383 const MockCanvas::DrawTextData& data) {
384 return os << data.serialized_text << " " << data.paint << " " << data.offset;
385}
386
387bool operator==(const MockCanvas::DrawPictureData& a,
388 const MockCanvas::DrawPictureData& b) {
389 return a.serialized_picture->equals(b.serialized_picture.get()) &&
390 a.paint == b.paint && a.matrix == b.matrix;
391}
392
393std::ostream& operator<<(std::ostream& os,
394 const MockCanvas::DrawPictureData& data) {
395 return os << data.serialized_picture << " " << data.paint << " "
396 << data.matrix;
397}
398
399bool operator==(const MockCanvas::DrawShadowData& a,
400 const MockCanvas::DrawShadowData& b) {
401 return a.path == b.path;
402}
403
404std::ostream& operator<<(std::ostream& os,
405 const MockCanvas::DrawShadowData& data) {
406 return os << data.path;
407}
408
409bool operator==(const MockCanvas::ClipRectData& a,
410 const MockCanvas::ClipRectData& b) {
411 return a.rect == b.rect && a.clip_op == b.clip_op && a.style == b.style;
412}
413
414std::ostream& operator<<(std::ostream& os,
415 const MockCanvas::ClipRectData& data) {
416 return os << data.rect << " " << data.clip_op << " " << data.style;
417}
418
419bool operator==(const MockCanvas::ClipRRectData& a,
420 const MockCanvas::ClipRRectData& b) {
421 return a.rrect == b.rrect && a.clip_op == b.clip_op && a.style == b.style;
422}
423
424std::ostream& operator<<(std::ostream& os,
425 const MockCanvas::ClipRRectData& data) {
426 return os << data.rrect << " " << data.clip_op << " " << data.style;
427}
428
429bool operator==(const MockCanvas::ClipPathData& a,
430 const MockCanvas::ClipPathData& b) {
431 return a.path == b.path && a.clip_op == b.clip_op && a.style == b.style;
432}
433
434std::ostream& operator<<(std::ostream& os,
435 const MockCanvas::ClipPathData& data) {
436 return os << data.path << " " << data.clip_op << " " << data.style;
437}
438
439std::ostream& operator<<(std::ostream& os,
440 const MockCanvas::DrawCallData& data) {
441 std::visit([&os](auto& d) { os << d; }, data);
442 return os;
443}
444
445bool operator==(const MockCanvas::DrawCall& a, const MockCanvas::DrawCall& b) {
446 return a.layer == b.layer && a.data == b.data;
447}
448
449std::ostream& operator<<(std::ostream& os, const MockCanvas::DrawCall& draw) {
450 return os << "[Layer: " << draw.layer << ", Data: " << draw.data << "]";
451}
452
453} // namespace testing
454} // namespace flutter
455