blob: b53a01ca51caf3a60f9377cf7155e52ee5d3a90c [file] [log] [blame] [edit]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "flutter/testing/mock_canvas.h"
#include "flutter/fml/logging.h"
#include "third_party/skia/include/core/SkImageInfo.h"
#include "third_party/skia/include/core/SkPicture.h"
#include "third_party/skia/include/core/SkSerialProcs.h"
#include "third_party/skia/include/core/SkSize.h"
#include "third_party/skia/include/core/SkTextBlob.h"
namespace flutter {
namespace testing {
constexpr SkISize kSize = SkISize::Make(64, 64);
MockCanvas::MockCanvas()
: SkCanvasVirtualEnforcer<SkCanvas>(kSize.fWidth, kSize.fHeight),
internal_canvas_(imageInfo().width(), imageInfo().height()),
current_layer_(0) {
internal_canvas_.addCanvas(this);
}
MockCanvas::~MockCanvas() {
EXPECT_EQ(current_layer_, 0);
}
void MockCanvas::willSave() {
draw_calls_.emplace_back(
DrawCall{current_layer_, SaveData{current_layer_ + 1}});
current_layer_++; // Must go here; func params order of eval is undefined
}
SkCanvas::SaveLayerStrategy MockCanvas::getSaveLayerStrategy(
const SaveLayerRec& rec) {
// saveLayer calls this prior to running, so we use it to track saveLayer
// calls
draw_calls_.emplace_back(DrawCall{
current_layer_,
SaveLayerData{rec.fBounds ? *rec.fBounds : SkRect(),
rec.fPaint ? *rec.fPaint : SkPaint(),
rec.fBackdrop ? sk_ref_sp<SkImageFilter>(rec.fBackdrop)
: sk_sp<SkImageFilter>(),
current_layer_ + 1}});
current_layer_++; // Must go here; func params order of eval is undefined
return kNoLayer_SaveLayerStrategy;
}
void MockCanvas::willRestore() {
FML_DCHECK(current_layer_ > 0);
draw_calls_.emplace_back(
DrawCall{current_layer_, RestoreData{current_layer_ - 1}});
current_layer_--; // Must go here; func params order of eval is undefined
}
void MockCanvas::didConcat44(const SkM44& matrix) {
draw_calls_.emplace_back(DrawCall{current_layer_, ConcatMatrixData{matrix}});
}
void MockCanvas::didSetM44(const SkM44& matrix) {
draw_calls_.emplace_back(DrawCall{current_layer_, SetMatrixData{matrix}});
}
void MockCanvas::didScale(SkScalar x, SkScalar y) {
this->didConcat44(SkM44::Scale(x, y));
}
void MockCanvas::didTranslate(SkScalar x, SkScalar y) {
this->didConcat44(SkM44::Translate(x, y));
}
void MockCanvas::onDrawTextBlob(const SkTextBlob* text,
SkScalar x,
SkScalar y,
const SkPaint& paint) {
// This duplicates existing logic in SkCanvas::onDrawPicture
// that should probably be split out so it doesn't need to be here as well.
SkRect storage;
if (paint.canComputeFastBounds()) {
storage = text->bounds().makeOffset(x, y);
SkRect tmp;
if (this->quickReject(paint.computeFastBounds(storage, &tmp))) {
return;
}
}
draw_calls_.emplace_back(DrawCall{
current_layer_, DrawTextData{text ? text->serialize(SkSerialProcs{})
: SkData::MakeUninitialized(0),
paint, SkPoint::Make(x, y)}});
}
void MockCanvas::onDrawRect(const SkRect& rect, const SkPaint& paint) {
draw_calls_.emplace_back(DrawCall{current_layer_, DrawRectData{rect, paint}});
}
void MockCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
draw_calls_.emplace_back(DrawCall{current_layer_, DrawPathData{path, paint}});
}
void MockCanvas::onDrawShadowRec(const SkPath& path,
const SkDrawShadowRec& rec) {
// See: https://bugs.chromium.org/p/skia/issues/detail?id=12125
(void)rec; // Can't use b/c Skia keeps this type anonymous.
draw_calls_.emplace_back(DrawCall{current_layer_, DrawShadowData{path}});
}
void MockCanvas::onDrawImage2(const SkImage* image,
SkScalar x,
SkScalar y,
const SkSamplingOptions& options,
const SkPaint* paint) {
if (paint) {
draw_calls_.emplace_back(
DrawCall{current_layer_,
DrawImageData{sk_ref_sp(image), x, y, options, *paint}});
} else {
draw_calls_.emplace_back(DrawCall{
current_layer_, DrawImageDataNoPaint{sk_ref_sp(image), x, y, options}});
}
}
void MockCanvas::onDrawPicture(const SkPicture* picture,
const SkMatrix* matrix,
const SkPaint* paint) {
// This duplicates existing logic in SkCanvas::onDrawPicture
// that should probably be split out so it doesn't need to be here as well.
if (!paint || paint->canComputeFastBounds()) {
SkRect bounds = picture->cullRect();
if (paint) {
paint->computeFastBounds(bounds, &bounds);
}
if (matrix) {
matrix->mapRect(&bounds);
}
if (this->quickReject(bounds)) {
return;
}
}
draw_calls_.emplace_back(DrawCall{
current_layer_,
DrawPictureData{
picture ? picture->serialize() : SkData::MakeUninitialized(0),
paint ? *paint : SkPaint(), matrix ? *matrix : SkMatrix()}});
}
void MockCanvas::onClipRect(const SkRect& rect,
SkClipOp op,
ClipEdgeStyle style) {
draw_calls_.emplace_back(
DrawCall{current_layer_, ClipRectData{rect, op, style}});
// quickReject() is handled by base class and needs accurate clip information
SkCanvas::onClipRect(rect, op, style);
}
void MockCanvas::onClipRRect(const SkRRect& rrect,
SkClipOp op,
ClipEdgeStyle style) {
draw_calls_.emplace_back(
DrawCall{current_layer_, ClipRRectData{rrect, op, style}});
// quickReject() is handled by base class and needs accurate clip information
SkCanvas::onClipRRect(rrect, op, style);
}
void MockCanvas::onClipPath(const SkPath& path,
SkClipOp op,
ClipEdgeStyle style) {
draw_calls_.emplace_back(
DrawCall{current_layer_, ClipPathData{path, op, style}});
// quickReject() is handled by base class and needs accurate clip information
SkCanvas::onClipPath(path, op, style);
}
bool MockCanvas::onDoSaveBehind(const SkRect*) {
FML_DCHECK(false);
return false;
}
void MockCanvas::onDrawAnnotation(const SkRect&, const char[], SkData*) {
FML_DCHECK(false);
}
void MockCanvas::onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) {
FML_DCHECK(false);
}
void MockCanvas::onDrawDrawable(SkDrawable*, const SkMatrix*) {
FML_DCHECK(false);
}
void MockCanvas::onDrawPatch(const SkPoint[12],
const SkColor[4],
const SkPoint[4],
SkBlendMode,
const SkPaint&) {
FML_DCHECK(false);
}
void MockCanvas::onDrawPaint(const SkPaint& skPaint) {
draw_calls_.emplace_back(DrawCall{current_layer_, DrawPaint{skPaint}});
}
void MockCanvas::onDrawBehind(const SkPaint&) {
FML_DCHECK(false);
}
void MockCanvas::onDrawPoints(PointMode,
size_t,
const SkPoint[],
const SkPaint&) {
FML_DCHECK(false);
}
void MockCanvas::onDrawRegion(const SkRegion&, const SkPaint&) {
FML_DCHECK(false);
}
void MockCanvas::onDrawOval(const SkRect&, const SkPaint&) {
FML_DCHECK(false);
}
void MockCanvas::onDrawArc(const SkRect&,
SkScalar,
SkScalar,
bool,
const SkPaint&) {
FML_DCHECK(false);
}
void MockCanvas::onDrawRRect(const SkRRect&, const SkPaint&) {
FML_DCHECK(false);
}
void MockCanvas::onDrawImageRect2(const SkImage*,
const SkRect&,
const SkRect&,
const SkSamplingOptions&,
const SkPaint*,
SrcRectConstraint) {
FML_DCHECK(false);
}
void MockCanvas::onDrawImageLattice2(const SkImage*,
const Lattice&,
const SkRect&,
SkFilterMode,
const SkPaint*) {
FML_DCHECK(false);
}
void MockCanvas::onDrawVerticesObject(const SkVertices*,
SkBlendMode,
const SkPaint&) {
FML_DCHECK(false);
}
void MockCanvas::onDrawAtlas2(const SkImage*,
const SkRSXform[],
const SkRect[],
const SkColor[],
int,
SkBlendMode,
const SkSamplingOptions&,
const SkRect*,
const SkPaint*) {
FML_DCHECK(false);
}
void MockCanvas::onDrawEdgeAAQuad(const SkRect&,
const SkPoint[4],
QuadAAFlags,
const SkColor4f&,
SkBlendMode) {
FML_DCHECK(false);
}
void MockCanvas::onClipRegion(const SkRegion&, SkClipOp) {
FML_DCHECK(false);
}
bool operator==(const MockCanvas::SaveData& a, const MockCanvas::SaveData& b) {
return a.save_to_layer == b.save_to_layer;
}
std::ostream& operator<<(std::ostream& os, const MockCanvas::SaveData& data) {
return os << data.save_to_layer;
}
bool operator==(const MockCanvas::SaveLayerData& a,
const MockCanvas::SaveLayerData& b) {
return a.save_bounds == b.save_bounds && a.restore_paint == b.restore_paint &&
a.backdrop_filter == b.backdrop_filter &&
a.save_to_layer == b.save_to_layer;
}
std::ostream& operator<<(std::ostream& os,
const MockCanvas::SaveLayerData& data) {
return os << data.save_bounds << " " << data.restore_paint << " "
<< data.backdrop_filter << " " << data.save_to_layer;
}
bool operator==(const MockCanvas::RestoreData& a,
const MockCanvas::RestoreData& b) {
return a.restore_to_layer == b.restore_to_layer;
}
std::ostream& operator<<(std::ostream& os,
const MockCanvas::RestoreData& data) {
return os << data.restore_to_layer;
}
bool operator==(const MockCanvas::ConcatMatrixData& a,
const MockCanvas::ConcatMatrixData& b) {
return a.matrix == b.matrix;
}
std::ostream& operator<<(std::ostream& os,
const MockCanvas::ConcatMatrixData& data) {
return os << data.matrix;
}
bool operator==(const MockCanvas::SetMatrixData& a,
const MockCanvas::SetMatrixData& b) {
return a.matrix == b.matrix;
}
std::ostream& operator<<(std::ostream& os,
const MockCanvas::SetMatrixData& data) {
return os << data.matrix;
}
bool operator==(const MockCanvas::DrawRectData& a,
const MockCanvas::DrawRectData& b) {
return a.rect == b.rect && a.paint == b.paint;
}
std::ostream& operator<<(std::ostream& os,
const MockCanvas::DrawRectData& data) {
return os << data.rect << " " << data.paint;
}
bool operator==(const MockCanvas::DrawPathData& a,
const MockCanvas::DrawPathData& b) {
return a.path == b.path && a.paint == b.paint;
}
std::ostream& operator<<(std::ostream& os,
const MockCanvas::DrawPathData& data) {
return os << data.path << " " << data.paint;
}
bool operator==(const MockCanvas::DrawTextData& a,
const MockCanvas::DrawTextData& b) {
return a.serialized_text->equals(b.serialized_text.get()) &&
a.paint == b.paint && a.offset == b.offset;
}
std::ostream& operator<<(std::ostream& os,
const MockCanvas::DrawTextData& data) {
return os << data.serialized_text << " " << data.paint << " " << data.offset;
}
bool operator==(const MockCanvas::DrawImageData& a,
const MockCanvas::DrawImageData& b) {
return a.image == b.image && a.x == b.x && a.y == b.y &&
a.options == b.options && a.paint == b.paint;
}
std::ostream& operator<<(std::ostream& os,
const MockCanvas::DrawImageData& data) {
return os << data.image << " " << data.x << " " << data.y << " "
<< data.options << " " << data.paint;
}
bool operator==(const MockCanvas::DrawImageDataNoPaint& a,
const MockCanvas::DrawImageDataNoPaint& b) {
return a.image == b.image && a.x == b.x && a.y == b.y &&
a.options == b.options;
}
std::ostream& operator<<(std::ostream& os,
const MockCanvas::DrawImageDataNoPaint& data) {
return os << data.image << " " << data.x << " " << data.y << " "
<< data.options;
}
bool operator==(const MockCanvas::DrawPictureData& a,
const MockCanvas::DrawPictureData& b) {
return a.serialized_picture->equals(b.serialized_picture.get()) &&
a.paint == b.paint && a.matrix == b.matrix;
}
std::ostream& operator<<(std::ostream& os,
const MockCanvas::DrawPictureData& data) {
return os << data.serialized_picture << " " << data.paint << " "
<< data.matrix;
}
bool operator==(const MockCanvas::DrawShadowData& a,
const MockCanvas::DrawShadowData& b) {
return a.path == b.path;
}
std::ostream& operator<<(std::ostream& os,
const MockCanvas::DrawShadowData& data) {
return os << data.path;
}
bool operator==(const MockCanvas::ClipRectData& a,
const MockCanvas::ClipRectData& b) {
return a.rect == b.rect && a.clip_op == b.clip_op && a.style == b.style;
}
std::ostream& operator<<(std::ostream& os,
const MockCanvas::ClipRectData& data) {
return os << data.rect << " " << data.clip_op << " " << data.style;
}
bool operator==(const MockCanvas::ClipRRectData& a,
const MockCanvas::ClipRRectData& b) {
return a.rrect == b.rrect && a.clip_op == b.clip_op && a.style == b.style;
}
std::ostream& operator<<(std::ostream& os,
const MockCanvas::ClipRRectData& data) {
return os << data.rrect << " " << data.clip_op << " " << data.style;
}
bool operator==(const MockCanvas::ClipPathData& a,
const MockCanvas::ClipPathData& b) {
return a.path == b.path && a.clip_op == b.clip_op && a.style == b.style;
}
std::ostream& operator<<(std::ostream& os,
const MockCanvas::ClipPathData& data) {
return os << data.path << " " << data.clip_op << " " << data.style;
}
std::ostream& operator<<(std::ostream& os,
const MockCanvas::DrawCallData& data) {
std::visit([&os](auto& d) { os << d; }, data);
return os;
}
bool operator==(const MockCanvas::DrawCall& a, const MockCanvas::DrawCall& b) {
return a.layer == b.layer && a.data == b.data;
}
std::ostream& operator<<(std::ostream& os, const MockCanvas::DrawCall& draw) {
return os << "[Layer: " << draw.layer << ", Data: " << draw.data << "]";
}
bool operator==(const MockCanvas::DrawPaint& a,
const MockCanvas::DrawPaint& b) {
return a.paint == b.paint;
}
std::ostream& operator<<(std::ostream& os, const MockCanvas::DrawPaint& data) {
return os << data.paint;
}
} // namespace testing
} // namespace flutter