From 0c0038be3bd3c1b6f715e58e087de60c9e839c3c Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Tue, 12 Oct 2021 23:40:00 -0700 Subject: [PATCH 1/7] call DisplayListBuilder methods directly from flutter::Canvas --- flow/display_list.cc | 348 ++- flow/display_list.h | 453 +++- flow/display_list_canvas.cc | 152 +- flow/display_list_canvas.h | 78 +- flow/display_list_canvas_unittests.cc | 3181 +++++++++++++++---------- flow/display_list_unittests.cc | 256 +- flow/display_list_utils.cc | 150 +- flow/display_list_utils.h | 69 +- lib/ui/painting/canvas.cc | 388 +-- lib/ui/painting/canvas.h | 4 +- lib/ui/painting/paint.cc | 182 +- lib/ui/painting/paint.h | 20 +- lib/ui/text/paragraph_builder.cc | 10 +- 13 files changed, 3386 insertions(+), 1905 deletions(-) diff --git a/flow/display_list.cc b/flow/display_list.cc index 3e3806dab66a4..d9416107cde15 100644 --- a/flow/display_list.cc +++ b/flow/display_list.cc @@ -7,10 +7,7 @@ #include "flutter/flow/display_list.h" #include "flutter/flow/display_list_canvas.h" #include "flutter/flow/display_list_utils.h" -#include "flutter/fml/logging.h" -#include "third_party/skia/include/core/SkImageFilter.h" -#include "third_party/skia/include/core/SkMaskFilter.h" #include "third_party/skia/include/core/SkPath.h" #include "third_party/skia/include/core/SkRSXform.h" #include "third_party/skia/include/core/SkTextBlob.h" @@ -1085,65 +1082,83 @@ DisplayListBuilder::~DisplayListBuilder() { } } -void DisplayListBuilder::setAntiAlias(bool aa) { - Push(0, 0, aa); +void DisplayListBuilder::onSetAntiAlias(bool aa) { + Push(0, 0, current_anti_alias_ = aa); } -void DisplayListBuilder::setDither(bool dither) { - Push(0, 0, dither); +void DisplayListBuilder::onSetDither(bool dither) { + Push(0, 0, current_dither_ = dither); } -void DisplayListBuilder::setInvertColors(bool invert) { - Push(0, 0, invert); +void DisplayListBuilder::onSetInvertColors(bool invert) { + Push(0, 0, current_invert_colors_ = invert); } -void DisplayListBuilder::setStrokeCap(SkPaint::Cap cap) { - Push(0, 0, cap); +void DisplayListBuilder::onSetStrokeCap(SkPaint::Cap cap) { + Push(0, 0, current_stroke_cap_ = cap); } -void DisplayListBuilder::setStrokeJoin(SkPaint::Join join) { - Push(0, 0, join); +void DisplayListBuilder::onSetStrokeJoin(SkPaint::Join join) { + Push(0, 0, current_stroke_join_ = join); } -void DisplayListBuilder::setStyle(SkPaint::Style style) { - Push(0, 0, style); +void DisplayListBuilder::onSetStyle(SkPaint::Style style) { + Push(0, 0, current_style_ = style); } -void DisplayListBuilder::setStrokeWidth(SkScalar width) { - Push(0, 0, width); +void DisplayListBuilder::onSetStrokeWidth(SkScalar width) { + Push(0, 0, current_stroke_width_ = width); } -void DisplayListBuilder::setStrokeMiter(SkScalar limit) { - Push(0, 0, limit); +void DisplayListBuilder::onSetStrokeMiter(SkScalar limit) { + Push(0, 0, current_stroke_miter_ = limit); } -void DisplayListBuilder::setColor(SkColor color) { - Push(0, 0, color); +void DisplayListBuilder::onSetColor(SkColor color) { + Push(0, 0, current_color_ = color); } -void DisplayListBuilder::setBlendMode(SkBlendMode mode) { - Push(0, 0, mode); +void DisplayListBuilder::onSetBlendMode(SkBlendMode mode) { + current_blender_ = nullptr; + Push(0, 0, current_blend_mode_ = mode); } -void DisplayListBuilder::setBlender(sk_sp blender) { - blender // - ? Push(0, 0, std::move(blender)) - : Push(0, 0); +void DisplayListBuilder::onSetBlender(sk_sp blender) { + // setBlender(nullptr) should be redirected to setBlendMode(SrcOver) + // by the set method, if not then the following is inefficient but works + FML_DCHECK(blender); + SkPaint p; + p.setBlender(blender); + if (p.asBlendMode()) { + setBlendMode(p.asBlendMode().value()); + } else { + (current_blender_ = blender) // + ? Push(0, 0, std::move(blender)) + : Push(0, 0); + } } -void DisplayListBuilder::setShader(sk_sp shader) { - shader // +void DisplayListBuilder::onSetShader(sk_sp shader) { + (current_shader_ = shader) // ? Push(0, 0, std::move(shader)) : Push(0, 0); } -void DisplayListBuilder::setImageFilter(sk_sp filter) { - filter // +void DisplayListBuilder::onSetImageFilter(sk_sp filter) { + (current_image_filter_ = filter) // ? Push(0, 0, std::move(filter)) : Push(0, 0); } -void DisplayListBuilder::setColorFilter(sk_sp filter) { - filter // +void DisplayListBuilder::onSetColorFilter(sk_sp filter) { + (current_color_filter_ = filter) // ? Push(0, 0, std::move(filter)) : Push(0, 0); } -void DisplayListBuilder::setPathEffect(sk_sp effect) { - effect // +void DisplayListBuilder::onSetPathEffect(sk_sp effect) { + (current_path_effect_ = effect) // ? Push(0, 0, std::move(effect)) : Push(0, 0); } -void DisplayListBuilder::setMaskFilter(sk_sp filter) { +void DisplayListBuilder::onSetMaskFilter(sk_sp filter) { + current_mask_sigma_ = kInvalidSigma; + current_mask_filter_ = filter; Push(0, 0, std::move(filter)); } -void DisplayListBuilder::setMaskBlurFilter(SkBlurStyle style, SkScalar sigma) { +void DisplayListBuilder::onSetMaskBlurFilter(SkBlurStyle style, + SkScalar sigma) { + // Valid sigma is checked by setMaskBlurFilter + FML_DCHECK(mask_sigma_valid(sigma)); + current_mask_filter_ = nullptr; + current_mask_style_ = style; + current_mask_sigma_ = sigma; switch (style) { case kNormal_SkBlurStyle: Push(0, 0, sigma); @@ -1160,6 +1175,55 @@ void DisplayListBuilder::setMaskBlurFilter(SkBlurStyle style, SkScalar sigma) { } } +void DisplayListBuilder::setAttributesFromPaint(const SkPaint& paint, + const DisplayListAttributeFlags flags) { + if (flags.applies_anti_alias()) { + setAntiAlias(paint.isAntiAlias()); + } + if (flags.applies_dither()) { + setDither(paint.isDither()); + } + if (flags.applies_alpha_or_color()) { + setColor(paint.getColor()); + } + if (flags.applies_blend()) { + skstd::optional mode_optional = paint.asBlendMode(); + if (mode_optional) { + setBlendMode(mode_optional.value()); + } else { + setBlender(sk_ref_sp(paint.getBlender())); + } + } + if (flags.applies_style()) { + setStyle(paint.getStyle()); + } + if (flags.is_stroked(paint.getStyle())) { + setStrokeWidth(paint.getStrokeWidth()); + setStrokeMiter(paint.getStrokeMiter()); + setStrokeCap(paint.getStrokeCap()); + setStrokeJoin(paint.getStrokeJoin()); + } + if (flags.applies_shader()) { + setShader(sk_ref_sp(paint.getShader())); + } + if (flags.applies_color_filter()) { + // invert colors is a Flutter::Paint thing, not an SkPaint thing + // we must clear it because it is a second potential color filter + // that is composed with the paint's color filter. + setInvertColors(false); + setColorFilter(sk_ref_sp(paint.getColorFilter())); + } + if (flags.applies_image_filter()) { + setImageFilter(sk_ref_sp(paint.getImageFilter())); + } + if (flags.applies_path_effect()) { + setPathEffect(sk_ref_sp(paint.getPathEffect())); + } + if (flags.applies_mask_filter()) { + setMaskFilter(sk_ref_sp(paint.getMaskFilter())); + } +} + void DisplayListBuilder::save() { save_level_++; Push(0, 1); @@ -1170,24 +1234,36 @@ void DisplayListBuilder::restore() { save_level_--; } } -void DisplayListBuilder::saveLayer(const SkRect* bounds, bool with_paint) { +void DisplayListBuilder::saveLayer(const SkRect* bounds, + bool restore_with_paint) { save_level_++; bounds // - ? Push(0, 1, *bounds, with_paint) - : Push(0, 1, with_paint); + ? Push(0, 1, *bounds, restore_with_paint) + : Push(0, 1, restore_with_paint); } void DisplayListBuilder::translate(SkScalar tx, SkScalar ty) { - Push(0, 1, tx, ty); + if (SkScalarIsFinite(tx) && SkScalarIsFinite(ty) && + (tx != 0.0 || ty != 0.0)) { + Push(0, 1, tx, ty); + } } void DisplayListBuilder::scale(SkScalar sx, SkScalar sy) { - Push(0, 1, sx, sy); + if (SkScalarIsFinite(sx) && SkScalarIsFinite(sy) && + (sx != 1.0 || sy != 1.0)) { + Push(0, 1, sx, sy); + } } void DisplayListBuilder::rotate(SkScalar degrees) { - Push(0, 1, degrees); + if (SkScalarMod(degrees, 360.0) != 0.0) { + Push(0, 1, degrees); + } } void DisplayListBuilder::skew(SkScalar sx, SkScalar sy) { - Push(0, 1, sx, sy); + if (SkScalarIsFinite(sx) && SkScalarIsFinite(sy) && + (sx != 0.0 || sy != 0.0)) { + Push(0, 1, sx, sy); + } } // clang-format off @@ -1196,7 +1272,10 @@ void DisplayListBuilder::skew(SkScalar sx, SkScalar sy) { void DisplayListBuilder::transform2DAffine( SkScalar mxx, SkScalar mxy, SkScalar mxt, SkScalar myx, SkScalar myy, SkScalar myt) { - if (!(mxx == 1 && mxy == 0 && mxt == 0 && + if (SkScalarsAreFinite(mxx, myx) && + SkScalarsAreFinite(mxy, myy) && + SkScalarsAreFinite(mxt, myt) && + !(mxx == 1 && mxy == 0 && mxt == 0 && myx == 0 && myy == 1 && myt == 0)) { Push(0, 1, mxx, mxy, mxt, @@ -1215,7 +1294,10 @@ void DisplayListBuilder::transformFullPerspective( mwx == 0 && mwy == 0 && mwz == 0 && mwt == 1) { transform2DAffine(mxx, mxy, mxt, myx, myy, myt); - } else { + } else if (SkScalarsAreFinite(mxx, mxy) && SkScalarsAreFinite(mxz, mxt) && + SkScalarsAreFinite(myx, myy) && SkScalarsAreFinite(myz, myt) && + SkScalarsAreFinite(mzx, mzy) && SkScalarsAreFinite(mzz, mzt) && + SkScalarsAreFinite(mwx, mwy) && SkScalarsAreFinite(mwz, mwt)) { Push(0, 1, mxx, mxy, mxz, mxt, myx, myy, myz, myt, @@ -1368,7 +1450,7 @@ void DisplayListBuilder::drawImageLattice(const sk_sp image, const SkCanvas::Lattice& lattice, const SkRect& dst, SkFilterMode filter, - bool with_paint) { + bool render_with_attributes) { int xDivCount = lattice.fXCount; int yDivCount = lattice.fYCount; FML_DCHECK((lattice.fRectTypes == nullptr) || (lattice.fColors != nullptr)); @@ -1379,9 +1461,9 @@ void DisplayListBuilder::drawImageLattice(const sk_sp image, (xDivCount + yDivCount) * sizeof(int) + cellCount * (sizeof(SkColor) + sizeof(SkCanvas::Lattice::RectType)); SkIRect src = lattice.fBounds ? *lattice.fBounds : image->bounds(); - void* pod = this->Push(bytes, 1, std::move(image), - xDivCount, yDivCount, cellCount, - src, dst, filter, with_paint); + void* pod = this->Push( + bytes, 1, std::move(image), xDivCount, yDivCount, cellCount, src, dst, + filter, render_with_attributes); CopyV(pod, lattice.fXDivs, xDivCount, lattice.fYDivs, yDivCount, lattice.fColors, cellCount, lattice.fRectTypes, cellCount); } @@ -1463,4 +1545,170 @@ void DisplayListBuilder::drawShadow(const SkPath& path, : Push(0, 1, path, color, elevation, dpr); } +// Flags common to all primitives that apply colors +#define PAINT_FLAGS (kUsesDither_ | \ + kUsesColor_ | \ + kUsesAlpha_ | \ + kUsesBlend_ | \ + kUsesShader_ | \ + kUsesColorFilter_ | \ + kUsesImageFilter_) + +// Flags common to all primitives that stroke or fill +#define STROKE_OR_FILL_FLAGS (kIsDrawnGeometry_ | \ + kUsesAntiAlias_ | \ + kUsesMaskFilter_ | \ + kUsesPathEffect_) + +// Flags common to primitives that stroke geometry +#define STROKE_FLAGS (kIsStrokedGeometry_ | \ + kUsesAntiAlias_ | \ + kUsesMaskFilter_ | \ + kUsesPathEffect_) + +// Flags common to primitives that render an image with paint attributes +#define IMAGE_FLAGS_BASE (kIsNonGeometric_ | \ + kUsesAlpha_ | \ + kUsesDither_ | \ + kUsesBlend_ | \ + kUsesColorFilter_ | \ + kUsesImageFilter_) + +const DisplayListAttributeFlags // + DisplayListAttributeFlags::kSaveLayerFlags = + DisplayListAttributeFlags(kIgnoresPaint_); + +const DisplayListAttributeFlags // + DisplayListAttributeFlags::kSaveLayerWithPaintFlags = + DisplayListAttributeFlags(kIsNonGeometric_ | // + kUsesAlpha_ | // + kUsesBlend_ | // + kUsesColorFilter_ | // + kUsesImageFilter_); + +const DisplayListAttributeFlags // + DisplayListAttributeFlags::kDrawColorFlags = + DisplayListAttributeFlags(kFloodsSurface_ | kIgnoresPaint_); + +const DisplayListAttributeFlags // + DisplayListAttributeFlags::kDrawPaintFlags = + DisplayListAttributeFlags(PAINT_FLAGS | kFloodsSurface_); + +const DisplayListAttributeFlags // + DisplayListAttributeFlags::kDrawHVLineFlags = + DisplayListAttributeFlags(PAINT_FLAGS | STROKE_FLAGS); + +const DisplayListAttributeFlags // + DisplayListAttributeFlags::kDrawLineFlags = + kDrawHVLineFlags.with(kMayHaveDiagonalCaps_); + +const DisplayListAttributeFlags // + DisplayListAttributeFlags::kDrawRectFlags = + DisplayListAttributeFlags(PAINT_FLAGS | STROKE_OR_FILL_FLAGS); + +const DisplayListAttributeFlags // + DisplayListAttributeFlags::kDrawOvalFlags = + DisplayListAttributeFlags(PAINT_FLAGS | STROKE_OR_FILL_FLAGS); + +const DisplayListAttributeFlags // + DisplayListAttributeFlags::kDrawCircleFlags = + DisplayListAttributeFlags(PAINT_FLAGS | STROKE_OR_FILL_FLAGS); + +const DisplayListAttributeFlags // + DisplayListAttributeFlags::kDrawRRectFlags = + DisplayListAttributeFlags(PAINT_FLAGS | STROKE_OR_FILL_FLAGS); + +const DisplayListAttributeFlags // + DisplayListAttributeFlags::kDrawDRRectFlags = + DisplayListAttributeFlags(PAINT_FLAGS | STROKE_OR_FILL_FLAGS); + +const DisplayListAttributeFlags // + DisplayListAttributeFlags::kDrawPathFlags = + DisplayListAttributeFlags(PAINT_FLAGS | STROKE_OR_FILL_FLAGS | kMayHaveDiagonalCaps_ | kMayHaveAcuteJoins_); + +const DisplayListAttributeFlags // + DisplayListAttributeFlags::kDrawArcFlags = + DisplayListAttributeFlags(PAINT_FLAGS | STROKE_OR_FILL_FLAGS | kMayHaveDiagonalCaps_); + +const DisplayListAttributeFlags // + DisplayListAttributeFlags::kDrawPointsAsPointsFlags = + DisplayListAttributeFlags(PAINT_FLAGS | STROKE_FLAGS); + +const DisplayListAttributeFlags // + DisplayListAttributeFlags::kDrawPointsAsLinesFlags = + DisplayListAttributeFlags(PAINT_FLAGS | STROKE_FLAGS | kMayHaveDiagonalCaps_); + +// Polygon mode just draws (count-1) separate lines, no joins +const DisplayListAttributeFlags // + DisplayListAttributeFlags::kDrawPointsAsPolygonFlags = + DisplayListAttributeFlags(PAINT_FLAGS | STROKE_FLAGS | kMayHaveDiagonalCaps_); + +const DisplayListAttributeFlags // + DisplayListAttributeFlags::kDrawVerticesFlags = + DisplayListAttributeFlags(kIsNonGeometric_ | // + kUsesDither_ | + kUsesAlpha_ | + kUsesShader_ | // + kUsesBlend_ | + kUsesColorFilter_ | + kUsesImageFilter_); + +const DisplayListAttributeFlags // + DisplayListAttributeFlags::kDrawImageFlags = + DisplayListAttributeFlags(kIgnoresPaint_); + +const DisplayListAttributeFlags // + DisplayListAttributeFlags::kDrawImageWithPaintFlags = // + DisplayListAttributeFlags(IMAGE_FLAGS_BASE | kUsesAntiAlias_ | kUsesMaskFilter_); + +const DisplayListAttributeFlags DisplayListAttributeFlags::kDrawImageRectFlags = + DisplayListAttributeFlags(kIgnoresPaint_); + +const DisplayListAttributeFlags + DisplayListAttributeFlags::kDrawImageRectWithPaintFlags = + DisplayListAttributeFlags(IMAGE_FLAGS_BASE | kUsesAntiAlias_ | kUsesMaskFilter_); + +const DisplayListAttributeFlags DisplayListAttributeFlags::kDrawImageNineFlags = + DisplayListAttributeFlags(kIgnoresPaint_); + +const DisplayListAttributeFlags + DisplayListAttributeFlags::kDrawImageNineWithPaintFlags = + DisplayListAttributeFlags(IMAGE_FLAGS_BASE); + +const DisplayListAttributeFlags + DisplayListAttributeFlags::kDrawImageLatticeFlags = + DisplayListAttributeFlags(kIgnoresPaint_); + +const DisplayListAttributeFlags + DisplayListAttributeFlags::kDrawImageLatticeWithPaintFlags = + DisplayListAttributeFlags(IMAGE_FLAGS_BASE); + +const DisplayListAttributeFlags DisplayListAttributeFlags::kDrawAtlasFlags = + DisplayListAttributeFlags(kIgnoresPaint_); + +const DisplayListAttributeFlags + DisplayListAttributeFlags::kDrawAtlasWithPaintFlags = + DisplayListAttributeFlags(IMAGE_FLAGS_BASE); + +const DisplayListAttributeFlags DisplayListAttributeFlags::kDrawPictureFlags = + DisplayListAttributeFlags(kIgnoresPaint_); + +const DisplayListAttributeFlags + DisplayListAttributeFlags::kDrawPictureWithPaintFlags = + kSaveLayerWithPaintFlags; + +const DisplayListAttributeFlags DisplayListAttributeFlags::kDrawDisplayListFlags = + DisplayListAttributeFlags(kIgnoresPaint_); + +const DisplayListAttributeFlags DisplayListAttributeFlags::kDrawTextBlobFlags = + DisplayListAttributeFlags(PAINT_FLAGS | STROKE_OR_FILL_FLAGS).without(kUsesAntiAlias_); + +const DisplayListAttributeFlags DisplayListAttributeFlags::kDrawShadowFlags = + DisplayListAttributeFlags(kIgnoresPaint_); + +#undef PAINT_FLAGS +#undef STROKE_OR_FILL_FLAGS +#undef STROKE_FLAGS +#undef IMAGE_FLAGS_BASE + } // namespace flutter diff --git a/flow/display_list.h b/flow/display_list.h index 327ad9df2ea21..292423570bc49 100644 --- a/flow/display_list.h +++ b/flow/display_list.h @@ -5,17 +5,22 @@ #ifndef FLUTTER_FLOW_DISPLAY_LIST_H_ #define FLUTTER_FLOW_DISPLAY_LIST_H_ +#include + #include "third_party/skia/include/core/SkBlender.h" #include "third_party/skia/include/core/SkBlurTypes.h" #include "third_party/skia/include/core/SkCanvas.h" #include "third_party/skia/include/core/SkColorFilter.h" #include "third_party/skia/include/core/SkImage.h" #include "third_party/skia/include/core/SkImageFilter.h" +#include "third_party/skia/include/core/SkMaskFilter.h" #include "third_party/skia/include/core/SkPathEffect.h" #include "third_party/skia/include/core/SkPicture.h" #include "third_party/skia/include/core/SkShader.h" #include "third_party/skia/include/core/SkVertices.h" +#include "flutter/fml/logging.h" + // The Flutter DisplayList mechanism encapsulates a persistent sequence of // rendering operations. // @@ -463,6 +468,264 @@ class Dispatcher { SkScalar dpr) = 0; }; +/// The base class for the classes that maintain a list of +/// attributes that might be important for a number of operations +/// including which rendering attributes need to be set before +/// calling a rendering method (all |drawSomething| calls), +/// or for determining which exceptional conditions may need +/// to be accounted for in bounds calculations. +/// This class contains only protected definitions and helper methods +/// for the public classes |DisplayListAttributeFlags| and +/// |DisplayListSpecialGeometryFlags|. +class DisplayListFlagsBase { + protected: + DisplayListFlagsBase(int flags) : flags_(flags) {} + + const int flags_; + + bool has_any(int qFlags) const { return (flags_ & qFlags) != 0; } + bool has_all(int qFlags) const { return (flags_ & qFlags) == qFlags; } + bool has_none(int qFlags) const { return (flags_ & qFlags) == 0; } + + // A drawing operation that is not geometric in nature (but which + // may still apply a MaskFilter - see |kApplyMaskFilter| below). + static constexpr int kIsNonGeometric_ = 0; + + // A geometric operation that is defined as a fill operation + // regardless of what the current paint Style is set to. + // This flag will automatically assume |kApplyMaskFilter|. + static constexpr int kIsFilledGeometry_ = 1 << 0; + + // A geometric operation that is defined as a stroke operation + // regardless of what the current paint Style is set to. + // This flag will automatically assume |kApplyMaskFilter|. + static constexpr int kIsStrokedGeometry_ = 1 << 1; + + // A geometric operation that may be a stroke or fill operation + // depending on the current state of the paint Style attribute. + // This flag will automatically assume |kApplyMaskFilter|. + static constexpr int kIsDrawnGeometry_ = 1 << 2; + + static constexpr int kIsAnyGeometryMask_ = // + kIsFilledGeometry_ | // + kIsStrokedGeometry_ | // + kIsDrawnGeometry_; + + // A primitive that floods the surface (or clip) with no + // natural bounds, such as |drawColor| or |drawPaint|. + static constexpr int kFloodsSurface_ = 1 << 3; + + // A geometric operation which has a path that might have + // end caps that are not rectilinear which means that square + // end caps might project further than half the stroke width + // from the geometry bounds. + // A rectilinear path such as |drawRect| will not have + // diagonal end caps. |drawLine| might have diagonal end + // caps depending on the angle of the line, and more likely + // |drawPath| will often have such end caps. + static constexpr int kMayHaveDiagonalCaps_ = 1 << 4; + + // A geometric operation which has joined vertices that are + // not guaranteed to be smooth (angles of incoming and outgoing) + // segments at some joins may not have the same angle) or + // rectilinear (squares have right angles at the corners, but + // those corners will never extend past the bounding box of + // the geometry pre-transform). + // |drawRect|, |drawOval| and |drawRRect| all have well + // behaved joins, but |drawPath| might have joins that cause + // mitered extensions outside the pre-transformed bounding box. + static constexpr int kMayHaveAcuteJoins_ = 1 << 5; + + static constexpr int kAnySpecialGeometryMask_ = // + kMayHaveDiagonalCaps_ | kMayHaveAcuteJoins_; + + // clang-format off + static constexpr int kUsesAntiAlias_ = 1 << 10; + static constexpr int kUsesDither_ = 1 << 11; + static constexpr int kUsesAlpha_ = 1 << 12; + static constexpr int kUsesColor_ = 1 << 13; + static constexpr int kUsesBlend_ = 1 << 14; + static constexpr int kUsesShader_ = 1 << 15; + static constexpr int kUsesColorFilter_ = 1 << 16; + static constexpr int kUsesPathEffect_ = 1 << 17; + static constexpr int kUsesMaskFilter_ = 1 << 18; + static constexpr int kUsesImageFilter_ = 1 << 19; + static constexpr int kIgnoresPaint_ = 1 << 20; + // clang-format on + + static constexpr int kAnyAttributeMask_ = // + kUsesAntiAlias_ | kUsesDither_ | kUsesColor_ | kUsesAlpha_ | kUsesBlend_ | + kUsesShader_ | kUsesColorFilter_ | kUsesPathEffect_ | kUsesMaskFilter_ | + kUsesImageFilter_; +}; + +/// An attribute class for advertising specific properties of +/// a geometric attribute that can affect the computation of +/// the bounds of the primitive. +class DisplayListSpecialGeometryFlags : DisplayListFlagsBase { + public: + /// The geometry may have segments that end on a diagonal + /// such that their end caps extend further than the default + /// |strokeWidth * 0.5| margin around the geometry. + bool may_have_diagonal_caps() { return has_any(kMayHaveDiagonalCaps_); } + + /// The geometry may have segments that meet at vertices at + /// an acute angle such that the miter joins will extend + /// further than the default |strokeWidth * 0.5| margin around + /// the geometry. + bool may_have_acute_joins() { return has_any(kMayHaveAcuteJoins_); } + + private: + DisplayListSpecialGeometryFlags(int flags) + : DisplayListFlagsBase(flags) { + FML_DCHECK((flags & kAnySpecialGeometryMask_) == flags); + } + + const DisplayListSpecialGeometryFlags with(int extra) const { + return extra == 0 ? *this : DisplayListSpecialGeometryFlags(flags_ | extra); + } + + friend class DisplayListAttributeFlags; +}; + +class DisplayListAttributeFlags : DisplayListFlagsBase { + public: + static const DisplayListAttributeFlags kSaveLayerFlags; + static const DisplayListAttributeFlags kSaveLayerWithPaintFlags; + static const DisplayListAttributeFlags kDrawColorFlags; + static const DisplayListAttributeFlags kDrawPaintFlags; + static const DisplayListAttributeFlags kDrawLineFlags; + // Special case flags for horizonal and vertical lines + static const DisplayListAttributeFlags kDrawHVLineFlags; + static const DisplayListAttributeFlags kDrawRectFlags; + static const DisplayListAttributeFlags kDrawOvalFlags; + static const DisplayListAttributeFlags kDrawCircleFlags; + static const DisplayListAttributeFlags kDrawRRectFlags; + static const DisplayListAttributeFlags kDrawDRRectFlags; + static const DisplayListAttributeFlags kDrawPathFlags; + static const DisplayListAttributeFlags kDrawArcFlags; + static const DisplayListAttributeFlags kDrawPointsAsPointsFlags; + static const DisplayListAttributeFlags kDrawPointsAsLinesFlags; + static const DisplayListAttributeFlags kDrawPointsAsPolygonFlags; + static const DisplayListAttributeFlags kDrawVerticesFlags; + static const DisplayListAttributeFlags kDrawImageFlags; + static const DisplayListAttributeFlags kDrawImageWithPaintFlags; + static const DisplayListAttributeFlags kDrawImageRectFlags; + static const DisplayListAttributeFlags kDrawImageRectWithPaintFlags; + static const DisplayListAttributeFlags kDrawImageNineFlags; + static const DisplayListAttributeFlags kDrawImageNineWithPaintFlags; + static const DisplayListAttributeFlags kDrawImageLatticeFlags; + static const DisplayListAttributeFlags kDrawImageLatticeWithPaintFlags; + static const DisplayListAttributeFlags kDrawAtlasFlags; + static const DisplayListAttributeFlags kDrawAtlasWithPaintFlags; + static const DisplayListAttributeFlags kDrawPictureFlags; + static const DisplayListAttributeFlags kDrawPictureWithPaintFlags; + static const DisplayListAttributeFlags kDrawDisplayListFlags; + static const DisplayListAttributeFlags kDrawTextBlobFlags; + static const DisplayListAttributeFlags kDrawShadowFlags; + + const DisplayListSpecialGeometryFlags WithPathEffect( + sk_sp effect) const { + if (is_geometric() && effect) { + SkPathEffect::DashInfo info; + if (effect->asADash(&info) == SkPathEffect::kDash_DashType) { + // A dash effect has a very simple impact. It cannot introduce any + // miter joins that weren't already present in the original path + // and it does not grow the bounds of the path, but it can add + // end caps to areas that might not have had them before so all + // we need to do is to indicate the potential for diagonal + // end caps and move on. + return special_flags_.with(kMayHaveDiagonalCaps_); + } else { + // An arbitrary path effect can introduce joins at an arbitrary + // angle and may change the geometry of the end caps + return special_flags_.with(kMayHaveDiagonalCaps_ | // + kMayHaveAcuteJoins_); + } + } + return special_flags_; + } + + bool ignores_paint() const { return has_any(kIgnoresPaint_); } + + bool applies_anti_alias() const { return has_any(kUsesAntiAlias_); } + bool applies_dither() const { return has_any(kUsesDither_); } + bool applies_color() const { return has_any(kUsesColor_); } + bool applies_alpha() const { return has_any(kUsesAlpha_); } + bool applies_alpha_or_color() const { return has_any(kUsesAlpha_ | kUsesColor_); } + + /// The primitive dynamically determines whether it is a stroke or fill + /// operation (or both) based on the setting of the |Style| attribute. + bool applies_style() const { return has_any(kIsDrawnGeometry_); } + /// The primitive can use any of the stroke attributes, such as + /// StrokeWidth, StrokeMiter, StrokeCap, or StrokeJoin. This + /// method will return if the primitive is defined as one that + /// strokes its geometry (such as |drawLine|) or if it is defined + /// as one that honors the Style attribute. If the Style attribute + /// is known then a more accurate answer can be returned from + /// the |is_stroked| method by supplying the actual setting of + /// the style. + bool applies_stroke_attributes() const { return is_stroked(); } + + bool applies_shader() const { return has_any(kUsesShader_); } + /// The primitive honors the current SkColorFilter, including + /// the related attribute InvertColors + bool applies_color_filter() const { return has_any(kUsesColorFilter_); } + /// The primitive honors the SkBlendMode or SkBlender + bool applies_blend() const { return has_any(kUsesBlend_); } + bool applies_path_effect() const { return has_any(kUsesPathEffect_); } + /// The primitive honors the SkMaskFilter whether set using the + /// filter object or using the convenience method |setMaskBlurFilter| + bool applies_mask_filter() const { return has_any(kUsesMaskFilter_); } + bool applies_image_filter() const { return has_any(kUsesImageFilter_); } + + bool is_geometric() const { return has_any(kIsAnyGeometryMask_); } + bool is_stroked(SkPaint::Style style = SkPaint::Style::kStroke_Style) const { + return ( + has_any(kIsStrokedGeometry_) || + (style != SkPaint::Style::kFill_Style && has_any(kIsDrawnGeometry_))); + } + bool is_flood() const { + return has_any(kFloodsSurface_); + } + + bool may_stroke_geometry() { + return has_any(kIsDrawnGeometry_ | kIsStrokedGeometry_); + } + /// Only valid after calling |WithStyle| with the current |Style| + bool does_stroke_geometry() { return has_any(kIsStrokedGeometry_); } + + private: + DisplayListAttributeFlags(int flags) + : DisplayListFlagsBase(flags), + special_flags_(flags & kAnySpecialGeometryMask_) { + FML_DCHECK((flags & kIsAnyGeometryMask_) == kIsNonGeometric_ || + (flags & kIsAnyGeometryMask_) == kIsFilledGeometry_ || + (flags & kIsAnyGeometryMask_) == kIsStrokedGeometry_ || + (flags & kIsAnyGeometryMask_) == kIsDrawnGeometry_); + FML_DCHECK(((flags & kAnyAttributeMask_) == 0) != + ((flags & kIgnoresPaint_) == 0)); + FML_DCHECK((flags & kIsAnyGeometryMask_) != 0 || + (flags & kAnySpecialGeometryMask_) == 0); + } + + const DisplayListAttributeFlags with(int extra) const { + return extra == 0 ? *this : DisplayListAttributeFlags(flags_ | extra); + } + + const DisplayListAttributeFlags without(int remove) const { + FML_DCHECK(has_all(remove)); + return (flags_ & ~remove); + } + + const DisplayListAttributeFlags with( + const DisplayListAttributeFlags& extra) const { + return with(extra.flags_); + } + + const DisplayListSpecialGeometryFlags special_flags_; +}; + // The primary class used to build a display list. The list of methods // here matches the list of methods invoked on a |Dispatcher|. // If there is some code that already renders to an SkCanvas object, @@ -473,33 +736,137 @@ class DisplayListBuilder final : public virtual Dispatcher, public SkRefCnt { DisplayListBuilder(const SkRect& cull_rect = kMaxCullRect_); ~DisplayListBuilder(); - void setAntiAlias(bool aa) override; - void setDither(bool dither) override; - void setInvertColors(bool invert) override; - void setStrokeCap(SkPaint::Cap cap) override; - void setStrokeJoin(SkPaint::Join join) override; - void setStyle(SkPaint::Style style) override; - void setStrokeWidth(SkScalar width) override; - void setStrokeMiter(SkScalar limit) override; - void setColor(SkColor color) override; - void setBlendMode(SkBlendMode mode) override; - void setBlender(sk_sp blender) override; - void setShader(sk_sp shader) override; - void setImageFilter(sk_sp filter) override; - void setColorFilter(sk_sp filter) override; - void setPathEffect(sk_sp effect) override; - void setMaskFilter(sk_sp filter) override; - void setMaskBlurFilter(SkBlurStyle style, SkScalar sigma) override; + void setAntiAlias(bool aa) override { + if (current_anti_alias_ != aa) { + onSetAntiAlias(aa); + } + } + void setDither(bool dither) override { + if (current_dither_ != dither) { + onSetDither(dither); + } + } + void setInvertColors(bool invert) override { + if (current_invert_colors_ != invert) { + onSetInvertColors(invert); + } + } + void setStrokeCap(SkPaint::Cap cap) override { + if (current_stroke_cap_ != cap) { + onSetStrokeCap(cap); + } + } + void setStrokeJoin(SkPaint::Join join) override { + if (current_stroke_join_ != join) { + onSetStrokeJoin(join); + } + } + void setStyle(SkPaint::Style style) override { + if (current_style_ != style) { + onSetStyle(style); + } + } + void setStrokeWidth(SkScalar width) override { + if (current_stroke_width_ != width) { + onSetStrokeWidth(width); + } + } + void setStrokeMiter(SkScalar limit) override { + if (current_stroke_miter_ != limit) { + onSetStrokeMiter(limit); + } + } + void setColor(SkColor color) override { + if (current_color_ != color) { + onSetColor(color); + } + } + void setBlendMode(SkBlendMode mode) override { + if (current_blender_ || current_blend_mode_ != mode) { + onSetBlendMode(mode); + } + } + void setBlender(sk_sp blender) override { + if (!blender) { + setBlendMode(SkBlendMode::kSrcOver); + } else if (current_blender_ != blender) { + onSetBlender(std::move(blender)); + } + } + void setShader(sk_sp shader) override { + if (current_shader_ != shader) { + onSetShader(std::move(shader)); + } + } + void setImageFilter(sk_sp filter) override { + if (current_image_filter_ != filter) { + onSetImageFilter(std::move(filter)); + } + } + void setColorFilter(sk_sp filter) override { + if (current_color_filter_ != filter) { + onSetColorFilter(std::move(filter)); + } + } + void setPathEffect(sk_sp effect) override { + if (current_path_effect_ != effect) { + onSetPathEffect(std::move(effect)); + } + } + void setMaskFilter(sk_sp filter) override { + if (mask_sigma_valid(current_mask_sigma_) || + current_mask_filter_ != filter) { + onSetMaskFilter(std::move(filter)); + } + } + void setMaskBlurFilter(SkBlurStyle style, SkScalar sigma) override { + if (mask_sigma_valid(sigma) && + (current_mask_style_ != style || current_mask_sigma_ != sigma)) { + onSetMaskBlurFilter(style, sigma); + } + } + bool isAntiAlias() const { return current_anti_alias_; } + bool isDither() const { return current_dither_; } + SkPaint::Style getStyle() const { return current_style_; } + SkColor getColor() const { return current_color_; } + SkScalar getStrokeWidth() const { return current_stroke_width_; } + SkScalar getStrokeMiter() const { return current_stroke_miter_; } + SkPaint::Cap getStrokeCap() const { return current_stroke_cap_; } + SkPaint::Join getStrokeJoin() const { return current_stroke_join_; } + sk_sp getShader() const { return current_shader_; } + sk_sp getColorFilter() const { return current_color_filter_; } + bool isInvertColors() const { return current_invert_colors_; } + std::optional getBlendMode() const { + if (current_blender_) { + // The setters will turn "Mode" style blenders into "blend_mode"s + return {}; + } + return current_blend_mode_; + } + sk_sp getBlender() const { + return current_blender_ + ? current_blender_ + : SkBlender::Mode(current_blend_mode_); + } + sk_sp getPathEffect() const { return current_path_effect_; } + sk_sp getMaskFilter() const { return current_mask_filter_; } + // No utility getter for the utility setter: + // void setMaskBlurFilter (SkBlurStyle style, SkScalar sigma) + sk_sp getImageFilter() const { return current_image_filter_; } void save() override; - void saveLayer(const SkRect* bounds, bool restoreWithPaint) override; + void saveLayer(const SkRect* bounds, bool restore_with_paint) override; void restore() override; + int getSaveCount() { return save_level_ + 1; } void translate(SkScalar tx, SkScalar ty) override; void scale(SkScalar sx, SkScalar sy) override; void rotate(SkScalar degrees) override; void skew(SkScalar sx, SkScalar sy) override; + void setAttributesFromPaint(const SkPaint& paint, + const DisplayListAttributeFlags flags); + // clang-format off // 2x3 2D affine subset of a 4x4 transform in row major order @@ -514,9 +881,9 @@ class DisplayListBuilder final : public virtual Dispatcher, public SkRefCnt { // clang-format on - void clipRect(const SkRect& rect, SkClipOp clip_op, bool isAA) override; - void clipRRect(const SkRRect& rrect, SkClipOp clip_op, bool isAA) override; - void clipPath(const SkPath& path, SkClipOp clip_op, bool isAA) override; + void clipRect(const SkRect& rect, SkClipOp clip_op, bool is_aa) override; + void clipRRect(const SkRRect& rrect, SkClipOp clip_op, bool is_aa) override; + void clipPath(const SkPath& path, SkClipOp clip_op, bool is_aa) override; void drawPaint() override; void drawColor(SkColor color, SkBlendMode mode) override; @@ -599,6 +966,50 @@ class DisplayListBuilder final : public virtual Dispatcher, public SkRefCnt { template void* Push(size_t extra, int op_inc, Args&&... args); + + // kInvalidSigma is used to indicate that no MaskBlur is currently set. + static constexpr SkScalar kInvalidSigma = 0.0; + bool mask_sigma_valid(SkScalar sigma) { + return SkScalarIsFinite(sigma) && sigma > 0.0; + } + + void onSetAntiAlias(bool aa); + void onSetDither(bool dither); + void onSetInvertColors(bool invert); + void onSetStrokeCap(SkPaint::Cap cap); + void onSetStrokeJoin(SkPaint::Join join); + void onSetStyle(SkPaint::Style style); + void onSetStrokeWidth(SkScalar width); + void onSetStrokeMiter(SkScalar limit); + void onSetColor(SkColor color); + void onSetBlendMode(SkBlendMode mode); + void onSetBlender(sk_sp blender); + void onSetShader(sk_sp shader); + void onSetImageFilter(sk_sp filter); + void onSetColorFilter(sk_sp filter); + void onSetPathEffect(sk_sp effect); + void onSetMaskFilter(sk_sp filter); + void onSetMaskBlurFilter(SkBlurStyle style, SkScalar sigma); + + // These values should match the defaults of the Dart Paint object. + bool current_anti_alias_ = false; + bool current_dither_ = false; + bool current_invert_colors_ = false; + SkColor current_color_ = 0xFF000000; + SkBlendMode current_blend_mode_ = SkBlendMode::kSrcOver; + SkPaint::Style current_style_ = SkPaint::Style::kFill_Style; + SkScalar current_stroke_width_ = 0.0; + SkScalar current_stroke_miter_ = 4.0; + SkPaint::Cap current_stroke_cap_ = SkPaint::Cap::kButt_Cap; + SkPaint::Join current_stroke_join_ = SkPaint::Join::kMiter_Join; + sk_sp current_blender_; + sk_sp current_shader_; + sk_sp current_color_filter_; + sk_sp current_image_filter_; + sk_sp current_path_effect_; + sk_sp current_mask_filter_; + int current_mask_style_; + SkScalar current_mask_sigma_ = kInvalidSigma; }; } // namespace flutter diff --git a/flow/display_list_canvas.cc b/flow/display_list_canvas.cc index 72b8574d3c9f1..868a149ba22aa 100644 --- a/flow/display_list_canvas.cc +++ b/flow/display_list_canvas.cc @@ -261,7 +261,7 @@ void DisplayListCanvasRecorder::willSave() { SkCanvas::SaveLayerStrategy DisplayListCanvasRecorder::getSaveLayerStrategy( const SaveLayerRec& rec) { if (rec.fPaint) { - RecordPaintAttributes(rec.fPaint, DrawType::kSaveLayerOpType); + builder_->setAttributesFromPaint(*rec.fPaint, DisplayListAttributeFlags::kSaveLayerWithPaintFlags); builder_->saveLayer(rec.fBounds, true); } else { builder_->saveLayer(rec.fBounds, false); @@ -273,28 +273,28 @@ void DisplayListCanvasRecorder::didRestore() { } void DisplayListCanvasRecorder::onDrawPaint(const SkPaint& paint) { - RecordPaintAttributes(&paint, DrawType::kFillOpType); + builder_->setAttributesFromPaint(paint, DisplayListAttributeFlags::kDrawPaintFlags); builder_->drawPaint(); } void DisplayListCanvasRecorder::onDrawRect(const SkRect& rect, const SkPaint& paint) { - RecordPaintAttributes(&paint, DrawType::kDrawOpType); + builder_->setAttributesFromPaint(paint, DisplayListAttributeFlags::kDrawRectFlags); builder_->drawRect(rect); } void DisplayListCanvasRecorder::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) { - RecordPaintAttributes(&paint, DrawType::kDrawOpType); + builder_->setAttributesFromPaint(paint, DisplayListAttributeFlags::kDrawRRectFlags); builder_->drawRRect(rrect); } void DisplayListCanvasRecorder::onDrawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint) { - RecordPaintAttributes(&paint, DrawType::kDrawOpType); + builder_->setAttributesFromPaint(paint, DisplayListAttributeFlags::kDrawDRRectFlags); builder_->drawDRRect(outer, inner); } void DisplayListCanvasRecorder::onDrawOval(const SkRect& rect, const SkPaint& paint) { - RecordPaintAttributes(&paint, DrawType::kDrawOpType); + builder_->setAttributesFromPaint(paint, DisplayListAttributeFlags::kDrawOvalFlags); builder_->drawOval(rect); } void DisplayListCanvasRecorder::onDrawArc(const SkRect& rect, @@ -302,12 +302,12 @@ void DisplayListCanvasRecorder::onDrawArc(const SkRect& rect, SkScalar sweepAngle, bool useCenter, const SkPaint& paint) { - RecordPaintAttributes(&paint, DrawType::kDrawOpType); + builder_->setAttributesFromPaint(paint, DisplayListAttributeFlags::kDrawArcFlags); builder_->drawArc(rect, startAngle, sweepAngle, useCenter); } void DisplayListCanvasRecorder::onDrawPath(const SkPath& path, const SkPaint& paint) { - RecordPaintAttributes(&paint, DrawType::kDrawOpType); + builder_->setAttributesFromPaint(paint, DisplayListAttributeFlags::kDrawPathFlags); builder_->drawPath(path); } @@ -315,7 +315,17 @@ void DisplayListCanvasRecorder::onDrawPoints(SkCanvas::PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) { - RecordPaintAttributes(&paint, DrawType::kStrokeOpType); + switch (mode) { + case SkCanvas::kPoints_PointMode: + builder_->setAttributesFromPaint(paint, DisplayListAttributeFlags::kDrawPointsAsPointsFlags); + break; + case SkCanvas::kLines_PointMode: + builder_->setAttributesFromPaint(paint, DisplayListAttributeFlags::kDrawPointsAsLinesFlags); + break; + case SkCanvas::kPolygon_PointMode: + builder_->setAttributesFromPaint(paint, DisplayListAttributeFlags::kDrawPointsAsPolygonFlags); + break; + } if (mode == SkCanvas::PointMode::kLines_PointMode && count == 2) { builder_->drawLine(pts[0], pts[1]); } else { @@ -330,7 +340,7 @@ void DisplayListCanvasRecorder::onDrawPoints(SkCanvas::PointMode mode, void DisplayListCanvasRecorder::onDrawVerticesObject(const SkVertices* vertices, SkBlendMode mode, const SkPaint& paint) { - RecordPaintAttributes(&paint, DrawType::kDrawOpType); + builder_->setAttributesFromPaint(paint, DisplayListAttributeFlags::kDrawVerticesFlags); builder_->drawVertices(sk_ref_sp(vertices), mode); } @@ -340,7 +350,7 @@ void DisplayListCanvasRecorder::onDrawImage2(const SkImage* image, const SkSamplingOptions& sampling, const SkPaint* paint) { if (paint != nullptr) { - RecordPaintAttributes(paint, DrawType::kImageOpType); + builder_->setAttributesFromPaint(*paint, DisplayListAttributeFlags::kDrawImageWithPaintFlags); } builder_->drawImage(sk_ref_sp(image), SkPoint::Make(dx, dy), sampling, paint != nullptr); @@ -353,7 +363,7 @@ void DisplayListCanvasRecorder::onDrawImageRect2( const SkPaint* paint, SrcRectConstraint constraint) { if (paint != nullptr) { - RecordPaintAttributes(paint, DrawType::kImageRectOpType); + builder_->setAttributesFromPaint(*paint, DisplayListAttributeFlags::kDrawImageRectWithPaintFlags); } builder_->drawImageRect(sk_ref_sp(image), src, dst, sampling, paint != nullptr, constraint); @@ -370,7 +380,7 @@ void DisplayListCanvasRecorder::onDrawImageLattice2(const SkImage* image, if (*paint == default_paint) { paint = nullptr; } else { - RecordPaintAttributes(paint, DrawType::kImageOpType); + builder_->setAttributesFromPaint(*paint, DisplayListAttributeFlags::kDrawImageLatticeWithPaintFlags); } } builder_->drawImageLattice(sk_ref_sp(image), lattice, dst, filter, @@ -386,7 +396,7 @@ void DisplayListCanvasRecorder::onDrawAtlas2(const SkImage* image, const SkRect* cull, const SkPaint* paint) { if (paint != nullptr) { - RecordPaintAttributes(paint, DrawType::kImageOpType); + builder_->setAttributesFromPaint(*paint, DisplayListAttributeFlags::kDrawAtlasWithPaintFlags); } builder_->drawAtlas(sk_ref_sp(image), xform, src, colors, count, mode, sampling, cull, paint != nullptr); @@ -396,7 +406,7 @@ void DisplayListCanvasRecorder::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, const SkPaint& paint) { - RecordPaintAttributes(&paint, DrawType::kDrawOpType); + builder_->setAttributesFromPaint(paint, DisplayListAttributeFlags::kDrawTextBlobFlags); builder_->drawTextBlob(sk_ref_sp(blob), x, y); } void DisplayListCanvasRecorder::onDrawShadowRec(const SkPath& path, @@ -411,119 +421,9 @@ void DisplayListCanvasRecorder::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) { if (paint != nullptr) { - RecordPaintAttributes(paint, DrawType::kSaveLayerOpType); + builder_->setAttributesFromPaint(*paint, DisplayListAttributeFlags::kDrawPictureWithPaintFlags); } builder_->drawPicture(sk_ref_sp(picture), matrix, paint != nullptr); } -void DisplayListCanvasRecorder::RecordPaintAttributes(const SkPaint* paint, - DrawType type) { - int dataNeeded; - switch (type) { - case DrawType::kDrawOpType: - dataNeeded = kDrawMask_; - break; - case DrawType::kFillOpType: - dataNeeded = kPaintMask_; - break; - case DrawType::kStrokeOpType: - dataNeeded = kStrokeMask_; - break; - case DrawType::kImageOpType: - dataNeeded = kImageMask_; - break; - case DrawType::kImageRectOpType: - dataNeeded = kImageRectMask_; - break; - case DrawType::kSaveLayerOpType: - dataNeeded = kSaveLayerMask_; - break; - default: - FML_DCHECK(false); - return; - } - if (paint == nullptr) { - paint = new SkPaint(); - } - if ((dataNeeded & kAaNeeded_) != 0 && current_aa_ != paint->isAntiAlias()) { - builder_->setAntiAlias(current_aa_ = paint->isAntiAlias()); - } - if ((dataNeeded & kDitherNeeded_) != 0 && - current_dither_ != paint->isDither()) { - builder_->setDither(current_dither_ = paint->isDither()); - } - if ((dataNeeded & kColorNeeded_) != 0 && - current_color_ != paint->getColor()) { - builder_->setColor(current_color_ = paint->getColor()); - } - if ((dataNeeded & kBlendNeeded_)) { - skstd::optional mode_optional = paint->asBlendMode(); - if (mode_optional) { - SkBlendMode mode = mode_optional.value(); - if (current_blender_ || current_blend_ != mode) { - builder_->setBlendMode(current_blend_ = mode); - current_blender_ = nullptr; - } - } else { - if (current_blender_.get() != paint->getBlender()) { - builder_->setBlender(current_blender_ = sk_ref_sp(paint->getBlender())); - } - } - } - // invert colors is a Flutter::Paint thing, not an SkPaint thing - // if ((dataNeeded & invertColorsNeeded_) != 0 && - // currentInvertColors_ != paint->???) { - // currentInvertColors_ = paint->invertColors; - // addOp_(currentInvertColors_ - // ? _CanvasOp.setInvertColors - // : _CanvasOp.clearInvertColors, 0); - // } - if ((dataNeeded & kPaintStyleNeeded_) != 0) { - if (current_style_ != paint->getStyle()) { - builder_->setStyle(current_style_ = paint->getStyle()); - } - if (current_style_ == SkPaint::Style::kStroke_Style) { - dataNeeded |= kStrokeStyleNeeded_; - } - } - if ((dataNeeded & kStrokeStyleNeeded_) != 0) { - if (current_stroke_width_ != paint->getStrokeWidth()) { - builder_->setStrokeWidth(current_stroke_width_ = paint->getStrokeWidth()); - } - if (current_cap_ != paint->getStrokeCap()) { - builder_->setStrokeCap(current_cap_ = paint->getStrokeCap()); - } - if (current_join_ != paint->getStrokeJoin()) { - builder_->setStrokeJoin(current_join_ = paint->getStrokeJoin()); - } - if (current_miter_limit_ != paint->getStrokeMiter()) { - builder_->setStrokeMiter(current_miter_limit_ = paint->getStrokeMiter()); - } - } - if ((dataNeeded & kShaderNeeded_) != 0 && - current_shader_.get() != paint->getShader()) { - builder_->setShader(current_shader_ = sk_ref_sp(paint->getShader())); - } - if ((dataNeeded & kColorFilterNeeded_) != 0 && - current_color_filter_.get() != paint->getColorFilter()) { - builder_->setColorFilter(current_color_filter_ = - sk_ref_sp(paint->getColorFilter())); - } - if ((dataNeeded & kImageFilterNeeded_) != 0 && - current_image_filter_.get() != paint->getImageFilter()) { - builder_->setImageFilter(current_image_filter_ = - sk_ref_sp(paint->getImageFilter())); - } - if ((dataNeeded & kPathEffectNeeded_) != 0 && - current_path_effect_.get() != paint->getPathEffect()) { - builder_->setPathEffect(current_path_effect_ = - sk_ref_sp(paint->getPathEffect())); - } - if ((dataNeeded & kMaskFilterNeeded_) != 0 && - current_mask_filter_.get() != paint->getMaskFilter()) { - builder_->setMaskFilter(current_mask_filter_ = - sk_ref_sp(paint->getMaskFilter())); - } -} - } // namespace flutter diff --git a/flow/display_list_canvas.h b/flow/display_list_canvas.h index a6ef6902f6b10..4551f650df484 100644 --- a/flow/display_list_canvas.h +++ b/flow/display_list_canvas.h @@ -49,9 +49,9 @@ class DisplayListCanvasDispatcher : public virtual Dispatcher, SkScalar mwx, SkScalar mwy, SkScalar mwz, SkScalar mwt) override; // clang-format on - void clipRect(const SkRect& rect, SkClipOp clip_op, bool isAA) override; - void clipRRect(const SkRRect& rrect, SkClipOp clip_op, bool isAA) override; - void clipPath(const SkPath& path, SkClipOp clip_op, bool isAA) override; + void clipRect(const SkRect& rect, SkClipOp clip_op, bool is_aa) override; + void clipRRect(const SkRRect& rrect, SkClipOp clip_op, bool is_aa) override; + void clipPath(const SkPath& path, SkClipOp clip_op, bool is_aa) override; void drawPaint() override; void drawColor(SkColor color, SkBlendMode mode) override; @@ -233,80 +233,8 @@ class DisplayListCanvasRecorder const SkMatrix* matrix, const SkPaint* paint) override; - enum class DrawType { - // The operation will be an image operation - kImageOpType, - // The operation will be an imageRect operation - kImageRectOpType, - // The operation will be a fill or stroke depending on the paint.style - kDrawOpType, - // The operation will be a fill (ignoring paint.style) - kFillOpType, - // The operation will be a stroke (ignoring paint.style) - kStrokeOpType, - // The operation will be a saveLayer with a paint object - kSaveLayerOpType, - }; - - void RecordPaintAttributes(const SkPaint* paint, DrawType type); - private: sk_sp builder_; - - // Mask bits for the various attributes that might be needed for a given - // operation. - // clang-format off - static constexpr int kAaNeeded_ = 1 << 0; - static constexpr int kColorNeeded_ = 1 << 1; - static constexpr int kBlendNeeded_ = 1 << 2; - static constexpr int kInvertColorsNeeded_ = 1 << 3; - static constexpr int kPaintStyleNeeded_ = 1 << 4; - static constexpr int kStrokeStyleNeeded_ = 1 << 5; - static constexpr int kShaderNeeded_ = 1 << 6; - static constexpr int kColorFilterNeeded_ = 1 << 7; - static constexpr int kImageFilterNeeded_ = 1 << 8; - static constexpr int kPathEffectNeeded_ = 1 << 9; - static constexpr int kMaskFilterNeeded_ = 1 << 10; - static constexpr int kDitherNeeded_ = 1 << 11; - // clang-format on - - // Combinations of the above mask bits that are common to typical "draw" - // calls. - // Note that the strokeStyle_ is handled conditionally depending on whether - // the paintStyle_ attribute value is synchronized. It can also be manually - // specified for operations that will be always stroking, like [drawLine]. - static constexpr int kPaintMask_ = kAaNeeded_ | kColorNeeded_ | - kBlendNeeded_ | kInvertColorsNeeded_ | - kColorFilterNeeded_ | kShaderNeeded_ | - kDitherNeeded_ | kImageFilterNeeded_; - static constexpr int kDrawMask_ = kPaintMask_ | kPaintStyleNeeded_ | - kMaskFilterNeeded_ | kPathEffectNeeded_; - static constexpr int kStrokeMask_ = kPaintMask_ | kStrokeStyleNeeded_ | - kMaskFilterNeeded_ | kPathEffectNeeded_; - static constexpr int kImageMask_ = kColorNeeded_ | kBlendNeeded_ | - kInvertColorsNeeded_ | - kColorFilterNeeded_ | kDitherNeeded_ | - kImageFilterNeeded_ | kMaskFilterNeeded_; - static constexpr int kImageRectMask_ = kImageMask_ | kAaNeeded_; - static constexpr int kSaveLayerMask_ = - kColorNeeded_ | kBlendNeeded_ | kInvertColorsNeeded_ | - kColorFilterNeeded_ | kImageFilterNeeded_; - - bool current_aa_ = false; - bool current_dither_ = false; - SkColor current_color_ = 0xFF000000; - SkBlendMode current_blend_ = SkBlendMode::kSrcOver; - SkPaint::Style current_style_ = SkPaint::Style::kFill_Style; - SkScalar current_stroke_width_ = 0.0; - SkScalar current_miter_limit_ = 4.0; - SkPaint::Cap current_cap_ = SkPaint::Cap::kButt_Cap; - SkPaint::Join current_join_ = SkPaint::Join::kMiter_Join; - sk_sp current_blender_; - sk_sp current_shader_; - sk_sp current_color_filter_; - sk_sp current_image_filter_; - sk_sp current_path_effect_; - sk_sp current_mask_filter_; }; } // namespace flutter diff --git a/flow/display_list_canvas_unittests.cc b/flow/display_list_canvas_unittests.cc index 9bb35ebd9c77d..ccc389dcd7ae5 100644 --- a/flow/display_list_canvas_unittests.cc +++ b/flow/display_list_canvas_unittests.cc @@ -70,11 +70,13 @@ constexpr SkScalar Miter4DiamondOffsetY = RenderWidth * 0.14; // Render 3 vertical and horizontal diamonds each // designed to break at the tested miter limits // 0.0, 4.0 and 10.0 -constexpr SkScalar x_off_0 = RenderCenterX; -constexpr SkScalar x_off_l1 = RenderCenterX - Miter4DiamondOffsetX; +// Center is biased by 0.5 to include more pixel centers in the +// thin miters +constexpr SkScalar x_off_0 = RenderCenterX + 0.5; +constexpr SkScalar x_off_l1 = x_off_0 - Miter4DiamondOffsetX; constexpr SkScalar x_off_l2 = x_off_l1 - Miter10DiamondOffsetX; constexpr SkScalar x_off_l3 = x_off_l2 - Miter10DiamondOffsetX; -constexpr SkScalar x_off_r1 = RenderCenterX + Miter4DiamondOffsetX; +constexpr SkScalar x_off_r1 = x_off_0 + Miter4DiamondOffsetX; constexpr SkScalar x_off_r2 = x_off_r1 + MiterExtremeDiamondOffsetX; constexpr SkScalar x_off_r3 = x_off_r2 + MiterExtremeDiamondOffsetX; constexpr SkPoint VerticalMiterDiamondPoints[] = { @@ -104,11 +106,11 @@ constexpr SkPoint VerticalMiterDiamondPoints[] = { const int VerticalMiterDiamondPointCount = sizeof(VerticalMiterDiamondPoints) / sizeof(VerticalMiterDiamondPoints[0]); -constexpr SkScalar y_off_0 = RenderCenterY; -constexpr SkScalar y_off_u1 = RenderCenterY - Miter4DiamondOffsetY; +constexpr SkScalar y_off_0 = RenderCenterY + 0.5; +constexpr SkScalar y_off_u1 = x_off_0 - Miter4DiamondOffsetY; constexpr SkScalar y_off_u2 = y_off_u1 - Miter10DiamondOffsetY; constexpr SkScalar y_off_u3 = y_off_u2 - Miter10DiamondOffsetY; -constexpr SkScalar y_off_d1 = RenderCenterY + Miter4DiamondOffsetY; +constexpr SkScalar y_off_d1 = x_off_0 + Miter4DiamondOffsetY; constexpr SkScalar y_off_d2 = y_off_d1 + MiterExtremeDiamondOffsetY; constexpr SkScalar y_off_d3 = y_off_d2 + MiterExtremeDiamondOffsetY; const SkPoint HorizontalMiterDiamondPoints[] = { @@ -153,75 +155,67 @@ const int HorizontalMiterDiamondPointCount = // avoid false bounds overflow notifications. class BoundsTolerance { public: - BoundsTolerance() : BoundsTolerance(0, 0, 1, 1, 0, 0, 0) {} - BoundsTolerance(SkScalar bounds_pad_x, - SkScalar bounds_pad_y, - SkScalar scale_x, - SkScalar scale_y, - SkScalar absolute_pad_x, - SkScalar absolute_pad_y, - SkScalar discrete_offset) - : bounds_pad_x_(bounds_pad_x), - bounds_pad_y_(bounds_pad_y), - scale_x_(scale_x), - scale_y_(scale_y), - absolute_pad_x_(absolute_pad_x), - absolute_pad_y_(absolute_pad_y), - discrete_offset_(discrete_offset) {} + BoundsTolerance() = default; + BoundsTolerance(const BoundsTolerance&) = default; BoundsTolerance addBoundsPadding(SkScalar bounds_pad_x, SkScalar bounds_pad_y) const { - return {bounds_pad_x_ + bounds_pad_x, - bounds_pad_y_ + bounds_pad_y, - scale_x_, - scale_y_, - absolute_pad_x_, - absolute_pad_y_, - discrete_offset_}; + BoundsTolerance copy = BoundsTolerance(*this); + copy.bounds_pad_.offset(bounds_pad_x, bounds_pad_y); + return copy; } - BoundsTolerance addScale(SkScalar scale_x, SkScalar scale_y) const { - return {bounds_pad_x_, // - bounds_pad_y_, // - scale_x_ * scale_x, // - scale_y_ * scale_y, // - absolute_pad_x_, // - absolute_pad_y_, // - discrete_offset_}; + BoundsTolerance mulScale(SkScalar scale_x, SkScalar scale_y) const { + BoundsTolerance copy = BoundsTolerance(*this); + copy.scale_.fX *= scale_x; + copy.scale_.fY *= scale_y; + return copy; } BoundsTolerance addAbsolutePadding(SkScalar absolute_pad_x, SkScalar absolute_pad_y) const { - return {bounds_pad_x_, - bounds_pad_y_, - scale_x_, - scale_y_, - absolute_pad_x_ + absolute_pad_x, - absolute_pad_y_ + absolute_pad_y, - discrete_offset_}; + BoundsTolerance copy = BoundsTolerance(*this); + copy.absolute_pad_.offset(absolute_pad_x, absolute_pad_y); + return copy; } BoundsTolerance addDiscreteOffset(SkScalar discrete_offset) const { - return {bounds_pad_x_, - bounds_pad_y_, - scale_x_, - scale_y_, - absolute_pad_x_, - absolute_pad_y_, - discrete_offset_ + discrete_offset}; + BoundsTolerance copy = BoundsTolerance(*this); + copy.discrete_offset_ += discrete_offset; + return copy; } - bool overflows(SkISize pix_size, + BoundsTolerance clip(SkRect clip) const { + BoundsTolerance copy = BoundsTolerance(*this); + if (!copy.clip_.intersect(clip)) { + copy.clip_.setEmpty(); + } + return copy; + } + + static SkRect Scale(const SkRect& rect, const SkPoint& scales) { + SkScalar outset_x = rect.width() * (scales.fX - 1); + SkScalar outset_y = rect.height() * (scales.fY - 1); + return rect.makeOutset(outset_x, outset_y); + } + + bool overflows(SkIRect pix_bounds, int worst_bounds_pad_x, int worst_bounds_pad_y) const { - int scaled_bounds_pad_x = - std::ceil((pix_size.width() + bounds_pad_x_) * scale_x_); - int allowed_width = scaled_bounds_pad_x + absolute_pad_x_; - int scaled_bounds_pad_y = - std::ceil((pix_size.height() + bounds_pad_y_) * scale_y_); - int allowed_height = scaled_bounds_pad_y + absolute_pad_y_; - int allowed_pad_x = allowed_width - pix_size.width(); - int allowed_pad_y = allowed_height - pix_size.height(); + SkRect allowed = SkRect::Make(pix_bounds); + allowed.outset(bounds_pad_.fX, bounds_pad_.fY); + allowed = Scale(allowed, scale_); + allowed.outset(absolute_pad_.fX, absolute_pad_.fY); + if (!allowed.intersect(clip_)) { + allowed.setEmpty(); + } + SkIRect rounded = allowed.roundOut(); + int padLeft = std::max(0, pix_bounds.fLeft - rounded.fLeft); + int padTop = std::max(0, pix_bounds.fTop - rounded.fTop); + int padRight = std::max(0, pix_bounds.fRight - rounded.fRight); + int padBottom = std::max(0, pix_bounds.fBottom - rounded.fBottom); + int allowed_pad_x = std::max(padLeft, padRight); + int allowed_pad_y = std::max(padTop, padBottom); if (worst_bounds_pad_x > allowed_pad_x || worst_bounds_pad_y > allowed_pad_y) { FML_LOG(ERROR) << "allowed pad: " // @@ -234,14 +228,202 @@ class BoundsTolerance { SkScalar discrete_offset() const { return discrete_offset_; } private: - SkScalar bounds_pad_x_; - SkScalar bounds_pad_y_; - SkScalar scale_x_; - SkScalar scale_y_; - SkScalar absolute_pad_x_; - SkScalar absolute_pad_y_; - - SkScalar discrete_offset_; + SkPoint bounds_pad_ = {0, 0}; + SkPoint scale_ = {1, 1}; + SkPoint absolute_pad_ = {0, 0}; + SkRect clip_ = {-1E9, -1E9, 1E9, 1E9}; + + SkScalar discrete_offset_ = 0; +}; + +enum class ReferencePixelExpectation { + kPixelsMatchReference, + kPixelsDoNotMatchReference, + kIgnoreReference, +}; + +typedef const std::function CvRenderer; +typedef const std::function DlRenderer; +static void EmptyCvRenderer(SkCanvas*, SkPaint&) {} +static void EmptyDlRenderer(DisplayListBuilder&) {} + +class TestParameters { + public: + TestParameters(const CvRenderer& cv_renderer, const DlRenderer& dl_renderer, + const DisplayListAttributeFlags& flags) + : cv_renderer_(cv_renderer), dl_renderer_(dl_renderer), flags_(flags) {} + + ReferencePixelExpectation under_transform() const { + return matches_if(flags_.is_flood()); + } + + ReferencePixelExpectation over_other_rendering() const { + return matches_if(flags_.is_flood()); + } + + ReferencePixelExpectation with_clip() const { + return matches_if(false); + } + + bool uses_paint() const { return !flags_.ignores_paint(); } + + ReferencePixelExpectation with_anti_alias(bool is_aa) const { + // For some reason the Ahem font does not produce AA results + return matches_if(!is_aa || !flags_.applies_anti_alias()); + } + + ReferencePixelExpectation with_dither(bool is_dither) const { + return matches_if(!is_dither || !flags_.applies_dither()); + } + + ReferencePixelExpectation with_color(SkColor color) const { + return matches_if(color == SK_ColorBLACK || !flags_.applies_color()); + } + + ReferencePixelExpectation with_blending(SkBlendMode mode) const { + return matches_if(mode == SkBlendMode::kSrcOver || !flags_.applies_blend()); + } + + ReferencePixelExpectation with_blending(sk_sp blender) const { + return matches_if(!blender || !flags_.applies_blend()); + } + + ReferencePixelExpectation with_shader(sk_sp shader) const { + return matches_if(!shader || !flags_.applies_shader()); + } + + ReferencePixelExpectation with_image_filter(sk_sp filter) const { + return matches_if(!filter || !flags_.applies_image_filter()); + } + + ReferencePixelExpectation with_color_filter(sk_sp filter) const { + return matches_if(!filter || !flags_.applies_color_filter()); + } + + ReferencePixelExpectation with_mask_filter(sk_sp filter) const { + return matches_if(!filter || !flags_.applies_mask_filter()); + } + + ReferencePixelExpectation with_path_effect(sk_sp effect) const { + return matches_if(!effect || !flags_.applies_path_effect()); + } + + ReferencePixelExpectation with_stroke_style(SkPaint::Style style) const { + return matches_if(flags_.is_stroked(style) == flags_.is_stroked(SkPaint::kFill_Style)); + } + + static ReferencePixelExpectation Or(ReferencePixelExpectation a, + ReferencePixelExpectation b) { + return matches_if(a == ReferencePixelExpectation::kPixelsMatchReference && + b == ReferencePixelExpectation::kPixelsMatchReference); + } + + const BoundsTolerance adjust(const BoundsTolerance& tolerance, + const SkPaint& paint, + const SkMatrix& matrix) const { + if (is_draw_text_blob_ && tolerance.discrete_offset() > 0) { + // drawTextBlob needs just a little more leeway when using a + // discrete path effect. + return tolerance.addBoundsPadding(2, 2); + } + // Shadow primitives could use just a little more horizontal bounds + // tolerance when drawn with a perspective transform. + // if (is_draw_shadows_ && matrix.hasPerspective()) { + // return tolerance.mulScale(1.04, 1.0); + // } + return tolerance; + } + + const CvRenderer& cv_renderer() const { return cv_renderer_; } + void render_to(SkCanvas* canvas, SkPaint& paint) const { + cv_renderer_(canvas, paint); + } + + const DlRenderer& dl_renderer() const { return dl_renderer_; } + void render_to(DisplayListBuilder& builder) const { + dl_renderer_(builder); + } + + bool is_draw_shadows() const { return is_draw_shadows_; } + bool is_draw_vertices() const { return is_draw_vertices_; } + bool is_draw_text_blob() const { return is_draw_text_blob_; } + bool is_draw_atlas() const { return is_draw_atlas_; } + bool is_draw_display_list() const { return is_draw_display_list_; } + + TestParameters& set_draw_shadows() { is_draw_shadows_ = true; return *this; } + TestParameters& set_draw_vertices() { is_draw_vertices_ = true; return *this; } + TestParameters& set_draw_text_blob() { is_draw_text_blob_ = true; return *this; } + TestParameters& set_draw_atlas() { is_draw_atlas_ = true; return *this; } + TestParameters& set_draw_display_list() { is_draw_display_list_ = true; return *this; } + + protected: + static ReferencePixelExpectation matches_if(bool should_match) { + return should_match // + ? ReferencePixelExpectation::kPixelsMatchReference + : ReferencePixelExpectation::kPixelsDoNotMatchReference; + } + + private: + const CvRenderer& cv_renderer_; + const DlRenderer& dl_renderer_; + const DisplayListAttributeFlags& flags_; + + bool is_draw_shadows_ = false; + bool is_draw_vertices_ = false; + bool is_draw_text_blob_ = false; + bool is_draw_atlas_ = false; + bool is_draw_display_list_ = false; +}; + +class CaseParameters { + public: + CaseParameters(std::string info) + : CaseParameters(info, EmptyCvRenderer, EmptyDlRenderer) {} + + CaseParameters(std::string info, + CvRenderer cv_setup, + DlRenderer dl_setup, + SkColor bg = SK_ColorTRANSPARENT) + : CaseParameters(info, cv_setup, dl_setup, EmptyCvRenderer, EmptyDlRenderer, bg) {} + + CaseParameters(std::string info, + CvRenderer cv_setup, + DlRenderer dl_setup, + CvRenderer cv_restore, + DlRenderer dl_restore, + SkColor bg = SK_ColorTRANSPARENT) + : info_(info), bg_(bg), + cv_setup_(cv_setup), dl_setup_(dl_setup), + cv_restore_(cv_restore), dl_restore_(dl_restore) {} + + std::string info() const { return info_; } + SkColor bg() const { return bg_; } + + CvRenderer cv_setup() const { return cv_setup_; } + DlRenderer dl_setup() const { return dl_setup_; } + CvRenderer cv_restore() const { return cv_restore_; } + DlRenderer dl_restore() const { return dl_restore_; } + + void render_to(SkCanvas* canvas, const TestParameters& testP) const { + SkPaint paint; + cv_setup_(canvas, paint); + testP.render_to(canvas, paint); + cv_restore_(canvas, paint); + } + + void render_to(DisplayListBuilder& builder, const TestParameters& testP) const { + dl_setup_(builder); + testP.render_to(builder); + dl_restore_(builder); + } + + private: + const std::string info_; + const SkColor bg_; + const CvRenderer cv_setup_; + const DlRenderer dl_setup_; + const CvRenderer cv_restore_; + const DlRenderer dl_restore_; }; class CanvasCompareTester { @@ -253,28 +435,103 @@ class CanvasCompareTester { // that use shadows, we can perform a lot of tests, but not the tests // that require SkCanvas->DisplayList transfers. // See: https://bugs.chromium.org/p/skia/issues/detail?id=12125 - static bool TestingDrawShadows; + // static bool TestingDrawShadows; // The CPU renders nothing for drawVertices with a Blender. // See: https://bugs.chromium.org/p/skia/issues/detail?id=12200 - static bool TestingDrawVertices; + // static bool TestingDrawVertices; // The CPU renders nothing for drawAtlas with a Blender. // See: https://bugs.chromium.org/p/skia/issues/detail?id=12199 - static bool TestingDrawAtlas; + // static bool TestingDrawAtlas; + // static bool TestingDrawTextBlob; + + static constexpr ReferencePixelExpectation kPixelsDoNotMatchReference = + ReferencePixelExpectation::kPixelsDoNotMatchReference; + static constexpr ReferencePixelExpectation kPixelsMatchReference = + ReferencePixelExpectation::kPixelsMatchReference; + static constexpr ReferencePixelExpectation kIgnoreReference = + ReferencePixelExpectation::kIgnoreReference; public: - typedef const std::function CvRenderer; - typedef const std::function DlRenderer; - typedef const std::function - ToleranceAdjuster; + // typedef const std::function + // ToleranceAdjuster; static BoundsTolerance DefaultTolerance; - static const BoundsTolerance DefaultAdjuster(const BoundsTolerance& tolerance, - const SkPaint& paint, - const SkMatrix& matrix) { - return tolerance; - } + + class RenderSurface { + public: + RenderSurface(sk_sp surface) : surface_(surface) {} + ~RenderSurface() { sk_free(addr_); } + + SkCanvas* canvas() { return surface_->getCanvas(); } + + const SkPixmap* pixmap() { + if (!pixmap_.addr()) { + SkImageInfo info = surface_->imageInfo(); + if (info.colorType() != kN32_SkColorType || + !surface_->peekPixels(&pixmap_)) { + info = SkImageInfo::MakeN32Premul(info.dimensions()); + addr_ = malloc(info.computeMinByteSize() * info.height()); + pixmap_.reset(info, addr_, info.minRowBytes()); + EXPECT_TRUE(surface_->readPixels(pixmap_, 0, 0)); + } + } + return &pixmap_; + } + + private: + sk_sp surface_; + SkPixmap pixmap_; + void *addr_ = nullptr; + }; + + class RenderEnvironment { + public: + static RenderEnvironment Make565() { + return RenderEnvironment(SkImageInfo::Make({1, 1}, + kRGB_565_SkColorType, + kOpaque_SkAlphaType, + nullptr)); + } + + static RenderEnvironment MakeN32() { + return RenderEnvironment(SkImageInfo::MakeN32Premul(1, 1)); + } + + RenderSurface MakeSurface(const SkColor bg = SK_ColorTRANSPARENT, // + int width = TestWidth, int height = TestHeight) const { + sk_sp surface = SkSurface::MakeRaster(info_.makeWH(width, height)); + surface->getCanvas()->clear(bg); + return RenderSurface(surface); + } + + void init_ref(CvRenderer& cv_renderer, SkColor bg = SK_ColorTRANSPARENT) { + init_ref([=](SkCanvas*, SkPaint&) {}, cv_renderer, bg); + } + + void init_ref(CvRenderer& cv_setup, + CvRenderer& cv_renderer, + SkColor bg = SK_ColorTRANSPARENT) { + ref_canvas()->clear(bg); + SkPaint paint; + cv_setup(ref_canvas(), paint); + cv_renderer(ref_canvas(), paint); + ref_pixmap_ = ref_surface_.pixmap(); + } + + SkCanvas* ref_canvas() { return ref_surface_.canvas(); } + const SkPixmap* ref_pixmap() const { return ref_pixmap_; } + + private: + RenderEnvironment(const SkImageInfo& info) + : info_(info), ref_surface_(MakeSurface()) {} + + const SkImageInfo info_; + + RenderSurface ref_surface_; + const SkPixmap* ref_pixmap_ = nullptr; + }; // All of the tests should eventually use this method except for the // tests that call |RenderNoAttributes| because they do not use the @@ -282,83 +539,96 @@ class CanvasCompareTester { // But there are a couple of conditions beyond our control which require // the use of one of the variant methods below (|RenderShadows|, // |RenderVertices|, |RenderAtlas|). - static void RenderAll(CvRenderer& cv_renderer, - DlRenderer& dl_renderer, - ToleranceAdjuster& adjuster = DefaultAdjuster, + static void RenderAll(const TestParameters& params, const BoundsTolerance& tolerance = DefaultTolerance) { - RenderNoAttributes(cv_renderer, dl_renderer, adjuster, tolerance); - RenderWithAttributes(cv_renderer, dl_renderer, adjuster, tolerance); + RenderEnvironment env = RenderEnvironment::MakeN32(); + env.init_ref(params.cv_renderer()); + RenderWithTransforms(params, env, tolerance); + RenderWithClips(params, env, tolerance); + RenderWithSaveRestore(params, env, tolerance); + // Only test all attributes if the canvas version uses the supplied paint object + if (params.uses_paint()) { + RenderWithAttributes(params, env, tolerance); + } } // Used by the tests that render shadows to deal with a condition where // we cannot recapture the shadow information from an SkCanvas stream // due to the DrawShadowRec used by Skia is not properly exported. // See: https://bugs.chromium.org/p/skia/issues/detail?id=12125 - static void RenderShadows( - CvRenderer& cv_renderer, - DlRenderer& dl_renderer, - ToleranceAdjuster& adjuster = DefaultAdjuster, - const BoundsTolerance& tolerance = DefaultTolerance) { - TestingDrawShadows = true; - RenderNoAttributes(cv_renderer, dl_renderer, adjuster, tolerance); - TestingDrawShadows = false; - } + // static void RenderShadows( + // CvRenderer& cv_renderer, + // DlRenderer& dl_renderer, + // ToleranceAdjuster& adjuster = DefaultAdjuster, + // const BoundsTolerance& tolerance = DefaultTolerance) { + // TestingDrawShadows = true; + // MutationPredictor predictor = + // MutationPredictor(DisplayListAttributeFlags::kDrawShadowFlags); + // RenderAll(cv_renderer, dl_renderer, predictor, adjuster, tolerance); + // TestingDrawShadows = false; + // } // Used by the tests that call drawVertices to avoid using an SkBlender // during testing because the CPU renderer appears not to render anything. // See: https://bugs.chromium.org/p/skia/issues/detail?id=12200 - static void RenderVertices(CvRenderer& cv_renderer, DlRenderer& dl_renderer) { - TestingDrawVertices = true; - RenderAll(cv_renderer, dl_renderer); - TestingDrawVertices = false; - } + // static void RenderVertices(CvRenderer& cv_renderer, DlRenderer& dl_renderer) { + // TestingDrawVertices = true; + // MutationPredictor predictor = MutationPredictor( + // DisplayListAttributeFlags::kDrawVerticesFlags); + // RenderAll(cv_renderer, dl_renderer, predictor); + // TestingDrawVertices = false; + // } // Used by the tests that call drawAtlas to avoid using an SkBlender // during testing because the CPU renderer appears not to render anything. // See: https://bugs.chromium.org/p/skia/issues/detail?id=12199 - static void RenderAtlas(CvRenderer& cv_renderer, DlRenderer& dl_renderer) { - TestingDrawAtlas = true; - RenderAll(cv_renderer, dl_renderer); - TestingDrawAtlas = false; - } - - // Used by the tests that call a draw method that does not take a paint - // call. Those tests could use |RenderAll| but there would be a lot of - // wasted test runs that prepare an SkPaint that is never used. - static void RenderNoAttributes( - CvRenderer& cv_renderer, - DlRenderer& dl_renderer, - ToleranceAdjuster& adjuster = DefaultAdjuster, - const BoundsTolerance& tolerance = DefaultTolerance) { - RenderWith([=](SkCanvas*, SkPaint& p) {}, // - [=](DisplayListBuilder& d) {}, // - cv_renderer, dl_renderer, adjuster, tolerance, "Base Test"); - RenderWithTransforms(cv_renderer, dl_renderer, adjuster, tolerance); - RenderWithClips(cv_renderer, dl_renderer, adjuster, tolerance); - RenderWithSaveRestore(cv_renderer, dl_renderer, adjuster, tolerance); - } - - static void RenderWithSaveRestore(CvRenderer& cv_renderer, - DlRenderer& dl_renderer, - ToleranceAdjuster& adjuster, + // static void RenderAtlas(CvRenderer& cv_renderer, DlRenderer& dl_renderer, bool with_paint) { + // TestingDrawAtlas = true; + // MutationPredictor predictor = with_paint + // ? MutationPredictor( + // DisplayListAttributeFlags::kDrawAtlasWithPaintFlags) + // : MutationPredictor( + // DisplayListAttributeFlags::kDrawAtlasFlags); + // RenderAll(cv_renderer, dl_renderer, predictor); + // TestingDrawAtlas = false; + // } + + // Used by the tests that call drawTextBlob so that we can skip the unique() + // tests on shared pointer attributes that get stored in an internal Skia + // cache. + // See: (TBD(flar) - file Skia bug) + // static void RenderTextBlob(CvRenderer& cv_renderer, DlRenderer& dl_renderer, + // ToleranceAdjuster& adjuster, + // const BoundsTolerance& tolerance) { + // TestingDrawTextBlob = true; + // MutationPredictor predictor = MutationPredictor( + // DisplayListAttributeFlags::kDrawTextBlobFlags); + // RenderAll(cv_renderer, dl_renderer, predictor, adjuster, tolerance); + // TestingDrawTextBlob = false; + // } + + static void RenderWithSaveRestore(const TestParameters& testP, + const RenderEnvironment& env, const BoundsTolerance& tolerance) { SkRect clip = SkRect::MakeLTRB(0, 0, 10, 10); SkRect rect = SkRect::MakeLTRB(5, 5, 15, 15); SkColor alpha_layer_color = SkColorSetARGB(0x7f, 0x00, 0xff, 0xff); SkColor default_color = SkPaint().getColor(); - CvRenderer cv_restored = [=](SkCanvas* cv, SkPaint& p) { + CvRenderer cv_restore = [=](SkCanvas* cv, SkPaint& p) { // Draw more than one primitive to disable peephole optimizations - cv->drawRect(RenderBounds.makeOutset(5, 5), p); - cv_renderer(cv, p); + cv->drawRect(RenderBounds.makeOffset(500, 500), p); + // params.cv_renderer()(cv, p); cv->restore(); }; - DlRenderer dl_restored = [=](DisplayListBuilder& b) { + DlRenderer dl_restore = [=](DisplayListBuilder& b) { // Draw more than one primitive to disable peephole optimizations - b.drawRect(RenderBounds.makeOutset(5, 5)); - dl_renderer(b); + b.drawRect(RenderBounds.makeOffset(500, 500)); + // params.dl_renderer()(b); b.restore(); }; - RenderWith( + SkRect layer_bounds = RenderBounds.makeInset(15, 15); + RenderWith(testP, env, CaseParameters( + "With prior save/clip/restore", [=](SkCanvas* cv, SkPaint& p) { cv->save(); cv->clipRect(clip, SkClipOp::kIntersect, false); @@ -370,217 +640,387 @@ class CanvasCompareTester { b.clipRect(clip, SkClipOp::kIntersect, false); b.drawRect(rect); b.restore(); - }, - cv_renderer, dl_renderer, adjuster, tolerance, - "With prior save/clip/restore"); - RenderWith( + }), + tolerance, testP.over_other_rendering()); + RenderWith(testP, env, CaseParameters( + "saveLayer no paint, no bounds", [=](SkCanvas* cv, SkPaint& p) { // cv->saveLayer(nullptr, nullptr); }, [=](DisplayListBuilder& b) { // b.saveLayer(nullptr, false); }, - cv_restored, dl_restored, adjuster, tolerance, - "saveLayer no paint, no bounds"); - RenderWith( - [=](SkCanvas* cv, SkPaint& p) { // - cv->saveLayer(RenderBounds, nullptr); - }, - [=](DisplayListBuilder& b) { // - b.saveLayer(&RenderBounds, false); - }, - cv_restored, dl_restored, adjuster, tolerance, - "saveLayer no paint, with bounds"); - RenderWith( - [=](SkCanvas* cv, SkPaint& p) { - SkPaint save_p; - save_p.setColor(alpha_layer_color); - cv->saveLayer(nullptr, &save_p); - }, - [=](DisplayListBuilder& b) { - b.setColor(alpha_layer_color); - b.saveLayer(nullptr, true); - b.setColor(default_color); - }, - cv_restored, dl_restored, adjuster, tolerance, - "saveLayer with alpha, no bounds"); - RenderWith( - [=](SkCanvas* cv, SkPaint& p) { - SkPaint save_p; - save_p.setColor(alpha_layer_color); - cv->saveLayer(RenderBounds, &save_p); - }, - [=](DisplayListBuilder& b) { - b.setColor(alpha_layer_color); - b.saveLayer(&RenderBounds, true); - b.setColor(default_color); - }, - cv_restored, dl_restored, adjuster, tolerance, - "saveLayer with alpha and bounds"); + cv_restore, dl_restore), + tolerance, kPixelsMatchReference); + RenderWith(testP, env, + CaseParameters( + "saveLayer no paint, with bounds", + [=](SkCanvas* cv, SkPaint& p) { // + cv->saveLayer(layer_bounds, nullptr); + }, + [=](DisplayListBuilder& b) { // + b.saveLayer(&layer_bounds, false); + }, + cv_restore, dl_restore + ), + tolerance, kPixelsDoNotMatchReference); + RenderWith(testP, env, + CaseParameters( + "saveLayer with alpha, no bounds", + [=](SkCanvas* cv, SkPaint& p) { + SkPaint save_p; + save_p.setColor(alpha_layer_color); + cv->saveLayer(nullptr, &save_p); + }, + [=](DisplayListBuilder& b) { + b.setColor(alpha_layer_color); + b.saveLayer(nullptr, true); + b.setColor(default_color); + }, + cv_restore, dl_restore + ), + tolerance, kPixelsDoNotMatchReference); + RenderWith(testP, env, + CaseParameters( + "saveLayer with alpha and bounds", + [=](SkCanvas* cv, SkPaint& p) { + SkPaint save_p; + save_p.setColor(alpha_layer_color); + cv->saveLayer(layer_bounds, &save_p); + }, + [=](DisplayListBuilder& b) { + b.setColor(alpha_layer_color); + b.saveLayer(&layer_bounds, true); + b.setColor(default_color); + }, + cv_restore, dl_restore + ), + tolerance, kPixelsDoNotMatchReference); + { + // clang-format off + constexpr float rotate_alpha_color_matrix[20] = { + 0, 1, 0, 0 , 0, + 0, 0, 1, 0 , 0, + 1, 0, 0, 0 , 0, + 0, 0, 0, 0.5, 0, + }; + // clang-format on + sk_sp filter = SkColorFilters::Matrix(rotate_alpha_color_matrix); + { + RenderWith(testP, env, + CaseParameters( + "saveLayer ColorFilter, no bounds", + [=](SkCanvas* cv, SkPaint& p) { + SkPaint save_p; + save_p.setColorFilter(filter); + cv->saveLayer(nullptr, &save_p); + p.setStrokeWidth(5.0); + }, + [=](DisplayListBuilder& b) { + b.setColorFilter(filter); + b.saveLayer(nullptr, true); + b.setColorFilter(nullptr); + b.setStrokeWidth(5.0); + }, + cv_restore, dl_restore + ), + tolerance, kPixelsDoNotMatchReference); + } + EXPECT_TRUE(filter->unique()) + << "saveLayer ColorFilter, no bounds Cleanup"; + { + RenderWith(testP, env, + CaseParameters( + "saveLayer ColorFilter and bounds", + [=](SkCanvas* cv, SkPaint& p) { + SkPaint save_p; + save_p.setColorFilter(filter); + cv->saveLayer(RenderBounds, &save_p); + p.setStrokeWidth(5.0); + }, + [=](DisplayListBuilder& b) { + b.setColorFilter(filter); + b.saveLayer(&RenderBounds, true); + b.setColorFilter(nullptr); + b.setStrokeWidth(5.0); + }, + cv_restore, dl_restore + ), + tolerance, kPixelsDoNotMatchReference); + } + EXPECT_TRUE(filter->unique()) + << "saveLayer ColorFilter and bounds Cleanup"; + } { sk_sp filter = - SkImageFilters::Blur(5.0, 5.0, SkTileMode::kDecal, nullptr, nullptr); - BoundsTolerance blur5Tolerance = tolerance.addBoundsPadding(4, 4); + SkImageFilters::Arithmetic(0.1, 0.1, 0.1, 0.25, true, nullptr, nullptr); { - RenderWith( - [=](SkCanvas* cv, SkPaint& p) { - SkPaint save_p; - save_p.setImageFilter(filter); - cv->saveLayer(nullptr, &save_p); - p.setStrokeWidth(5.0); - }, - [=](DisplayListBuilder& b) { - b.setImageFilter(filter); - b.saveLayer(nullptr, true); - b.setImageFilter(nullptr); - b.setStrokeWidth(5.0); - }, - cv_restored, dl_restored, adjuster, blur5Tolerance, - "saveLayer ImageFilter, no bounds"); + RenderWith(testP, env, + CaseParameters( + "saveLayer ImageFilter, no bounds", + [=](SkCanvas* cv, SkPaint& p) { + SkPaint save_p; + save_p.setImageFilter(filter); + cv->saveLayer(nullptr, &save_p); + p.setStrokeWidth(5.0); + }, + [=](DisplayListBuilder& b) { + b.setImageFilter(filter); + b.saveLayer(nullptr, true); + b.setImageFilter(nullptr); + b.setStrokeWidth(5.0); + }, + cv_restore, dl_restore + ), + tolerance, kPixelsDoNotMatchReference); } - ASSERT_TRUE(filter->unique()) + EXPECT_TRUE(filter->unique()) << "saveLayer ImageFilter, no bounds Cleanup"; { - RenderWith( - [=](SkCanvas* cv, SkPaint& p) { - SkPaint save_p; - save_p.setImageFilter(filter); - cv->saveLayer(RenderBounds, &save_p); - p.setStrokeWidth(5.0); - }, - [=](DisplayListBuilder& b) { - b.setImageFilter(filter); - b.saveLayer(&RenderBounds, true); - b.setImageFilter(nullptr); - b.setStrokeWidth(5.0); - }, - cv_restored, dl_restored, adjuster, blur5Tolerance, - "saveLayer ImageFilter and bounds"); + RenderWith(testP, env, + CaseParameters( + "saveLayer ImageFilter and bounds", + [=](SkCanvas* cv, SkPaint& p) { + SkPaint save_p; + save_p.setImageFilter(filter); + cv->saveLayer(RenderBounds, &save_p); + p.setStrokeWidth(5.0); + }, + [=](DisplayListBuilder& b) { + b.setImageFilter(filter); + b.saveLayer(&RenderBounds, true); + b.setImageFilter(nullptr); + b.setStrokeWidth(5.0); + }, + cv_restore, dl_restore + ), + tolerance, kPixelsDoNotMatchReference); } - ASSERT_TRUE(filter->unique()) + EXPECT_TRUE(filter->unique()) << "saveLayer ImageFilter and bounds Cleanup"; } } - static void RenderWithAttributes(CvRenderer& cv_renderer, - DlRenderer& dl_renderer, - ToleranceAdjuster& adjuster, + static void RenderWithAttributes(const TestParameters& testP, + const RenderEnvironment& env, const BoundsTolerance& tolerance) { - RenderWith([=](SkCanvas*, SkPaint& p) {}, // - [=](DisplayListBuilder& d) {}, // - cv_renderer, dl_renderer, adjuster, tolerance, "Base Test"); - - RenderWith([=](SkCanvas*, SkPaint& p) { p.setAntiAlias(true); }, // - [=](DisplayListBuilder& b) { b.setAntiAlias(true); }, // - cv_renderer, dl_renderer, adjuster, tolerance, - "AntiAlias == True"); - RenderWith([=](SkCanvas*, SkPaint& p) { p.setAntiAlias(false); }, // - [=](DisplayListBuilder& b) { b.setAntiAlias(false); }, // - cv_renderer, dl_renderer, adjuster, tolerance, - "AntiAlias == False"); - - RenderWith([=](SkCanvas*, SkPaint& p) { p.setDither(true); }, // - [=](DisplayListBuilder& b) { b.setDither(true); }, // - cv_renderer, dl_renderer, adjuster, tolerance, "Dither == True"); - RenderWith([=](SkCanvas*, SkPaint& p) { p.setDither(false); }, // - [=](DisplayListBuilder& b) { b.setDither(false); }, // - cv_renderer, dl_renderer, adjuster, tolerance, "Dither = False"); - - RenderWith([=](SkCanvas*, SkPaint& p) { p.setColor(SK_ColorBLUE); }, // - [=](DisplayListBuilder& b) { b.setColor(SK_ColorBLUE); }, // - cv_renderer, dl_renderer, adjuster, tolerance, "Color == Blue"); - RenderWith([=](SkCanvas*, SkPaint& p) { p.setColor(SK_ColorGREEN); }, // - [=](DisplayListBuilder& b) { b.setColor(SK_ColorGREEN); }, // - cv_renderer, dl_renderer, adjuster, tolerance, "Color == Green"); - - RenderWithStrokes(cv_renderer, dl_renderer, adjuster, tolerance); + RenderWith(testP, env, CaseParameters("Defaults Test"), + tolerance, kPixelsMatchReference); + + { + // CPU renderer with default line width of 0 does not show antialiasing + // for stroked primitives, so we make a new reference with a non-trivial + // stroke width to demonstrate the differences + RenderEnvironment aa_env = RenderEnvironment::MakeN32(); + // Tweak the bounds tolerance for the displacement of 1/10 of a pixel + const BoundsTolerance aa_tolerance = tolerance.addBoundsPadding(1, 1); + CvRenderer cv_aa_setup = [=](SkCanvas* cv, SkPaint& p) { + cv->translate(0.1, 0.1); + p.setStrokeWidth(5.0); + }; + DlRenderer dl_aa_setup = [=](DisplayListBuilder& b) { + b.translate(0.1, 0.1); + b.setStrokeWidth(5.0); + }; + aa_env.init_ref(cv_aa_setup, testP.cv_renderer()); + RenderWith(testP, aa_env, + CaseParameters( + "AntiAlias == True", + [=](SkCanvas* cv, SkPaint& p) { + cv_aa_setup(cv, p); + p.setAntiAlias(true); + }, + [=](DisplayListBuilder& b) { + dl_aa_setup(b); + b.setAntiAlias(true); + } + ), + aa_tolerance, testP.with_anti_alias(true)); + RenderWith(testP, aa_env, + CaseParameters( + "AntiAlias == False", + [=](SkCanvas* cv, SkPaint& p) { + cv_aa_setup(cv, p); + p.setAntiAlias(false); + }, + [=](DisplayListBuilder& b) { + dl_aa_setup(b); + b.setAntiAlias(false); + } + ), + aa_tolerance, testP.with_anti_alias(false)); + } + + { + // The CPU renderer does not always dither for solid colors and we + // need to use a non-default color (default is black) on an opaque + // surface, so we use a shader instead of a color. Also, thin stroked + // primitives (mainly drawLine and drawPoints) do not show much + // dithering so we use a non-trivial stroke width as well. + RenderEnvironment dither_env = RenderEnvironment::Make565(); + SkColor dither_bg = SK_ColorBLACK; + CvRenderer cv_dither_setup = [=](SkCanvas*, SkPaint& p) { + p.setShader(testImageShader); + p.setAlpha(0xf0); + p.setStrokeWidth(5.0); + }; + DlRenderer dl_dither_setup = [=](DisplayListBuilder& b) { + b.setShader(testImageShader); + b.setColor(SkColor(0xf0000000)); + b.setStrokeWidth(5.0); + }; + dither_env.init_ref(cv_dither_setup, testP.cv_renderer(), dither_bg); + RenderWith(testP, dither_env, + CaseParameters( + "Dither == True", + [=](SkCanvas* cv, SkPaint& p) { + cv_dither_setup(cv, p); + p.setDither(true); + }, + [=](DisplayListBuilder& b) { + dl_dither_setup(b); + b.setDither(true); + }, + dither_bg), + tolerance, testP.with_dither(true)); + RenderWith(testP, dither_env, + CaseParameters( + "Dither = False", + [=](SkCanvas* cv, SkPaint& p) { + cv_dither_setup(cv, p); + p.setDither(false); + }, + [=](DisplayListBuilder& b) { + dl_dither_setup(b); + b.setDither(false); + }, + dither_bg), + tolerance, kPixelsMatchReference); + EXPECT_TRUE(testImageShader->unique()) << "Dither Cleanup"; + } + + RenderWith(testP, env, + CaseParameters( + "Color == Blue", + [=](SkCanvas*, SkPaint& p) { p.setColor(SK_ColorBLUE); }, + [=](DisplayListBuilder& b) { b.setColor(SK_ColorBLUE); } + ), tolerance, testP.with_color(SK_ColorBLUE)); + RenderWith(testP, env, + CaseParameters( + "Color == Green", + [=](SkCanvas*, SkPaint& p) { p.setColor(SK_ColorGREEN); }, + [=](DisplayListBuilder& b) { b.setColor(SK_ColorGREEN); } + ), tolerance, testP.with_color(SK_ColorGREEN)); + + RenderWithStrokes(testP, env, tolerance); { // half opaque cyan SkColor blendableColor = SkColorSetARGB(0x7f, 0x00, 0xff, 0xff); SkColor bg = SK_ColorWHITE; - RenderWith( - [=](SkCanvas*, SkPaint& p) { - p.setBlendMode(SkBlendMode::kSrcIn); - p.setColor(blendableColor); - }, - [=](DisplayListBuilder& b) { - b.setBlendMode(SkBlendMode::kSrcIn); - b.setColor(blendableColor); - }, - cv_renderer, dl_renderer, adjuster, tolerance, "Blend == SrcIn", &bg); - RenderWith( - [=](SkCanvas*, SkPaint& p) { - p.setBlendMode(SkBlendMode::kDstIn); - p.setColor(blendableColor); - }, - [=](DisplayListBuilder& b) { - b.setBlendMode(SkBlendMode::kDstIn); - b.setColor(blendableColor); - }, - cv_renderer, dl_renderer, adjuster, tolerance, "Blend == DstIn", &bg); + RenderWith(testP, env, + CaseParameters( + "Blend == SrcIn", + [=](SkCanvas*, SkPaint& p) { + p.setBlendMode(SkBlendMode::kSrcIn); + p.setColor(blendableColor); + }, + [=](DisplayListBuilder& b) { + b.setBlendMode(SkBlendMode::kSrcIn); + b.setColor(blendableColor); + }, + bg + ), tolerance, testP.with_blending(SkBlendMode::kSrcIn)); + RenderWith(testP, env, + CaseParameters( + "Blend == DstIn", + [=](SkCanvas*, SkPaint& p) { + p.setBlendMode(SkBlendMode::kDstIn); + p.setColor(blendableColor); + }, + [=](DisplayListBuilder& b) { + b.setBlendMode(SkBlendMode::kDstIn); + b.setColor(blendableColor); + }, + bg + ), tolerance, testP.with_blending(SkBlendMode::kDstIn)); } - if (!(TestingDrawAtlas || TestingDrawVertices)) { + if (!(testP.is_draw_atlas() || testP.is_draw_vertices())) { sk_sp blender = SkBlenders::Arithmetic(0.25, 0.25, 0.25, 0.25, false); { - RenderWith([=](SkCanvas*, SkPaint& p) { p.setBlender(blender); }, - [=](DisplayListBuilder& b) { b.setBlender(blender); }, - cv_renderer, dl_renderer, adjuster, tolerance, - "ImageFilter == Blender Arithmetic 0.25-false"); + RenderWith(testP, env, + CaseParameters( + "ImageFilter == Blender Arithmetic 0.25-false", + [=](SkCanvas*, SkPaint& p) { p.setBlender(blender); }, + [=](DisplayListBuilder& b) { b.setBlender(blender); } + ), tolerance, testP.with_blending(blender)); } - ASSERT_TRUE(blender->unique()) << "Blender Cleanup"; + EXPECT_TRUE(blender->unique()) << "Blender Cleanup"; blender = SkBlenders::Arithmetic(0.25, 0.25, 0.25, 0.25, true); { - RenderWith([=](SkCanvas*, SkPaint& p) { p.setBlender(blender); }, - [=](DisplayListBuilder& b) { b.setBlender(blender); }, - cv_renderer, dl_renderer, adjuster, tolerance, - "ImageFilter == Blender Arithmetic 0.25-true"); + RenderWith(testP, env, + CaseParameters( + "ImageFilter == Blender Arithmetic 0.25-true", + [=](SkCanvas*, SkPaint& p) { p.setBlender(blender); }, + [=](DisplayListBuilder& b) { b.setBlender(blender); } + ), tolerance, testP.with_blending(blender)); } - ASSERT_TRUE(blender->unique()) << "Blender Cleanup"; + EXPECT_TRUE(blender->unique()) << "Blender Cleanup"; } { + // Being able to see a blur requires some non-default attributes, + // like a non-trivial stroke width and a shader rather than a color + // (for drawPaint) so we create a new environment for these tests. + RenderEnvironment blur_env = RenderEnvironment::MakeN32(); + CvRenderer cv_blur_setup = [=](SkCanvas*, SkPaint& p) { + p.setShader(testImageShader); + p.setStrokeWidth(5.0); + }; + DlRenderer dl_blur_setup = [=](DisplayListBuilder& b) { + b.setShader(testImageShader); + b.setStrokeWidth(5.0); + }; + blur_env.init_ref(cv_blur_setup, testP.cv_renderer()); sk_sp filter = SkImageFilters::Blur(5.0, 5.0, SkTileMode::kDecal, nullptr, nullptr); BoundsTolerance blur5Tolerance = tolerance.addBoundsPadding(4, 4); { - RenderWith( - [=](SkCanvas*, SkPaint& p) { - // Provide some non-trivial stroke size to get blurred - p.setStrokeWidth(5.0); - p.setImageFilter(filter); - }, - [=](DisplayListBuilder& b) { - // Provide some non-trivial stroke size to get blurred - b.setStrokeWidth(5.0); - b.setImageFilter(filter); - }, - cv_renderer, dl_renderer, adjuster, blur5Tolerance, - "ImageFilter == Decal Blur 5"); + RenderWith(testP, blur_env, + CaseParameters( + "ImageFilter == Decal Blur 5", + [=](SkCanvas* cv, SkPaint& p) { + cv_blur_setup(cv, p); + p.setImageFilter(filter); + }, + [=](DisplayListBuilder& b) { + dl_blur_setup(b); + b.setImageFilter(filter); + } + ), blur5Tolerance, testP.with_image_filter(filter)); } - ASSERT_TRUE(filter->unique()) << "ImageFilter Cleanup"; + EXPECT_TRUE(filter->unique()) << "ImageFilter Cleanup"; filter = SkImageFilters::Blur(5.0, 5.0, SkTileMode::kClamp, nullptr, nullptr); { - RenderWith( - [=](SkCanvas*, SkPaint& p) { - // Provide some non-trivial stroke size to get blurred - p.setStrokeWidth(5.0); - p.setImageFilter(filter); - }, - [=](DisplayListBuilder& b) { - // Provide some non-trivial stroke size to get blurred - b.setStrokeWidth(5.0); - b.setImageFilter(filter); - }, - cv_renderer, dl_renderer, adjuster, blur5Tolerance, - "ImageFilter == Clamp Blur 5"); + RenderWith(testP, blur_env, + CaseParameters( + "ImageFilter == Clamp Blur 5", + [=](SkCanvas* cv, SkPaint& p) { + cv_blur_setup(cv, p); + p.setImageFilter(filter); + }, + [=](DisplayListBuilder& b) { + dl_blur_setup(b); + b.setImageFilter(filter); + } + ), blur5Tolerance, testP.with_image_filter(filter)); } - ASSERT_TRUE(filter->unique()) << "ImageFilter Cleanup"; + EXPECT_TRUE(filter->unique()) << "ImageFilter Cleanup"; } { @@ -601,35 +1041,37 @@ class CanvasCompareTester { sk_sp filter = SkColorFilters::Matrix(rotate_color_matrix); { SkColor bg = SK_ColorWHITE; - RenderWith( - [=](SkCanvas*, SkPaint& p) { - p.setColor(SK_ColorYELLOW); - p.setColorFilter(filter); - }, - [=](DisplayListBuilder& b) { - b.setColor(SK_ColorYELLOW); - b.setColorFilter(filter); - }, - cv_renderer, dl_renderer, adjuster, tolerance, - "ColorFilter == RotateRGB", &bg); + RenderWith(testP, env, + CaseParameters( + "ColorFilter == RotateRGB", + [=](SkCanvas*, SkPaint& p) { + p.setColor(SK_ColorYELLOW); + p.setColorFilter(filter); + }, + [=](DisplayListBuilder& b) { + b.setColor(SK_ColorYELLOW); + b.setColorFilter(filter); + }, bg + ), tolerance, testP.with_color_filter(filter)); } - ASSERT_TRUE(filter->unique()) << "ColorFilter == RotateRGB Cleanup"; + EXPECT_TRUE(filter->unique()) << "ColorFilter == RotateRGB Cleanup"; filter = SkColorFilters::Matrix(invert_color_matrix); { SkColor bg = SK_ColorWHITE; - RenderWith( - [=](SkCanvas*, SkPaint& p) { - p.setColor(SK_ColorYELLOW); - p.setColorFilter(filter); - }, - [=](DisplayListBuilder& b) { - b.setColor(SK_ColorYELLOW); - b.setInvertColors(true); - }, - cv_renderer, dl_renderer, adjuster, tolerance, - "ColorFilter == Invert", &bg); + RenderWith(testP, env, + CaseParameters( + "ColorFilter == Invert", + [=](SkCanvas*, SkPaint& p) { + p.setColor(SK_ColorYELLOW); + p.setColorFilter(filter); + }, + [=](DisplayListBuilder& b) { + b.setColor(SK_ColorYELLOW); + b.setInvertColors(true); + }, bg + ), tolerance, testP.with_color_filter(filter)); } - ASSERT_TRUE(filter->unique()) << "ColorFilter == Invert Cleanup"; + EXPECT_TRUE(filter->unique()) << "ColorFilter == Invert Cleanup"; } { @@ -637,58 +1079,60 @@ class CanvasCompareTester { { // Discrete path effects need a stroke width for drawPointsAsPoints // to do something realistic - RenderWith( - [=](SkCanvas*, SkPaint& p) { - p.setStrokeWidth(5.0); - // A Discrete(3, 5) effect produces miters that are near - // maximal for a miter limit of 3.0. - p.setStrokeMiter(3.0); - p.setPathEffect(effect); - }, - [=](DisplayListBuilder& b) { - b.setStrokeWidth(5.0); - // A Discrete(3, 5) effect produces miters that are near - // maximal for a miter limit of 3.0. - b.setStrokeMiter(3.0); - b.setPathEffect(effect); - }, - cv_renderer, dl_renderer, adjuster, - tolerance - // register the discrete offset so adjusters can compensate - .addDiscreteOffset(5) - // the miters in the 3-5 discrete effect don't always fill - // their conservative bounds, so tolerate a couple of pixels - .addBoundsPadding(2, 2), - "PathEffect == Discrete-3-5"); + // And a Discrete(3, 5) effect produces miters that are near + // maximal for a miter limit of 3.0. + RenderWith(testP, env, + CaseParameters( + "PathEffect == Discrete-3-5", + [=](SkCanvas*, SkPaint& p) { + p.setStrokeWidth(5.0); + p.setStrokeMiter(3.0); + p.setPathEffect(effect); + }, + [=](DisplayListBuilder& b) { + b.setStrokeWidth(5.0); + b.setStrokeMiter(3.0); + b.setPathEffect(effect); + } + ), tolerance + // register the discrete offset so adjusters can compensate + .addDiscreteOffset(5) + // the miters in the 3-5 discrete effect don't always fill + // their conservative bounds, so tolerate a couple of pixels + .addBoundsPadding(2, 2), + testP.with_path_effect(effect)); } - ASSERT_TRUE(effect->unique()) << "PathEffect == Discrete-3-5 Cleanup"; + EXPECT_TRUE(testP.is_draw_text_blob() || effect->unique()) + << "PathEffect == Discrete-3-5 Cleanup"; effect = SkDiscretePathEffect::Make(2, 3); { - RenderWith( - [=](SkCanvas*, SkPaint& p) { - p.setStrokeWidth(5.0); - // A Discrete(2, 3) effect produces miters that are near - // maximal for a miter limit of 2.5. - p.setStrokeMiter(2.5); - p.setPathEffect(effect); - }, - [=](DisplayListBuilder& b) { - b.setStrokeWidth(5.0); - // A Discrete(2, 3) effect produces miters that are near - // maximal for a miter limit of 2.5. - b.setStrokeMiter(2.5); - b.setPathEffect(effect); - }, - cv_renderer, dl_renderer, adjuster, - tolerance - // register the discrete offset so adjusters can compensate - .addDiscreteOffset(3) - // the miters in the 3-5 discrete effect don't always fill - // their conservative bounds, so tolerate a couple of pixels - .addBoundsPadding(2, 2), - "PathEffect == Discrete-2-3"); + // Discrete path effects need a stroke width for drawPointsAsPoints + // to do something realistic + // And a Discrete(2, 3) effect produces miters that are near + // maximal for a miter limit of 2.5. + RenderWith(testP, env, + CaseParameters( + "PathEffect == Discrete-2-3", + [=](SkCanvas*, SkPaint& p) { + p.setStrokeWidth(5.0); + p.setStrokeMiter(2.5); + p.setPathEffect(effect); + }, + [=](DisplayListBuilder& b) { + b.setStrokeWidth(5.0); + b.setStrokeMiter(2.5); + b.setPathEffect(effect); + } + ), tolerance + // register the discrete offset so adjusters can compensate + .addDiscreteOffset(3) + // the miters in the 3-5 discrete effect don't always fill + // their conservative bounds, so tolerate a couple of pixels + .addBoundsPadding(2, 2), + testP.with_path_effect(effect)); } - ASSERT_TRUE(effect->unique()) << "PathEffect == Discrete-2-3 Cleanup"; + EXPECT_TRUE(testP.is_draw_text_blob() || effect->unique()) + << "PathEffect == Discrete-2-3 Cleanup"; } { @@ -696,37 +1140,39 @@ class CanvasCompareTester { SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, 5.0); BoundsTolerance blur5Tolerance = tolerance.addBoundsPadding(4, 4); { - RenderWith( - [=](SkCanvas*, SkPaint& p) { - // Provide some non-trivial stroke size to get blurred - p.setStrokeWidth(5.0); - p.setMaskFilter(filter); - }, - [=](DisplayListBuilder& b) { - // Provide some non-trivial stroke size to get blurred - b.setStrokeWidth(5.0); - b.setMaskFilter(filter); - }, - cv_renderer, dl_renderer, adjuster, blur5Tolerance, - "MaskFilter == Blur 5"); + // Stroked primitives require some non-trivial stroke size to get blurred + RenderWith(testP, env, + CaseParameters( + "MaskFilter == Blur 5", + [=](SkCanvas*, SkPaint& p) { + p.setStrokeWidth(5.0); + p.setMaskFilter(filter); + }, + [=](DisplayListBuilder& b) { + b.setStrokeWidth(5.0); + b.setMaskFilter(filter); + } + ), blur5Tolerance, testP.with_mask_filter(filter)); } - ASSERT_TRUE(filter->unique()) << "MaskFilter == Blur 5 Cleanup"; + EXPECT_TRUE(testP.is_draw_text_blob() || filter->unique()) + << "MaskFilter == Blur 5 Cleanup"; { - RenderWith( - [=](SkCanvas*, SkPaint& p) { - // Provide some non-trivial stroke size to get blurred - p.setStrokeWidth(5.0); - p.setMaskFilter(filter); - }, - [=](DisplayListBuilder& b) { - // Provide some non-trivial stroke size to get blurred - b.setStrokeWidth(5.0); - b.setMaskBlurFilter(kNormal_SkBlurStyle, 5.0); - }, - cv_renderer, dl_renderer, adjuster, blur5Tolerance, - "MaskFilter == Blur(Normal, 5.0)"); + RenderWith(testP, env, + CaseParameters( + "MaskFilter == Blur(Normal, 5.0)", + [=](SkCanvas*, SkPaint& p) { + // Provide some non-trivial stroke size to get blurred + p.setStrokeWidth(5.0); + p.setMaskFilter(filter); + }, + [=](DisplayListBuilder& b) { + // Provide some non-trivial stroke size to get blurred + b.setStrokeWidth(5.0); + b.setMaskBlurFilter(kNormal_SkBlurStyle, 5.0); + } + ), blur5Tolerance, testP.with_mask_filter(filter)); } - ASSERT_TRUE(filter->unique()) + EXPECT_TRUE(testP.is_draw_text_blob() || filter->unique()) << "MaskFilter == Blur(Normal, 5.0) Cleanup"; } @@ -737,7 +1183,7 @@ class CanvasCompareTester { }; SkColor colors[] = { SK_ColorGREEN, - SK_ColorYELLOW, + SkColorSetA(SK_ColorYELLOW, 0x7f), SK_ColorBLUE, }; float stops[] = { @@ -748,241 +1194,271 @@ class CanvasCompareTester { sk_sp shader = SkGradientShader::MakeLinear( end_points, colors, stops, 3, SkTileMode::kMirror, 0, nullptr); { - RenderWith([=](SkCanvas*, SkPaint& p) { p.setShader(shader); }, - [=](DisplayListBuilder& b) { b.setShader(shader); }, - cv_renderer, dl_renderer, adjuster, tolerance, - "LinearGradient GYB"); + RenderWith(testP, env, + CaseParameters( + "LinearGradient GYB", + [=](SkCanvas*, SkPaint& p) { p.setShader(shader); }, + [=](DisplayListBuilder& b) { b.setShader(shader); } + ), tolerance, testP.with_shader(shader)); } - ASSERT_TRUE(shader->unique()) << "LinearGradient GYB Cleanup"; + EXPECT_TRUE(shader->unique()) << "LinearGradient GYB Cleanup"; } } - static void RenderWithStrokes(CvRenderer& cv_renderer, - DlRenderer& dl_renderer, - ToleranceAdjuster& adjuster, + static void RenderWithStrokes(const TestParameters& testP, + const RenderEnvironment env, const BoundsTolerance& tolerance_in) { // The test cases were generated with geometry that will try to fill // out the various miter limits used for testing, but they can be off // by a couple of pixels so we will relax bounds testing for strokes by // a couple of pixels. BoundsTolerance tolerance = tolerance_in.addBoundsPadding(2, 2); - RenderWith( // - [=](SkCanvas*, SkPaint& p) { p.setStyle(SkPaint::kFill_Style); }, - [=](DisplayListBuilder& b) { b.setStyle(SkPaint::kFill_Style); }, - cv_renderer, dl_renderer, adjuster, tolerance, "Fill"); - RenderWith( - [=](SkCanvas*, SkPaint& p) { p.setStyle(SkPaint::kStroke_Style); }, - [=](DisplayListBuilder& b) { b.setStyle(SkPaint::kStroke_Style); }, - cv_renderer, dl_renderer, adjuster, tolerance, "Stroke + defaults"); - - RenderWith( - [=](SkCanvas*, SkPaint& p) { - p.setStyle(SkPaint::kFill_Style); - p.setStrokeWidth(10.0); - }, - [=](DisplayListBuilder& b) { - b.setStyle(SkPaint::kFill_Style); - b.setStrokeWidth(10.0); - }, - cv_renderer, dl_renderer, adjuster, tolerance, - "Fill + unnecessary StrokeWidth 10"); - - RenderWith( - [=](SkCanvas*, SkPaint& p) { - p.setStyle(SkPaint::kStroke_Style); - p.setStrokeWidth(10.0); - }, - [=](DisplayListBuilder& b) { - b.setStyle(SkPaint::kStroke_Style); - b.setStrokeWidth(10.0); - }, - cv_renderer, dl_renderer, adjuster, tolerance, "Stroke Width 10"); - RenderWith( - [=](SkCanvas*, SkPaint& p) { - p.setStyle(SkPaint::kStroke_Style); - p.setStrokeWidth(5.0); - }, - [=](DisplayListBuilder& b) { - b.setStyle(SkPaint::kStroke_Style); - b.setStrokeWidth(5.0); - }, - cv_renderer, dl_renderer, adjuster, tolerance, "Stroke Width 5"); - - RenderWith( - [=](SkCanvas*, SkPaint& p) { - p.setStyle(SkPaint::kStroke_Style); - p.setStrokeWidth(5.0); - p.setStrokeCap(SkPaint::kButt_Cap); - }, - [=](DisplayListBuilder& b) { - b.setStyle(SkPaint::kStroke_Style); - b.setStrokeWidth(5.0); - b.setStrokeCap(SkPaint::kButt_Cap); - }, - cv_renderer, dl_renderer, adjuster, tolerance, - "Stroke Width 5, Butt Cap"); - RenderWith( - [=](SkCanvas*, SkPaint& p) { - p.setStyle(SkPaint::kStroke_Style); - p.setStrokeWidth(5.0); - p.setStrokeCap(SkPaint::kRound_Cap); - }, - [=](DisplayListBuilder& b) { - b.setStyle(SkPaint::kStroke_Style); - b.setStrokeWidth(5.0); - b.setStrokeCap(SkPaint::kRound_Cap); - }, - cv_renderer, dl_renderer, adjuster, tolerance, - "Stroke Width 5, Round Cap"); - - RenderWith( - [=](SkCanvas*, SkPaint& p) { - p.setStyle(SkPaint::kStroke_Style); - p.setStrokeWidth(5.0); - p.setStrokeJoin(SkPaint::kBevel_Join); - }, - [=](DisplayListBuilder& b) { - b.setStyle(SkPaint::kStroke_Style); - b.setStrokeWidth(5.0); - b.setStrokeJoin(SkPaint::kBevel_Join); - }, - cv_renderer, dl_renderer, adjuster, tolerance, - "Stroke Width 5, Bevel Join"); - RenderWith( - [=](SkCanvas*, SkPaint& p) { - p.setStyle(SkPaint::kStroke_Style); - p.setStrokeWidth(5.0); - p.setStrokeJoin(SkPaint::kRound_Join); - }, - [=](DisplayListBuilder& b) { - b.setStyle(SkPaint::kStroke_Style); - b.setStrokeWidth(5.0); - b.setStrokeJoin(SkPaint::kRound_Join); - }, - cv_renderer, dl_renderer, adjuster, tolerance, - "Stroke Width 5, Round Join"); - - RenderWith( - [=](SkCanvas*, SkPaint& p) { - p.setStyle(SkPaint::kStroke_Style); - p.setStrokeWidth(5.0); - p.setStrokeMiter(10.0); - p.setStrokeJoin(SkPaint::kMiter_Join); - // AA helps fill in the peaks of the really thin miters better - // for bounds accuracy testing - p.setAntiAlias(true); - }, - [=](DisplayListBuilder& b) { - b.setStyle(SkPaint::kStroke_Style); - b.setStrokeWidth(5.0); - b.setStrokeMiter(10.0); - b.setStrokeJoin(SkPaint::kMiter_Join); - // AA helps fill in the peaks of the really thin miters better - // for bounds accuracy testing - b.setAntiAlias(true); - }, - cv_renderer, dl_renderer, adjuster, tolerance, - "Stroke Width 5, Miter 10"); - - RenderWith( - [=](SkCanvas*, SkPaint& p) { - p.setStyle(SkPaint::kStroke_Style); - p.setStrokeWidth(5.0); - p.setStrokeMiter(0.0); - p.setStrokeJoin(SkPaint::kMiter_Join); - }, - [=](DisplayListBuilder& b) { - b.setStyle(SkPaint::kStroke_Style); - b.setStrokeWidth(5.0); - b.setStrokeMiter(0.0); - b.setStrokeJoin(SkPaint::kMiter_Join); - }, - cv_renderer, dl_renderer, adjuster, tolerance, - "Stroke Width 5, Miter 0"); + ReferencePixelExpectation stroke_expect = testP.with_stroke_style(SkPaint::kStroke_Style); + RenderWith(testP, env, + CaseParameters( + "Fill", + [=](SkCanvas*, SkPaint& p) { p.setStyle(SkPaint::kFill_Style); }, + [=](DisplayListBuilder& b) { b.setStyle(SkPaint::kFill_Style); } + ), tolerance, kPixelsMatchReference); + RenderWith(testP, env, + CaseParameters( + "Stroke + defaults", + [=](SkCanvas*, SkPaint& p) { p.setStyle(SkPaint::kStroke_Style); }, + [=](DisplayListBuilder& b) { b.setStyle(SkPaint::kStroke_Style); } + ), tolerance, stroke_expect); + + RenderWith(testP, env, + CaseParameters( + "Fill + unnecessary StrokeWidth 10", + [=](SkCanvas*, SkPaint& p) { + p.setStyle(SkPaint::kFill_Style); + p.setStrokeWidth(10.0); + }, + [=](DisplayListBuilder& b) { + b.setStyle(SkPaint::kFill_Style); + b.setStrokeWidth(10.0); + } + ), tolerance, testP.with_stroke_style(SkPaint::kFill_Style)); + + RenderWith(testP, env, + CaseParameters( + "Stroke Width 10", + [=](SkCanvas*, SkPaint& p) { + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeWidth(10.0); + }, + [=](DisplayListBuilder& b) { + b.setStyle(SkPaint::kStroke_Style); + b.setStrokeWidth(10.0); + } + ), tolerance, stroke_expect); + RenderWith(testP, env, + CaseParameters( + "Stroke Width 5", + [=](SkCanvas*, SkPaint& p) { + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeWidth(5.0); + }, + [=](DisplayListBuilder& b) { + b.setStyle(SkPaint::kStroke_Style); + b.setStrokeWidth(5.0); + } + ), tolerance, stroke_expect); + + RenderWith(testP, env, + CaseParameters( + "Stroke Width 5, Butt Cap", + [=](SkCanvas*, SkPaint& p) { + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeWidth(5.0); + p.setStrokeCap(SkPaint::kButt_Cap); + }, + [=](DisplayListBuilder& b) { + b.setStyle(SkPaint::kStroke_Style); + b.setStrokeWidth(5.0); + b.setStrokeCap(SkPaint::kButt_Cap); + } + ), tolerance, stroke_expect); + RenderWith(testP, env, + CaseParameters( + "Stroke Width 5, Round Cap", + [=](SkCanvas*, SkPaint& p) { + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeWidth(5.0); + p.setStrokeCap(SkPaint::kRound_Cap); + }, + [=](DisplayListBuilder& b) { + b.setStyle(SkPaint::kStroke_Style); + b.setStrokeWidth(5.0); + b.setStrokeCap(SkPaint::kRound_Cap); + } + ), tolerance, stroke_expect); + + RenderWith(testP, env, + CaseParameters( + "Stroke Width 5, Bevel Join", + [=](SkCanvas*, SkPaint& p) { + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeWidth(5.0); + p.setStrokeJoin(SkPaint::kBevel_Join); + }, + [=](DisplayListBuilder& b) { + b.setStyle(SkPaint::kStroke_Style); + b.setStrokeWidth(5.0); + b.setStrokeJoin(SkPaint::kBevel_Join); + } + ), tolerance, stroke_expect); + RenderWith(testP, env, + CaseParameters( + "Stroke Width 5, Round Join", + [=](SkCanvas*, SkPaint& p) { + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeWidth(5.0); + p.setStrokeJoin(SkPaint::kRound_Join); + }, + [=](DisplayListBuilder& b) { + b.setStyle(SkPaint::kStroke_Style); + b.setStrokeWidth(5.0); + b.setStrokeJoin(SkPaint::kRound_Join); + } + ), tolerance, stroke_expect); + + RenderWith(testP, env, + CaseParameters( + "Stroke Width 5, Miter 10", + [=](SkCanvas*, SkPaint& p) { + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeWidth(5.0); + p.setStrokeMiter(10.0); + p.setStrokeJoin(SkPaint::kMiter_Join); + // AA helps fill in the peaks of the really thin miters better + // for bounds accuracy testing + // p.setAntiAlias(true); + }, + [=](DisplayListBuilder& b) { + b.setStyle(SkPaint::kStroke_Style); + b.setStrokeWidth(5.0); + b.setStrokeMiter(10.0); + b.setStrokeJoin(SkPaint::kMiter_Join); + // AA helps fill in the peaks of the really thin miters better + // for bounds accuracy testing + // b.setAntiAlias(true); + } + ), tolerance, stroke_expect); + + RenderWith(testP, env, + CaseParameters( + "Stroke Width 5, Miter 0", + [=](SkCanvas*, SkPaint& p) { + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeWidth(5.0); + p.setStrokeMiter(0.0); + p.setStrokeJoin(SkPaint::kMiter_Join); + }, + [=](DisplayListBuilder& b) { + b.setStyle(SkPaint::kStroke_Style); + b.setStrokeWidth(5.0); + b.setStrokeMiter(0.0); + b.setStrokeJoin(SkPaint::kMiter_Join); + } + ), tolerance, stroke_expect); { const SkScalar TestDashes1[] = {29.0, 2.0}; const SkScalar TestDashes2[] = {17.0, 1.5}; sk_sp effect = SkDashPathEffect::Make(TestDashes1, 2, 0.0f); { - RenderWith( - [=](SkCanvas*, SkPaint& p) { - // Need stroke style to see dashing properly - p.setStyle(SkPaint::kStroke_Style); - // Provide some non-trivial stroke size to get dashed - p.setStrokeWidth(5.0); - p.setPathEffect(effect); - }, - [=](DisplayListBuilder& b) { - // Need stroke style to see dashing properly - b.setStyle(SkPaint::kStroke_Style); - // Provide some non-trivial stroke size to get dashed - b.setStrokeWidth(5.0); - b.setPathEffect(effect); - }, - cv_renderer, dl_renderer, adjuster, tolerance, - "PathEffect == Dash-29-2"); + RenderWith(testP, env, + CaseParameters( + "PathEffect == Dash-29-2", + [=](SkCanvas*, SkPaint& p) { + // Need stroke style to see dashing properly + p.setStyle(SkPaint::kStroke_Style); + // Provide some non-trivial stroke size to get dashed + p.setStrokeWidth(5.0); + p.setPathEffect(effect); + }, + [=](DisplayListBuilder& b) { + // Need stroke style to see dashing properly + b.setStyle(SkPaint::kStroke_Style); + // Provide some non-trivial stroke size to get dashed + b.setStrokeWidth(5.0); + b.setPathEffect(effect); + } + ), tolerance, stroke_expect); } - ASSERT_TRUE(effect->unique()) << "PathEffect == Dash-29-2 Cleanup"; + EXPECT_TRUE(testP.is_draw_text_blob() || effect->unique()) + << "PathEffect == Dash-29-2 Cleanup"; effect = SkDashPathEffect::Make(TestDashes2, 2, 0.0f); { - RenderWith( - [=](SkCanvas*, SkPaint& p) { - // Need stroke style to see dashing properly - p.setStyle(SkPaint::kStroke_Style); - // Provide some non-trivial stroke size to get dashed - p.setStrokeWidth(5.0); - p.setPathEffect(effect); - }, - [=](DisplayListBuilder& b) { - // Need stroke style to see dashing properly - b.setStyle(SkPaint::kStroke_Style); - // Provide some non-trivial stroke size to get dashed - b.setStrokeWidth(5.0); - b.setPathEffect(effect); - }, - cv_renderer, dl_renderer, adjuster, tolerance, - "PathEffect == Dash-17-1.5"); + RenderWith(testP, env, + CaseParameters( + "PathEffect == Dash-17-1.5", + [=](SkCanvas*, SkPaint& p) { + // Need stroke style to see dashing properly + p.setStyle(SkPaint::kStroke_Style); + // Provide some non-trivial stroke size to get dashed + p.setStrokeWidth(5.0); + p.setPathEffect(effect); + }, + [=](DisplayListBuilder& b) { + // Need stroke style to see dashing properly + b.setStyle(SkPaint::kStroke_Style); + // Provide some non-trivial stroke size to get dashed + b.setStrokeWidth(5.0); + b.setPathEffect(effect); + } + ), tolerance, stroke_expect); } - ASSERT_TRUE(effect->unique()) << "PathEffect == Dash-17-1.5 Cleanup"; + EXPECT_TRUE(testP.is_draw_text_blob() || effect->unique()) + << "PathEffect == Dash-17-1.5 Cleanup"; } } - static void RenderWithTransforms(CvRenderer& cv_renderer, - DlRenderer& dl_renderer, - ToleranceAdjuster& adjuster, + static void RenderWithTransforms(const TestParameters& testP, + const RenderEnvironment& env, const BoundsTolerance& tolerance) { - // If there is bounds padding for some conservative bounds overestimate - // then that padding will be even more pronounced in rotated or skewed - // coordinate systems so we scale the padding by about 5% to compensate. - BoundsTolerance skewed_tolerance = tolerance.addScale(1.05, 1.05); - RenderWith([=](SkCanvas* c, SkPaint&) { c->translate(5, 10); }, // - [=](DisplayListBuilder& b) { b.translate(5, 10); }, // - cv_renderer, dl_renderer, adjuster, tolerance, - "Translate 5, 10"); - RenderWith([=](SkCanvas* c, SkPaint&) { c->scale(1.05, 1.05); }, // - [=](DisplayListBuilder& b) { b.scale(1.05, 1.05); }, // - cv_renderer, dl_renderer, adjuster, tolerance, // - "Scale +5%"); - RenderWith([=](SkCanvas* c, SkPaint&) { c->rotate(5); }, // - [=](DisplayListBuilder& b) { b.rotate(5); }, // - cv_renderer, dl_renderer, adjuster, skewed_tolerance, - "Rotate 5 degrees"); - RenderWith([=](SkCanvas* c, SkPaint&) { c->skew(0.05, 0.05); }, // - [=](DisplayListBuilder& b) { b.skew(0.05, 0.05); }, // - cv_renderer, dl_renderer, adjuster, skewed_tolerance, // - "Skew 5%"); + // If the rendering method does not fill the corners of the original + // bounds, then the estimate under rotation or skewing will be off + // so we scale the padding by about 5% to compensate. + BoundsTolerance skewed_tolerance = tolerance.mulScale(1.05, 1.05); + RenderWith(testP, env, + CaseParameters( + "Translate 5, 10", + [=](SkCanvas* c, SkPaint&) { c->translate(5, 10); }, + [=](DisplayListBuilder& b) { b.translate(5, 10); } + ), tolerance, testP.under_transform()); + RenderWith(testP, env, + CaseParameters( + "Scale +5%", + [=](SkCanvas* c, SkPaint&) { c->scale(1.05, 1.05); }, + [=](DisplayListBuilder& b) { b.scale(1.05, 1.05); } + ), tolerance, testP.under_transform()); + RenderWith(testP, env, + CaseParameters( + "Rotate 5 degrees", + [=](SkCanvas* c, SkPaint&) { c->rotate(5); }, + [=](DisplayListBuilder& b) { b.rotate(5); } + ), skewed_tolerance, testP.under_transform()); + RenderWith(testP, env, + CaseParameters( + "Skew 5%", + [=](SkCanvas* c, SkPaint&) { c->skew(0.05, 0.05); }, + [=](DisplayListBuilder& b) { b.skew(0.05, 0.05); } + ), skewed_tolerance, testP.under_transform()); { SkMatrix tx = SkMatrix::MakeAll(1.10, 0.10, 5, // 0.05, 1.05, 10, // 0, 0, 1); - RenderWith([=](SkCanvas* c, SkPaint&) { c->concat(tx); }, // - [=](DisplayListBuilder& b) { - b.transform2DAffine(tx[0], tx[1], tx[2], // - tx[3], tx[4], tx[5]); - }, // - cv_renderer, dl_renderer, adjuster, skewed_tolerance, - "Transform 2D Affine"); + RenderWith(testP, env, + CaseParameters( + "Transform 2D Affine", + [=](SkCanvas* c, SkPaint&) { c->concat(tx); }, + [=](DisplayListBuilder& b) { + b.transform2DAffine(tx[0], tx[1], tx[2], // + tx[3], tx[4], tx[5]); + } + ), skewed_tolerance, testP.under_transform()); } { SkM44 m44 = SkM44(1, 0, 0, RenderCenterX, // @@ -992,173 +1468,191 @@ class CanvasCompareTester { m44.preConcat(SkM44::Rotate({1, 0, 0}, M_PI / 60)); // 3 degrees around X m44.preConcat(SkM44::Rotate({0, 1, 0}, M_PI / 45)); // 4 degrees around Y m44.preTranslate(-RenderCenterX, -RenderCenterY); - RenderWith([=](SkCanvas* c, SkPaint&) { c->concat(m44); }, // - [=](DisplayListBuilder& b) { - b.transformFullPerspective( - m44.rc(0, 0), m44.rc(0, 1), m44.rc(0, 2), m44.rc(0, 3), - m44.rc(1, 0), m44.rc(1, 1), m44.rc(1, 2), m44.rc(1, 3), - m44.rc(2, 0), m44.rc(2, 1), m44.rc(2, 2), m44.rc(2, 3), - m44.rc(3, 0), m44.rc(3, 1), m44.rc(3, 2), m44.rc(3, 3)); - }, // - cv_renderer, dl_renderer, adjuster, skewed_tolerance, - "Transform Full Perspective"); + RenderWith(testP, env, + CaseParameters( + "Transform Full Perspective", + [=](SkCanvas* c, SkPaint&) { c->concat(m44); }, // + [=](DisplayListBuilder& b) { + b.transformFullPerspective( + m44.rc(0, 0), m44.rc(0, 1), m44.rc(0, 2), m44.rc(0, 3), + m44.rc(1, 0), m44.rc(1, 1), m44.rc(1, 2), m44.rc(1, 3), + m44.rc(2, 0), m44.rc(2, 1), m44.rc(2, 2), m44.rc(2, 3), + m44.rc(3, 0), m44.rc(3, 1), m44.rc(3, 2), m44.rc(3, 3)); + } + ), skewed_tolerance, testP.under_transform()); } } - static void RenderWithClips(CvRenderer& cv_renderer, - DlRenderer& dl_renderer, - ToleranceAdjuster& diff_adjuster, + static void RenderWithClips(const TestParameters& testP, + const RenderEnvironment& env, const BoundsTolerance& diff_tolerance) { SkRect r_clip = RenderBounds.makeInset(15.5, 15.5); - // For kIntersect clips we can be really strict on tolerance - ToleranceAdjuster& intersect_adjuster = DefaultAdjuster; - BoundsTolerance& intersect_tolerance = DefaultTolerance; - RenderWith( - [=](SkCanvas* c, SkPaint&) { - c->clipRect(r_clip, SkClipOp::kIntersect, false); - }, - [=](DisplayListBuilder& b) { - b.clipRect(r_clip, SkClipOp::kIntersect, false); - }, - cv_renderer, dl_renderer, intersect_adjuster, intersect_tolerance, - "Hard ClipRect inset by 15.5"); - RenderWith( - [=](SkCanvas* c, SkPaint&) { - c->clipRect(r_clip, SkClipOp::kIntersect, true); - }, - [=](DisplayListBuilder& b) { - b.clipRect(r_clip, SkClipOp::kIntersect, true); - }, - cv_renderer, dl_renderer, intersect_adjuster, intersect_tolerance, - "AntiAlias ClipRect inset by 15.5"); - RenderWith( - [=](SkCanvas* c, SkPaint&) { - c->clipRect(r_clip, SkClipOp::kDifference, false); - }, - [=](DisplayListBuilder& b) { - b.clipRect(r_clip, SkClipOp::kDifference, false); - }, - cv_renderer, dl_renderer, diff_adjuster, diff_tolerance, - "Hard ClipRect Diff, inset by 15.5"); + BoundsTolerance intersect_tolerance = diff_tolerance.clip(r_clip); + RenderWith(testP, env, + CaseParameters( + "Hard ClipRect inset by 15.5", + [=](SkCanvas* c, SkPaint&) { + c->clipRect(r_clip, SkClipOp::kIntersect, false); + }, + [=](DisplayListBuilder& b) { + b.clipRect(r_clip, SkClipOp::kIntersect, false); + } + ), intersect_tolerance, testP.with_clip()); + RenderWith(testP, env, + CaseParameters( + "AntiAlias ClipRect inset by 15.5", + [=](SkCanvas* c, SkPaint&) { + c->clipRect(r_clip, SkClipOp::kIntersect, true); + }, + [=](DisplayListBuilder& b) { + b.clipRect(r_clip, SkClipOp::kIntersect, true); + } + ), intersect_tolerance, testP.with_clip()); + RenderWith(testP, env, + CaseParameters( + "Hard ClipRect Diff, inset by 15.5", + [=](SkCanvas* c, SkPaint&) { + c->clipRect(r_clip, SkClipOp::kDifference, false); + }, + [=](DisplayListBuilder& b) { + b.clipRect(r_clip, SkClipOp::kDifference, false); + } + ), diff_tolerance, testP.with_clip()); SkRRect rr_clip = SkRRect::MakeRectXY(r_clip, 1.8, 2.7); - RenderWith( - [=](SkCanvas* c, SkPaint&) { - c->clipRRect(rr_clip, SkClipOp::kIntersect, false); - }, - [=](DisplayListBuilder& b) { - b.clipRRect(rr_clip, SkClipOp::kIntersect, false); - }, - cv_renderer, dl_renderer, intersect_adjuster, intersect_tolerance, - "Hard ClipRRect inset by 15.5"); - RenderWith( - [=](SkCanvas* c, SkPaint&) { - c->clipRRect(rr_clip, SkClipOp::kIntersect, true); - }, - [=](DisplayListBuilder& b) { - b.clipRRect(rr_clip, SkClipOp::kIntersect, true); - }, - cv_renderer, dl_renderer, intersect_adjuster, intersect_tolerance, - "AntiAlias ClipRRect inset by 15.5"); - RenderWith( - [=](SkCanvas* c, SkPaint&) { - c->clipRRect(rr_clip, SkClipOp::kDifference, false); - }, - [=](DisplayListBuilder& b) { - b.clipRRect(rr_clip, SkClipOp::kDifference, false); - }, - cv_renderer, dl_renderer, diff_adjuster, diff_tolerance, - "Hard ClipRRect Diff, inset by 15.5"); + RenderWith(testP, env, + CaseParameters( + "Hard ClipRRect inset by 15.5", + [=](SkCanvas* c, SkPaint&) { + c->clipRRect(rr_clip, SkClipOp::kIntersect, false); + }, + [=](DisplayListBuilder& b) { + b.clipRRect(rr_clip, SkClipOp::kIntersect, false); + } + ), intersect_tolerance, testP.with_clip()); + RenderWith(testP, env, + CaseParameters( + "AntiAlias ClipRRect inset by 15.5", + [=](SkCanvas* c, SkPaint&) { + c->clipRRect(rr_clip, SkClipOp::kIntersect, true); + }, + [=](DisplayListBuilder& b) { + b.clipRRect(rr_clip, SkClipOp::kIntersect, true); + } + ), intersect_tolerance, testP.with_clip()); + RenderWith(testP, env, + CaseParameters( + "Hard ClipRRect Diff, inset by 15.5", + [=](SkCanvas* c, SkPaint&) { + c->clipRRect(rr_clip, SkClipOp::kDifference, false); + }, + [=](DisplayListBuilder& b) { + b.clipRRect(rr_clip, SkClipOp::kDifference, false); + } + ), diff_tolerance, testP.with_clip()); SkPath path_clip = SkPath(); path_clip.setFillType(SkPathFillType::kEvenOdd); path_clip.addRect(r_clip); path_clip.addCircle(RenderCenterX, RenderCenterY, 1.0); - RenderWith( - [=](SkCanvas* c, SkPaint&) { - c->clipPath(path_clip, SkClipOp::kIntersect, false); - }, - [=](DisplayListBuilder& b) { - b.clipPath(path_clip, SkClipOp::kIntersect, false); - }, - cv_renderer, dl_renderer, intersect_adjuster, intersect_tolerance, - "Hard ClipPath inset by 15.5"); - RenderWith( - [=](SkCanvas* c, SkPaint&) { - c->clipPath(path_clip, SkClipOp::kIntersect, true); - }, - [=](DisplayListBuilder& b) { - b.clipPath(path_clip, SkClipOp::kIntersect, true); - }, - cv_renderer, dl_renderer, intersect_adjuster, intersect_tolerance, - "AntiAlias ClipPath inset by 15.5"); - RenderWith( - [=](SkCanvas* c, SkPaint&) { - c->clipPath(path_clip, SkClipOp::kDifference, false); - }, - [=](DisplayListBuilder& b) { - b.clipPath(path_clip, SkClipOp::kDifference, false); - }, - cv_renderer, dl_renderer, diff_adjuster, diff_tolerance, - "Hard ClipPath Diff, inset by 15.5"); + RenderWith(testP, env, + CaseParameters( + "Hard ClipPath inset by 15.5", + [=](SkCanvas* c, SkPaint&) { + c->clipPath(path_clip, SkClipOp::kIntersect, false); + }, + [=](DisplayListBuilder& b) { + b.clipPath(path_clip, SkClipOp::kIntersect, false); + } + ), intersect_tolerance, testP.with_clip()); + RenderWith(testP, env, + CaseParameters( + "AntiAlias ClipPath inset by 15.5", + [=](SkCanvas* c, SkPaint&) { + c->clipPath(path_clip, SkClipOp::kIntersect, true); + }, + [=](DisplayListBuilder& b) { + b.clipPath(path_clip, SkClipOp::kIntersect, true); + } + ), intersect_tolerance, testP.with_clip()); + RenderWith(testP, env, + CaseParameters( + "Hard ClipPath Diff, inset by 15.5", + [=](SkCanvas* c, SkPaint&) { + c->clipPath(path_clip, SkClipOp::kDifference, false); + }, + [=](DisplayListBuilder& b) { + b.clipPath(path_clip, SkClipOp::kDifference, false); + } + ), diff_tolerance, testP.with_clip()); } - static sk_sp getSkPicture(CvRenderer& cv_setup, - CvRenderer& cv_render) { + static sk_sp getSkPicture(const TestParameters& testP, + const CaseParameters& caseP) { SkPictureRecorder recorder; SkRTreeFactory rtree_factory; SkCanvas* cv = recorder.beginRecording(TestBounds, &rtree_factory); - SkPaint p; - cv_setup(cv, p); - cv_render(cv, p); + caseP.render_to(cv, testP); return recorder.finishRecordingAsPicture(); } - static void RenderWith(CvRenderer& cv_setup, - DlRenderer& dl_setup, - CvRenderer& cv_render, - DlRenderer& dl_render, - ToleranceAdjuster& adjuster, + static void RenderWith(const TestParameters& testP, + const RenderEnvironment& env, + const CaseParameters& caseP, const BoundsTolerance& tolerance_in, - const std::string info, - const SkColor* bg = nullptr) { - // surface1 is direct rendering via SkCanvas to SkSurface + ReferencePixelExpectation ref_action) { + // sk_surface is a direct rendering via SkCanvas to SkSurface // DisplayList mechanisms are not involved in this operation - sk_sp ref_surface = makeSurface(bg); - SkPaint paint1; - cv_setup(ref_surface->getCanvas(), paint1); - const BoundsTolerance tolerance = adjuster( - tolerance_in, paint1, ref_surface->getCanvas()->getTotalMatrix()); - cv_render(ref_surface->getCanvas(), paint1); - sk_sp ref_picture = getSkPicture(cv_setup, cv_render); - SkRect ref_bounds = ref_picture->cullRect(); - SkPixmap ref_pixels; - ASSERT_TRUE(ref_surface->peekPixels(&ref_pixels)) << info; - ASSERT_EQ(ref_pixels.width(), TestWidth) << info; - ASSERT_EQ(ref_pixels.height(), TestHeight) << info; - ASSERT_EQ(ref_pixels.info().bytesPerPixel(), 4) << info; - checkPixels(&ref_pixels, ref_bounds, info + " (Skia reference)", bg); + const std::string info = caseP.info(); + const SkColor bg = caseP.bg(); + RenderSurface sk_surface = env.MakeSurface(bg); + SkCanvas* sk_canvas = sk_surface.canvas(); + SkPaint sk_paint; + caseP.cv_setup()(sk_canvas, sk_paint); + const BoundsTolerance tolerance = testP.adjust(tolerance_in, sk_paint, + sk_canvas->getTotalMatrix()); + testP.render_to(sk_canvas, sk_paint); + caseP.cv_restore()(sk_canvas, sk_paint); + const sk_sp sk_picture = getSkPicture(testP, caseP); + SkRect sk_bounds = sk_picture->cullRect(); + const SkPixmap* sk_pixels = sk_surface.pixmap(); + ASSERT_EQ(sk_pixels->width(), TestWidth) << info; + ASSERT_EQ(sk_pixels->height(), TestHeight) << info; + ASSERT_EQ(sk_pixels->info().bytesPerPixel(), 4) << info; + checkPixels(sk_pixels, sk_bounds, info + " (Skia reference)", bg); + + switch (ref_action) { + case kPixelsMatchReference: + quickCompareToReference(env.ref_pixmap(), sk_pixels, true, + info + " (attribute has no effect)"); + break; + case kPixelsDoNotMatchReference: + quickCompareToReference(env.ref_pixmap(), sk_pixels, false, + info + " (attribute affects rendering)"); + break; + case kIgnoreReference: + break; + } { // This sequence plays the provided equivalently constructed // DisplayList onto the SkCanvas of the surface // DisplayList => direct rendering - sk_sp test_surface = makeSurface(bg); + RenderSurface dl_surface = env.MakeSurface(bg); DisplayListBuilder builder(TestBounds); - dl_setup(builder); - dl_render(builder); + caseP.render_to(builder, testP); sk_sp display_list = builder.Build(); SkRect dl_bounds = display_list->bounds(); - if (!ref_bounds.roundOut().contains(dl_bounds)) { + if (!sk_bounds.roundOut().contains(dl_bounds)) { FML_LOG(ERROR) << "For " << info; - FML_LOG(ERROR) << "ref: " // - << ref_bounds.fLeft << ", " << ref_bounds.fTop << " => " - << ref_bounds.fRight << ", " << ref_bounds.fBottom; + FML_LOG(ERROR) << "sk ref: " // + << sk_bounds.fLeft << ", " << sk_bounds.fTop << " => " + << sk_bounds.fRight << ", " << sk_bounds.fBottom; FML_LOG(ERROR) << "dl: " // << dl_bounds.fLeft << ", " << dl_bounds.fTop << " => " << dl_bounds.fRight << ", " << dl_bounds.fBottom; - if (!dl_bounds.contains(ref_bounds)) { + if (!dl_bounds.contains(sk_bounds)) { FML_LOG(ERROR) << "DisplayList bounds are too small!"; } - if (!ref_bounds.roundOut().contains(dl_bounds.roundOut())) { + if (!sk_bounds.roundOut().contains(dl_bounds.roundOut())) { FML_LOG(ERROR) << "###### DisplayList bounds larger than reference!"; } } @@ -1169,30 +1663,35 @@ class CanvasCompareTester { // trigger, we will trust the DL bounds. // EXPECT_TRUE(dl_bounds.contains(ref_bounds)) << info; - EXPECT_EQ(display_list->op_count(), ref_picture->approximateOpCount()) - << info; + // When we are drawing a DisplayList, the display_list built above + // will contain just a single drawDisplayList call plus the case + // attribute. The sk_picture will, however, contain a list of all + // of the embedded calls in the display list and so the op counts + // will not be equal between the two. + if (!testP.is_draw_display_list()) { + EXPECT_EQ(display_list->op_count(), sk_picture->approximateOpCount()) + << info; + } - display_list->RenderTo(test_surface->getCanvas()); - compareToReference(test_surface.get(), &ref_pixels, + display_list->RenderTo(dl_surface.canvas()); + compareToReference(dl_surface.pixmap(), sk_pixels, info + " (DisplayList built directly -> surface)", &dl_bounds, &tolerance, bg); } // This test cannot work if the rendering is using shadows until // we can access the Skia ShadowRec via public headers. - if (!TestingDrawShadows) { + if (!testP.is_draw_shadows()) { // This sequence renders SkCanvas calls to a DisplayList and then // plays them back on SkCanvas to SkSurface // SkCanvas calls => DisplayList => rendering - sk_sp test_surface = makeSurface(bg); + RenderSurface cv_dl_surface = env.MakeSurface(bg); DisplayListCanvasRecorder dl_recorder(TestBounds); - SkPaint test_paint; - cv_setup(&dl_recorder, test_paint); - cv_render(&dl_recorder, test_paint); - dl_recorder.builder()->Build()->RenderTo(test_surface->getCanvas()); - compareToReference(test_surface.get(), &ref_pixels, + caseP.render_to(&dl_recorder, testP); + dl_recorder.builder()->Build()->RenderTo(cv_dl_surface.canvas()); + compareToReference(cv_dl_surface.pixmap(), sk_pixels, info + " (Skia calls -> DisplayList -> surface)", - nullptr, nullptr, nullptr); + nullptr, nullptr, bg); } { @@ -1205,41 +1704,38 @@ class CanvasCompareTester { const int TestHeight2 = TestHeight * 2; const SkScalar TestScale = 2.0; - SkPictureRecorder sk_recorder; - SkCanvas* ref_canvas = sk_recorder.beginRecording(TestBounds); + SkPictureRecorder sk_x2_recorder; + SkCanvas* ref_canvas = sk_x2_recorder.beginRecording(TestBounds); SkPaint ref_paint; - cv_setup(ref_canvas, ref_paint); - cv_render(ref_canvas, ref_paint); - sk_sp ref_picture = sk_recorder.finishRecordingAsPicture(); - sk_sp ref_surface2 = makeSurface(bg, TestWidth2, TestHeight2); - SkCanvas* ref_canvas2 = ref_surface2->getCanvas(); - ref_canvas2->scale(TestScale, TestScale); - ref_picture->playback(ref_canvas2); - SkPixmap ref_pixels2; - ASSERT_TRUE(ref_surface2->peekPixels(&ref_pixels2)) << info; - ASSERT_EQ(ref_pixels2.width(), TestWidth2) << info; - ASSERT_EQ(ref_pixels2.height(), TestHeight2) << info; - ASSERT_EQ(ref_pixels2.info().bytesPerPixel(), 4) << info; - - DisplayListBuilder builder(TestBounds); - dl_setup(builder); - dl_render(builder); - sk_sp display_list = builder.Build(); - sk_sp test_surface = makeSurface(bg, TestWidth2, TestHeight2); - SkCanvas* test_canvas = test_surface->getCanvas(); - test_canvas->scale(TestScale, TestScale); - display_list->RenderTo(test_canvas); - compareToReference(test_surface.get(), &ref_pixels2, + caseP.render_to(ref_canvas, testP); + sk_sp ref_x2_picture = sk_x2_recorder.finishRecordingAsPicture(); + RenderSurface ref_x2_surface = env.MakeSurface(bg, TestWidth2, TestHeight2); + SkCanvas* ref_x2_canvas = ref_x2_surface.canvas(); + ref_x2_canvas->scale(TestScale, TestScale); + ref_x2_picture->playback(ref_x2_canvas); + const SkPixmap* ref_x2_pixels = ref_x2_surface.pixmap(); + ASSERT_EQ(ref_x2_pixels->width(), TestWidth2) << info; + ASSERT_EQ(ref_x2_pixels->height(), TestHeight2) << info; + ASSERT_EQ(ref_x2_pixels->info().bytesPerPixel(), 4) << info; + + DisplayListBuilder builder_x2(TestBounds); + caseP.render_to(builder_x2, testP); + sk_sp display_list_x2 = builder_x2.Build(); + RenderSurface test_x2_surface = env.MakeSurface(bg, TestWidth2, TestHeight2); + SkCanvas* test_x2_canvas = test_x2_surface.canvas(); + test_x2_canvas->scale(TestScale, TestScale); + display_list_x2->RenderTo(test_x2_canvas); + compareToReference(test_x2_surface.pixmap(), ref_x2_pixels, info + " (Both rendered scaled 2x)", nullptr, nullptr, - nullptr, TestWidth2, TestHeight2, false); + bg, TestWidth2, TestHeight2, false); } } - static void checkPixels(SkPixmap* ref_pixels, + static void checkPixels(const SkPixmap* ref_pixels, SkRect ref_bounds, const std::string info, - const SkColor* bg) { - SkPMColor untouched = (bg) ? SkPreMultiplyColor(*bg) : 0; + const SkColor bg) { + SkPMColor untouched = SkPreMultiplyColor(bg); int pixels_touched = 0; int pixels_oob = 0; SkIRect i_bounds = ref_bounds.roundOut(); @@ -1258,21 +1754,45 @@ class CanvasCompareTester { ASSERT_GT(pixels_touched, 0) << info; } - static void compareToReference(SkSurface* test_surface, - SkPixmap* reference, + static void quickCompareToReference(const SkPixmap* ref_pixels, + const SkPixmap* test_pixels, + bool should_match, + const std::string info) { + ASSERT_EQ(test_pixels->width(), ref_pixels->width()) << info; + ASSERT_EQ(test_pixels->height(), ref_pixels->height()) << info; + ASSERT_EQ(test_pixels->info().bytesPerPixel(), 4) << info; + ASSERT_EQ(ref_pixels->info().bytesPerPixel(), 4) << info; + int pixels_different = 0; + for (int y = 0; y < test_pixels->height(); y++) { + const uint32_t* ref_row = ref_pixels->addr32(0, y); + const uint32_t* test_row = test_pixels->addr32(0, y); + for (int x = 0; x < test_pixels->width(); x++) { + if (ref_row[x] != test_row[x]) { + pixels_different++; + } + } + } + if (should_match) { + ASSERT_EQ(pixels_different, 0) << info; + } else { + ASSERT_NE(pixels_different, 0) << info; + } + } + + static void compareToReference(const SkPixmap* test_pixels, + const SkPixmap* ref_pixels, const std::string info, SkRect* bounds, const BoundsTolerance* tolerance, - const SkColor* bg, + const SkColor bg, int width = TestWidth, int height = TestHeight, bool printMismatches = false) { - SkPMColor untouched = (bg) ? SkPreMultiplyColor(*bg) : 0; - SkPixmap test_pixels; - ASSERT_TRUE(test_surface->peekPixels(&test_pixels)) << info; - ASSERT_EQ(test_pixels.width(), width) << info; - ASSERT_EQ(test_pixels.height(), height) << info; - ASSERT_EQ(test_pixels.info().bytesPerPixel(), 4) << info; + SkPMColor untouched = SkPreMultiplyColor(bg); + ASSERT_EQ(test_pixels->width(), width) << info; + ASSERT_EQ(test_pixels->height(), height) << info; + ASSERT_EQ(test_pixels->info().bytesPerPixel(), 4) << info; + ASSERT_EQ(ref_pixels->info().bytesPerPixel(), 4) << info; SkIRect i_bounds = bounds ? bounds->roundOut() : SkIRect::MakeWH(width, height); @@ -1283,8 +1803,8 @@ class CanvasCompareTester { int maxX = 0; int maxY = 0; for (int y = 0; y < height; y++) { - const uint32_t* ref_row = reference->addr32(0, y); - const uint32_t* test_row = test_pixels.addr32(0, y); + const uint32_t* ref_row = ref_pixels->addr32(0, y); + const uint32_t* test_row = test_pixels->addr32(0, y); for (int x = 0; x < width; x++) { if (bounds && test_row[x] != untouched) { if (minX > x) @@ -1336,12 +1856,13 @@ class CanvasCompareTester { int pad_top = std::max(0, pixTop - bounds.fTop); int pad_right = std::max(0, bounds.fRight - pixRight); int pad_bottom = std::max(0, bounds.fBottom - pixBottom); - int pixWidth = pixRight - pixLeft; - int pixHeight = pixBottom - pixTop; - SkISize pixSize = SkISize::Make(pixWidth, pixHeight); + SkIRect pix_bounds = SkIRect::MakeLTRB(pixLeft, pixTop, pixRight, pixBottom); + SkISize pix_size = pix_bounds.size(); + int pixWidth = pix_size.width(); + int pixHeight = pix_size.height(); int worst_pad_x = std::max(pad_left, pad_right); int worst_pad_y = std::max(pad_top, pad_bottom); - if (tolerance->overflows(pixSize, worst_pad_x, worst_pad_y)) { + if (tolerance->overflows(pix_bounds, worst_pad_x, worst_pad_y)) { FML_LOG(ERROR) << "Overflow for " << info; FML_LOG(ERROR) << "pix bounds[" // << pixLeft << ", " << pixTop << " => " // @@ -1356,7 +1877,7 @@ class CanvasCompareTester { << worst_pad_x << ", " << worst_pad_y // << " (" << (worst_pad_x * 100.0 / pixWidth) // << "%, " << (worst_pad_y * 100.0 / pixHeight) << "%)"; - int pix_area = pixSize.area(); + int pix_area = pix_size.area(); int dl_area = bounds.width() * bounds.height(); FML_LOG(ERROR) << "Total overflow area: " << (dl_area - pix_area) // << " (+" << (dl_area * 100.0 / pix_area - 100.0) << "%)"; @@ -1364,16 +1885,6 @@ class CanvasCompareTester { } } - static sk_sp makeSurface(const SkColor* bg, - int width = TestWidth, - int height = TestHeight) { - sk_sp surface = SkSurface::MakeRasterN32Premul(width, height); - if (bg) { - surface->getCanvas()->drawColor(*bg); - } - return surface; - } - static const sk_sp testImage; static const sk_sp makeTestImage() { sk_sp surface = @@ -1381,7 +1892,7 @@ class CanvasCompareTester { SkCanvas* canvas = surface->getCanvas(); SkPaint p0, p1; p0.setStyle(SkPaint::kFill_Style); - p0.setColor(SK_ColorGREEN); + p0.setColor(SkColorSetARGB(0xff, 0x00, 0xfe, 0x00)); // off-green p1.setStyle(SkPaint::kFill_Style); p1.setColor(SK_ColorBLUE); // Some pixels need some transparency for DstIn testing @@ -1396,6 +1907,8 @@ class CanvasCompareTester { return surface->makeImageSnapshot(); } + static const sk_sp testImageShader; + static sk_sp MakeTextBlob(std::string string, SkScalar font_height) { SkFont font(SkTypeface::MakeFromName("ahem", SkFontStyle::Normal()), @@ -1405,33 +1918,40 @@ class CanvasCompareTester { } }; -bool CanvasCompareTester::TestingDrawShadows = false; -bool CanvasCompareTester::TestingDrawVertices = false; -bool CanvasCompareTester::TestingDrawAtlas = false; +// bool CanvasCompareTester::TestingDrawShadows = false; +// bool CanvasCompareTester::TestingDrawVertices = false; +// bool CanvasCompareTester::TestingDrawAtlas = false; +// bool CanvasCompareTester::TestingDrawTextBlob = false; BoundsTolerance CanvasCompareTester::DefaultTolerance = BoundsTolerance().addAbsolutePadding(1, 1); -const sk_sp CanvasCompareTester::testImage = - CanvasCompareTester::makeTestImage(); +const sk_sp CanvasCompareTester::testImage = makeTestImage(); +const sk_sp CanvasCompareTester::testImageShader = + makeTestImage()->makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat, + SkSamplingOptions()); TEST(DisplayListCanvas, DrawPaint) { - CanvasCompareTester::RenderAll( - [=](SkCanvas* canvas, SkPaint& paint) { // - canvas->drawPaint(paint); - }, - [=](DisplayListBuilder& builder) { // - builder.drawPaint(); - }); + CanvasCompareTester::RenderAll(TestParameters( + [=](SkCanvas* canvas, SkPaint& paint) { + canvas->drawPaint(paint); + }, + [=](DisplayListBuilder& builder) { + builder.drawPaint(); + }, + DisplayListAttributeFlags::kDrawPaintFlags) + ); } TEST(DisplayListCanvas, DrawColor) { - CanvasCompareTester::RenderNoAttributes( // - [=](SkCanvas* canvas, SkPaint& paint) { // - canvas->drawColor(SK_ColorMAGENTA); - }, - [=](DisplayListBuilder& builder) { // - builder.drawColor(SK_ColorMAGENTA, SkBlendMode::kSrcOver); - }); + CanvasCompareTester::RenderAll(TestParameters( + [=](SkCanvas* canvas, SkPaint& paint) { + canvas->drawColor(SK_ColorMAGENTA); + }, + [=](DisplayListBuilder& builder) { + builder.drawColor(SK_ColorMAGENTA, SkBlendMode::kSrcOver); + }, + DisplayListAttributeFlags::kDrawColorFlags) + ); } BoundsTolerance lineTolerance(const BoundsTolerance& tolerance, @@ -1456,7 +1976,7 @@ BoundsTolerance lineTolerance(const BoundsTolerance& tolerance, adjust = std::max(adjust, half_width); } if (adjust == 0) { - return CanvasCompareTester::DefaultAdjuster(tolerance, paint, matrix); + return tolerance; } SkScalar hTolerance; SkScalar vTolerance; @@ -1475,34 +1995,34 @@ BoundsTolerance lineTolerance(const BoundsTolerance& tolerance, } BoundsTolerance new_tolerance = tolerance.addBoundsPadding(hTolerance, vTolerance); - return CanvasCompareTester::DefaultAdjuster(new_tolerance, paint, matrix); + return new_tolerance; } // For drawing horizontal lines -BoundsTolerance hLineTolerance(const BoundsTolerance& tolerance, - const SkPaint& paint, - const SkMatrix& matrix) { +BoundsTolerance hLineAdjuster(const BoundsTolerance& tolerance, + const SkPaint& paint, + const SkMatrix& matrix) { return lineTolerance(tolerance, paint, matrix, true, false, false); } // For drawing vertical lines -BoundsTolerance vLineTolerance(const BoundsTolerance& tolerance, - const SkPaint& paint, - const SkMatrix& matrix) { +BoundsTolerance vLineAdjuster(const BoundsTolerance& tolerance, + const SkPaint& paint, + const SkMatrix& matrix) { return lineTolerance(tolerance, paint, matrix, false, true, false); } // For drawing diagonal lines -BoundsTolerance dLineTolerance(const BoundsTolerance& tolerance, - const SkPaint& paint, - const SkMatrix& matrix) { +BoundsTolerance dLineAdjuster(const BoundsTolerance& tolerance, + const SkPaint& paint, + const SkMatrix& matrix) { return lineTolerance(tolerance, paint, matrix, false, false, false); } // For drawing individual points (drawPoints(Point_Mode)) -BoundsTolerance pointsTolerance(const BoundsTolerance& tolerance, - const SkPaint& paint, - const SkMatrix& matrix) { +BoundsTolerance pointsAdjuster(const BoundsTolerance& tolerance, + const SkPaint& paint, + const SkMatrix& matrix) { return lineTolerance(tolerance, paint, matrix, false, false, true); } @@ -1513,6 +2033,7 @@ TEST(DisplayListCanvas, DrawDiagonalLines) { SkPoint p4 = SkPoint::Make(RenderRight, RenderTop); CanvasCompareTester::RenderAll( + TestParameters( [=](SkCanvas* canvas, SkPaint& paint) { // // Skia requires kStroke style on horizontal and vertical // lines to get the bounds correct. @@ -1526,89 +2047,101 @@ TEST(DisplayListCanvas, DrawDiagonalLines) { builder.drawLine(p1, p2); builder.drawLine(p3, p4); }, - dLineTolerance); + DisplayListAttributeFlags::kDrawLineFlags + )); } -TEST(DisplayListCanvas, DrawHorizontalLine) { - SkPoint p1 = SkPoint::Make(RenderLeft, RenderCenterY); - SkPoint p2 = SkPoint::Make(RenderRight, RenderCenterY); - - CanvasCompareTester::RenderAll( - [=](SkCanvas* canvas, SkPaint& paint) { // - // Skia requires kStroke style on horizontal and vertical - // lines to get the bounds correct. - // See https://bugs.chromium.org/p/skia/issues/detail?id=12446 - SkPaint p = paint; - p.setStyle(SkPaint::kStroke_Style); - canvas->drawLine(p1, p2, p); - }, - [=](DisplayListBuilder& builder) { // - builder.drawLine(p1, p2); - }, - hLineTolerance); -} - -TEST(DisplayListCanvas, DrawVerticalLine) { - SkPoint p1 = SkPoint::Make(RenderCenterX, RenderTop); - SkPoint p2 = SkPoint::Make(RenderCenterY, RenderBottom); - - CanvasCompareTester::RenderAll( - [=](SkCanvas* canvas, SkPaint& paint) { // - // Skia requires kStroke style on horizontal and vertical - // lines to get the bounds correct. - // See https://bugs.chromium.org/p/skia/issues/detail?id=12446 - SkPaint p = paint; - p.setStyle(SkPaint::kStroke_Style); - canvas->drawLine(p1, p2, p); - }, - [=](DisplayListBuilder& builder) { // - builder.drawLine(p1, p2); - }, - vLineTolerance); -} +// TEST(DisplayListCanvas, DrawHorizontalLine) { +// SkPoint p1 = SkPoint::Make(RenderLeft, RenderCenterY); +// SkPoint p2 = SkPoint::Make(RenderRight, RenderCenterY); + +// CanvasCompareTester::RenderAll( +// [=](SkCanvas* canvas, SkPaint& paint) { // +// // Skia requires kStroke style on horizontal and vertical +// // lines to get the bounds correct. +// // See https://bugs.chromium.org/p/skia/issues/detail?id=12446 +// SkPaint p = paint; +// p.setStyle(SkPaint::kStroke_Style); +// canvas->drawLine(p1, p2, p); +// }, +// [=](DisplayListBuilder& builder) { // +// builder.drawLine(p1, p2); +// }, +// MutationPredictor(DisplayListAttributeFlags::kDrawHVLineFlags), +// hLineAdjuster); +// } + +// TEST(DisplayListCanvas, DrawVerticalLine) { +// SkPoint p1 = SkPoint::Make(RenderCenterX, RenderTop); +// SkPoint p2 = SkPoint::Make(RenderCenterY, RenderBottom); + +// CanvasCompareTester::RenderAll( +// [=](SkCanvas* canvas, SkPaint& paint) { // +// // Skia requires kStroke style on horizontal and vertical +// // lines to get the bounds correct. +// // See https://bugs.chromium.org/p/skia/issues/detail?id=12446 +// SkPaint p = paint; +// p.setStyle(SkPaint::kStroke_Style); +// canvas->drawLine(p1, p2, p); +// }, +// [=](DisplayListBuilder& builder) { // +// builder.drawLine(p1, p2); +// }, +// MutationPredictor(DisplayListAttributeFlags::kDrawHVLineFlags), +// vLineAdjuster); +// } TEST(DisplayListCanvas, DrawRect) { - CanvasCompareTester::RenderAll( - [=](SkCanvas* canvas, SkPaint& paint) { // - canvas->drawRect(RenderBounds, paint); - }, - [=](DisplayListBuilder& builder) { // - builder.drawRect(RenderBounds); - }); + // Bounds are offset by 0.5 pixels to induce AA + CanvasCompareTester::RenderAll(TestParameters( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawRect(RenderBounds.makeOffset(0.5, 0.5), paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawRect(RenderBounds.makeOffset(0.5, 0.5)); + }, + DisplayListAttributeFlags::kDrawRectFlags) + ); } TEST(DisplayListCanvas, DrawOval) { SkRect rect = RenderBounds.makeInset(0, 10); - CanvasCompareTester::RenderAll( - [=](SkCanvas* canvas, SkPaint& paint) { // - canvas->drawOval(rect, paint); - }, - [=](DisplayListBuilder& builder) { // - builder.drawOval(rect); - }); + CanvasCompareTester::RenderAll(TestParameters( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawOval(rect, paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawOval(rect); + }, + DisplayListAttributeFlags::kDrawOvalFlags) + ); } TEST(DisplayListCanvas, DrawCircle) { - CanvasCompareTester::RenderAll( - [=](SkCanvas* canvas, SkPaint& paint) { // - canvas->drawCircle(TestCenter, RenderRadius, paint); - }, - [=](DisplayListBuilder& builder) { // - builder.drawCircle(TestCenter, RenderRadius); - }); + CanvasCompareTester::RenderAll(TestParameters( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawCircle(TestCenter, RenderRadius, paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawCircle(TestCenter, RenderRadius); + }, + DisplayListAttributeFlags::kDrawCircleFlags) + ); } TEST(DisplayListCanvas, DrawRRect) { SkRRect rrect = SkRRect::MakeRectXY(RenderBounds, RenderCornerRadius, RenderCornerRadius); - CanvasCompareTester::RenderAll( - [=](SkCanvas* canvas, SkPaint& paint) { // - canvas->drawRRect(rrect, paint); - }, - [=](DisplayListBuilder& builder) { // - builder.drawRRect(rrect); - }); + CanvasCompareTester::RenderAll(TestParameters( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawRRect(rrect, paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawRRect(rrect); + }, + DisplayListAttributeFlags::kDrawRRectFlags) + ); } TEST(DisplayListCanvas, DrawDRRect) { @@ -1617,13 +2150,15 @@ TEST(DisplayListCanvas, DrawDRRect) { SkRect innerBounds = RenderBounds.makeInset(30.0, 30.0); SkRRect inner = SkRRect::MakeRectXY(innerBounds, RenderCornerRadius, RenderCornerRadius); - CanvasCompareTester::RenderAll( - [=](SkCanvas* canvas, SkPaint& paint) { // - canvas->drawDRRect(outer, inner, paint); - }, - [=](DisplayListBuilder& builder) { // - builder.drawDRRect(outer, inner); - }); + CanvasCompareTester::RenderAll(TestParameters( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawDRRect(outer, inner, paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawDRRect(outer, inner); + }, + DisplayListAttributeFlags::kDrawDRRectFlags) + ); } TEST(DisplayListCanvas, DrawPath) { @@ -1639,154 +2174,163 @@ TEST(DisplayListCanvas, DrawPath) { path.lineTo(HorizontalMiterDiamondPoints[i]); } path.close(); - CanvasCompareTester::RenderAll( - [=](SkCanvas* canvas, SkPaint& paint) { // - canvas->drawPath(path, paint); - }, - [=](DisplayListBuilder& builder) { // - builder.drawPath(path); - }); + CanvasCompareTester::RenderAll(TestParameters( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawPath(path, paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawPath(path); + }, + DisplayListAttributeFlags::kDrawPathFlags) + ); } TEST(DisplayListCanvas, DrawArc) { - CanvasCompareTester::RenderAll( - [=](SkCanvas* canvas, SkPaint& paint) { // - canvas->drawArc(RenderBounds, 60, 330, false, paint); - }, - [=](DisplayListBuilder& builder) { // - builder.drawArc(RenderBounds, 60, 330, false); - }); + CanvasCompareTester::RenderAll(TestParameters( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawArc(RenderBounds, 60, 330, false, paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawArc(RenderBounds, 60, 330, false); + }, + DisplayListAttributeFlags::kDrawArcFlags) + ); } TEST(DisplayListCanvas, DrawArcCenter) { - CanvasCompareTester::RenderAll( - [=](SkCanvas* canvas, SkPaint& paint) { // - canvas->drawArc(RenderBounds, 60, 330, true, paint); - }, - [=](DisplayListBuilder& builder) { // - builder.drawArc(RenderBounds, 60, 330, true); - }); -} - -TEST(DisplayListCanvas, DrawPointsAsPoints) { - // The +/- 16 points are designed to fall just inside the clips - // that are tested against so we avoid lots of undrawn pixels - // in the accumulated bounds. - const SkScalar x0 = RenderLeft; - const SkScalar x1 = RenderLeft + 16; - const SkScalar x2 = (RenderLeft + RenderCenterX) * 0.5; - const SkScalar x3 = RenderCenterX; - const SkScalar x4 = (RenderRight + RenderCenterX) * 0.5; - const SkScalar x5 = RenderRight - 16; - const SkScalar x6 = RenderRight; - - const SkScalar y0 = RenderTop; - const SkScalar y1 = RenderTop + 16; - const SkScalar y2 = (RenderTop + RenderCenterY) * 0.5; - const SkScalar y3 = RenderCenterY; - const SkScalar y4 = (RenderBottom + RenderCenterY) * 0.5; - const SkScalar y5 = RenderBottom - 16; - const SkScalar y6 = RenderBottom; - - // clang-format off - const SkPoint points[] = { - {x0, y0}, {x1, y0}, {x2, y0}, {x3, y0}, {x4, y0}, {x5, y0}, {x6, y0}, - {x0, y1}, {x1, y1}, {x2, y1}, {x3, y1}, {x4, y1}, {x5, y1}, {x6, y1}, - {x0, y2}, {x1, y2}, {x2, y2}, {x3, y2}, {x4, y2}, {x5, y2}, {x6, y2}, - {x0, y3}, {x1, y3}, {x2, y3}, {x3, y3}, {x4, y3}, {x5, y3}, {x6, y3}, - {x0, y4}, {x1, y4}, {x2, y4}, {x3, y4}, {x4, y4}, {x5, y4}, {x6, y4}, - {x0, y5}, {x1, y5}, {x2, y5}, {x3, y5}, {x4, y5}, {x5, y5}, {x6, y5}, - {x0, y6}, {x1, y6}, {x2, y6}, {x3, y6}, {x4, y6}, {x5, y6}, {x6, y6}, - }; - // clang-format on - const int count = sizeof(points) / sizeof(points[0]); - - CanvasCompareTester::RenderAll( - [=](SkCanvas* canvas, SkPaint& paint) { // - // Skia requires kStroke style on horizontal and vertical - // lines to get the bounds correct. - // See https://bugs.chromium.org/p/skia/issues/detail?id=12446 - SkPaint p = paint; - p.setStyle(SkPaint::kStroke_Style); - canvas->drawPoints(SkCanvas::kPoints_PointMode, count, points, p); - }, - [=](DisplayListBuilder& builder) { // - builder.drawPoints(SkCanvas::kPoints_PointMode, count, points); - }, - pointsTolerance); + CanvasCompareTester::RenderAll(TestParameters( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawArc(RenderBounds, 60, 330, true, paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawArc(RenderBounds, 60, 330, true); + }, + DisplayListAttributeFlags::kDrawArcFlags) + ); } -TEST(DisplayListCanvas, DrawPointsAsLines) { - const SkScalar x0 = RenderLeft + 1; - const SkScalar x1 = RenderLeft + 16; - const SkScalar x2 = RenderRight - 16; - const SkScalar x3 = RenderRight - 1; - - const SkScalar y0 = RenderTop; - const SkScalar y1 = RenderTop + 16; - const SkScalar y2 = RenderBottom - 16; - const SkScalar y3 = RenderBottom; - - // clang-format off - const SkPoint points[] = { - // Outer box - {x0, y0}, {x3, y0}, - {x3, y0}, {x3, y3}, - {x3, y3}, {x0, y3}, - {x0, y3}, {x0, y0}, - - // Diagonals - {x0, y0}, {x3, y3}, {x3, y0}, {x0, y3}, - - // Inner box - {x1, y1}, {x2, y1}, - {x2, y1}, {x2, y2}, - {x2, y2}, {x1, y2}, - {x1, y2}, {x1, y1}, - }; - // clang-format on - - const int count = sizeof(points) / sizeof(points[0]); - ASSERT_TRUE((count & 1) == 0); - CanvasCompareTester::RenderAll( - [=](SkCanvas* canvas, SkPaint& paint) { // - // Skia requires kStroke style on horizontal and vertical - // lines to get the bounds correct. - // See https://bugs.chromium.org/p/skia/issues/detail?id=12446 - SkPaint p = paint; - p.setStyle(SkPaint::kStroke_Style); - canvas->drawPoints(SkCanvas::kLines_PointMode, count, points, p); - }, - [=](DisplayListBuilder& builder) { // - builder.drawPoints(SkCanvas::kLines_PointMode, count, points); - }); -} - -TEST(DisplayListCanvas, DrawPointsAsPolygon) { - const SkPoint points1[] = { - // RenderBounds box with a diagonal - SkPoint::Make(RenderLeft, RenderTop), - SkPoint::Make(RenderRight, RenderTop), - SkPoint::Make(RenderRight, RenderBottom), - SkPoint::Make(RenderLeft, RenderBottom), - SkPoint::Make(RenderLeft, RenderTop), - SkPoint::Make(RenderRight, RenderBottom), - }; - const int count1 = sizeof(points1) / sizeof(points1[0]); - - CanvasCompareTester::RenderAll( - [=](SkCanvas* canvas, SkPaint& paint) { // - // Skia requires kStroke style on horizontal and vertical - // lines to get the bounds correct. - // See https://bugs.chromium.org/p/skia/issues/detail?id=12446 - SkPaint p = paint; - p.setStyle(SkPaint::kStroke_Style); - canvas->drawPoints(SkCanvas::kPolygon_PointMode, count1, points1, p); - }, - [=](DisplayListBuilder& builder) { // - builder.drawPoints(SkCanvas::kPolygon_PointMode, count1, points1); - }); -} +// TEST(DisplayListCanvas, DrawPointsAsPoints) { +// // The +/- 16 points are designed to fall just inside the clips +// // that are tested against so we avoid lots of undrawn pixels +// // in the accumulated bounds. +// const SkScalar x0 = RenderLeft; +// const SkScalar x1 = RenderLeft + 16; +// const SkScalar x2 = (RenderLeft + RenderCenterX) * 0.5; +// const SkScalar x3 = RenderCenterX + 0.1; +// const SkScalar x4 = (RenderRight + RenderCenterX) * 0.5; +// const SkScalar x5 = RenderRight - 16; +// const SkScalar x6 = RenderRight; + +// const SkScalar y0 = RenderTop; +// const SkScalar y1 = RenderTop + 16; +// const SkScalar y2 = (RenderTop + RenderCenterY) * 0.5; +// const SkScalar y3 = RenderCenterY + 0.1; +// const SkScalar y4 = (RenderBottom + RenderCenterY) * 0.5; +// const SkScalar y5 = RenderBottom - 16; +// const SkScalar y6 = RenderBottom; + +// // clang-format off +// const SkPoint points[] = { +// {x0, y0}, {x1, y0}, {x2, y0}, {x3, y0}, {x4, y0}, {x5, y0}, {x6, y0}, +// {x0, y1}, {x1, y1}, {x2, y1}, {x3, y1}, {x4, y1}, {x5, y1}, {x6, y1}, +// {x0, y2}, {x1, y2}, {x2, y2}, {x3, y2}, {x4, y2}, {x5, y2}, {x6, y2}, +// {x0, y3}, {x1, y3}, {x2, y3}, {x3, y3}, {x4, y3}, {x5, y3}, {x6, y3}, +// {x0, y4}, {x1, y4}, {x2, y4}, {x3, y4}, {x4, y4}, {x5, y4}, {x6, y4}, +// {x0, y5}, {x1, y5}, {x2, y5}, {x3, y5}, {x4, y5}, {x5, y5}, {x6, y5}, +// {x0, y6}, {x1, y6}, {x2, y6}, {x3, y6}, {x4, y6}, {x5, y6}, {x6, y6}, +// }; +// // clang-format on +// const int count = sizeof(points) / sizeof(points[0]); + +// CanvasCompareTester::RenderAll( +// [=](SkCanvas* canvas, SkPaint& paint) { // +// // Skia requires kStroke style on horizontal and vertical +// // lines to get the bounds correct. +// // See https://bugs.chromium.org/p/skia/issues/detail?id=12446 +// SkPaint p = paint; +// p.setStyle(SkPaint::kStroke_Style); +// canvas->drawPoints(SkCanvas::kPoints_PointMode, count, points, p); +// }, +// [=](DisplayListBuilder& builder) { // +// builder.drawPoints(SkCanvas::kPoints_PointMode, count, points); +// }, +// MutationPredictor(DisplayListAttributeFlags::kDrawPointsAsPointsFlags), +// pointsAdjuster); +// } + +// TEST(DisplayListCanvas, DrawPointsAsLines) { +// const SkScalar x0 = RenderLeft + 1; +// const SkScalar x1 = RenderLeft + 16; +// const SkScalar x2 = RenderRight - 16; +// const SkScalar x3 = RenderRight - 1; + +// const SkScalar y0 = RenderTop; +// const SkScalar y1 = RenderTop + 16; +// const SkScalar y2 = RenderBottom - 16; +// const SkScalar y3 = RenderBottom; + +// // clang-format off +// const SkPoint points[] = { +// // Outer box +// {x0, y0}, {x3, y0}, +// {x3, y0}, {x3, y3}, +// {x3, y3}, {x0, y3}, +// {x0, y3}, {x0, y0}, + +// // Diagonals +// {x0, y0}, {x3, y3}, {x3, y0}, {x0, y3}, + +// // Inner box +// {x1, y1}, {x2, y1}, +// {x2, y1}, {x2, y2}, +// {x2, y2}, {x1, y2}, +// {x1, y2}, {x1, y1}, +// }; +// // clang-format on + +// const int count = sizeof(points) / sizeof(points[0]); +// ASSERT_TRUE((count & 1) == 0); +// CanvasCompareTester::RenderAll( +// [=](SkCanvas* canvas, SkPaint& paint) { // +// // Skia requires kStroke style on horizontal and vertical +// // lines to get the bounds correct. +// // See https://bugs.chromium.org/p/skia/issues/detail?id=12446 +// SkPaint p = paint; +// p.setStyle(SkPaint::kStroke_Style); +// canvas->drawPoints(SkCanvas::kLines_PointMode, count, points, p); +// }, +// [=](DisplayListBuilder& builder) { // +// builder.drawPoints(SkCanvas::kLines_PointMode, count, points); +// }, +// MutationPredictor(DisplayListAttributeFlags::kDrawPointsAsLinesFlags)); +// } + +// TEST(DisplayListCanvas, DrawPointsAsPolygon) { +// const SkPoint points1[] = { +// // RenderBounds box with a diagonal +// SkPoint::Make(RenderLeft, RenderTop), +// SkPoint::Make(RenderRight, RenderTop), +// SkPoint::Make(RenderRight, RenderBottom), +// SkPoint::Make(RenderLeft, RenderBottom), +// SkPoint::Make(RenderLeft, RenderTop), +// SkPoint::Make(RenderRight, RenderBottom), +// }; +// const int count1 = sizeof(points1) / sizeof(points1[0]); + +// CanvasCompareTester::RenderAll( +// [=](SkCanvas* canvas, SkPaint& paint) { // +// // Skia requires kStroke style on horizontal and vertical +// // lines to get the bounds correct. +// // See https://bugs.chromium.org/p/skia/issues/detail?id=12446 +// SkPaint p = paint; +// p.setStyle(SkPaint::kStroke_Style); +// canvas->drawPoints(SkCanvas::kPolygon_PointMode, count1, points1, p); +// }, +// [=](DisplayListBuilder& builder) { // +// builder.drawPoints(SkCanvas::kPolygon_PointMode, count1, points1); +// }, +// MutationPredictor(DisplayListAttributeFlags::kDrawPointsAsPolygonFlags)); +// } TEST(DisplayListCanvas, DrawVerticesWithColors) { // Cover as many sides of the box with only 6 vertices: @@ -1814,14 +2358,19 @@ TEST(DisplayListCanvas, DrawVerticesWithColors) { }; const sk_sp vertices = SkVertices::MakeCopy( SkVertices::kTriangles_VertexMode, 6, pts, nullptr, colors); - CanvasCompareTester::RenderVertices( + + CanvasCompareTester::RenderAll( + TestParameters( [=](SkCanvas* canvas, SkPaint& paint) { // canvas->drawVertices(vertices.get(), SkBlendMode::kSrcOver, paint); }, [=](DisplayListBuilder& builder) { // builder.drawVertices(vertices, SkBlendMode::kSrcOver); - }); - ASSERT_TRUE(vertices->unique()); + }, + DisplayListAttributeFlags::kDrawVerticesFlags + ).set_draw_vertices() + ); + EXPECT_TRUE(vertices->unique()); } TEST(DisplayListCanvas, DrawVerticesWithImage) { @@ -1854,223 +2403,255 @@ TEST(DisplayListCanvas, DrawVerticesWithImage) { }; const sk_sp vertices = SkVertices::MakeCopy( SkVertices::kTriangles_VertexMode, 6, pts, tex, nullptr); - const sk_sp shader = CanvasCompareTester::testImage->makeShader( - SkTileMode::kRepeat, SkTileMode::kRepeat, SkSamplingOptions()); - CanvasCompareTester::RenderVertices( + + CanvasCompareTester::RenderAll( + TestParameters( [=](SkCanvas* canvas, SkPaint& paint) { // - paint.setShader(shader); + if (paint.getShader() == nullptr) { + paint.setShader(CanvasCompareTester::testImageShader); + } canvas->drawVertices(vertices.get(), SkBlendMode::kSrcOver, paint); }, [=](DisplayListBuilder& builder) { // - builder.setShader(shader); + if (builder.getShader() == nullptr) { + builder.setShader(CanvasCompareTester::testImageShader); + } builder.drawVertices(vertices, SkBlendMode::kSrcOver); - }); - ASSERT_TRUE(vertices->unique()); - ASSERT_TRUE(shader->unique()); + }, + DisplayListAttributeFlags::kDrawVerticesFlags + ).set_draw_vertices() + ); + + EXPECT_TRUE(vertices->unique()); + EXPECT_TRUE(CanvasCompareTester::testImageShader->unique()); } TEST(DisplayListCanvas, DrawImageNearest) { - CanvasCompareTester::RenderAll( - [=](SkCanvas* canvas, SkPaint& paint) { // - canvas->drawImage(CanvasCompareTester::testImage, RenderLeft, RenderTop, - DisplayList::NearestSampling, &paint); - }, - [=](DisplayListBuilder& builder) { // - builder.drawImage(CanvasCompareTester::testImage, - SkPoint::Make(RenderLeft, RenderTop), - DisplayList::NearestSampling, true); - }); + CanvasCompareTester::RenderAll(TestParameters( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawImage(CanvasCompareTester::testImage, RenderLeft, RenderTop, + DisplayList::NearestSampling, &paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawImage(CanvasCompareTester::testImage, + SkPoint::Make(RenderLeft, RenderTop), + DisplayList::NearestSampling, true); + }, + DisplayListAttributeFlags::kDrawImageWithPaintFlags) + ); } TEST(DisplayListCanvas, DrawImageNearestNoPaint) { - CanvasCompareTester::RenderAll( - [=](SkCanvas* canvas, SkPaint& paint) { // - canvas->drawImage(CanvasCompareTester::testImage, RenderLeft, RenderTop, - DisplayList::NearestSampling, nullptr); - }, - [=](DisplayListBuilder& builder) { // - builder.drawImage(CanvasCompareTester::testImage, - SkPoint::Make(RenderLeft, RenderTop), - DisplayList::NearestSampling, false); - }); + CanvasCompareTester::RenderAll(TestParameters( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawImage(CanvasCompareTester::testImage, RenderLeft, RenderTop, + DisplayList::NearestSampling, nullptr); + }, + [=](DisplayListBuilder& builder) { // + builder.drawImage(CanvasCompareTester::testImage, + SkPoint::Make(RenderLeft, RenderTop), + DisplayList::NearestSampling, false); + }, + DisplayListAttributeFlags::kDrawImageFlags) + ); } TEST(DisplayListCanvas, DrawImageLinear) { - CanvasCompareTester::RenderAll( - [=](SkCanvas* canvas, SkPaint& paint) { // - canvas->drawImage(CanvasCompareTester::testImage, RenderLeft, RenderTop, - DisplayList::LinearSampling, &paint); - }, - [=](DisplayListBuilder& builder) { // - builder.drawImage(CanvasCompareTester::testImage, - SkPoint::Make(RenderLeft, RenderTop), - DisplayList::LinearSampling, true); - }); + CanvasCompareTester::RenderAll(TestParameters( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawImage(CanvasCompareTester::testImage, RenderLeft, RenderTop, + DisplayList::LinearSampling, &paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawImage(CanvasCompareTester::testImage, + SkPoint::Make(RenderLeft, RenderTop), + DisplayList::LinearSampling, true); + }, + DisplayListAttributeFlags::kDrawImageWithPaintFlags) + ); } TEST(DisplayListCanvas, DrawImageRectNearest) { SkRect src = SkRect::MakeIWH(RenderWidth, RenderHeight).makeInset(5, 5); - SkRect dst = RenderBounds.makeInset(15.5, 10.5); - CanvasCompareTester::RenderAll( - [=](SkCanvas* canvas, SkPaint& paint) { // - canvas->drawImageRect(CanvasCompareTester::testImage, src, dst, - DisplayList::NearestSampling, &paint, - SkCanvas::kFast_SrcRectConstraint); - }, - [=](DisplayListBuilder& builder) { // - builder.drawImageRect(CanvasCompareTester::testImage, src, dst, - DisplayList::NearestSampling, true); - }); + SkRect dst = RenderBounds.makeInset(10.5, 10.5); + CanvasCompareTester::RenderAll(TestParameters( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawImageRect(CanvasCompareTester::testImage, src, dst, + DisplayList::NearestSampling, &paint, + SkCanvas::kFast_SrcRectConstraint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawImageRect(CanvasCompareTester::testImage, src, dst, + DisplayList::NearestSampling, true); + }, + DisplayListAttributeFlags::kDrawImageRectWithPaintFlags) + ); } TEST(DisplayListCanvas, DrawImageRectNearestNoPaint) { SkRect src = SkRect::MakeIWH(RenderWidth, RenderHeight).makeInset(5, 5); - SkRect dst = RenderBounds.makeInset(15.5, 10.5); - CanvasCompareTester::RenderAll( - [=](SkCanvas* canvas, SkPaint& paint) { // - canvas->drawImageRect(CanvasCompareTester::testImage, src, dst, - DisplayList::NearestSampling, nullptr, - SkCanvas::kFast_SrcRectConstraint); - }, - [=](DisplayListBuilder& builder) { // - builder.drawImageRect(CanvasCompareTester::testImage, src, dst, - DisplayList::NearestSampling, false); - }); + SkRect dst = RenderBounds.makeInset(10.5, 10.5); + CanvasCompareTester::RenderAll(TestParameters( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawImageRect(CanvasCompareTester::testImage, src, dst, + DisplayList::NearestSampling, nullptr, + SkCanvas::kFast_SrcRectConstraint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawImageRect(CanvasCompareTester::testImage, src, dst, + DisplayList::NearestSampling, false); + }, + DisplayListAttributeFlags::kDrawImageRectFlags) + ); } TEST(DisplayListCanvas, DrawImageRectLinear) { SkRect src = SkRect::MakeIWH(RenderWidth, RenderHeight).makeInset(5, 5); - SkRect dst = RenderBounds.makeInset(15.5, 10.5); - CanvasCompareTester::RenderAll( - [=](SkCanvas* canvas, SkPaint& paint) { // - canvas->drawImageRect(CanvasCompareTester::testImage, src, dst, - DisplayList::LinearSampling, &paint, - SkCanvas::kFast_SrcRectConstraint); - }, - [=](DisplayListBuilder& builder) { // - builder.drawImageRect(CanvasCompareTester::testImage, src, dst, - DisplayList::LinearSampling, true); - }); + SkRect dst = RenderBounds.makeInset(10.5, 10.5); + CanvasCompareTester::RenderAll(TestParameters( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawImageRect(CanvasCompareTester::testImage, src, dst, + DisplayList::LinearSampling, &paint, + SkCanvas::kFast_SrcRectConstraint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawImageRect(CanvasCompareTester::testImage, src, dst, + DisplayList::LinearSampling, true); + }, + DisplayListAttributeFlags::kDrawImageRectWithPaintFlags) + ); } TEST(DisplayListCanvas, DrawImageNineNearest) { - SkIRect src = SkIRect::MakeWH(RenderWidth, RenderHeight).makeInset(5, 5); - SkRect dst = RenderBounds.makeInset(15.5, 10.5); - CanvasCompareTester::RenderAll( - [=](SkCanvas* canvas, SkPaint& paint) { // - canvas->drawImageNine(CanvasCompareTester::testImage.get(), src, dst, - SkFilterMode::kNearest, &paint); - }, - [=](DisplayListBuilder& builder) { // - builder.drawImageNine(CanvasCompareTester::testImage, src, dst, - SkFilterMode::kNearest, true); - }); + SkIRect src = SkIRect::MakeWH(RenderWidth, RenderHeight).makeInset(25, 25); + SkRect dst = RenderBounds.makeInset(10.5, 10.5); + CanvasCompareTester::RenderAll(TestParameters( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawImageNine(CanvasCompareTester::testImage.get(), src, dst, + SkFilterMode::kNearest, &paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawImageNine(CanvasCompareTester::testImage, src, dst, + SkFilterMode::kNearest, true); + }, + DisplayListAttributeFlags::kDrawImageNineWithPaintFlags) + ); } TEST(DisplayListCanvas, DrawImageNineNearestNoPaint) { - SkIRect src = SkIRect::MakeWH(RenderWidth, RenderHeight).makeInset(5, 5); - SkRect dst = RenderBounds.makeInset(15.5, 10.5); - CanvasCompareTester::RenderAll( - [=](SkCanvas* canvas, SkPaint& paint) { // - canvas->drawImageNine(CanvasCompareTester::testImage.get(), src, dst, - SkFilterMode::kNearest, nullptr); - }, - [=](DisplayListBuilder& builder) { // - builder.drawImageNine(CanvasCompareTester::testImage, src, dst, - SkFilterMode::kNearest, false); - }); + SkIRect src = SkIRect::MakeWH(RenderWidth, RenderHeight).makeInset(25, 25); + SkRect dst = RenderBounds.makeInset(10.5, 10.5); + CanvasCompareTester::RenderAll(TestParameters( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawImageNine(CanvasCompareTester::testImage.get(), src, dst, + SkFilterMode::kNearest, nullptr); + }, + [=](DisplayListBuilder& builder) { // + builder.drawImageNine(CanvasCompareTester::testImage, src, dst, + SkFilterMode::kNearest, false); + }, + DisplayListAttributeFlags::kDrawImageNineFlags) + ); } TEST(DisplayListCanvas, DrawImageNineLinear) { - SkIRect src = SkIRect::MakeWH(RenderWidth, RenderHeight).makeInset(5, 5); - SkRect dst = RenderBounds.makeInset(15.5, 10.5); - CanvasCompareTester::RenderAll( - [=](SkCanvas* canvas, SkPaint& paint) { // - canvas->drawImageNine(CanvasCompareTester::testImage.get(), src, dst, - SkFilterMode::kLinear, &paint); - }, - [=](DisplayListBuilder& builder) { // - builder.drawImageNine(CanvasCompareTester::testImage, src, dst, - SkFilterMode::kLinear, true); - }); + SkIRect src = SkIRect::MakeWH(RenderWidth, RenderHeight).makeInset(25, 25); + SkRect dst = RenderBounds.makeInset(10.5, 10.5); + CanvasCompareTester::RenderAll(TestParameters( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawImageNine(CanvasCompareTester::testImage.get(), src, dst, + SkFilterMode::kLinear, &paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawImageNine(CanvasCompareTester::testImage, src, dst, + SkFilterMode::kLinear, true); + }, + DisplayListAttributeFlags::kDrawImageNineWithPaintFlags) + ); } TEST(DisplayListCanvas, DrawImageLatticeNearest) { - const SkRect dst = RenderBounds.makeInset(15.5, 10.5); + const SkRect dst = RenderBounds.makeInset(10.5, 10.5); const int divX[] = { - (RenderLeft + RenderCenterX) / 2, - RenderCenterX, - (RenderRight + RenderCenterX) / 2, + RenderWidth * 1 / 4, + RenderWidth * 2 / 4, + RenderWidth * 3 / 4, }; const int divY[] = { - (RenderTop + RenderCenterY) / 2, - RenderCenterY, - (RenderBottom + RenderCenterY) / 2, + RenderHeight * 1 / 4, + RenderHeight * 2 / 4, + RenderHeight * 3 / 4, }; SkCanvas::Lattice lattice = { divX, divY, nullptr, 3, 3, nullptr, nullptr, }; - CanvasCompareTester::RenderAll( - [=](SkCanvas* canvas, SkPaint& paint) { // - canvas->drawImageLattice(CanvasCompareTester::testImage.get(), lattice, - dst, SkFilterMode::kNearest, &paint); - }, - [=](DisplayListBuilder& builder) { // - builder.drawImageLattice(CanvasCompareTester::testImage, lattice, // - dst, SkFilterMode::kNearest, true); - }); + CanvasCompareTester::RenderAll(TestParameters( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawImageLattice(CanvasCompareTester::testImage.get(), lattice, + dst, SkFilterMode::kNearest, &paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawImageLattice(CanvasCompareTester::testImage, lattice, // + dst, SkFilterMode::kNearest, true); + }, + DisplayListAttributeFlags::kDrawImageLatticeWithPaintFlags) + ); } TEST(DisplayListCanvas, DrawImageLatticeNearestNoPaint) { - const SkRect dst = RenderBounds.makeInset(15.5, 10.5); + const SkRect dst = RenderBounds.makeInset(10.5, 10.5); const int divX[] = { - (RenderLeft + RenderCenterX) / 2, - RenderCenterX, - (RenderRight + RenderCenterX) / 2, + RenderWidth * 1 / 4, + RenderWidth * 2 / 4, + RenderWidth * 3 / 4, }; const int divY[] = { - (RenderTop + RenderCenterY) / 2, - RenderCenterY, - (RenderBottom + RenderCenterY) / 2, + RenderHeight * 1 / 4, + RenderHeight * 2 / 4, + RenderHeight * 3 / 4, }; SkCanvas::Lattice lattice = { divX, divY, nullptr, 3, 3, nullptr, nullptr, }; - CanvasCompareTester::RenderAll( - [=](SkCanvas* canvas, SkPaint& paint) { // - canvas->drawImageLattice(CanvasCompareTester::testImage.get(), lattice, - dst, SkFilterMode::kNearest, nullptr); - }, - [=](DisplayListBuilder& builder) { // - builder.drawImageLattice(CanvasCompareTester::testImage, lattice, // - dst, SkFilterMode::kNearest, false); - }); + CanvasCompareTester::RenderAll(TestParameters( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawImageLattice(CanvasCompareTester::testImage.get(), lattice, + dst, SkFilterMode::kNearest, nullptr); + }, + [=](DisplayListBuilder& builder) { // + builder.drawImageLattice(CanvasCompareTester::testImage, lattice, // + dst, SkFilterMode::kNearest, false); + }, + DisplayListAttributeFlags::kDrawImageLatticeFlags) + ); } TEST(DisplayListCanvas, DrawImageLatticeLinear) { - const SkRect dst = RenderBounds.makeInset(15.5, 10.5); + const SkRect dst = RenderBounds.makeInset(10.5, 10.5); const int divX[] = { - (RenderLeft + RenderCenterX) / 2, - RenderCenterX, - (RenderRight + RenderCenterX) / 2, + RenderWidth / 4, + RenderWidth / 2, + RenderWidth * 3 / 4, }; const int divY[] = { - (RenderTop + RenderCenterY) / 2, - RenderCenterY, - (RenderBottom + RenderCenterY) / 2, + RenderHeight / 4, + RenderHeight / 2, + RenderHeight * 3 / 4, }; SkCanvas::Lattice lattice = { divX, divY, nullptr, 3, 3, nullptr, nullptr, }; - CanvasCompareTester::RenderAll( - [=](SkCanvas* canvas, SkPaint& paint) { // - canvas->drawImageLattice(CanvasCompareTester::testImage.get(), lattice, - dst, SkFilterMode::kLinear, &paint); - }, - [=](DisplayListBuilder& builder) { // - builder.drawImageLattice(CanvasCompareTester::testImage, lattice, // - dst, SkFilterMode::kLinear, true); - }); + CanvasCompareTester::RenderAll(TestParameters( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawImageLattice(CanvasCompareTester::testImage.get(), lattice, + dst, SkFilterMode::kLinear, &paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawImageLattice(CanvasCompareTester::testImage, lattice, // + dst, SkFilterMode::kLinear, true); + }, + DisplayListAttributeFlags::kDrawImageLatticeWithPaintFlags) + ); } TEST(DisplayListCanvas, DrawAtlasNearest) { @@ -2097,7 +2678,8 @@ TEST(DisplayListCanvas, DrawAtlasNearest) { SK_ColorMAGENTA, }; const sk_sp image = CanvasCompareTester::testImage; - CanvasCompareTester::RenderAtlas( + CanvasCompareTester::RenderAll( + TestParameters( [=](SkCanvas* canvas, SkPaint& paint) { canvas->drawAtlas(image.get(), xform, tex, colors, 4, SkBlendMode::kSrcOver, DisplayList::NearestSampling, @@ -2107,7 +2689,9 @@ TEST(DisplayListCanvas, DrawAtlasNearest) { builder.drawAtlas(image, xform, tex, colors, 4, // SkBlendMode::kSrcOver, DisplayList::NearestSampling, nullptr, true); - }); + }, + DisplayListAttributeFlags::kDrawAtlasWithPaintFlags + ).set_draw_atlas()); } TEST(DisplayListCanvas, DrawAtlasNearestNoPaint) { @@ -2134,7 +2718,8 @@ TEST(DisplayListCanvas, DrawAtlasNearestNoPaint) { SK_ColorMAGENTA, }; const sk_sp image = CanvasCompareTester::testImage; - CanvasCompareTester::RenderAtlas( + CanvasCompareTester::RenderAll( + TestParameters( [=](SkCanvas* canvas, SkPaint& paint) { canvas->drawAtlas(image.get(), xform, tex, colors, 4, SkBlendMode::kSrcOver, DisplayList::NearestSampling, @@ -2144,7 +2729,9 @@ TEST(DisplayListCanvas, DrawAtlasNearestNoPaint) { builder.drawAtlas(image, xform, tex, colors, 4, // SkBlendMode::kSrcOver, DisplayList::NearestSampling, nullptr, false); - }); + }, + DisplayListAttributeFlags::kDrawAtlasFlags + ).set_draw_atlas()); } TEST(DisplayListCanvas, DrawAtlasLinear) { @@ -2171,7 +2758,8 @@ TEST(DisplayListCanvas, DrawAtlasLinear) { SK_ColorMAGENTA, }; const sk_sp image = CanvasCompareTester::testImage; - CanvasCompareTester::RenderAtlas( + CanvasCompareTester::RenderAll( + TestParameters( [=](SkCanvas* canvas, SkPaint& paint) { canvas->drawAtlas(image.get(), xform, tex, colors, 2, // SkBlendMode::kSrcOver, DisplayList::LinearSampling, @@ -2181,7 +2769,9 @@ TEST(DisplayListCanvas, DrawAtlasLinear) { builder.drawAtlas(image, xform, tex, colors, 2, // SkBlendMode::kSrcOver, DisplayList::LinearSampling, nullptr, true); - }); + }, + DisplayListAttributeFlags::kDrawAtlasWithPaintFlags + ).set_draw_atlas()); } sk_sp makeTestPicture() { @@ -2189,88 +2779,83 @@ sk_sp makeTestPicture() { SkCanvas* cv = recorder.beginRecording(RenderBounds); SkPaint p; p.setStyle(SkPaint::kFill_Style); - SkScalar x_coords[] = { - RenderLeft, - RenderCenterX, - RenderRight, - }; - SkScalar y_coords[] = { - RenderTop, - RenderCenterY, - RenderBottom, - }; - SkColor colors[][2] = { - { - SK_ColorRED, - SK_ColorBLUE, - }, - { - SK_ColorGREEN, - SK_ColorYELLOW, - }, - }; - for (int j = 0; j < 2; j++) { - for (int i = 0; i < 2; i++) { - SkRect rect = { - x_coords[i], - y_coords[j], - x_coords[i + 1], - y_coords[j + 1], - }; - p.setColor(colors[i][j]); - cv->drawOval(rect, p); - } - } + p.setColor(SK_ColorRED); + cv->drawRect({RenderLeft, RenderTop, RenderCenterX, RenderCenterY}, p); + p.setColor(SK_ColorBLUE); + cv->drawRect({RenderCenterX, RenderTop, RenderRight, RenderCenterY}, p); + p.setColor(SK_ColorGREEN); + cv->drawRect({RenderLeft, RenderCenterY, RenderCenterX, RenderBottom}, p); + p.setColor(SK_ColorYELLOW); + cv->drawRect({RenderCenterX, RenderCenterY, RenderRight, RenderBottom}, p); return recorder.finishRecordingAsPicture(); } TEST(DisplayListCanvas, DrawPicture) { sk_sp picture = makeTestPicture(); - CanvasCompareTester::RenderNoAttributes( - [=](SkCanvas* canvas, SkPaint& paint) { // - canvas->drawPicture(picture, nullptr, nullptr); - }, - [=](DisplayListBuilder& builder) { // - builder.drawPicture(picture, nullptr, false); - }); + CanvasCompareTester::RenderAll(TestParameters( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawPicture(picture, nullptr, nullptr); + }, + [=](DisplayListBuilder& builder) { // + builder.drawPicture(picture, nullptr, false); + }, + DisplayListAttributeFlags::kDrawPictureFlags) + ); } TEST(DisplayListCanvas, DrawPictureWithMatrix) { sk_sp picture = makeTestPicture(); SkMatrix matrix = SkMatrix::Scale(0.95, 0.95); - CanvasCompareTester::RenderNoAttributes( - [=](SkCanvas* canvas, SkPaint& paint) { // - canvas->drawPicture(picture, &matrix, nullptr); - }, - [=](DisplayListBuilder& builder) { // - builder.drawPicture(picture, &matrix, false); - }); + CanvasCompareTester::RenderAll(TestParameters( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawPicture(picture, &matrix, nullptr); + }, + [=](DisplayListBuilder& builder) { // + builder.drawPicture(picture, &matrix, false); + }, + DisplayListAttributeFlags::kDrawPictureFlags) + ); } TEST(DisplayListCanvas, DrawPictureWithPaint) { sk_sp picture = makeTestPicture(); - CanvasCompareTester::RenderAll( - [=](SkCanvas* canvas, SkPaint& paint) { // - canvas->drawPicture(picture, nullptr, &paint); - }, - [=](DisplayListBuilder& builder) { // - builder.drawPicture(picture, nullptr, true); - }); + CanvasCompareTester::RenderAll(TestParameters( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawPicture(picture, nullptr, &paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawPicture(picture, nullptr, true); + }, + DisplayListAttributeFlags::kDrawPictureWithPaintFlags) + ); } -TEST(DisplayListCanvas, DrawDisplayList) { +sk_sp makeTestDisplayList() { DisplayListBuilder builder; builder.setStyle(SkPaint::kFill_Style); + builder.setColor(SK_ColorRED); + builder.drawRect({RenderLeft, RenderTop, RenderCenterX, RenderCenterY}); builder.setColor(SK_ColorBLUE); - builder.drawOval(RenderBounds); - sk_sp display_list = builder.Build(); - CanvasCompareTester::RenderNoAttributes( + builder.drawRect({RenderCenterX, RenderTop, RenderRight, RenderCenterY}); + builder.setColor(SK_ColorGREEN); + builder.drawRect({RenderLeft, RenderCenterY, RenderCenterX, RenderBottom}); + builder.setColor(SK_ColorYELLOW); + builder.drawRect({RenderCenterX, RenderCenterY, RenderRight, RenderBottom}); + return builder.Build(); +} + +TEST(DisplayListCanvas, DrawDisplayList) { + sk_sp display_list = makeTestDisplayList(); + CanvasCompareTester::RenderAll( + TestParameters( [=](SkCanvas* canvas, SkPaint& paint) { // display_list->RenderTo(canvas); }, [=](DisplayListBuilder& builder) { // builder.drawDisplayList(display_list); - }); + }, + DisplayListAttributeFlags::kDrawDisplayListFlags + ).set_draw_display_list()); } TEST(DisplayListCanvas, DrawTextBlob) { @@ -2282,9 +2867,10 @@ TEST(DisplayListCanvas, DrawTextBlob) { #endif // OS_FUCHSIA sk_sp blob = CanvasCompareTester::MakeTextBlob("Testing", RenderHeight * 0.33f); - SkScalar RenderY1_3 = RenderTop + RenderHeight * 0.33; - SkScalar RenderY2_3 = RenderTop + RenderHeight * 0.66; - CanvasCompareTester::RenderNoAttributes( + SkScalar RenderY1_3 = RenderTop + RenderHeight * 0.3; + SkScalar RenderY2_3 = RenderTop + RenderHeight * 0.6; + CanvasCompareTester::RenderAll( + TestParameters( [=](SkCanvas* canvas, SkPaint& paint) { // canvas->drawTextBlob(blob, RenderLeft, RenderY1_3, paint); canvas->drawTextBlob(blob, RenderLeft, RenderY2_3, paint); @@ -2295,22 +2881,15 @@ TEST(DisplayListCanvas, DrawTextBlob) { builder.drawTextBlob(blob, RenderLeft, RenderY2_3); builder.drawTextBlob(blob, RenderLeft, RenderBottom); }, - CanvasCompareTester::DefaultAdjuster, - // From examining the bounds differential for the "Default" case, the - // SkTextBlob adds a padding of ~31 on the left, ~30 on the right, - // ~12 on top and ~8 on the bottom, so we add 32h & 13v allowed - // padding to the tolerance - CanvasCompareTester::DefaultTolerance.addBoundsPadding(32, 13)); -} - -const BoundsTolerance shadowTolerance(const BoundsTolerance& tolerance, - const SkPaint& paint, - const SkMatrix& matrix) { - // Shadow primitives could use just a little more horizontal bounds - // tolerance when drawn with a perspective transform. - return CanvasCompareTester::DefaultAdjuster( - matrix.hasPerspective() ? tolerance.addScale(1.04, 1.0) : tolerance, - paint, matrix); + DisplayListAttributeFlags::kDrawTextBlobFlags + ).set_draw_text_blob(), + // From examining the bounds differential for the "Default" case, the + // SkTextBlob adds a padding of ~32 on the left, ~30 on the right, + // ~12 on top and ~8 on the bottom, so we add 33h & 13v allowed + // padding to the tolerance + CanvasCompareTester::DefaultTolerance.addBoundsPadding(33, 13) + ); + EXPECT_TRUE(blob->unique()); } TEST(DisplayListCanvas, DrawShadow) { @@ -2326,7 +2905,8 @@ TEST(DisplayListCanvas, DrawShadow) { const SkColor color = SK_ColorDKGRAY; const SkScalar elevation = 5; - CanvasCompareTester::RenderShadows( + CanvasCompareTester::RenderAll( + TestParameters( [=](SkCanvas* canvas, SkPaint& paint) { // PhysicalShapeLayer::DrawShadow(canvas, path, color, elevation, false, 1.0); @@ -2334,8 +2914,9 @@ TEST(DisplayListCanvas, DrawShadow) { [=](DisplayListBuilder& builder) { // builder.drawShadow(path, color, elevation, false, 1.0); }, - shadowTolerance, - CanvasCompareTester::DefaultTolerance.addBoundsPadding(3, 3)); + DisplayListAttributeFlags::kDrawShadowFlags + ).set_draw_shadows(), + CanvasCompareTester::DefaultTolerance.addBoundsPadding(3, 3)); } TEST(DisplayListCanvas, DrawShadowTransparentOccluder) { @@ -2351,7 +2932,8 @@ TEST(DisplayListCanvas, DrawShadowTransparentOccluder) { const SkColor color = SK_ColorDKGRAY; const SkScalar elevation = 5; - CanvasCompareTester::RenderShadows( + CanvasCompareTester::RenderAll( + TestParameters( [=](SkCanvas* canvas, SkPaint& paint) { // PhysicalShapeLayer::DrawShadow(canvas, path, color, elevation, true, 1.0); @@ -2359,8 +2941,9 @@ TEST(DisplayListCanvas, DrawShadowTransparentOccluder) { [=](DisplayListBuilder& builder) { // builder.drawShadow(path, color, elevation, true, 1.0); }, - shadowTolerance, - CanvasCompareTester::DefaultTolerance.addBoundsPadding(3, 3)); + DisplayListAttributeFlags::kDrawShadowFlags + ).set_draw_shadows(), + CanvasCompareTester::DefaultTolerance.addBoundsPadding(3, 3)); } TEST(DisplayListCanvas, DrawShadowDpr) { @@ -2376,7 +2959,8 @@ TEST(DisplayListCanvas, DrawShadowDpr) { const SkColor color = SK_ColorDKGRAY; const SkScalar elevation = 5; - CanvasCompareTester::RenderShadows( + CanvasCompareTester::RenderAll( + TestParameters( [=](SkCanvas* canvas, SkPaint& paint) { // PhysicalShapeLayer::DrawShadow(canvas, path, color, elevation, false, 1.5); @@ -2384,8 +2968,9 @@ TEST(DisplayListCanvas, DrawShadowDpr) { [=](DisplayListBuilder& builder) { // builder.drawShadow(path, color, elevation, false, 1.5); }, - shadowTolerance, - CanvasCompareTester::DefaultTolerance.addBoundsPadding(3, 3)); + DisplayListAttributeFlags::kDrawShadowFlags + ).set_draw_shadows(), + CanvasCompareTester::DefaultTolerance.addBoundsPadding(3, 3)); } } // namespace testing diff --git a/flow/display_list_unittests.cc b/flow/display_list_unittests.cc index 8339a60c91aff..56b1eeec51b8f 100644 --- a/flow/display_list_unittests.cc +++ b/flow/display_list_unittests.cc @@ -272,97 +272,99 @@ struct DisplayListInvocationGroup { std::vector allGroups = { { "SetAntiAlias", { - {0, 8, 0, 0, [](DisplayListBuilder& b) {b.setAntiAlias(false);}}, {0, 8, 0, 0, [](DisplayListBuilder& b) {b.setAntiAlias(true);}}, + {0, 0, 0, 0, [](DisplayListBuilder& b) {b.setAntiAlias(false);}}, } }, { "SetDither", { - {0, 8, 0, 0, [](DisplayListBuilder& b) {b.setDither(false);}}, {0, 8, 0, 0, [](DisplayListBuilder& b) {b.setDither(true);}}, + {0, 0, 0, 0, [](DisplayListBuilder& b) {b.setDither(false);}}, } }, { "SetInvertColors", { - {0, 8, 0, 0, [](DisplayListBuilder& b) {b.setInvertColors(false);}}, {0, 8, 0, 0, [](DisplayListBuilder& b) {b.setInvertColors(true);}}, + {0, 0, 0, 0, [](DisplayListBuilder& b) {b.setInvertColors(false);}}, } }, { "SetStrokeCap", { - {0, 8, 0, 0, [](DisplayListBuilder& b) {b.setStrokeCap(SkPaint::kButt_Cap);}}, {0, 8, 0, 0, [](DisplayListBuilder& b) {b.setStrokeCap(SkPaint::kRound_Cap);}}, {0, 8, 0, 0, [](DisplayListBuilder& b) {b.setStrokeCap(SkPaint::kSquare_Cap);}}, + {0, 0, 0, 0, [](DisplayListBuilder& b) {b.setStrokeCap(SkPaint::kButt_Cap);}}, } }, { "SetStrokeJoin", { {0, 8, 0, 0, [](DisplayListBuilder& b) {b.setStrokeJoin(SkPaint::kBevel_Join);}}, {0, 8, 0, 0, [](DisplayListBuilder& b) {b.setStrokeJoin(SkPaint::kRound_Join);}}, - {0, 8, 0, 0, [](DisplayListBuilder& b) {b.setStrokeJoin(SkPaint::kMiter_Join);}}, + {0, 0, 0, 0, [](DisplayListBuilder& b) {b.setStrokeJoin(SkPaint::kMiter_Join);}}, } }, { "SetStyle", { - {0, 8, 0, 0, [](DisplayListBuilder& b) {b.setStyle(SkPaint::kFill_Style);}}, {0, 8, 0, 0, [](DisplayListBuilder& b) {b.setStyle(SkPaint::kStroke_Style);}}, + {0, 8, 0, 0, [](DisplayListBuilder& b) {b.setStyle(SkPaint::kStrokeAndFill_Style);}}, + {0, 0, 0, 0, [](DisplayListBuilder& b) {b.setStyle(SkPaint::kFill_Style);}}, } }, { "SetStrokeWidth", { - {0, 8, 0, 0, [](DisplayListBuilder& b) {b.setStrokeWidth(0.0);}}, + {0, 8, 0, 0, [](DisplayListBuilder& b) {b.setStrokeWidth(1.0);}}, {0, 8, 0, 0, [](DisplayListBuilder& b) {b.setStrokeWidth(5.0);}}, + {0, 0, 0, 0, [](DisplayListBuilder& b) {b.setStrokeWidth(0.0);}}, } }, { "SetStrokeMiter", { {0, 8, 0, 0, [](DisplayListBuilder& b) {b.setStrokeMiter(0.0);}}, {0, 8, 0, 0, [](DisplayListBuilder& b) {b.setStrokeMiter(5.0);}}, + {0, 0, 0, 0, [](DisplayListBuilder& b) {b.setStrokeMiter(4.0);}}, } }, { "SetColor", { {0, 8, 0, 0, [](DisplayListBuilder& b) {b.setColor(SK_ColorGREEN);}}, {0, 8, 0, 0, [](DisplayListBuilder& b) {b.setColor(SK_ColorBLUE);}}, + {0, 0, 0, 0, [](DisplayListBuilder& b) {b.setColor(SK_ColorBLACK);}}, } }, - { "SetBlendMode", { + { "SetBlendModeOrBlender", { {0, 8, 0, 0, [](DisplayListBuilder& b) {b.setBlendMode(SkBlendMode::kSrcIn);}}, {0, 8, 0, 0, [](DisplayListBuilder& b) {b.setBlendMode(SkBlendMode::kDstIn);}}, - } - }, - { "SetBlender", { - {0, 8, 0, 0, [](DisplayListBuilder& b) {b.setBlender(nullptr);}}, {0, 16, 0, 0, [](DisplayListBuilder& b) {b.setBlender(TestBlender1);}}, {0, 16, 0, 0, [](DisplayListBuilder& b) {b.setBlender(TestBlender2);}}, {0, 16, 0, 0, [](DisplayListBuilder& b) {b.setBlender(TestBlender3);}}, + {0, 0, 0, 0, [](DisplayListBuilder& b) {b.setBlendMode(SkBlendMode::kSrcOver);}}, + {0, 0, 0, 0, [](DisplayListBuilder& b) {b.setBlender(nullptr);}}, } }, { "SetShader", { - {0, 8, 0, 0, [](DisplayListBuilder& b) {b.setShader(nullptr);}}, {0, 16, 0, 0, [](DisplayListBuilder& b) {b.setShader(TestShader1);}}, {0, 16, 0, 0, [](DisplayListBuilder& b) {b.setShader(TestShader2);}}, {0, 16, 0, 0, [](DisplayListBuilder& b) {b.setShader(TestShader3);}}, + {0, 0, 0, 0, [](DisplayListBuilder& b) {b.setShader(nullptr);}}, } }, { "SetImageFilter", { - {0, 8, 0, 0, [](DisplayListBuilder& b) {b.setImageFilter(nullptr);}}, {0, 16, 0, 0, [](DisplayListBuilder& b) {b.setImageFilter(TestImageFilter1);}}, {0, 16, 0, 0, [](DisplayListBuilder& b) {b.setImageFilter(TestImageFilter2);}}, + {0, 0, 0, 0, [](DisplayListBuilder& b) {b.setImageFilter(nullptr);}}, } }, { "SetColorFilter", { - {0, 8, 0, 0, [](DisplayListBuilder& b) {b.setColorFilter(nullptr);}}, {0, 16, 0, 0, [](DisplayListBuilder& b) {b.setColorFilter(TestColorFilter1);}}, {0, 16, 0, 0, [](DisplayListBuilder& b) {b.setColorFilter(TestColorFilter2);}}, + {0, 0, 0, 0, [](DisplayListBuilder& b) {b.setColorFilter(nullptr);}}, } }, { "SetPathEffect", { - {0, 8, 0, 0, [](DisplayListBuilder& b) {b.setPathEffect(nullptr);}}, {0, 16, 0, 0, [](DisplayListBuilder& b) {b.setPathEffect(TestPathEffect1);}}, {0, 16, 0, 0, [](DisplayListBuilder& b) {b.setPathEffect(TestPathEffect2);}}, + {0, 0, 0, 0, [](DisplayListBuilder& b) {b.setPathEffect(nullptr);}}, } }, { "SetMaskFilter", { - {0, 16, 0, 0, [](DisplayListBuilder& b) {b.setMaskFilter(nullptr);}}, {0, 16, 0, 0, [](DisplayListBuilder& b) {b.setMaskFilter(TestMaskFilter);}}, {0, 8, 0, 0, [](DisplayListBuilder& b) {b.setMaskBlurFilter(kNormal_SkBlurStyle, 3.0);}}, {0, 8, 0, 0, [](DisplayListBuilder& b) {b.setMaskBlurFilter(kNormal_SkBlurStyle, 5.0);}}, {0, 8, 0, 0, [](DisplayListBuilder& b) {b.setMaskBlurFilter(kSolid_SkBlurStyle, 3.0);}}, {0, 8, 0, 0, [](DisplayListBuilder& b) {b.setMaskBlurFilter(kInner_SkBlurStyle, 3.0);}}, {0, 8, 0, 0, [](DisplayListBuilder& b) {b.setMaskBlurFilter(kOuter_SkBlurStyle, 3.0);}}, + {0, 0, 0, 0, [](DisplayListBuilder& b) {b.setMaskFilter(nullptr);}}, } }, { "Save(Layer)+Restore", { @@ -376,33 +378,34 @@ std::vector allGroups = { }, { "Translate", { // cv.translate(0, 0) is ignored - {1, 16, 0, 0, [](DisplayListBuilder& b) {b.translate(0, 0);}}, {1, 16, 1, 16, [](DisplayListBuilder& b) {b.translate(10, 10);}}, {1, 16, 1, 16, [](DisplayListBuilder& b) {b.translate(10, 15);}}, {1, 16, 1, 16, [](DisplayListBuilder& b) {b.translate(15, 10);}}, + {0, 0, 0, 0, [](DisplayListBuilder& b) {b.translate(0, 0);}}, } }, { "Scale", { // cv.scale(1, 1) is ignored - {1, 16, 0, 0, [](DisplayListBuilder& b) {b.scale(1, 1);}}, {1, 16, 1, 16, [](DisplayListBuilder& b) {b.scale(2, 2);}}, {1, 16, 1, 16, [](DisplayListBuilder& b) {b.scale(2, 3);}}, {1, 16, 1, 16, [](DisplayListBuilder& b) {b.scale(3, 2);}}, + {0, 0, 0, 0, [](DisplayListBuilder& b) {b.scale(1, 1);}}, } }, { "Rotate", { // cv.rotate(0) is ignored, otherwise expressed as concat(rotmatrix) - {1, 8, 0, 0, [](DisplayListBuilder& b) {b.rotate(0);}}, {1, 8, 1, 32, [](DisplayListBuilder& b) {b.rotate(30);}}, {1, 8, 1, 32, [](DisplayListBuilder& b) {b.rotate(45);}}, + {0, 0, 0, 0, [](DisplayListBuilder& b) {b.rotate(0);}}, + {0, 0, 0, 0, [](DisplayListBuilder& b) {b.rotate(360);}}, } }, { "Skew", { // cv.skew(0, 0) is ignored, otherwise expressed as concat(skewmatrix) - {1, 16, 0, 0, [](DisplayListBuilder& b) {b.skew(0, 0);}}, {1, 16, 1, 32, [](DisplayListBuilder& b) {b.skew(0.1, 0.1);}}, {1, 16, 1, 32, [](DisplayListBuilder& b) {b.skew(0.1, 0.2);}}, {1, 16, 1, 32, [](DisplayListBuilder& b) {b.skew(0.2, 0.1);}}, + {0, 0, 0, 0, [](DisplayListBuilder& b) {b.skew(0, 0);}}, } }, { "Transform2DAffine", { @@ -833,7 +836,9 @@ TEST(DisplayList, SingleOpDisplayListsCompareToEachOther) { sk_sp listB = listsB[j]; auto desc = group.op_name + "(variant " + std::to_string(i + 1) + " ==? variant " + std::to_string(j + 1) + ")"; - if (i == j) { + if (i == j || + (group.variants[i].is_empty() && group.variants[j].is_empty())) { + // They are the same variant, or both variants are NOPs ASSERT_EQ(listA->op_count(false), listB->op_count(false)) << desc; ASSERT_EQ(listA->bytes(false), listB->bytes(false)) << desc; ASSERT_EQ(listA->op_count(true), listB->op_count(true)) << desc; @@ -852,6 +857,31 @@ TEST(DisplayList, SingleOpDisplayListsCompareToEachOther) { } } +TEST(DisplayList, FullRotationsAreNop) { + DisplayListBuilder builder; + builder.rotate(0); + builder.rotate(360); + builder.rotate(720); + builder.rotate(1080); + builder.rotate(1440); + sk_sp dl = builder.Build(); + ASSERT_EQ(dl->bytes(false), sizeof(DisplayList)); + ASSERT_EQ(dl->bytes(true), sizeof(DisplayList)); + ASSERT_EQ(dl->op_count(false), 0); + ASSERT_EQ(dl->op_count(true), 0); +} + +TEST(DisplayList, AllBlendModeNops) { + DisplayListBuilder builder; + builder.setBlendMode(SkBlendMode::kSrcOver); + builder.setBlender(nullptr); + sk_sp dl = builder.Build(); + ASSERT_EQ(dl->bytes(false), sizeof(DisplayList)); + ASSERT_EQ(dl->bytes(true), sizeof(DisplayList)); + ASSERT_EQ(dl->op_count(false), 0); + ASSERT_EQ(dl->op_count(true), 0); +} + static sk_sp Build(size_t g_index, size_t v_index) { DisplayListBuilder builder; int op_count = 0; @@ -872,7 +902,7 @@ static sk_sp Build(size_t g_index, size_t v_index) { name = "Default"; } else { name = allGroups[g_index].op_name; - if (v_index < 0) { + if (v_index >= allGroups[g_index].variants.size()) { name += " skipped"; } else { name += " variant " + std::to_string(v_index + 1); @@ -889,12 +919,12 @@ TEST(DisplayList, DisplayListsWithVaryingOpComparisons) { for (size_t gi = 0; gi < allGroups.size(); gi++) { DisplayListInvocationGroup& group = allGroups[gi]; sk_sp missing_dl = Build(gi, group.variants.size()); - auto desc = "[Group " + std::to_string(gi + 1) + " omitted]"; + auto desc = "[Group " + group.op_name + " omitted]"; ASSERT_TRUE(missing_dl->Equals(*missing_dl)) << desc << " == itself"; ASSERT_FALSE(missing_dl->Equals(*default_dl)) << desc << " != Default"; ASSERT_FALSE(default_dl->Equals(*missing_dl)) << "Default != " << desc; for (size_t vi = 0; vi < group.variants.size(); vi++) { - auto desc = "[Group " + std::to_string(gi + 1) + " variant " + + auto desc = "[Group " + group.op_name + " variant " + std::to_string(vi + 1) + "]"; sk_sp variant_dl = Build(gi, vi); ASSERT_TRUE(variant_dl->Equals(*variant_dl)) << desc << " == itself"; @@ -1117,5 +1147,181 @@ TEST(DisplayList, NestedOpCountMetricsSameAsSkPicture) { ASSERT_EQ(display_list->op_count(true), 36); } +class AttributeRefTester { + public: + virtual void setRefToPaint(SkPaint& paint) const = 0; + virtual void setRefToDisplayList(DisplayListBuilder& builder) const = 0; + virtual bool ref_is_unique() const = 0; + + void testDisplayList() { + { + DisplayListBuilder builder; + setRefToDisplayList(builder); + builder.drawRect(SkRect::MakeLTRB(50, 50, 100, 100)); + ASSERT_FALSE(ref_is_unique()); + } + ASSERT_TRUE(ref_is_unique()); + } + void testPaint() { + { + SkPaint paint; + setRefToPaint(paint); + ASSERT_FALSE(ref_is_unique()); + } + ASSERT_TRUE(ref_is_unique()); + } + void testCanvasRecorder() { + { + sk_sp display_list; + { + DisplayListCanvasRecorder recorder(SkRect::MakeLTRB(0, 0, 200, 200)); + { + { + SkPaint paint; + setRefToPaint(paint); + recorder.drawRect(SkRect::MakeLTRB(50, 50, 100, 100), paint); + ASSERT_FALSE(ref_is_unique()); + } + ASSERT_FALSE(ref_is_unique()); + } + display_list = recorder.Build(); + ASSERT_FALSE(ref_is_unique()); + } + ASSERT_FALSE(ref_is_unique()); + } + ASSERT_TRUE(ref_is_unique()); + } + + void test() { + testDisplayList(); + testPaint(); + testCanvasRecorder(); + } +}; + +TEST(DisplayList, DisplayListImageFilterRefHandling) { + class ImageFilterRefTester : public virtual AttributeRefTester { + public: + void setRefToPaint(SkPaint& paint) const override { + paint.setImageFilter(image_filter); + } + void setRefToDisplayList(DisplayListBuilder& builder) const override { + builder.setImageFilter(image_filter); + } + bool ref_is_unique() const override { return image_filter->unique(); } + + private: + sk_sp image_filter = SkImageFilters::Blur(2.0, 2.0, nullptr); + }; + + ImageFilterRefTester tester; + tester.test(); + ASSERT_TRUE(tester.ref_is_unique()); +} + +TEST(DisplayList, DisplayListColorFilterRefHandling) { + class ColorFilterRefTester : public virtual AttributeRefTester { + public: + void setRefToPaint(SkPaint& paint) const override { + paint.setColorFilter(color_filter); + } + void setRefToDisplayList(DisplayListBuilder& builder) const override { + builder.setColorFilter(color_filter); + } + bool ref_is_unique() const override { return color_filter->unique(); } + + private: + sk_sp color_filter = + SkColorFilters::Blend(SK_ColorBLUE, SkBlendMode::kSrcIn); + }; + + ColorFilterRefTester tester; + tester.test(); + ASSERT_TRUE(tester.ref_is_unique()); +} + +TEST(DisplayList, DisplayListMaskFilterRefHandling) { + class MaskFilterRefTester : public virtual AttributeRefTester { + public: + void setRefToPaint(SkPaint& paint) const override { + paint.setMaskFilter(mask_filter); + } + void setRefToDisplayList(DisplayListBuilder& builder) const override { + builder.setMaskFilter(mask_filter); + } + bool ref_is_unique() const override { return mask_filter->unique(); } + + private: + sk_sp mask_filter = + SkMaskFilter::MakeBlur(SkBlurStyle::kNormal_SkBlurStyle, 2.0); + }; + + MaskFilterRefTester tester; + tester.test(); + ASSERT_TRUE(tester.ref_is_unique()); +} + +TEST(DisplayList, DisplayListBlenderRefHandling) { + class BlenderRefTester : public virtual AttributeRefTester { + public: + void setRefToPaint(SkPaint& paint) const override { + paint.setBlender(blender); + } + void setRefToDisplayList(DisplayListBuilder& builder) const override { + builder.setBlender(blender); + } + bool ref_is_unique() const override { return blender->unique(); } + + private: + sk_sp blender = + SkBlenders::Arithmetic(0.25, 0.25, 0.25, 0.25, true); + }; + + BlenderRefTester tester; + tester.test(); + ASSERT_TRUE(tester.ref_is_unique()); +} + +TEST(DisplayList, DisplayListShaderRefHandling) { + class ShaderRefTester : public virtual AttributeRefTester { + public: + void setRefToPaint(SkPaint& paint) const override { + paint.setShader(shader); + } + void setRefToDisplayList(DisplayListBuilder& builder) const override { + builder.setShader(shader); + } + bool ref_is_unique() const override { return shader->unique(); } + + private: + sk_sp shader = SkShaders::Color(SK_ColorBLUE); + }; + + ShaderRefTester tester; + tester.test(); + ASSERT_TRUE(tester.ref_is_unique()); +} + +TEST(DisplayList, DisplayListPathEffectRefHandling) { + class PathEffectRefTester : public virtual AttributeRefTester { + public: + void setRefToPaint(SkPaint& paint) const override { + paint.setPathEffect(path_effect); + } + void setRefToDisplayList(DisplayListBuilder& builder) const override { + builder.setPathEffect(path_effect); + } + bool ref_is_unique() const override { return path_effect->unique(); } + + private: + sk_sp path_effect = + SkDashPathEffect::Make(TestDashes1, 2, 0.0); + }; + + PathEffectRefTester tester; + tester.test(); + ASSERT_TRUE(tester.ref_is_unique()); +} + } // namespace testing } // namespace flutter diff --git a/flow/display_list_utils.cc b/flow/display_list_utils.cc index b6d86fa533894..7681bc6db8c0c 100644 --- a/flow/display_list_utils.cc +++ b/flow/display_list_utils.cc @@ -234,8 +234,7 @@ void DisplayListBoundsCalculator::setStrokeJoin(SkPaint::Join join) { join_is_miter_ = (join == SkPaint::kMiter_Join); } void DisplayListBoundsCalculator::setStyle(SkPaint::Style style) { - style_flag_ = (style == SkPaint::kFill_Style) ? kIsFilledGeometry // - : kIsStrokedGeometry; + style_ = style; } void DisplayListBoundsCalculator::setStrokeWidth(SkScalar width) { half_stroke_width_ = std::max(width * 0.5f, kMinStrokeWidth); @@ -290,7 +289,10 @@ void DisplayListBoundsCalculator::saveLayer(const SkRect* bounds, // Accumulate the layer in its own coordinate system and then // filter and transform its bounds on restore. SkMatrixDispatchHelper::reset(); - ClipBoundsDispatchHelper::reset(bounds); + if (bounds) { + clipRect(*bounds, SkClipOp::kIntersect, false); + } + // ClipBoundsDispatchHelper::reset(bounds); } void DisplayListBoundsCalculator::restore() { if (layer_infos_.size() > 1) { @@ -310,7 +312,7 @@ void DisplayListBoundsCalculator::restore() { // modifications based on the attributes that were in place // when it was instantiated. Modifying it further base on the // current attributes would mix attribute states. - AccumulateRect(layer_bounds, kIsUnfiltered); + AccumulateRect(layer_bounds, DisplayListAttributeFlags::kSaveLayerFlags); } if (layer_unbounded) { AccumulateUnbounded(); @@ -327,39 +329,36 @@ void DisplayListBoundsCalculator::drawColor(SkColor color, SkBlendMode mode) { void DisplayListBoundsCalculator::drawLine(const SkPoint& p0, const SkPoint& p1) { SkRect bounds = SkRect::MakeLTRB(p0.fX, p0.fY, p1.fX, p1.fY).makeSorted(); - int cap_flag = kIsStrokedGeometry; - if (bounds.width() > 0.0f && bounds.height() > 0.0f) { - cap_flag |= kGeometryMayHaveDiagonalEndCaps; - } - AccumulateRect(bounds, cap_flag); + DisplayListAttributeFlags flags = + (bounds.width() > 0.0f && bounds.height() > 0.0f) + ? DisplayListAttributeFlags::kDrawLineFlags + : DisplayListAttributeFlags::kDrawHVLineFlags; + AccumulateRect(bounds, flags); } void DisplayListBoundsCalculator::drawRect(const SkRect& rect) { - AccumulateRect(rect, kIsDrawnGeometry); + AccumulateRect(rect, DisplayListAttributeFlags::kDrawRectFlags); } void DisplayListBoundsCalculator::drawOval(const SkRect& bounds) { - AccumulateRect(bounds, kIsDrawnGeometry); + AccumulateRect(bounds, DisplayListAttributeFlags::kDrawOvalFlags); } void DisplayListBoundsCalculator::drawCircle(const SkPoint& center, SkScalar radius) { AccumulateRect(SkRect::MakeLTRB(center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius), - kIsDrawnGeometry); + DisplayListAttributeFlags::kDrawCircleFlags); } void DisplayListBoundsCalculator::drawRRect(const SkRRect& rrect) { - AccumulateRect(rrect.getBounds(), kIsDrawnGeometry); + AccumulateRect(rrect.getBounds(), DisplayListAttributeFlags::kDrawRRectFlags); } void DisplayListBoundsCalculator::drawDRRect(const SkRRect& outer, const SkRRect& inner) { - AccumulateRect(outer.getBounds(), kIsDrawnGeometry); + AccumulateRect(outer.getBounds(), DisplayListAttributeFlags::kDrawDRRectFlags); } void DisplayListBoundsCalculator::drawPath(const SkPath& path) { if (path.isInverseFillType()) { AccumulateUnbounded(); } else { - AccumulateRect(path.getBounds(), // - (kIsDrawnGeometry | // - kGeometryMayHaveDiagonalEndCaps | // - kGeometryMayHaveProblematicJoins)); + AccumulateRect(path.getBounds(), DisplayListAttributeFlags::kDrawPathFlags); } } void DisplayListBoundsCalculator::drawArc(const SkRect& bounds, @@ -369,7 +368,7 @@ void DisplayListBoundsCalculator::drawArc(const SkRect& bounds, // This could be tighter if we compute where the start and end // angles are and then also consider the quadrants swept and // the center if specified. - AccumulateRect(bounds, kIsDrawnGeometry | kGeometryMayHaveDiagonalEndCaps); + AccumulateRect(bounds, DisplayListAttributeFlags::kDrawArcFlags); } void DisplayListBoundsCalculator::drawPoints(SkCanvas::PointMode mode, uint32_t count, @@ -379,17 +378,23 @@ void DisplayListBoundsCalculator::drawPoints(SkCanvas::PointMode mode, for (size_t i = 0; i < count; i++) { ptBounds.accumulate(pts[i]); } - int flags = kIsStrokedGeometry; - if (mode != SkCanvas::kPoints_PointMode) { - flags |= kGeometryMayHaveDiagonalEndCaps; - // Even Polygon mode just draws (count-1) separate lines, no joins + SkRect point_bounds = ptBounds.bounds(); + switch (mode) { + case SkCanvas::kPoints_PointMode: + AccumulateRect(point_bounds, DisplayListAttributeFlags::kDrawPointsAsPointsFlags); + break; + case SkCanvas::kLines_PointMode: + AccumulateRect(point_bounds, DisplayListAttributeFlags::kDrawPointsAsLinesFlags); + break; + case SkCanvas::kPolygon_PointMode: + AccumulateRect(point_bounds, DisplayListAttributeFlags::kDrawPointsAsPolygonFlags); + break; } - AccumulateRect(ptBounds.bounds(), flags); } } void DisplayListBoundsCalculator::drawVertices(const sk_sp vertices, SkBlendMode mode) { - AccumulateRect(vertices->bounds(), kIsNonGeometric); + AccumulateRect(vertices->bounds(), DisplayListAttributeFlags::kDrawVerticesFlags); } void DisplayListBoundsCalculator::drawImage(const sk_sp image, const SkPoint point, @@ -397,8 +402,9 @@ void DisplayListBoundsCalculator::drawImage(const sk_sp image, bool render_with_attributes) { SkRect bounds = SkRect::MakeXYWH(point.fX, point.fY, // image->width(), image->height()); - int flags = render_with_attributes ? kIsNonGeometric | kApplyMaskFilter - : kIsUnfiltered; + DisplayListAttributeFlags flags = render_with_attributes + ? DisplayListAttributeFlags::kDrawImageWithPaintFlags + : DisplayListAttributeFlags::kDrawImageFlags; AccumulateRect(bounds, flags); } void DisplayListBoundsCalculator::drawImageRect( @@ -408,8 +414,9 @@ void DisplayListBoundsCalculator::drawImageRect( const SkSamplingOptions& sampling, bool render_with_attributes, SkCanvas::SrcRectConstraint constraint) { - int flags = render_with_attributes ? kIsNonGeometric | kApplyMaskFilter - : kIsUnfiltered; + DisplayListAttributeFlags flags = render_with_attributes + ? DisplayListAttributeFlags::kDrawImageRectWithPaintFlags + : DisplayListAttributeFlags::kDrawImageRectFlags; AccumulateRect(dst, flags); } void DisplayListBoundsCalculator::drawImageNine(const sk_sp image, @@ -417,7 +424,10 @@ void DisplayListBoundsCalculator::drawImageNine(const sk_sp image, const SkRect& dst, SkFilterMode filter, bool render_with_attributes) { - AccumulateRect(dst, render_with_attributes ? kIsNonGeometric : kIsUnfiltered); + DisplayListAttributeFlags flags = render_with_attributes + ? DisplayListAttributeFlags::kDrawImageNineWithPaintFlags + : DisplayListAttributeFlags::kDrawImageNineFlags; + AccumulateRect(dst, flags); } void DisplayListBoundsCalculator::drawImageLattice( const sk_sp image, @@ -425,8 +435,9 @@ void DisplayListBoundsCalculator::drawImageLattice( const SkRect& dst, SkFilterMode filter, bool render_with_attributes) { - int flags = render_with_attributes ? kIsNonGeometric | kApplyMaskFilter - : kIsUnfiltered; + DisplayListAttributeFlags flags = render_with_attributes + ? DisplayListAttributeFlags::kDrawImageLatticeWithPaintFlags + : DisplayListAttributeFlags::kDrawImageLatticeFlags; AccumulateRect(dst, flags); } void DisplayListBoundsCalculator::drawAtlas(const sk_sp atlas, @@ -448,7 +459,9 @@ void DisplayListBoundsCalculator::drawAtlas(const sk_sp atlas, } } if (atlasBounds.is_not_empty()) { - int flags = render_with_attributes ? kIsNonGeometric : kIsUnfiltered; + DisplayListAttributeFlags flags = render_with_attributes + ? DisplayListAttributeFlags::kDrawAtlasWithPaintFlags + : DisplayListAttributeFlags::kDrawAtlasFlags; AccumulateRect(atlasBounds.bounds(), flags); } } @@ -462,17 +475,19 @@ void DisplayListBoundsCalculator::drawPicture(const sk_sp picture, if (pic_matrix) { pic_matrix->mapRect(&bounds); } - AccumulateRect(bounds, - render_with_attributes ? kIsNonGeometric : kIsUnfiltered); + DisplayListAttributeFlags flags = render_with_attributes + ? DisplayListAttributeFlags::kDrawPictureWithPaintFlags + : DisplayListAttributeFlags::kDrawPictureFlags; + AccumulateRect(bounds, flags); } void DisplayListBoundsCalculator::drawDisplayList( const sk_sp display_list) { - AccumulateRect(display_list->bounds(), kIsUnfiltered); + AccumulateRect(display_list->bounds(), DisplayListAttributeFlags::kDrawDisplayListFlags); } void DisplayListBoundsCalculator::drawTextBlob(const sk_sp blob, SkScalar x, SkScalar y) { - AccumulateRect(blob->bounds().makeOffset(x, y), kIsFilledGeometry); + AccumulateRect(blob->bounds().makeOffset(x, y), DisplayListAttributeFlags::kDrawTextBlobFlags); } void DisplayListBoundsCalculator::drawShadow(const SkPath& path, const SkColor color, @@ -481,7 +496,7 @@ void DisplayListBoundsCalculator::drawShadow(const SkPath& path, SkScalar dpr) { SkRect shadow_bounds = PhysicalShapeLayer::ComputeShadowBounds(path, elevation, dpr, matrix()); - AccumulateRect(shadow_bounds, kIsUnfiltered); + AccumulateRect(shadow_bounds, DisplayListAttributeFlags::kDrawShadowFlags); } bool DisplayListBoundsCalculator::ComputeFilteredBounds(SkRect& bounds, @@ -496,63 +511,38 @@ bool DisplayListBoundsCalculator::ComputeFilteredBounds(SkRect& bounds, } bool DisplayListBoundsCalculator::AdjustBoundsForPaint(SkRect& bounds, - int flags) { - if ((flags & kIsUnfiltered) != 0) { - FML_DCHECK(flags == kIsUnfiltered); + DisplayListAttributeFlags flags) { + if (flags.ignores_paint()) { return true; } - if ((flags & kIsAnyGeometryMask) != 0) { - if ((flags & kIsDrawnGeometry) != 0) { - FML_DCHECK((flags & (kIsFilledGeometry | kIsStrokedGeometry)) == 0); - flags |= style_flag_; - } - + if (flags.is_geometric()) { // Path effect occurs before stroking... + DisplayListSpecialGeometryFlags special_flags = flags.WithPathEffect(path_effect_); if (path_effect_) { - SkPathEffect::DashInfo info; - if (path_effect_->asADash(&info) == SkPathEffect::kDash_DashType) { - // A dash effect has a very simple impact. It cannot introduce any - // miter joins that weren't already present in the original path - // and it does not grow the bounds of the path, but it can add - // end caps to areas that might not have had them before so all - // we need to do is to indicate the potential for diagonal - // end caps and move on. - flags |= kGeometryMayHaveDiagonalEndCaps; - } else { - SkPaint p; - p.setPathEffect(path_effect_); - if (!p.canComputeFastBounds()) { - return false; - } - bounds = p.computeFastBounds(bounds, &bounds); - flags |= (kGeometryMayHaveDiagonalEndCaps | - kGeometryMayHaveProblematicJoins); + SkPaint p; + p.setPathEffect(path_effect_); + if (!p.canComputeFastBounds()) { + return false; } + bounds = p.computeFastBounds(bounds, &bounds); } - if ((flags & kIsStrokedGeometry) != 0) { - FML_DCHECK((flags & kIsFilledGeometry) == 0); + if (flags.is_stroked(style_)) { // Determine the max multiplier to the stroke width first. SkScalar pad = 1.0f; - if (join_is_miter_ && (flags & kGeometryMayHaveProblematicJoins) != 0) { + if (join_is_miter_ && special_flags.may_have_acute_joins()) { pad = std::max(pad, miter_limit_); } - if (cap_is_square_ && (flags & kGeometryMayHaveDiagonalEndCaps) != 0) { + if (cap_is_square_ && special_flags.may_have_diagonal_caps()) { pad = std::max(pad, SK_ScalarSqrt2); } pad *= half_stroke_width_; bounds.outset(pad, pad); - } else { - FML_DCHECK((flags & kIsStrokedGeometry) == 0); } - flags |= kApplyMaskFilter; - } else { - FML_DCHECK((flags & (kGeometryMayHaveDiagonalEndCaps | - kGeometryMayHaveProblematicJoins)) == 0); } - if ((flags & kApplyMaskFilter) != 0) { + if (flags.applies_mask_filter()) { if (mask_filter_) { SkPaint p; p.setMaskFilter(mask_filter_); @@ -566,7 +556,11 @@ bool DisplayListBoundsCalculator::AdjustBoundsForPaint(SkRect& bounds, } } - return ComputeFilteredBounds(bounds, image_filter_.get()); + if (flags.applies_image_filter()) { + return ComputeFilteredBounds(bounds, image_filter_.get()); + } + + return true; } void DisplayListBoundsCalculator::AccumulateUnbounded() { @@ -576,7 +570,7 @@ void DisplayListBoundsCalculator::AccumulateUnbounded() { layer_infos_.back()->set_unbounded(); } } -void DisplayListBoundsCalculator::AccumulateRect(SkRect& rect, int flags) { +void DisplayListBoundsCalculator::AccumulateRect(SkRect& rect, DisplayListAttributeFlags flags) { if (AdjustBoundsForPaint(rect, flags)) { matrix().mapRect(&rect); if (!has_clip() || rect.intersect(clip_bounds())) { diff --git a/flow/display_list_utils.h b/flow/display_list_utils.h index 495a62f294fc7..dd2fcea8eb85c 100644 --- a/flow/display_list_utils.h +++ b/flow/display_list_utils.h @@ -65,9 +65,9 @@ class IgnoreAttributeDispatchHelper : public virtual Dispatcher { // A utility class that will ignore all Dispatcher methods relating // to setting a clip. class IgnoreClipDispatchHelper : public virtual Dispatcher { - void clipRect(const SkRect& rect, SkClipOp clip_op, bool isAA) override {} - void clipRRect(const SkRRect& rrect, SkClipOp clip_op, bool isAA) override {} - void clipPath(const SkPath& path, SkClipOp clip_op, bool isAA) override {} + void clipRect(const SkRect& rect, SkClipOp clip_op, bool is_aa) override {} + void clipRRect(const SkRRect& rrect, SkClipOp clip_op, bool is_aa) override {} + void clipPath(const SkPath& path, SkClipOp clip_op, bool is_aa) override {} }; // A utility class that will ignore all Dispatcher methods relating @@ -531,61 +531,6 @@ class DisplayListBoundsCalculator final std::vector> layer_infos_; - // A drawing operation that is not geometric in nature (but which - // may still apply a MaskFilter - see |kApplyMaskFilter| below). - static constexpr int kIsNonGeometric = 0x00; - - // A geometric operation that is defined as a fill operation - // regardless of what the current paint Style is set to. - // This flag will automatically assume |kApplyMaskFilter|. - static constexpr int kIsFilledGeometry = 0x01; - - // A geometric operation that is defined as a stroke operation - // regardless of what the current paint Style is set to. - // This flag will automatically assume |kApplyMaskFilter|. - static constexpr int kIsStrokedGeometry = 0x02; - - // A geometric operation that may be a stroke or fill operation - // depending on the current state of the paint Style attribute. - // This flag will automatically assume |kApplyMaskFilter|. - static constexpr int kIsDrawnGeometry = 0x04; - - static constexpr int kIsAnyGeometryMask = // - kIsFilledGeometry | // - kIsStrokedGeometry | // - kIsDrawnGeometry; - - // A geometric operation which has a path that might have - // end caps that are not rectilinear which means that square - // end caps might project further than half the stroke width - // from the geometry bounds. - // A rectilinear path such as |drawRect| will not have - // diagonal end caps. |drawLine| might have diagonal end - // caps depending on the angle of the line, and more likely - // |drawPath| will often have such end caps. - static constexpr int kGeometryMayHaveDiagonalEndCaps = 0x08; - - // A geometric operation which has joined vertices that are - // not guaranteed to be smooth (angles of incoming and outgoing) - // segments at some joins may not have the same angle) or - // rectilinear (squares have right angles at the corners, but - // those corners will never extend past the bounding box of - // the geometry pre-transform). - // |drawRect|, |drawOval| and |drawRRect| all have well - // behaved joins, but |drawPath| might have joins that cause - // mitered extensions outside the pre-transformed bounding box. - static constexpr int kGeometryMayHaveProblematicJoins = 0x10; - - // Some operations are inherently non-geometric and yet have the - // mask filter applied anyway. - // |drawImage| variants behave this way. - static constexpr int kApplyMaskFilter = 0x20; - - // In very rare circumstances the ImageFilter is ignored. - // This is one of the few flags that turns off a step in - // estimating the bounds, rather than turning on any steps. - static constexpr int kIsUnfiltered = 0x40; - static constexpr SkScalar kMinStrokeWidth = 0.01; skstd::optional blend_mode_ = SkBlendMode::kSrcOver; @@ -593,7 +538,7 @@ class DisplayListBoundsCalculator final SkScalar half_stroke_width_ = kMinStrokeWidth; SkScalar miter_limit_ = 4.0; - int style_flag_ = kIsFilledGeometry; + SkPaint::Style style_ = SkPaint::Style::kFill_Style; bool join_is_miter_ = true; bool cap_is_square_ = false; sk_sp image_filter_; @@ -604,14 +549,14 @@ class DisplayListBoundsCalculator final bool paint_nops_on_transparency(); static bool ComputeFilteredBounds(SkRect& rect, SkImageFilter* filter); - bool AdjustBoundsForPaint(SkRect& bounds, int flags); + bool AdjustBoundsForPaint(SkRect& bounds, DisplayListAttributeFlags flags); void AccumulateUnbounded(); - void AccumulateRect(const SkRect& rect, int flags) { + void AccumulateRect(const SkRect& rect, DisplayListAttributeFlags flags) { SkRect bounds = rect; AccumulateRect(bounds, flags); } - void AccumulateRect(SkRect& rect, int flags); + void AccumulateRect(SkRect& rect, DisplayListAttributeFlags flags); }; } // namespace flutter diff --git a/lib/ui/painting/canvas.cc b/lib/ui/painting/canvas.cc index a6075d054d68e..bc6865740ae2c 100644 --- a/lib/ui/painting/canvas.cc +++ b/lib/ui/painting/canvas.cc @@ -94,19 +94,28 @@ Canvas::Canvas(SkCanvas* canvas) : canvas_(canvas) {} Canvas::~Canvas() {} void Canvas::save() { - if (!canvas_) { - return; + if (display_list_recorder_) { + builder()->save(); + } else if (canvas_) { + canvas_->save(); } - canvas_->save(); } void Canvas::saveLayerWithoutBounds(const Paint& paint, const PaintData& paint_data) { - if (!canvas_) { - return; + FML_DCHECK(paint.isNotNull()); + if (display_list_recorder_) { + bool restore_with_paint = + paint.sync_to(builder(), + DisplayListAttributeFlags::kSaveLayerWithPaintFlags); + FML_DCHECK(restore_with_paint); + TRACE_EVENT0("flutter", "ui.Canvas::saveLayer (Recorded)"); + builder()->saveLayer(nullptr, restore_with_paint); + } else if (canvas_) { + SkPaint sk_paint; + TRACE_EVENT0("flutter", "ui.Canvas::saveLayer (Recorded)"); + canvas_->saveLayer(nullptr, paint.paint(sk_paint)); } - TRACE_EVENT0("flutter", "ui.Canvas::saveLayer (Recorded)"); - canvas_->saveLayer(nullptr, paint.paint()); } void Canvas::saveLayer(double left, @@ -115,64 +124,85 @@ void Canvas::saveLayer(double left, double bottom, const Paint& paint, const PaintData& paint_data) { - if (!canvas_) { - return; - } - TRACE_EVENT0("flutter", "ui.Canvas::saveLayer (Recorded)"); + FML_DCHECK(paint.isNotNull()); SkRect bounds = SkRect::MakeLTRB(left, top, right, bottom); - canvas_->saveLayer(&bounds, paint.paint()); + if (display_list_recorder_) { + bool restore_with_paint = + paint.sync_to(builder(), + DisplayListAttributeFlags::kSaveLayerWithPaintFlags); + FML_DCHECK(restore_with_paint); + TRACE_EVENT0("flutter", "ui.Canvas::saveLayer (Recorded)"); + builder()->saveLayer(&bounds, restore_with_paint); + } else if (canvas_) { + SkPaint sk_paint; + TRACE_EVENT0("flutter", "ui.Canvas::saveLayer (Recorded)"); + canvas_->saveLayer(&bounds, paint.paint(sk_paint)); + } } void Canvas::restore() { - if (!canvas_) { - return; + if (display_list_recorder_) { + builder()->restore(); + } else if (canvas_) { + canvas_->restore(); } - canvas_->restore(); } int Canvas::getSaveCount() { - if (!canvas_) { + if (display_list_recorder_) { + return builder()->getSaveCount(); + } else if (canvas_) { + return canvas_->getSaveCount(); + } else { return 0; } - return canvas_->getSaveCount(); } void Canvas::translate(double dx, double dy) { - if (!canvas_) { - return; + if (display_list_recorder_) { + builder()->translate(dx, dy); + } else if (canvas_) { + canvas_->translate(dx, dy); } - canvas_->translate(dx, dy); } void Canvas::scale(double sx, double sy) { - if (!canvas_) { - return; + if (display_list_recorder_) { + builder()->scale(sx, sy); + } else if (canvas_) { + canvas_->scale(sx, sy); } - canvas_->scale(sx, sy); } void Canvas::rotate(double radians) { - if (!canvas_) { - return; + if (display_list_recorder_) { + builder()->rotate(radians * 180.0 / M_PI); + } if (canvas_) { + canvas_->rotate(radians * 180.0 / M_PI); } - canvas_->rotate(radians * 180.0 / M_PI); } void Canvas::skew(double sx, double sy) { - if (!canvas_) { - return; + if (display_list_recorder_) { + builder()->skew(sx, sy); + } else if (canvas_) { + canvas_->skew(sx, sy); } - canvas_->skew(sx, sy); } void Canvas::transform(const tonic::Float64List& matrix4) { - if (!canvas_) { - return; + if (display_list_recorder_) { + builder()->transformFullPerspective( + matrix4[ 0], matrix4[ 1], matrix4[ 2], matrix4[ 3], + matrix4[ 4], matrix4[ 5], matrix4[ 6], matrix4[ 7], + matrix4[ 8], matrix4[ 9], matrix4[10], matrix4[11], + matrix4[12], matrix4[13], matrix4[14], matrix4[15]); + } else if (canvas_) { + canvas_->concat(SkM44(matrix4[0], matrix4[4], matrix4[8], matrix4[12], + matrix4[1], matrix4[5], matrix4[9], matrix4[13], + matrix4[2], matrix4[6], matrix4[10], matrix4[14], + matrix4[3], matrix4[7], matrix4[11], matrix4[15])); } - canvas_->concat(SkM44(matrix4[0], matrix4[4], matrix4[8], matrix4[12], - matrix4[1], matrix4[5], matrix4[9], matrix4[13], - matrix4[2], matrix4[6], matrix4[10], matrix4[14], - matrix4[3], matrix4[7], matrix4[11], matrix4[15])); } void Canvas::clipRect(double left, @@ -181,37 +211,42 @@ void Canvas::clipRect(double left, double bottom, SkClipOp clipOp, bool doAntiAlias) { - if (!canvas_) { - return; + if (display_list_recorder_) { + builder()->clipRect(SkRect::MakeLTRB(left, top, right, bottom), clipOp, + doAntiAlias); + } else if (canvas_) { + canvas_->clipRect(SkRect::MakeLTRB(left, top, right, bottom), clipOp, + doAntiAlias); } - canvas_->clipRect(SkRect::MakeLTRB(left, top, right, bottom), clipOp, - doAntiAlias); } void Canvas::clipRRect(const RRect& rrect, bool doAntiAlias) { - if (!canvas_) { - return; + if (display_list_recorder_) { + builder()->clipRRect(rrect.sk_rrect, SkClipOp::kIntersect, doAntiAlias); + } else if (canvas_) { + canvas_->clipRRect(rrect.sk_rrect, doAntiAlias); } - canvas_->clipRRect(rrect.sk_rrect, doAntiAlias); } void Canvas::clipPath(const CanvasPath* path, bool doAntiAlias) { - if (!canvas_) { - return; - } if (!path) { Dart_ThrowException( ToDart("Canvas.clipPath called with non-genuine Path.")); return; } - canvas_->clipPath(path->path(), doAntiAlias); + if (display_list_recorder_) { + builder()->clipPath(path->path(), SkClipOp::kIntersect, doAntiAlias); + } else if (canvas_) { + canvas_->clipPath(path->path(), doAntiAlias); + } } void Canvas::drawColor(SkColor color, SkBlendMode blend_mode) { - if (!canvas_) { - return; + if (display_list_recorder_) { + builder()->drawColor(color, blend_mode); + } else if (canvas_) { + canvas_->drawColor(color, blend_mode); } - canvas_->drawColor(color, blend_mode); } void Canvas::drawLine(double x1, @@ -220,24 +255,38 @@ void Canvas::drawLine(double x1, double y2, const Paint& paint, const PaintData& paint_data) { - if (!canvas_) { - return; + FML_DCHECK(paint.isNotNull()); + if (display_list_recorder_) { + paint.sync_to(builder(), DisplayListAttributeFlags::kDrawLineFlags); + builder()->drawLine(SkPoint::Make(x1, y1), SkPoint::Make(x2, y2)); + } else if (canvas_) { + SkPaint sk_paint; + canvas_->drawLine(x1, y1, x2, y2, *paint.paint(sk_paint)); } - canvas_->drawLine(x1, y1, x2, y2, *paint.paint()); } void Canvas::drawPaint(const Paint& paint, const PaintData& paint_data) { - if (!canvas_) { - return; - } - const SkPaint* sk_paint = paint.paint(); - SkImageFilter* filter = sk_paint->getImageFilter(); - if (filter && !filter->asColorFilter(nullptr)) { - // drawPaint does an implicit saveLayer if an SkImageFilter is - // present that cannot be replaced by an SkColorFilter. - TRACE_EVENT0("flutter", "ui.Canvas::saveLayer (Recorded)"); + FML_DCHECK(paint.isNotNull()); + if (display_list_recorder_) { + paint.sync_to(builder(), DisplayListAttributeFlags::kDrawPaintFlags); + sk_sp filter = builder()->getImageFilter(); + if (filter && !filter->asColorFilter(nullptr)) { + // drawPaint does an implicit saveLayer if an SkImageFilter is + // present that cannot be replaced by an SkColorFilter. + TRACE_EVENT0("flutter", "ui.Canvas::saveLayer (Recorded)"); + } + builder()->drawPaint(); + } else if (canvas_) { + SkPaint sk_paint; + paint.paint(sk_paint); + SkImageFilter* filter = sk_paint.getImageFilter(); + if (filter && !filter->asColorFilter(nullptr)) { + // drawPaint does an implicit saveLayer if an SkImageFilter is + // present that cannot be replaced by an SkColorFilter. + TRACE_EVENT0("flutter", "ui.Canvas::saveLayer (Recorded)"); + } + canvas_->drawPaint(sk_paint); } - canvas_->drawPaint(*paint.paint()); } void Canvas::drawRect(double left, @@ -246,29 +295,41 @@ void Canvas::drawRect(double left, double bottom, const Paint& paint, const PaintData& paint_data) { - if (!canvas_) { - return; + FML_DCHECK(paint.isNotNull()); + if (display_list_recorder_) { + paint.sync_to(builder(), DisplayListAttributeFlags::kDrawRectFlags); + builder()->drawRect(SkRect::MakeLTRB(left, top, right, bottom)); + } else if (canvas_) { + SkPaint sk_paint; + canvas_->drawRect(SkRect::MakeLTRB(left, top, right, bottom), *paint.paint(sk_paint)); } - canvas_->drawRect(SkRect::MakeLTRB(left, top, right, bottom), *paint.paint()); } void Canvas::drawRRect(const RRect& rrect, const Paint& paint, const PaintData& paint_data) { - if (!canvas_) { - return; + FML_DCHECK(paint.isNotNull()); + if (display_list_recorder_) { + paint.sync_to(builder(), DisplayListAttributeFlags::kDrawRRectFlags); + builder()->drawRRect(rrect.sk_rrect); + } else if (canvas_) { + SkPaint sk_paint; + canvas_->drawRRect(rrect.sk_rrect, *paint.paint(sk_paint)); } - canvas_->drawRRect(rrect.sk_rrect, *paint.paint()); } void Canvas::drawDRRect(const RRect& outer, const RRect& inner, const Paint& paint, const PaintData& paint_data) { - if (!canvas_) { - return; + FML_DCHECK(paint.isNotNull()); + if (display_list_recorder_) { + paint.sync_to(builder(), DisplayListAttributeFlags::kDrawDRRectFlags); + builder()->drawDRRect(outer.sk_rrect, inner.sk_rrect); + } else if (canvas_) { + SkPaint sk_paint; + canvas_->drawDRRect(outer.sk_rrect, inner.sk_rrect, *paint.paint(sk_paint)); } - canvas_->drawDRRect(outer.sk_rrect, inner.sk_rrect, *paint.paint()); } void Canvas::drawOval(double left, @@ -277,10 +338,14 @@ void Canvas::drawOval(double left, double bottom, const Paint& paint, const PaintData& paint_data) { - if (!canvas_) { - return; + FML_DCHECK(paint.isNotNull()); + if (display_list_recorder_) { + paint.sync_to(builder(), DisplayListAttributeFlags::kDrawOvalFlags); + builder()->drawOval(SkRect::MakeLTRB(left, top, right, bottom)); + } else if (canvas_) { + SkPaint sk_paint; + canvas_->drawOval(SkRect::MakeLTRB(left, top, right, bottom), *paint.paint(sk_paint)); } - canvas_->drawOval(SkRect::MakeLTRB(left, top, right, bottom), *paint.paint()); } void Canvas::drawCircle(double x, @@ -288,10 +353,14 @@ void Canvas::drawCircle(double x, double radius, const Paint& paint, const PaintData& paint_data) { - if (!canvas_) { - return; + FML_DCHECK(paint.isNotNull()); + if (display_list_recorder_) { + paint.sync_to(builder(), DisplayListAttributeFlags::kDrawCircleFlags); + builder()->drawCircle(SkPoint::Make(x, y), radius); + } else if (canvas_) { + SkPaint sk_paint; + canvas_->drawCircle(x, y, radius, *paint.paint(sk_paint)); } - canvas_->drawCircle(x, y, radius, *paint.paint()); } void Canvas::drawArc(double left, @@ -303,26 +372,36 @@ void Canvas::drawArc(double left, bool useCenter, const Paint& paint, const PaintData& paint_data) { - if (!canvas_) { - return; + FML_DCHECK(paint.isNotNull()); + if (display_list_recorder_) { + paint.sync_to(builder(), DisplayListAttributeFlags::kDrawArcFlags); + builder()->drawArc(SkRect::MakeLTRB(left, top, right, bottom), + startAngle * 180.0 / M_PI, sweepAngle * 180.0 / M_PI, + useCenter); + } else if (canvas_) { + SkPaint sk_paint; + canvas_->drawArc(SkRect::MakeLTRB(left, top, right, bottom), + startAngle * 180.0 / M_PI, sweepAngle * 180.0 / M_PI, + useCenter, *paint.paint(sk_paint)); } - canvas_->drawArc(SkRect::MakeLTRB(left, top, right, bottom), - startAngle * 180.0 / M_PI, sweepAngle * 180.0 / M_PI, - useCenter, *paint.paint()); } void Canvas::drawPath(const CanvasPath* path, const Paint& paint, const PaintData& paint_data) { - if (!canvas_) { - return; - } + FML_DCHECK(paint.isNotNull()); if (!path) { Dart_ThrowException( ToDart("Canvas.drawPath called with non-genuine Path.")); return; } - canvas_->drawPath(path->path(), *paint.paint()); + if (display_list_recorder_) { + paint.sync_to(builder(), DisplayListAttributeFlags::kDrawPathFlags); + builder()->drawPath(path->path()); + } else if (canvas_) { + SkPaint sk_paint; + canvas_->drawPath(path->path(), *paint.paint(sk_paint)); + } } void Canvas::drawImage(const CanvasImage* image, @@ -331,16 +410,21 @@ void Canvas::drawImage(const CanvasImage* image, const Paint& paint, const PaintData& paint_data, int filterQualityIndex) { - if (!canvas_) { - return; - } + FML_DCHECK(paint.isNotNull()); if (!image) { Dart_ThrowException( ToDart("Canvas.drawImage called with non-genuine Image.")); return; } auto sampling = ImageFilter::SamplingFromIndex(filterQualityIndex); - canvas_->drawImage(image->image(), x, y, sampling, paint.paint()); + if (display_list_recorder_) { + bool with_attributes = + paint.sync_to(builder(), DisplayListAttributeFlags::kDrawImageWithPaintFlags); + builder()->drawImage(image->image(), SkPoint::Make(x, y), sampling, with_attributes); + } else if (canvas_) { + SkPaint sk_paint; + canvas_->drawImage(image->image(), x, y, sampling, paint.paint(sk_paint)); + } } void Canvas::drawImageRect(const CanvasImage* image, @@ -355,9 +439,7 @@ void Canvas::drawImageRect(const CanvasImage* image, const Paint& paint, const PaintData& paint_data, int filterQualityIndex) { - if (!canvas_) { - return; - } + FML_DCHECK(paint.isNotNull()); if (!image) { Dart_ThrowException( ToDart("Canvas.drawImageRect called with non-genuine Image.")); @@ -366,8 +448,16 @@ void Canvas::drawImageRect(const CanvasImage* image, SkRect src = SkRect::MakeLTRB(src_left, src_top, src_right, src_bottom); SkRect dst = SkRect::MakeLTRB(dst_left, dst_top, dst_right, dst_bottom); auto sampling = ImageFilter::SamplingFromIndex(filterQualityIndex); - canvas_->drawImageRect(image->image(), src, dst, sampling, paint.paint(), - SkCanvas::kFast_SrcRectConstraint); + if (display_list_recorder_) { + bool with_attributes = + paint.sync_to(builder(), DisplayListAttributeFlags::kDrawImageRectWithPaintFlags); + builder()->drawImageRect(image->image(), src, dst, sampling, with_attributes, + SkCanvas::kFast_SrcRectConstraint); + } else if (canvas_) { + SkPaint sk_paint; + canvas_->drawImageRect(image->image(), src, dst, sampling, paint.paint(sk_paint), + SkCanvas::kFast_SrcRectConstraint); + } } void Canvas::drawImageNine(const CanvasImage* image, @@ -382,9 +472,7 @@ void Canvas::drawImageNine(const CanvasImage* image, const Paint& paint, const PaintData& paint_data, int bitmapSamplingIndex) { - if (!canvas_) { - return; - } + FML_DCHECK(paint.isNotNull()); if (!image) { Dart_ThrowException( ToDart("Canvas.drawImageNine called with non-genuine Image.")); @@ -397,35 +485,32 @@ void Canvas::drawImageNine(const CanvasImage* image, SkRect dst = SkRect::MakeLTRB(dst_left, dst_top, dst_right, dst_bottom); auto filter = ImageFilter::FilterModeFromIndex(bitmapSamplingIndex); if (display_list_recorder_) { - // SkCanvas turns a simple 2-rect DrawImageNine operation into a - // drawImageLattice operation which has arrays to allocate and - // pass along. For simplicity, we will bypass the canvas and ask - // the recorder to record our paint attributes and record a much - // simpler DrawImageNineOp record directly. - display_list_recorder_->RecordPaintAttributes( - paint.paint(), DisplayListCanvasRecorder::DrawType::kImageOpType); - builder()->drawImageNine(image->image(), icenter, dst, filter, true); - } else { + bool with_attributes = + paint.sync_to(builder(), DisplayListAttributeFlags::kDrawImageNineWithPaintFlags); + builder()->drawImageNine(image->image(), icenter, dst, filter, with_attributes); + } else if (canvas_) { + SkPaint sk_paint; canvas_->drawImageNine(image->image().get(), icenter, dst, filter, - paint.paint()); + paint.paint(sk_paint)); } } void Canvas::drawPicture(Picture* picture) { - if (!canvas_) { - return; - } if (!picture) { Dart_ThrowException( ToDart("Canvas.drawPicture called with non-genuine Picture.")); return; } if (picture->picture()) { - canvas_->drawPicture(picture->picture().get()); + if (display_list_recorder_) { + builder()->drawPicture(picture->picture(), nullptr, false); + } else if (canvas_) { + canvas_->drawPicture(picture->picture().get()); + } } else if (picture->display_list()) { if (display_list_recorder_) { builder()->drawDisplayList(picture->display_list()); - } else { + } else if (canvas_) { picture->display_list()->RenderTo(canvas_); } } else { @@ -437,32 +522,51 @@ void Canvas::drawPoints(const Paint& paint, const PaintData& paint_data, SkCanvas::PointMode point_mode, const tonic::Float32List& points) { - if (!canvas_) { - return; - } - static_assert(sizeof(SkPoint) == sizeof(float) * 2, "SkPoint doesn't use floats."); - canvas_->drawPoints(point_mode, - points.num_elements() / 2, // SkPoints have two floats. - reinterpret_cast(points.data()), - *paint.paint()); + FML_DCHECK(paint.isNotNull()); + if (display_list_recorder_) { + switch (point_mode) { + case SkCanvas::kPoints_PointMode: + paint.sync_to(builder(), DisplayListAttributeFlags::kDrawPointsAsPointsFlags); + break; + case SkCanvas::kLines_PointMode: + paint.sync_to(builder(), DisplayListAttributeFlags::kDrawPointsAsLinesFlags); + break; + case SkCanvas::kPolygon_PointMode: + paint.sync_to(builder(), DisplayListAttributeFlags::kDrawPointsAsPolygonFlags); + break; + } + builder()->drawPoints(point_mode, + points.num_elements() / 2, // SkPoints have two floats. + reinterpret_cast(points.data())); + } else if (canvas_) { + SkPaint sk_paint; + canvas_->drawPoints(point_mode, + points.num_elements() / 2, // SkPoints have two floats. + reinterpret_cast(points.data()), + *paint.paint(sk_paint)); + } } void Canvas::drawVertices(const Vertices* vertices, SkBlendMode blend_mode, const Paint& paint, const PaintData& paint_data) { - if (!canvas_) { - return; - } if (!vertices) { Dart_ThrowException( ToDart("Canvas.drawVertices called with non-genuine Vertices.")); return; } - canvas_->drawVertices(vertices->vertices(), blend_mode, *paint.paint()); + FML_DCHECK(paint.isNotNull()); + if (display_list_recorder_) { + paint.sync_to(builder(), DisplayListAttributeFlags::kDrawVerticesFlags); + builder()->drawVertices(vertices->vertices(), blend_mode); + } else if (canvas_) { + SkPaint sk_paint; + canvas_->drawVertices(vertices->vertices(), blend_mode, *paint.paint(sk_paint)); + } } void Canvas::drawAtlas(const Paint& paint, @@ -474,9 +578,6 @@ void Canvas::drawAtlas(const Paint& paint, const tonic::Int32List& colors, SkBlendMode blend_mode, const tonic::Float32List& cull_rect) { - if (!canvas_) { - return; - } if (!atlas) { Dart_ThrowException( ToDart("Canvas.drawAtlas or Canvas.drawRawAtlas called with " @@ -493,13 +594,27 @@ void Canvas::drawAtlas(const Paint& paint, auto sampling = ImageFilter::SamplingFromIndex(filterQualityIndex); - canvas_->drawAtlas( - skImage.get(), reinterpret_cast(transforms.data()), - reinterpret_cast(rects.data()), - reinterpret_cast(colors.data()), - rects.num_elements() / 4, // SkRect have four floats. - blend_mode, sampling, reinterpret_cast(cull_rect.data()), - paint.paint()); + FML_DCHECK(paint.isNotNull()); + if (display_list_recorder_) { + bool with_attributes = + paint.sync_to(builder(), DisplayListAttributeFlags::kDrawAtlasWithPaintFlags); + builder()->drawAtlas( + skImage, reinterpret_cast(transforms.data()), + reinterpret_cast(rects.data()), + reinterpret_cast(colors.data()), + rects.num_elements() / 4, // SkRect have four floats. + blend_mode, sampling, reinterpret_cast(cull_rect.data()), + with_attributes); + } else if (canvas_) { + SkPaint sk_paint; + canvas_->drawAtlas( + skImage.get(), reinterpret_cast(transforms.data()), + reinterpret_cast(rects.data()), + reinterpret_cast(colors.data()), + rects.num_elements() / 4, // SkRect have four floats. + blend_mode, sampling, reinterpret_cast(cull_rect.data()), + paint.paint(sk_paint)); + } } void Canvas::drawShadow(const CanvasPath* path, @@ -526,7 +641,7 @@ void Canvas::drawShadow(const CanvasPath* path, // See: https://bugs.chromium.org/p/skia/issues/detail?id=12125 builder()->drawShadow(path->path(), color, elevation, transparentOccluder, dpr); - } else { + } else if (canvas_) { flutter::PhysicalShapeLayer::DrawShadow( canvas_, path->path(), color, elevation, transparentOccluder, dpr); } @@ -534,6 +649,7 @@ void Canvas::drawShadow(const CanvasPath* path, void Canvas::Invalidate() { canvas_ = nullptr; + display_list_recorder_ = nullptr; if (dart_wrapper()) { ClearDartWrapper(); } diff --git a/lib/ui/painting/canvas.h b/lib/ui/painting/canvas.h index ca328d310dbb6..f9314ba4ab72e 100644 --- a/lib/ui/painting/canvas.h +++ b/lib/ui/painting/canvas.h @@ -188,8 +188,8 @@ class Canvas : public RefCountedDartWrappable { // paint attributes from an SkPaint and an operation type as well as access // to the raw DisplayListBuilder for emitting custom rendering operations. sk_sp display_list_recorder_; - sk_sp builder() { - return display_list_recorder_->builder(); + DisplayListBuilder* builder() { + return display_list_recorder_->builder().get(); } }; diff --git a/lib/ui/painting/paint.cc b/lib/ui/painting/paint.cc index ee3c322418729..25e3913cfc323 100644 --- a/lib/ui/painting/paint.cc +++ b/lib/ui/painting/paint.cc @@ -65,28 +65,31 @@ constexpr float invert_colors[20] = { // Must be kept in sync with the MaskFilter private constants in painting.dart. enum MaskFilterType { Null, Blur }; -Paint::Paint(Dart_Handle paint_objects, Dart_Handle paint_data) { - is_null_ = Dart_IsNull(paint_data); - if (is_null_) { - return; +Paint::Paint(Dart_Handle paint_objects, Dart_Handle paint_data) + : paint_objects_(paint_objects), paint_data_(paint_data) {} + +const SkPaint* Paint::paint(SkPaint& paint) const { + if (isNull()) { + return nullptr; } + FML_DCHECK(paint == SkPaint()); - tonic::DartByteData byte_data(paint_data); + tonic::DartByteData byte_data(paint_data_); FML_CHECK(byte_data.length_in_bytes() == kDataByteCount); const uint32_t* uint_data = static_cast(byte_data.data()); const float* float_data = static_cast(byte_data.data()); Dart_Handle values[kObjectCount]; - if (!Dart_IsNull(paint_objects)) { - FML_DCHECK(Dart_IsList(paint_objects)); + if (!Dart_IsNull(paint_objects_)) { + FML_DCHECK(Dart_IsList(paint_objects_)); intptr_t length = 0; - Dart_ListLength(paint_objects, &length); + Dart_ListLength(paint_objects_, &length); FML_CHECK(length == kObjectCount); if (Dart_IsError( - Dart_ListGetRange(paint_objects, 0, kObjectCount, values))) { - return; + Dart_ListGetRange(paint_objects_, 0, kObjectCount, values))) { + return nullptr; } Dart_Handle shader = values[kShaderIndex]; @@ -94,75 +97,75 @@ Paint::Paint(Dart_Handle paint_objects, Dart_Handle paint_data) { Shader* decoded = tonic::DartConverter::FromDart(shader); auto sampling = ImageFilter::SamplingFromIndex(uint_data[kFilterQualityIndex]); - paint_.setShader(decoded->shader(sampling)); + paint.setShader(decoded->shader(sampling)); } Dart_Handle color_filter = values[kColorFilterIndex]; if (!Dart_IsNull(color_filter)) { ColorFilter* decoded_color_filter = tonic::DartConverter::FromDart(color_filter); - paint_.setColorFilter(decoded_color_filter->filter()); + paint.setColorFilter(decoded_color_filter->filter()); } Dart_Handle image_filter = values[kImageFilterIndex]; if (!Dart_IsNull(image_filter)) { ImageFilter* decoded = tonic::DartConverter::FromDart(image_filter); - paint_.setImageFilter(decoded->filter()); + paint.setImageFilter(decoded->filter()); } } - paint_.setAntiAlias(uint_data[kIsAntiAliasIndex] == 0); + paint.setAntiAlias(uint_data[kIsAntiAliasIndex] == 0); uint32_t encoded_color = uint_data[kColorIndex]; if (encoded_color) { SkColor color = encoded_color ^ kColorDefault; - paint_.setColor(color); + paint.setColor(color); } uint32_t encoded_blend_mode = uint_data[kBlendModeIndex]; if (encoded_blend_mode) { uint32_t blend_mode = encoded_blend_mode ^ kBlendModeDefault; - paint_.setBlendMode(static_cast(blend_mode)); + paint.setBlendMode(static_cast(blend_mode)); } uint32_t style = uint_data[kStyleIndex]; if (style) { - paint_.setStyle(static_cast(style)); + paint.setStyle(static_cast(style)); } float stroke_width = float_data[kStrokeWidthIndex]; if (stroke_width != 0.0) { - paint_.setStrokeWidth(stroke_width); + paint.setStrokeWidth(stroke_width); } uint32_t stroke_cap = uint_data[kStrokeCapIndex]; if (stroke_cap) { - paint_.setStrokeCap(static_cast(stroke_cap)); + paint.setStrokeCap(static_cast(stroke_cap)); } uint32_t stroke_join = uint_data[kStrokeJoinIndex]; if (stroke_join) { - paint_.setStrokeJoin(static_cast(stroke_join)); + paint.setStrokeJoin(static_cast(stroke_join)); } float stroke_miter_limit = float_data[kStrokeMiterLimitIndex]; if (stroke_miter_limit != 0.0) { - paint_.setStrokeMiter(stroke_miter_limit + kStrokeMiterLimitDefault); + paint.setStrokeMiter(stroke_miter_limit + kStrokeMiterLimitDefault); } if (uint_data[kInvertColorIndex]) { sk_sp invert_filter = ColorFilter::MakeColorMatrixFilter255(invert_colors); - sk_sp current_filter = paint_.refColorFilter(); + sk_sp current_filter = paint.refColorFilter(); if (current_filter) { invert_filter = invert_filter->makeComposed(current_filter); } - paint_.setColorFilter(invert_filter); + paint.setColorFilter(invert_filter); } if (uint_data[kDitherIndex]) { - paint_.setDither(true); + paint.setDither(true); } switch (uint_data[kMaskFilterIndex]) { @@ -172,9 +175,138 @@ Paint::Paint(Dart_Handle paint_objects, Dart_Handle paint_data) { SkBlurStyle blur_style = static_cast(uint_data[kMaskFilterBlurStyleIndex]); double sigma = float_data[kMaskFilterSigmaIndex]; - paint_.setMaskFilter(SkMaskFilter::MakeBlur(blur_style, sigma)); + paint.setMaskFilter(SkMaskFilter::MakeBlur(blur_style, sigma)); break; } + + return &paint; +} + +bool Paint::sync_to(DisplayListBuilder* builder, + const DisplayListAttributeFlags& flags) const { + if (isNull()) { + return false; + } + tonic::DartByteData byte_data(paint_data_); + FML_CHECK(byte_data.length_in_bytes() == kDataByteCount); + + const uint32_t* uint_data = static_cast(byte_data.data()); + const float* float_data = static_cast(byte_data.data()); + + Dart_Handle values[kObjectCount]; + if (Dart_IsNull(paint_objects_)) { + if (flags.applies_shader()) { + builder->setShader(nullptr); + } + if (flags.applies_color_filter()) { + builder->setColorFilter(nullptr); + } + if (flags.applies_image_filter()) { + builder->setImageFilter(nullptr); + } + } else { + FML_DCHECK(Dart_IsList(paint_objects_)); + intptr_t length = 0; + Dart_ListLength(paint_objects_, &length); + + FML_CHECK(length == kObjectCount); + if (Dart_IsError( + Dart_ListGetRange(paint_objects_, 0, kObjectCount, values))) { + return false; + } + + if (flags.applies_shader()) { + Dart_Handle shader = values[kShaderIndex]; + if (Dart_IsNull(shader)) { + builder->setShader(nullptr); + } else { + Shader* decoded = tonic::DartConverter::FromDart(shader); + auto sampling = + ImageFilter::SamplingFromIndex(uint_data[kFilterQualityIndex]); + builder->setShader(decoded->shader(sampling)); + } + } + + if (flags.applies_color_filter()) { + Dart_Handle color_filter = values[kColorFilterIndex]; + if (Dart_IsNull(color_filter)) { + builder->setColorFilter(nullptr); + } else { + ColorFilter* decoded_color_filter = + tonic::DartConverter::FromDart(color_filter); + builder->setColorFilter(decoded_color_filter->filter()); + } + } + + if (flags.applies_image_filter()) { + Dart_Handle image_filter = values[kImageFilterIndex]; + if (Dart_IsNull(image_filter)) { + builder->setImageFilter(nullptr); + } else { + ImageFilter* decoded = + tonic::DartConverter::FromDart(image_filter); + builder->setImageFilter(decoded->filter()); + } + } + } + + if (flags.applies_anti_alias()) { + builder->setAntiAlias(uint_data[kIsAntiAliasIndex] == 0); + } + + if (flags.applies_alpha_or_color()) { + uint32_t encoded_color = uint_data[kColorIndex]; + builder->setColor(encoded_color ^ kColorDefault); + } + + if (flags.applies_blend()) { + uint32_t encoded_blend_mode = uint_data[kBlendModeIndex]; + uint32_t blend_mode = encoded_blend_mode ^ kBlendModeDefault; + builder->setBlendMode(static_cast(blend_mode)); + } + + if (flags.applies_style()) { + uint32_t style = uint_data[kStyleIndex]; + builder->setStyle(static_cast(style)); + } + + if (flags.is_stroked(builder->getStyle())) { + float stroke_width = float_data[kStrokeWidthIndex]; + builder->setStrokeWidth(stroke_width); + + float stroke_miter_limit = float_data[kStrokeMiterLimitIndex]; + builder->setStrokeMiter(stroke_miter_limit + kStrokeMiterLimitDefault); + + uint32_t stroke_cap = uint_data[kStrokeCapIndex]; + builder->setStrokeCap(static_cast(stroke_cap)); + + uint32_t stroke_join = uint_data[kStrokeJoinIndex]; + builder->setStrokeJoin(static_cast(stroke_join)); + } + + if (flags.applies_color_filter()) { + builder->setInvertColors(uint_data[kInvertColorIndex] != 0); + } + + if (flags.applies_dither()) { + builder->setDither(uint_data[kDitherIndex] != 0); + } + + if (flags.applies_mask_filter()) { + switch (uint_data[kMaskFilterIndex]) { + case Null: + builder->setMaskFilter(nullptr); + break; + case Blur: + SkBlurStyle blur_style = + static_cast(uint_data[kMaskFilterBlurStyleIndex]); + double sigma = float_data[kMaskFilterSigmaIndex]; + builder->setMaskBlurFilter(blur_style, sigma); + break; + } + } + + return true; } } // namespace flutter diff --git a/lib/ui/painting/paint.h b/lib/ui/painting/paint.h index 2c6eef8455af2..63943003ef28d 100644 --- a/lib/ui/painting/paint.h +++ b/lib/ui/painting/paint.h @@ -8,6 +8,8 @@ #include "third_party/skia/include/core/SkPaint.h" #include "third_party/tonic/converter/dart_converter.h" +#include "flow/display_list.h" + namespace flutter { class Paint { @@ -15,13 +17,25 @@ class Paint { Paint() = default; Paint(Dart_Handle paint_objects, Dart_Handle paint_data); - const SkPaint* paint() const { return is_null_ ? nullptr : &paint_; } + const SkPaint* paint(SkPaint& paint) const; + + /// Synchronize the Dart properties to the display list according + /// to the attribute flags that indicate which properties are needed. + /// The return value indicates if the paint was non-null and can + /// either be DCHECKed or used to indicate to the DisplayList + /// draw operation whether or not to use the synchronized attributes + /// (mainly the drawImage and saveLayer methods). + bool sync_to(DisplayListBuilder* builder, + const DisplayListAttributeFlags& flags) const; + + bool isNull() const { return Dart_IsNull(paint_data_); } + bool isNotNull() const { return !Dart_IsNull(paint_data_); } private: friend struct tonic::DartConverter; - SkPaint paint_; - bool is_null_ = true; + Dart_Handle paint_objects_; + Dart_Handle paint_data_; }; // The PaintData argument is a placeholder to receive encoded data for Paint diff --git a/lib/ui/text/paragraph_builder.cc b/lib/ui/text/paragraph_builder.cc index 2ef379a2fe8c7..793af6601c699 100644 --- a/lib/ui/text/paragraph_builder.cc +++ b/lib/ui/text/paragraph_builder.cc @@ -452,17 +452,19 @@ void ParagraphBuilder::pushStyle(tonic::Int32List& encoded, if (mask & tsBackgroundMask) { Paint background(background_objects, background_data); - if (background.paint()) { + if (background.isNotNull()) { + SkPaint sk_paint; style.has_background = true; - style.background = *background.paint(); + style.background = *background.paint(sk_paint); } } if (mask & tsForegroundMask) { Paint foreground(foreground_objects, foreground_data); - if (foreground.paint()) { + if (foreground.isNotNull()) { + SkPaint sk_paint; style.has_foreground = true; - style.foreground = *foreground.paint(); + style.foreground = *foreground.paint(sk_paint); } } From ee7b1947b56eb5155fa2076019fce250819ea94c Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Mon, 1 Nov 2021 21:36:09 -0700 Subject: [PATCH 2/7] convert canvas_unittests to use new flags and code formatting --- flow/display_list.cc | 184 +- flow/display_list.h | 168 +- flow/display_list_canvas.cc | 39 +- flow/display_list_canvas.h | 3 +- flow/display_list_canvas_unittests.cc | 3240 +++++++++++++------------ flow/display_list_utils.cc | 80 +- flow/display_list_utils.h | 3 +- lib/ui/painting/canvas.cc | 80 +- lib/ui/painting/canvas.h | 2 +- 9 files changed, 1941 insertions(+), 1858 deletions(-) diff --git a/flow/display_list.cc b/flow/display_list.cc index d9416107cde15..28fdde88a4a29 100644 --- a/flow/display_list.cc +++ b/flow/display_list.cc @@ -1175,8 +1175,9 @@ void DisplayListBuilder::onSetMaskBlurFilter(SkBlurStyle style, } } -void DisplayListBuilder::setAttributesFromPaint(const SkPaint& paint, - const DisplayListAttributeFlags flags) { +void DisplayListBuilder::setAttributesFromPaint( + const SkPaint& paint, + const DisplayListAttributeFlags flags) { if (flags.applies_anti_alias()) { setAntiAlias(paint.isAntiAlias()); } @@ -1545,6 +1546,7 @@ void DisplayListBuilder::drawShadow(const SkPath& path, : Push(0, 1, path, color, elevation, dpr); } +// clang-format off // Flags common to all primitives that apply colors #define PAINT_FLAGS (kUsesDither_ | \ kUsesColor_ | \ @@ -1573,137 +1575,131 @@ void DisplayListBuilder::drawShadow(const SkPath& path, kUsesBlend_ | \ kUsesColorFilter_ | \ kUsesImageFilter_) +// clang-format on + +const DisplayListAttributeFlags DisplayListOpFlags::kSaveLayerFlags = + DisplayListAttributeFlags(kIgnoresPaint_); -const DisplayListAttributeFlags // - DisplayListAttributeFlags::kSaveLayerFlags = - DisplayListAttributeFlags(kIgnoresPaint_); +const DisplayListAttributeFlags DisplayListOpFlags::kSaveLayerWithPaintFlags = + DisplayListAttributeFlags(kIsNonGeometric_ | // + kUsesAlpha_ | // + kUsesBlend_ | // + kUsesColorFilter_ | // + kUsesImageFilter_); -const DisplayListAttributeFlags // - DisplayListAttributeFlags::kSaveLayerWithPaintFlags = - DisplayListAttributeFlags(kIsNonGeometric_ | // - kUsesAlpha_ | // - kUsesBlend_ | // - kUsesColorFilter_ | // - kUsesImageFilter_); +const DisplayListAttributeFlags DisplayListOpFlags::kDrawColorFlags = + DisplayListAttributeFlags(kFloodsSurface_ | kIgnoresPaint_); -const DisplayListAttributeFlags // - DisplayListAttributeFlags::kDrawColorFlags = - DisplayListAttributeFlags(kFloodsSurface_ | kIgnoresPaint_); +const DisplayListAttributeFlags DisplayListOpFlags::kDrawPaintFlags = + DisplayListAttributeFlags(PAINT_FLAGS | kFloodsSurface_); -const DisplayListAttributeFlags // - DisplayListAttributeFlags::kDrawPaintFlags = - DisplayListAttributeFlags(PAINT_FLAGS | kFloodsSurface_); +const DisplayListAttributeFlags DisplayListOpFlags::kDrawHVLineFlags = + DisplayListAttributeFlags(PAINT_FLAGS | STROKE_FLAGS | kMayHaveCaps_); -const DisplayListAttributeFlags // - DisplayListAttributeFlags::kDrawHVLineFlags = - DisplayListAttributeFlags(PAINT_FLAGS | STROKE_FLAGS); +const DisplayListAttributeFlags DisplayListOpFlags::kDrawLineFlags = + kDrawHVLineFlags.with(kMayHaveDiagonalCaps_); -const DisplayListAttributeFlags // - DisplayListAttributeFlags::kDrawLineFlags = - kDrawHVLineFlags.with(kMayHaveDiagonalCaps_); +const DisplayListAttributeFlags DisplayListOpFlags::kDrawRectFlags = + DisplayListAttributeFlags(PAINT_FLAGS | STROKE_OR_FILL_FLAGS | + kMayHaveJoins_); -const DisplayListAttributeFlags // - DisplayListAttributeFlags::kDrawRectFlags = - DisplayListAttributeFlags(PAINT_FLAGS | STROKE_OR_FILL_FLAGS); +const DisplayListAttributeFlags DisplayListOpFlags::kDrawOvalFlags = + DisplayListAttributeFlags(PAINT_FLAGS | STROKE_OR_FILL_FLAGS); -const DisplayListAttributeFlags // - DisplayListAttributeFlags::kDrawOvalFlags = - DisplayListAttributeFlags(PAINT_FLAGS | STROKE_OR_FILL_FLAGS); +const DisplayListAttributeFlags DisplayListOpFlags::kDrawCircleFlags = + DisplayListAttributeFlags(PAINT_FLAGS | STROKE_OR_FILL_FLAGS); -const DisplayListAttributeFlags // - DisplayListAttributeFlags::kDrawCircleFlags = - DisplayListAttributeFlags(PAINT_FLAGS | STROKE_OR_FILL_FLAGS); +const DisplayListAttributeFlags DisplayListOpFlags::kDrawRRectFlags = + DisplayListAttributeFlags(PAINT_FLAGS | STROKE_OR_FILL_FLAGS); -const DisplayListAttributeFlags // - DisplayListAttributeFlags::kDrawRRectFlags = - DisplayListAttributeFlags(PAINT_FLAGS | STROKE_OR_FILL_FLAGS); +const DisplayListAttributeFlags DisplayListOpFlags::kDrawDRRectFlags = + DisplayListAttributeFlags(PAINT_FLAGS | STROKE_OR_FILL_FLAGS); -const DisplayListAttributeFlags // - DisplayListAttributeFlags::kDrawDRRectFlags = - DisplayListAttributeFlags(PAINT_FLAGS | STROKE_OR_FILL_FLAGS); +const DisplayListAttributeFlags DisplayListOpFlags::kDrawPathFlags = + DisplayListAttributeFlags(PAINT_FLAGS | STROKE_OR_FILL_FLAGS | + kMayHaveCaps_ | kMayHaveDiagonalCaps_ | + kMayHaveJoins_ | kMayHaveAcuteJoins_); -const DisplayListAttributeFlags // - DisplayListAttributeFlags::kDrawPathFlags = - DisplayListAttributeFlags(PAINT_FLAGS | STROKE_OR_FILL_FLAGS | kMayHaveDiagonalCaps_ | kMayHaveAcuteJoins_); +const DisplayListAttributeFlags DisplayListOpFlags::kDrawArcNoCenterFlags = + DisplayListAttributeFlags(PAINT_FLAGS | STROKE_OR_FILL_FLAGS | + kMayHaveCaps_ | kMayHaveDiagonalCaps_); -const DisplayListAttributeFlags // - DisplayListAttributeFlags::kDrawArcFlags = - DisplayListAttributeFlags(PAINT_FLAGS | STROKE_OR_FILL_FLAGS | kMayHaveDiagonalCaps_); +const DisplayListAttributeFlags DisplayListOpFlags::kDrawArcWithCenterFlags = + DisplayListAttributeFlags(PAINT_FLAGS | STROKE_OR_FILL_FLAGS | + kMayHaveJoins_ | kMayHaveAcuteJoins_); -const DisplayListAttributeFlags // - DisplayListAttributeFlags::kDrawPointsAsPointsFlags = - DisplayListAttributeFlags(PAINT_FLAGS | STROKE_FLAGS); +const DisplayListAttributeFlags DisplayListOpFlags::kDrawPointsAsPointsFlags = + DisplayListAttributeFlags(PAINT_FLAGS | STROKE_FLAGS | // + kMayHaveCaps_ | kButtCapIsSquare_); -const DisplayListAttributeFlags // - DisplayListAttributeFlags::kDrawPointsAsLinesFlags = - DisplayListAttributeFlags(PAINT_FLAGS | STROKE_FLAGS | kMayHaveDiagonalCaps_); +const DisplayListAttributeFlags DisplayListOpFlags::kDrawPointsAsLinesFlags = + DisplayListAttributeFlags(PAINT_FLAGS | STROKE_FLAGS | // + kMayHaveCaps_ | kMayHaveDiagonalCaps_); // Polygon mode just draws (count-1) separate lines, no joins -const DisplayListAttributeFlags // - DisplayListAttributeFlags::kDrawPointsAsPolygonFlags = - DisplayListAttributeFlags(PAINT_FLAGS | STROKE_FLAGS | kMayHaveDiagonalCaps_); - -const DisplayListAttributeFlags // - DisplayListAttributeFlags::kDrawVerticesFlags = - DisplayListAttributeFlags(kIsNonGeometric_ | // - kUsesDither_ | - kUsesAlpha_ | - kUsesShader_ | // - kUsesBlend_ | - kUsesColorFilter_ | - kUsesImageFilter_); - -const DisplayListAttributeFlags // - DisplayListAttributeFlags::kDrawImageFlags = - DisplayListAttributeFlags(kIgnoresPaint_); - -const DisplayListAttributeFlags // - DisplayListAttributeFlags::kDrawImageWithPaintFlags = // - DisplayListAttributeFlags(IMAGE_FLAGS_BASE | kUsesAntiAlias_ | kUsesMaskFilter_); - -const DisplayListAttributeFlags DisplayListAttributeFlags::kDrawImageRectFlags = +const DisplayListAttributeFlags DisplayListOpFlags::kDrawPointsAsPolygonFlags = + DisplayListAttributeFlags(PAINT_FLAGS | STROKE_FLAGS | // + kMayHaveCaps_ | kMayHaveDiagonalCaps_); + +const DisplayListAttributeFlags DisplayListOpFlags::kDrawVerticesFlags = + DisplayListAttributeFlags(kIsNonGeometric_ | // + kUsesDither_ | // + kUsesAlpha_ | // + kUsesShader_ | // + kUsesBlend_ | // + kUsesColorFilter_ | // + kUsesImageFilter_); + +const DisplayListAttributeFlags DisplayListOpFlags::kDrawImageFlags = DisplayListAttributeFlags(kIgnoresPaint_); -const DisplayListAttributeFlags - DisplayListAttributeFlags::kDrawImageRectWithPaintFlags = - DisplayListAttributeFlags(IMAGE_FLAGS_BASE | kUsesAntiAlias_ | kUsesMaskFilter_); +const DisplayListAttributeFlags DisplayListOpFlags::kDrawImageWithPaintFlags = + DisplayListAttributeFlags(IMAGE_FLAGS_BASE | // + kUsesAntiAlias_ | kUsesMaskFilter_); -const DisplayListAttributeFlags DisplayListAttributeFlags::kDrawImageNineFlags = +const DisplayListAttributeFlags DisplayListOpFlags::kDrawImageRectFlags = DisplayListAttributeFlags(kIgnoresPaint_); const DisplayListAttributeFlags - DisplayListAttributeFlags::kDrawImageNineWithPaintFlags = - DisplayListAttributeFlags(IMAGE_FLAGS_BASE); + DisplayListOpFlags::kDrawImageRectWithPaintFlags = + DisplayListAttributeFlags(IMAGE_FLAGS_BASE | // + kUsesAntiAlias_ | kUsesMaskFilter_); -const DisplayListAttributeFlags - DisplayListAttributeFlags::kDrawImageLatticeFlags = - DisplayListAttributeFlags(kIgnoresPaint_); +const DisplayListAttributeFlags DisplayListOpFlags::kDrawImageNineFlags = + DisplayListAttributeFlags(kIgnoresPaint_); const DisplayListAttributeFlags - DisplayListAttributeFlags::kDrawImageLatticeWithPaintFlags = + DisplayListOpFlags::kDrawImageNineWithPaintFlags = DisplayListAttributeFlags(IMAGE_FLAGS_BASE); -const DisplayListAttributeFlags DisplayListAttributeFlags::kDrawAtlasFlags = +const DisplayListAttributeFlags DisplayListOpFlags::kDrawImageLatticeFlags = DisplayListAttributeFlags(kIgnoresPaint_); const DisplayListAttributeFlags - DisplayListAttributeFlags::kDrawAtlasWithPaintFlags = + DisplayListOpFlags::kDrawImageLatticeWithPaintFlags = DisplayListAttributeFlags(IMAGE_FLAGS_BASE); -const DisplayListAttributeFlags DisplayListAttributeFlags::kDrawPictureFlags = +const DisplayListAttributeFlags DisplayListOpFlags::kDrawAtlasFlags = DisplayListAttributeFlags(kIgnoresPaint_); -const DisplayListAttributeFlags - DisplayListAttributeFlags::kDrawPictureWithPaintFlags = - kSaveLayerWithPaintFlags; +const DisplayListAttributeFlags DisplayListOpFlags::kDrawAtlasWithPaintFlags = + DisplayListAttributeFlags(IMAGE_FLAGS_BASE); + +const DisplayListAttributeFlags DisplayListOpFlags::kDrawPictureFlags = + DisplayListAttributeFlags(kIgnoresPaint_); + +const DisplayListAttributeFlags DisplayListOpFlags::kDrawPictureWithPaintFlags = + kSaveLayerWithPaintFlags; -const DisplayListAttributeFlags DisplayListAttributeFlags::kDrawDisplayListFlags = +const DisplayListAttributeFlags DisplayListOpFlags::kDrawDisplayListFlags = DisplayListAttributeFlags(kIgnoresPaint_); -const DisplayListAttributeFlags DisplayListAttributeFlags::kDrawTextBlobFlags = - DisplayListAttributeFlags(PAINT_FLAGS | STROKE_OR_FILL_FLAGS).without(kUsesAntiAlias_); +const DisplayListAttributeFlags DisplayListOpFlags::kDrawTextBlobFlags = + DisplayListAttributeFlags(PAINT_FLAGS | STROKE_OR_FILL_FLAGS | + kMayHaveJoins_) + .without(kUsesAntiAlias_); -const DisplayListAttributeFlags DisplayListAttributeFlags::kDrawShadowFlags = +const DisplayListAttributeFlags DisplayListOpFlags::kDrawShadowFlags = DisplayListAttributeFlags(kIgnoresPaint_); #undef PAINT_FLAGS diff --git a/flow/display_list.h b/flow/display_list.h index 292423570bc49..afa73bea17bab 100644 --- a/flow/display_list.h +++ b/flow/display_list.h @@ -477,16 +477,8 @@ class Dispatcher { /// This class contains only protected definitions and helper methods /// for the public classes |DisplayListAttributeFlags| and /// |DisplayListSpecialGeometryFlags|. -class DisplayListFlagsBase { +class DisplayListFlags { protected: - DisplayListFlagsBase(int flags) : flags_(flags) {} - - const int flags_; - - bool has_any(int qFlags) const { return (flags_ & qFlags) != 0; } - bool has_all(int qFlags) const { return (flags_ & qFlags) == qFlags; } - bool has_none(int qFlags) const { return (flags_ & qFlags) == 0; } - // A drawing operation that is not geometric in nature (but which // may still apply a MaskFilter - see |kApplyMaskFilter| below). static constexpr int kIsNonGeometric_ = 0; @@ -515,6 +507,10 @@ class DisplayListFlagsBase { // natural bounds, such as |drawColor| or |drawPaint|. static constexpr int kFloodsSurface_ = 1 << 3; + static constexpr int kMayHaveCaps_ = 1 << 4; + static constexpr int kMayHaveJoins_ = 1 << 5; + static constexpr int kButtCapIsSquare_ = 1 << 6; + // A geometric operation which has a path that might have // end caps that are not rectilinear which means that square // end caps might project further than half the stroke width @@ -523,7 +519,7 @@ class DisplayListFlagsBase { // diagonal end caps. |drawLine| might have diagonal end // caps depending on the angle of the line, and more likely // |drawPath| will often have such end caps. - static constexpr int kMayHaveDiagonalCaps_ = 1 << 4; + static constexpr int kMayHaveDiagonalCaps_ = 1 << 7; // A geometric operation which has joined vertices that are // not guaranteed to be smooth (angles of incoming and outgoing) @@ -534,9 +530,10 @@ class DisplayListFlagsBase { // |drawRect|, |drawOval| and |drawRRect| all have well // behaved joins, but |drawPath| might have joins that cause // mitered extensions outside the pre-transformed bounding box. - static constexpr int kMayHaveAcuteJoins_ = 1 << 5; + static constexpr int kMayHaveAcuteJoins_ = 1 << 8; - static constexpr int kAnySpecialGeometryMask_ = // + static constexpr int kAnySpecialGeometryMask_ = // + kMayHaveCaps_ | kMayHaveJoins_ | kButtCapIsSquare_ | // kMayHaveDiagonalCaps_ | kMayHaveAcuteJoins_; // clang-format off @@ -550,34 +547,54 @@ class DisplayListFlagsBase { static constexpr int kUsesPathEffect_ = 1 << 17; static constexpr int kUsesMaskFilter_ = 1 << 18; static constexpr int kUsesImageFilter_ = 1 << 19; - static constexpr int kIgnoresPaint_ = 1 << 20; + + static constexpr int kIgnoresPaint_ = 1 << 30; // clang-format on - + static constexpr int kAnyAttributeMask_ = // - kUsesAntiAlias_ | kUsesDither_ | kUsesColor_ | kUsesAlpha_ | kUsesBlend_ | + kUsesAntiAlias_ | kUsesDither_ | kUsesAlpha_ | kUsesColor_ | kUsesBlend_ | kUsesShader_ | kUsesColorFilter_ | kUsesPathEffect_ | kUsesMaskFilter_ | kUsesImageFilter_; }; +class DisplayListFlagsBase : protected DisplayListFlags { + protected: + DisplayListFlagsBase(int flags) : flags_(flags) {} + + const int flags_; + + bool has_any(int qFlags) const { return (flags_ & qFlags) != 0; } + bool has_all(int qFlags) const { return (flags_ & qFlags) == qFlags; } + bool has_none(int qFlags) const { return (flags_ & qFlags) == 0; } +}; + /// An attribute class for advertising specific properties of /// a geometric attribute that can affect the computation of /// the bounds of the primitive. class DisplayListSpecialGeometryFlags : DisplayListFlagsBase { public: + /// The geometry may have segments that end without closing the path. + bool may_have_end_caps() const { return has_any(kMayHaveCaps_); } + + /// The geometry may have segments connect non-continuously. + bool may_have_joins() const { return has_any(kMayHaveJoins_); } + + /// Mainly for drawPoints(PointMode) where Butt caps are rendered as squares. + bool butt_cap_becomes_square() const { return has_any(kButtCapIsSquare_); } + /// The geometry may have segments that end on a diagonal /// such that their end caps extend further than the default /// |strokeWidth * 0.5| margin around the geometry. - bool may_have_diagonal_caps() { return has_any(kMayHaveDiagonalCaps_); } + bool may_have_diagonal_caps() const { return has_any(kMayHaveDiagonalCaps_); } /// The geometry may have segments that meet at vertices at /// an acute angle such that the miter joins will extend /// further than the default |strokeWidth * 0.5| margin around /// the geometry. - bool may_have_acute_joins() { return has_any(kMayHaveAcuteJoins_); } + bool may_have_acute_joins() const { return has_any(kMayHaveAcuteJoins_); } private: - DisplayListSpecialGeometryFlags(int flags) - : DisplayListFlagsBase(flags) { + DisplayListSpecialGeometryFlags(int flags) : DisplayListFlagsBase(flags) { FML_DCHECK((flags & kAnySpecialGeometryMask_) == flags); } @@ -590,40 +607,6 @@ class DisplayListSpecialGeometryFlags : DisplayListFlagsBase { class DisplayListAttributeFlags : DisplayListFlagsBase { public: - static const DisplayListAttributeFlags kSaveLayerFlags; - static const DisplayListAttributeFlags kSaveLayerWithPaintFlags; - static const DisplayListAttributeFlags kDrawColorFlags; - static const DisplayListAttributeFlags kDrawPaintFlags; - static const DisplayListAttributeFlags kDrawLineFlags; - // Special case flags for horizonal and vertical lines - static const DisplayListAttributeFlags kDrawHVLineFlags; - static const DisplayListAttributeFlags kDrawRectFlags; - static const DisplayListAttributeFlags kDrawOvalFlags; - static const DisplayListAttributeFlags kDrawCircleFlags; - static const DisplayListAttributeFlags kDrawRRectFlags; - static const DisplayListAttributeFlags kDrawDRRectFlags; - static const DisplayListAttributeFlags kDrawPathFlags; - static const DisplayListAttributeFlags kDrawArcFlags; - static const DisplayListAttributeFlags kDrawPointsAsPointsFlags; - static const DisplayListAttributeFlags kDrawPointsAsLinesFlags; - static const DisplayListAttributeFlags kDrawPointsAsPolygonFlags; - static const DisplayListAttributeFlags kDrawVerticesFlags; - static const DisplayListAttributeFlags kDrawImageFlags; - static const DisplayListAttributeFlags kDrawImageWithPaintFlags; - static const DisplayListAttributeFlags kDrawImageRectFlags; - static const DisplayListAttributeFlags kDrawImageRectWithPaintFlags; - static const DisplayListAttributeFlags kDrawImageNineFlags; - static const DisplayListAttributeFlags kDrawImageNineWithPaintFlags; - static const DisplayListAttributeFlags kDrawImageLatticeFlags; - static const DisplayListAttributeFlags kDrawImageLatticeWithPaintFlags; - static const DisplayListAttributeFlags kDrawAtlasFlags; - static const DisplayListAttributeFlags kDrawAtlasWithPaintFlags; - static const DisplayListAttributeFlags kDrawPictureFlags; - static const DisplayListAttributeFlags kDrawPictureWithPaintFlags; - static const DisplayListAttributeFlags kDrawDisplayListFlags; - static const DisplayListAttributeFlags kDrawTextBlobFlags; - static const DisplayListAttributeFlags kDrawShadowFlags; - const DisplayListSpecialGeometryFlags WithPathEffect( sk_sp effect) const { if (is_geometric() && effect) { @@ -635,12 +618,12 @@ class DisplayListAttributeFlags : DisplayListFlagsBase { // end caps to areas that might not have had them before so all // we need to do is to indicate the potential for diagonal // end caps and move on. - return special_flags_.with(kMayHaveDiagonalCaps_); + return special_flags_.with(kMayHaveCaps_ | kMayHaveDiagonalCaps_); } else { // An arbitrary path effect can introduce joins at an arbitrary // angle and may change the geometry of the end caps - return special_flags_.with(kMayHaveDiagonalCaps_ | // - kMayHaveAcuteJoins_); + return special_flags_.with(kMayHaveCaps_ | kMayHaveDiagonalCaps_ | + kMayHaveJoins_ | kMayHaveAcuteJoins_); } } return special_flags_; @@ -652,7 +635,9 @@ class DisplayListAttributeFlags : DisplayListFlagsBase { bool applies_dither() const { return has_any(kUsesDither_); } bool applies_color() const { return has_any(kUsesColor_); } bool applies_alpha() const { return has_any(kUsesAlpha_); } - bool applies_alpha_or_color() const { return has_any(kUsesAlpha_ | kUsesColor_); } + bool applies_alpha_or_color() const { + return has_any(kUsesAlpha_ | kUsesColor_); + } /// The primitive dynamically determines whether it is a stroke or fill /// operation (or both) based on the setting of the |Style| attribute. @@ -665,7 +650,7 @@ class DisplayListAttributeFlags : DisplayListFlagsBase { /// is known then a more accurate answer can be returned from /// the |is_stroked| method by supplying the actual setting of /// the style. - bool applies_stroke_attributes() const { return is_stroked(); } + // bool applies_stroke_attributes() const { return is_stroked(); } bool applies_shader() const { return has_any(kUsesShader_); } /// The primitive honors the current SkColorFilter, including @@ -680,20 +665,20 @@ class DisplayListAttributeFlags : DisplayListFlagsBase { bool applies_image_filter() const { return has_any(kUsesImageFilter_); } bool is_geometric() const { return has_any(kIsAnyGeometryMask_); } + bool always_stroked() const { return has_any(kIsStrokedGeometry_); } bool is_stroked(SkPaint::Style style = SkPaint::Style::kStroke_Style) const { return ( has_any(kIsStrokedGeometry_) || (style != SkPaint::Style::kFill_Style && has_any(kIsDrawnGeometry_))); } - bool is_flood() const { - return has_any(kFloodsSurface_); - } - bool may_stroke_geometry() { - return has_any(kIsDrawnGeometry_ | kIsStrokedGeometry_); - } - /// Only valid after calling |WithStyle| with the current |Style| - bool does_stroke_geometry() { return has_any(kIsStrokedGeometry_); } + bool is_flood() const { return has_any(kFloodsSurface_); } + + // bool may_stroke_geometry() { + // return has_any(kIsDrawnGeometry_ | kIsStrokedGeometry_); + // } + // /// Only valid after calling |WithStyle| with the current |Style| + // bool does_stroke_geometry() { return has_any(kIsStrokedGeometry_); } private: DisplayListAttributeFlags(int flags) @@ -718,12 +703,47 @@ class DisplayListAttributeFlags : DisplayListFlagsBase { return (flags_ & ~remove); } - const DisplayListAttributeFlags with( - const DisplayListAttributeFlags& extra) const { - return with(extra.flags_); - } - const DisplayListSpecialGeometryFlags special_flags_; + + friend class DisplayListOpFlags; +}; + +class DisplayListOpFlags : DisplayListFlags { + public: + static const DisplayListAttributeFlags kSaveLayerFlags; + static const DisplayListAttributeFlags kSaveLayerWithPaintFlags; + static const DisplayListAttributeFlags kDrawColorFlags; + static const DisplayListAttributeFlags kDrawPaintFlags; + static const DisplayListAttributeFlags kDrawLineFlags; + // Special case flags for horizonal and vertical lines + static const DisplayListAttributeFlags kDrawHVLineFlags; + static const DisplayListAttributeFlags kDrawRectFlags; + static const DisplayListAttributeFlags kDrawOvalFlags; + static const DisplayListAttributeFlags kDrawCircleFlags; + static const DisplayListAttributeFlags kDrawRRectFlags; + static const DisplayListAttributeFlags kDrawDRRectFlags; + static const DisplayListAttributeFlags kDrawPathFlags; + static const DisplayListAttributeFlags kDrawArcNoCenterFlags; + static const DisplayListAttributeFlags kDrawArcWithCenterFlags; + static const DisplayListAttributeFlags kDrawPointsAsPointsFlags; + static const DisplayListAttributeFlags kDrawPointsAsLinesFlags; + static const DisplayListAttributeFlags kDrawPointsAsPolygonFlags; + static const DisplayListAttributeFlags kDrawVerticesFlags; + static const DisplayListAttributeFlags kDrawImageFlags; + static const DisplayListAttributeFlags kDrawImageWithPaintFlags; + static const DisplayListAttributeFlags kDrawImageRectFlags; + static const DisplayListAttributeFlags kDrawImageRectWithPaintFlags; + static const DisplayListAttributeFlags kDrawImageNineFlags; + static const DisplayListAttributeFlags kDrawImageNineWithPaintFlags; + static const DisplayListAttributeFlags kDrawImageLatticeFlags; + static const DisplayListAttributeFlags kDrawImageLatticeWithPaintFlags; + static const DisplayListAttributeFlags kDrawAtlasFlags; + static const DisplayListAttributeFlags kDrawAtlasWithPaintFlags; + static const DisplayListAttributeFlags kDrawPictureFlags; + static const DisplayListAttributeFlags kDrawPictureWithPaintFlags; + static const DisplayListAttributeFlags kDrawDisplayListFlags; + static const DisplayListAttributeFlags kDrawTextBlobFlags; + static const DisplayListAttributeFlags kDrawShadowFlags; }; // The primary class used to build a display list. The list of methods @@ -825,6 +845,7 @@ class DisplayListBuilder final : public virtual Dispatcher, public SkRefCnt { onSetMaskBlurFilter(style, sigma); } } + bool isAntiAlias() const { return current_anti_alias_; } bool isDither() const { return current_dither_; } SkPaint::Style getStyle() const { return current_style_; } @@ -844,9 +865,8 @@ class DisplayListBuilder final : public virtual Dispatcher, public SkRefCnt { return current_blend_mode_; } sk_sp getBlender() const { - return current_blender_ - ? current_blender_ - : SkBlender::Mode(current_blend_mode_); + return current_blender_ ? current_blender_ + : SkBlender::Mode(current_blend_mode_); } sk_sp getPathEffect() const { return current_path_effect_; } sk_sp getMaskFilter() const { return current_mask_filter_; } diff --git a/flow/display_list_canvas.cc b/flow/display_list_canvas.cc index 868a149ba22aa..7f66a37515ec6 100644 --- a/flow/display_list_canvas.cc +++ b/flow/display_list_canvas.cc @@ -261,7 +261,7 @@ void DisplayListCanvasRecorder::willSave() { SkCanvas::SaveLayerStrategy DisplayListCanvasRecorder::getSaveLayerStrategy( const SaveLayerRec& rec) { if (rec.fPaint) { - builder_->setAttributesFromPaint(*rec.fPaint, DisplayListAttributeFlags::kSaveLayerWithPaintFlags); + builder_->setAttributesFromPaint(*rec.fPaint, kSaveLayerWithPaintFlags); builder_->saveLayer(rec.fBounds, true); } else { builder_->saveLayer(rec.fBounds, false); @@ -273,28 +273,28 @@ void DisplayListCanvasRecorder::didRestore() { } void DisplayListCanvasRecorder::onDrawPaint(const SkPaint& paint) { - builder_->setAttributesFromPaint(paint, DisplayListAttributeFlags::kDrawPaintFlags); + builder_->setAttributesFromPaint(paint, kDrawPaintFlags); builder_->drawPaint(); } void DisplayListCanvasRecorder::onDrawRect(const SkRect& rect, const SkPaint& paint) { - builder_->setAttributesFromPaint(paint, DisplayListAttributeFlags::kDrawRectFlags); + builder_->setAttributesFromPaint(paint, kDrawRectFlags); builder_->drawRect(rect); } void DisplayListCanvasRecorder::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) { - builder_->setAttributesFromPaint(paint, DisplayListAttributeFlags::kDrawRRectFlags); + builder_->setAttributesFromPaint(paint, kDrawRRectFlags); builder_->drawRRect(rrect); } void DisplayListCanvasRecorder::onDrawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint) { - builder_->setAttributesFromPaint(paint, DisplayListAttributeFlags::kDrawDRRectFlags); + builder_->setAttributesFromPaint(paint, kDrawDRRectFlags); builder_->drawDRRect(outer, inner); } void DisplayListCanvasRecorder::onDrawOval(const SkRect& rect, const SkPaint& paint) { - builder_->setAttributesFromPaint(paint, DisplayListAttributeFlags::kDrawOvalFlags); + builder_->setAttributesFromPaint(paint, kDrawOvalFlags); builder_->drawOval(rect); } void DisplayListCanvasRecorder::onDrawArc(const SkRect& rect, @@ -302,12 +302,15 @@ void DisplayListCanvasRecorder::onDrawArc(const SkRect& rect, SkScalar sweepAngle, bool useCenter, const SkPaint& paint) { - builder_->setAttributesFromPaint(paint, DisplayListAttributeFlags::kDrawArcFlags); + builder_->setAttributesFromPaint(paint, + useCenter // + ? kDrawArcWithCenterFlags + : kDrawArcNoCenterFlags); builder_->drawArc(rect, startAngle, sweepAngle, useCenter); } void DisplayListCanvasRecorder::onDrawPath(const SkPath& path, const SkPaint& paint) { - builder_->setAttributesFromPaint(paint, DisplayListAttributeFlags::kDrawPathFlags); + builder_->setAttributesFromPaint(paint, kDrawPathFlags); builder_->drawPath(path); } @@ -317,13 +320,13 @@ void DisplayListCanvasRecorder::onDrawPoints(SkCanvas::PointMode mode, const SkPaint& paint) { switch (mode) { case SkCanvas::kPoints_PointMode: - builder_->setAttributesFromPaint(paint, DisplayListAttributeFlags::kDrawPointsAsPointsFlags); + builder_->setAttributesFromPaint(paint, kDrawPointsAsPointsFlags); break; case SkCanvas::kLines_PointMode: - builder_->setAttributesFromPaint(paint, DisplayListAttributeFlags::kDrawPointsAsLinesFlags); + builder_->setAttributesFromPaint(paint, kDrawPointsAsLinesFlags); break; case SkCanvas::kPolygon_PointMode: - builder_->setAttributesFromPaint(paint, DisplayListAttributeFlags::kDrawPointsAsPolygonFlags); + builder_->setAttributesFromPaint(paint, kDrawPointsAsPolygonFlags); break; } if (mode == SkCanvas::PointMode::kLines_PointMode && count == 2) { @@ -340,7 +343,7 @@ void DisplayListCanvasRecorder::onDrawPoints(SkCanvas::PointMode mode, void DisplayListCanvasRecorder::onDrawVerticesObject(const SkVertices* vertices, SkBlendMode mode, const SkPaint& paint) { - builder_->setAttributesFromPaint(paint, DisplayListAttributeFlags::kDrawVerticesFlags); + builder_->setAttributesFromPaint(paint, kDrawVerticesFlags); builder_->drawVertices(sk_ref_sp(vertices), mode); } @@ -350,7 +353,7 @@ void DisplayListCanvasRecorder::onDrawImage2(const SkImage* image, const SkSamplingOptions& sampling, const SkPaint* paint) { if (paint != nullptr) { - builder_->setAttributesFromPaint(*paint, DisplayListAttributeFlags::kDrawImageWithPaintFlags); + builder_->setAttributesFromPaint(*paint, kDrawImageWithPaintFlags); } builder_->drawImage(sk_ref_sp(image), SkPoint::Make(dx, dy), sampling, paint != nullptr); @@ -363,7 +366,7 @@ void DisplayListCanvasRecorder::onDrawImageRect2( const SkPaint* paint, SrcRectConstraint constraint) { if (paint != nullptr) { - builder_->setAttributesFromPaint(*paint, DisplayListAttributeFlags::kDrawImageRectWithPaintFlags); + builder_->setAttributesFromPaint(*paint, kDrawImageRectWithPaintFlags); } builder_->drawImageRect(sk_ref_sp(image), src, dst, sampling, paint != nullptr, constraint); @@ -380,7 +383,7 @@ void DisplayListCanvasRecorder::onDrawImageLattice2(const SkImage* image, if (*paint == default_paint) { paint = nullptr; } else { - builder_->setAttributesFromPaint(*paint, DisplayListAttributeFlags::kDrawImageLatticeWithPaintFlags); + builder_->setAttributesFromPaint(*paint, kDrawImageLatticeWithPaintFlags); } } builder_->drawImageLattice(sk_ref_sp(image), lattice, dst, filter, @@ -396,7 +399,7 @@ void DisplayListCanvasRecorder::onDrawAtlas2(const SkImage* image, const SkRect* cull, const SkPaint* paint) { if (paint != nullptr) { - builder_->setAttributesFromPaint(*paint, DisplayListAttributeFlags::kDrawAtlasWithPaintFlags); + builder_->setAttributesFromPaint(*paint, kDrawAtlasWithPaintFlags); } builder_->drawAtlas(sk_ref_sp(image), xform, src, colors, count, mode, sampling, cull, paint != nullptr); @@ -406,7 +409,7 @@ void DisplayListCanvasRecorder::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, const SkPaint& paint) { - builder_->setAttributesFromPaint(paint, DisplayListAttributeFlags::kDrawTextBlobFlags); + builder_->setAttributesFromPaint(paint, kDrawTextBlobFlags); builder_->drawTextBlob(sk_ref_sp(blob), x, y); } void DisplayListCanvasRecorder::onDrawShadowRec(const SkPath& path, @@ -421,7 +424,7 @@ void DisplayListCanvasRecorder::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) { if (paint != nullptr) { - builder_->setAttributesFromPaint(*paint, DisplayListAttributeFlags::kDrawPictureWithPaintFlags); + builder_->setAttributesFromPaint(*paint, kDrawPictureWithPaintFlags); } builder_->drawPicture(sk_ref_sp(picture), matrix, paint != nullptr); } diff --git a/flow/display_list_canvas.h b/flow/display_list_canvas.h index 4551f650df484..d48b7760eaf81 100644 --- a/flow/display_list_canvas.h +++ b/flow/display_list_canvas.h @@ -120,7 +120,8 @@ class DisplayListCanvasDispatcher : public virtual Dispatcher, // Receives all methods on SkCanvas and sends them to a DisplayListBuilder class DisplayListCanvasRecorder : public SkCanvasVirtualEnforcer, - public SkRefCnt { + public SkRefCnt, + DisplayListOpFlags { public: DisplayListCanvasRecorder(const SkRect& bounds); diff --git a/flow/display_list_canvas_unittests.cc b/flow/display_list_canvas_unittests.cc index ccc389dcd7ae5..613180b7c4e59 100644 --- a/flow/display_list_canvas_unittests.cc +++ b/flow/display_list_canvas_unittests.cc @@ -236,86 +236,212 @@ class BoundsTolerance { SkScalar discrete_offset_ = 0; }; -enum class ReferencePixelExpectation { - kPixelsMatchReference, - kPixelsDoNotMatchReference, - kIgnoreReference, -}; - -typedef const std::function CvRenderer; +typedef const std::function CvSetup; +typedef const std::function CvRenderer; typedef const std::function DlRenderer; -static void EmptyCvRenderer(SkCanvas*, SkPaint&) {} +static void EmptyCvRenderer(SkCanvas*, const SkPaint&) {} static void EmptyDlRenderer(DisplayListBuilder&) {} -class TestParameters { +class RenderSurface { public: - TestParameters(const CvRenderer& cv_renderer, const DlRenderer& dl_renderer, - const DisplayListAttributeFlags& flags) - : cv_renderer_(cv_renderer), dl_renderer_(dl_renderer), flags_(flags) {} - - ReferencePixelExpectation under_transform() const { - return matches_if(flags_.is_flood()); + RenderSurface(sk_sp surface) : surface_(surface) {} + ~RenderSurface() { sk_free(addr_); } + + SkCanvas* canvas() { return surface_->getCanvas(); } + + const SkPixmap* pixmap() { + if (!pixmap_.addr()) { + SkImageInfo info = surface_->imageInfo(); + if (info.colorType() != kN32_SkColorType || + !surface_->peekPixels(&pixmap_)) { + info = SkImageInfo::MakeN32Premul(info.dimensions()); + addr_ = malloc(info.computeMinByteSize() * info.height()); + pixmap_.reset(info, addr_, info.minRowBytes()); + EXPECT_TRUE(surface_->readPixels(pixmap_, 0, 0)); + } + } + return &pixmap_; } - ReferencePixelExpectation over_other_rendering() const { - return matches_if(flags_.is_flood()); - } + private: + sk_sp surface_; + SkPixmap pixmap_; + void* addr_ = nullptr; +}; - ReferencePixelExpectation with_clip() const { - return matches_if(false); +class RenderEnvironment { + public: + static RenderEnvironment Make565() { + return RenderEnvironment(SkImageInfo::Make({1, 1}, kRGB_565_SkColorType, + kOpaque_SkAlphaType, nullptr)); } - bool uses_paint() const { return !flags_.ignores_paint(); } - - ReferencePixelExpectation with_anti_alias(bool is_aa) const { - // For some reason the Ahem font does not produce AA results - return matches_if(!is_aa || !flags_.applies_anti_alias()); + static RenderEnvironment MakeN32() { + return RenderEnvironment(SkImageInfo::MakeN32Premul(1, 1)); } - ReferencePixelExpectation with_dither(bool is_dither) const { - return matches_if(!is_dither || !flags_.applies_dither()); + RenderSurface MakeSurface(const SkColor bg = SK_ColorTRANSPARENT, + int width = TestWidth, + int height = TestHeight) const { + sk_sp surface = + SkSurface::MakeRaster(info_.makeWH(width, height)); + surface->getCanvas()->clear(bg); + return RenderSurface(surface); } - ReferencePixelExpectation with_color(SkColor color) const { - return matches_if(color == SK_ColorBLACK || !flags_.applies_color()); + void init_ref(CvRenderer& cv_renderer, SkColor bg = SK_ColorTRANSPARENT) { + init_ref([=](SkCanvas*, SkPaint&) {}, cv_renderer, bg); } - ReferencePixelExpectation with_blending(SkBlendMode mode) const { - return matches_if(mode == SkBlendMode::kSrcOver || !flags_.applies_blend()); + void init_ref(CvSetup& cv_setup, + CvRenderer& cv_renderer, + SkColor bg = SK_ColorTRANSPARENT) { + ref_canvas()->clear(bg); + cv_setup(ref_canvas(), ref_paint_); + ref_matrix_ = ref_canvas()->getTotalMatrix(); + ref_clip_ = ref_canvas()->getDeviceClipBounds(); + cv_renderer(ref_canvas(), ref_paint_); + ref_pixmap_ = ref_surface_.pixmap(); } - ReferencePixelExpectation with_blending(sk_sp blender) const { - return matches_if(!blender || !flags_.applies_blend()); - } + SkCanvas* ref_canvas() { return ref_surface_.canvas(); } + const SkPaint& ref_paint() const { return ref_paint_; } + const SkMatrix& ref_matrix() const { return ref_matrix_; } + const SkIRect& ref_clip_bounds() const { return ref_clip_; } + const SkPixmap* ref_pixmap() const { return ref_pixmap_; } - ReferencePixelExpectation with_shader(sk_sp shader) const { - return matches_if(!shader || !flags_.applies_shader()); - } + private: + RenderEnvironment(const SkImageInfo& info) + : info_(info), ref_surface_(MakeSurface()) {} - ReferencePixelExpectation with_image_filter(sk_sp filter) const { - return matches_if(!filter || !flags_.applies_image_filter()); - } + const SkImageInfo info_; - ReferencePixelExpectation with_color_filter(sk_sp filter) const { - return matches_if(!filter || !flags_.applies_color_filter()); - } + SkPaint ref_paint_; + SkMatrix ref_matrix_; + SkIRect ref_clip_; + RenderSurface ref_surface_; + const SkPixmap* ref_pixmap_ = nullptr; +}; - ReferencePixelExpectation with_mask_filter(sk_sp filter) const { - return matches_if(!filter || !flags_.applies_mask_filter()); - } +class CaseParameters; - ReferencePixelExpectation with_path_effect(sk_sp effect) const { - return matches_if(!effect || !flags_.applies_path_effect()); - } +class TestParameters { + public: + TestParameters(const CvRenderer& cv_renderer, + const DlRenderer& dl_renderer, + const DisplayListAttributeFlags& flags) + : cv_renderer_(cv_renderer), dl_renderer_(dl_renderer), flags_(flags) {} + + bool uses_paint() const { return !flags_.ignores_paint(); } - ReferencePixelExpectation with_stroke_style(SkPaint::Style style) const { - return matches_if(flags_.is_stroked(style) == flags_.is_stroked(SkPaint::kFill_Style)); + bool should_match(const RenderEnvironment& env, + const SkPaint& paint, + const SkMatrix& matrix, + const SkIRect& device_clip, + bool has_diff_clip, + bool has_mutating_save_layer) const { + if (has_mutating_save_layer) { + return false; + } + if (env.ref_clip_bounds() != device_clip || has_diff_clip) { + return false; + } + if (env.ref_matrix() != matrix && !flags_.is_flood()) { + return false; + } + if (flags_.ignores_paint()) { + return true; + } + const SkPaint& ref_paint = env.ref_paint(); + if (flags_.applies_anti_alias() && // + ref_paint.isAntiAlias() != paint.isAntiAlias()) { + return false; + } + if (flags_.applies_dither() && // + ref_paint.isDither() != paint.isDither()) { + return false; + } + if (flags_.applies_color() && // + ref_paint.getColor() != paint.getColor()) { + return false; + } + if (flags_.applies_alpha() && // + ref_paint.getAlpha() != paint.getAlpha()) { + return false; + } + if (flags_.applies_blend() && // + ref_paint.getBlender() != paint.getBlender()) { + return false; + } + if (flags_.applies_color_filter() && // + ref_paint.getColorFilter() != paint.getColorFilter()) { + return false; + } + if (flags_.applies_mask_filter() && // + ref_paint.getMaskFilter() != paint.getMaskFilter()) { + return false; + } + if (flags_.applies_image_filter() && // + ref_paint.getImageFilter() != paint.getImageFilter()) { + return false; + } + if (flags_.applies_shader() && // + ref_paint.getShader() != paint.getShader()) { + return false; + } + DisplayListSpecialGeometryFlags geo_flags = + flags_.WithPathEffect(paint.refPathEffect()); + if (flags_.applies_path_effect() && // + ref_paint.getPathEffect() != paint.getPathEffect()) { + SkPathEffect::DashInfo info; + if (paint.getPathEffect()->asADash(&info) != + SkPathEffect::kDash_DashType) { + return false; + } + if (!ignores_dashes_) { + return false; + } + } + bool is_stroked = flags_.is_stroked(ref_paint.getStyle()); + if (flags_.is_stroked(paint.getStyle()) != is_stroked) { + return false; + } + if (!is_stroked) { + return true; + } + if (ref_paint.getStrokeWidth() != paint.getStrokeWidth()) { + return false; + } + if (geo_flags.may_have_end_caps() && // + getCap(ref_paint, geo_flags) != getCap(paint, geo_flags)) { + return false; + } + if (geo_flags.may_have_joins()) { + if (ref_paint.getStrokeJoin() != paint.getStrokeJoin()) { + return false; + } + if (ref_paint.getStrokeJoin() == SkPaint::kMiter_Join) { + SkScalar ref_miter = ref_paint.getStrokeMiter(); + SkScalar test_miter = paint.getStrokeMiter(); + // miter limit < 1.4 affects right angles + if (geo_flags.may_have_acute_joins() || // + ref_miter < 1.4 || test_miter < 1.4) { + if (ref_miter != test_miter) { + return false; + } + } + } + } + return true; } - static ReferencePixelExpectation Or(ReferencePixelExpectation a, - ReferencePixelExpectation b) { - return matches_if(a == ReferencePixelExpectation::kPixelsMatchReference && - b == ReferencePixelExpectation::kPixelsMatchReference); + SkPaint::Cap getCap(const SkPaint& paint, + DisplayListSpecialGeometryFlags geo_flags) const { + SkPaint::Cap cap = paint.getStrokeCap(); + if (geo_flags.butt_cap_becomes_square() && cap == SkPaint::kButt_Cap) { + return SkPaint::kSquare_Cap; + } + return cap; } const BoundsTolerance adjust(const BoundsTolerance& tolerance, @@ -326,6 +452,21 @@ class TestParameters { // discrete path effect. return tolerance.addBoundsPadding(2, 2); } + if (is_draw_line_) { + return lineAdjust(tolerance, paint, matrix); + } + if (is_draw_arc_center_) { + if (paint.getStyle() != SkPaint::kFill_Style && + paint.getStrokeJoin() == SkPaint::kMiter_Join) { + // the miter join at the center of an arc does not really affect + // its bounds in any of our test cases, but the bounds code needs + // to take it into account for the cases where it might, so we + // relax our tolerance to reflect the miter bounds padding. + SkScalar miter_pad = + paint.getStrokeMiter() * paint.getStrokeWidth() * 0.5f; + return tolerance.addBoundsPadding(miter_pad, miter_pad); + } + } // Shadow primitives could use just a little more horizontal bounds // tolerance when drawn with a perspective transform. // if (is_draw_shadows_ && matrix.hasPerspective()) { @@ -334,33 +475,128 @@ class TestParameters { return tolerance; } + const BoundsTolerance lineAdjust(const BoundsTolerance& tolerance, + const SkPaint& paint, + const SkMatrix& matrix) const { + SkScalar adjust = 0.0; + SkScalar half_width = paint.getStrokeWidth() * 0.5f; + if (tolerance.discrete_offset() > 0) { + // When a discrete path effect is added, the bounds calculations must + // allow for miters in any direction, but a horizontal line will not + // have miters in the horizontal direction, similarly for vertical + // lines, and diagonal lines will have miters off at a "45 degree" + // angle that don't expand the bounds much at all. + // Also, the discrete offset will not move any points parallel with + // the line, so provide tolerance for both miters and offset. + adjust = + half_width * paint.getStrokeMiter() + tolerance.discrete_offset(); + } + DisplayListSpecialGeometryFlags geo_flags = + flags_.WithPathEffect(paint.refPathEffect()); + if (paint.getStrokeCap() == SkPaint::kButt_Cap && + !geo_flags.butt_cap_becomes_square()) { + adjust = std::max(adjust, half_width); + } + if (adjust == 0) { + return tolerance; + } + SkScalar hTolerance; + SkScalar vTolerance; + if (is_horizontal_line_) { + FML_DCHECK(!is_vertical_line_); + hTolerance = adjust; + vTolerance = 0; + } else if (is_vertical_line_) { + hTolerance = 0; + vTolerance = adjust; + } else { + // The perpendicular miters just do not impact the bounds of + // diagonal lines at all as they are aimed in the wrong direction + // to matter. So allow tolerance in both axes. + hTolerance = vTolerance = adjust; + } + BoundsTolerance new_tolerance = + tolerance.addBoundsPadding(hTolerance, vTolerance); + return new_tolerance; + } + const CvRenderer& cv_renderer() const { return cv_renderer_; } void render_to(SkCanvas* canvas, SkPaint& paint) const { cv_renderer_(canvas, paint); } const DlRenderer& dl_renderer() const { return dl_renderer_; } - void render_to(DisplayListBuilder& builder) const { + void render_to(DisplayListBuilder& builder) const { // dl_renderer_(builder); } + // If a test is using any shadow operations then we cannot currently + // record those in an SkCanvas and play it back into a DisplayList + // because internally the operation gets encapsulated in a Skia + // ShadowRec which is not exposed by their headers. For operations + // that use shadows, we can perform a lot of tests, but not the tests + // that require SkCanvas->DisplayList transfers. + // See: https://bugs.chromium.org/p/skia/issues/detail?id=12125 bool is_draw_shadows() const { return is_draw_shadows_; } + // The CPU renders nothing for drawVertices with a Blender. + // See: https://bugs.chromium.org/p/skia/issues/detail?id=12200 bool is_draw_vertices() const { return is_draw_vertices_; } - bool is_draw_text_blob() const { return is_draw_text_blob_; } + // The CPU renders nothing for drawAtlas with a Blender. + // See: https://bugs.chromium.org/p/skia/issues/detail?id=12199 bool is_draw_atlas() const { return is_draw_atlas_; } + // Tests that call drawTextBlob with an sk_ref paint attribute will cause + // those attributes to be stored in an internal Skia cache so we need + // to expect that the |sk_ref.unique()| call will fail in those cases. + // See: (TBD(flar) - file Skia bug) + bool is_draw_text_blob() const { return is_draw_text_blob_; } bool is_draw_display_list() const { return is_draw_display_list_; } + bool is_draw_line() const { return is_draw_line_; } + bool is_horizontal_line() const { return is_horizontal_line_; } + bool is_vertical_line() const { return is_vertical_line_; } - TestParameters& set_draw_shadows() { is_draw_shadows_ = true; return *this; } - TestParameters& set_draw_vertices() { is_draw_vertices_ = true; return *this; } - TestParameters& set_draw_text_blob() { is_draw_text_blob_ = true; return *this; } - TestParameters& set_draw_atlas() { is_draw_atlas_ = true; return *this; } - TestParameters& set_draw_display_list() { is_draw_display_list_ = true; return *this; } - - protected: - static ReferencePixelExpectation matches_if(bool should_match) { - return should_match // - ? ReferencePixelExpectation::kPixelsMatchReference - : ReferencePixelExpectation::kPixelsDoNotMatchReference; + TestParameters& set_draw_shadows() { + is_draw_shadows_ = true; + return *this; + } + TestParameters& set_draw_vertices() { + is_draw_vertices_ = true; + return *this; + } + TestParameters& set_draw_text_blob() { + is_draw_text_blob_ = true; + return *this; + } + TestParameters& set_draw_atlas() { + is_draw_atlas_ = true; + return *this; + } + TestParameters& set_draw_display_list() { + is_draw_display_list_ = true; + return *this; + } + TestParameters& set_draw_line() { + is_draw_line_ = true; + return *this; + } + TestParameters& set_draw_arc_center() { + is_draw_arc_center_ = true; + return *this; + } + TestParameters& set_ignores_dashes() { + ignores_dashes_ = true; + return *this; + } + TestParameters& set_horizontal_line() { + is_horizontal_line_ = true; + return *this; + } + TestParameters& set_vertical_line() { + is_vertical_line_ = true; + return *this; + } + TestParameters& set_max_miter_limit(SkScalar limit) { + max_miter_limit_ = limit; + return *this; } private: @@ -373,6 +609,13 @@ class TestParameters { bool is_draw_text_blob_ = false; bool is_draw_atlas_ = false; bool is_draw_display_list_ = false; + bool is_draw_line_ = false; + bool is_draw_arc_center_ = false; + bool ignores_dashes_ = false; + bool is_horizontal_line_ = false; + bool is_vertical_line_ = false; + + SkScalar max_miter_limit_ = 1E9; }; class CaseParameters { @@ -380,38 +623,71 @@ class CaseParameters { CaseParameters(std::string info) : CaseParameters(info, EmptyCvRenderer, EmptyDlRenderer) {} - CaseParameters(std::string info, - CvRenderer cv_setup, - DlRenderer dl_setup, - SkColor bg = SK_ColorTRANSPARENT) - : CaseParameters(info, cv_setup, dl_setup, EmptyCvRenderer, EmptyDlRenderer, bg) {} + CaseParameters(std::string info, CvSetup cv_setup, DlRenderer dl_setup) + : CaseParameters(info, + cv_setup, + dl_setup, + EmptyCvRenderer, + EmptyDlRenderer, + SK_ColorTRANSPARENT, + false, + false) {} CaseParameters(std::string info, - CvRenderer cv_setup, + CvSetup cv_setup, DlRenderer dl_setup, CvRenderer cv_restore, DlRenderer dl_restore, - SkColor bg = SK_ColorTRANSPARENT) - : info_(info), bg_(bg), - cv_setup_(cv_setup), dl_setup_(dl_setup), - cv_restore_(cv_restore), dl_restore_(dl_restore) {} + SkColor bg, + bool has_diff_clip, + bool has_mutating_save_layer) + : info_(info), + bg_(bg), + cv_setup_(cv_setup), + dl_setup_(dl_setup), + cv_restore_(cv_restore), + dl_restore_(dl_restore), + has_diff_clip_(has_diff_clip), + has_mutating_save_layer_(has_mutating_save_layer) {} + + CaseParameters with_restore(CvRenderer cv_restore, + DlRenderer dl_restore, + bool mutating_layer) { + return CaseParameters(info_, cv_setup_, dl_setup_, cv_restore, dl_restore, + bg_, has_diff_clip_, mutating_layer); + } + + CaseParameters with_bg(SkColor bg) { + return CaseParameters(info_, cv_setup_, dl_setup_, cv_restore_, dl_restore_, + bg, has_diff_clip_, has_mutating_save_layer_); + } + + CaseParameters with_diff_clip() { + return CaseParameters(info_, cv_setup_, dl_setup_, cv_restore_, dl_restore_, + bg_, true, has_mutating_save_layer_); + } std::string info() const { return info_; } SkColor bg() const { return bg_; } + bool has_diff_clip() const { return has_diff_clip_; } + bool has_mutating_save_layer() const { return has_mutating_save_layer_; } - CvRenderer cv_setup() const { return cv_setup_; } + CvSetup cv_setup() const { return cv_setup_; } DlRenderer dl_setup() const { return dl_setup_; } CvRenderer cv_restore() const { return cv_restore_; } DlRenderer dl_restore() const { return dl_restore_; } - void render_to(SkCanvas* canvas, const TestParameters& testP) const { + const SkPaint render_to(SkCanvas* canvas, // + const TestParameters& testP) const { SkPaint paint; cv_setup_(canvas, paint); testP.render_to(canvas, paint); cv_restore_(canvas, paint); + return paint; } - void render_to(DisplayListBuilder& builder, const TestParameters& testP) const { + void render_to(DisplayListBuilder& builder, + const TestParameters& testP) const { dl_setup_(builder); testP.render_to(builder); dl_restore_(builder); @@ -420,125 +696,18 @@ class CaseParameters { private: const std::string info_; const SkColor bg_; - const CvRenderer cv_setup_; + const CvSetup cv_setup_; const DlRenderer dl_setup_; const CvRenderer cv_restore_; const DlRenderer dl_restore_; + const bool has_diff_clip_; + const bool has_mutating_save_layer_; }; class CanvasCompareTester { - private: - // If a test is using any shadow operations then we cannot currently - // record those in an SkCanvas and play it back into a DisplayList - // because internally the operation gets encapsulated in a Skia - // ShadowRec which is not exposed by their headers. For operations - // that use shadows, we can perform a lot of tests, but not the tests - // that require SkCanvas->DisplayList transfers. - // See: https://bugs.chromium.org/p/skia/issues/detail?id=12125 - // static bool TestingDrawShadows; - // The CPU renders nothing for drawVertices with a Blender. - // See: https://bugs.chromium.org/p/skia/issues/detail?id=12200 - // static bool TestingDrawVertices; - // The CPU renders nothing for drawAtlas with a Blender. - // See: https://bugs.chromium.org/p/skia/issues/detail?id=12199 - // static bool TestingDrawAtlas; - // static bool TestingDrawTextBlob; - - static constexpr ReferencePixelExpectation kPixelsDoNotMatchReference = - ReferencePixelExpectation::kPixelsDoNotMatchReference; - static constexpr ReferencePixelExpectation kPixelsMatchReference = - ReferencePixelExpectation::kPixelsMatchReference; - static constexpr ReferencePixelExpectation kIgnoreReference = - ReferencePixelExpectation::kIgnoreReference; - public: - // typedef const std::function - // ToleranceAdjuster; - static BoundsTolerance DefaultTolerance; - class RenderSurface { - public: - RenderSurface(sk_sp surface) : surface_(surface) {} - ~RenderSurface() { sk_free(addr_); } - - SkCanvas* canvas() { return surface_->getCanvas(); } - - const SkPixmap* pixmap() { - if (!pixmap_.addr()) { - SkImageInfo info = surface_->imageInfo(); - if (info.colorType() != kN32_SkColorType || - !surface_->peekPixels(&pixmap_)) { - info = SkImageInfo::MakeN32Premul(info.dimensions()); - addr_ = malloc(info.computeMinByteSize() * info.height()); - pixmap_.reset(info, addr_, info.minRowBytes()); - EXPECT_TRUE(surface_->readPixels(pixmap_, 0, 0)); - } - } - return &pixmap_; - } - - private: - sk_sp surface_; - SkPixmap pixmap_; - void *addr_ = nullptr; - }; - - class RenderEnvironment { - public: - static RenderEnvironment Make565() { - return RenderEnvironment(SkImageInfo::Make({1, 1}, - kRGB_565_SkColorType, - kOpaque_SkAlphaType, - nullptr)); - } - - static RenderEnvironment MakeN32() { - return RenderEnvironment(SkImageInfo::MakeN32Premul(1, 1)); - } - - RenderSurface MakeSurface(const SkColor bg = SK_ColorTRANSPARENT, // - int width = TestWidth, int height = TestHeight) const { - sk_sp surface = SkSurface::MakeRaster(info_.makeWH(width, height)); - surface->getCanvas()->clear(bg); - return RenderSurface(surface); - } - - void init_ref(CvRenderer& cv_renderer, SkColor bg = SK_ColorTRANSPARENT) { - init_ref([=](SkCanvas*, SkPaint&) {}, cv_renderer, bg); - } - - void init_ref(CvRenderer& cv_setup, - CvRenderer& cv_renderer, - SkColor bg = SK_ColorTRANSPARENT) { - ref_canvas()->clear(bg); - SkPaint paint; - cv_setup(ref_canvas(), paint); - cv_renderer(ref_canvas(), paint); - ref_pixmap_ = ref_surface_.pixmap(); - } - - SkCanvas* ref_canvas() { return ref_surface_.canvas(); } - const SkPixmap* ref_pixmap() const { return ref_pixmap_; } - - private: - RenderEnvironment(const SkImageInfo& info) - : info_(info), ref_surface_(MakeSurface()) {} - - const SkImageInfo info_; - - RenderSurface ref_surface_; - const SkPixmap* ref_pixmap_ = nullptr; - }; - - // All of the tests should eventually use this method except for the - // tests that call |RenderNoAttributes| because they do not use the - // SkPaint object. - // But there are a couple of conditions beyond our control which require - // the use of one of the variant methods below (|RenderShadows|, - // |RenderVertices|, |RenderAtlas|). static void RenderAll(const TestParameters& params, const BoundsTolerance& tolerance = DefaultTolerance) { RenderEnvironment env = RenderEnvironment::MakeN32(); @@ -546,75 +715,20 @@ class CanvasCompareTester { RenderWithTransforms(params, env, tolerance); RenderWithClips(params, env, tolerance); RenderWithSaveRestore(params, env, tolerance); - // Only test all attributes if the canvas version uses the supplied paint object + // Only test attributes if the canvas version uses the paint object if (params.uses_paint()) { RenderWithAttributes(params, env, tolerance); } } - // Used by the tests that render shadows to deal with a condition where - // we cannot recapture the shadow information from an SkCanvas stream - // due to the DrawShadowRec used by Skia is not properly exported. - // See: https://bugs.chromium.org/p/skia/issues/detail?id=12125 - // static void RenderShadows( - // CvRenderer& cv_renderer, - // DlRenderer& dl_renderer, - // ToleranceAdjuster& adjuster = DefaultAdjuster, - // const BoundsTolerance& tolerance = DefaultTolerance) { - // TestingDrawShadows = true; - // MutationPredictor predictor = - // MutationPredictor(DisplayListAttributeFlags::kDrawShadowFlags); - // RenderAll(cv_renderer, dl_renderer, predictor, adjuster, tolerance); - // TestingDrawShadows = false; - // } - - // Used by the tests that call drawVertices to avoid using an SkBlender - // during testing because the CPU renderer appears not to render anything. - // See: https://bugs.chromium.org/p/skia/issues/detail?id=12200 - // static void RenderVertices(CvRenderer& cv_renderer, DlRenderer& dl_renderer) { - // TestingDrawVertices = true; - // MutationPredictor predictor = MutationPredictor( - // DisplayListAttributeFlags::kDrawVerticesFlags); - // RenderAll(cv_renderer, dl_renderer, predictor); - // TestingDrawVertices = false; - // } - - // Used by the tests that call drawAtlas to avoid using an SkBlender - // during testing because the CPU renderer appears not to render anything. - // See: https://bugs.chromium.org/p/skia/issues/detail?id=12199 - // static void RenderAtlas(CvRenderer& cv_renderer, DlRenderer& dl_renderer, bool with_paint) { - // TestingDrawAtlas = true; - // MutationPredictor predictor = with_paint - // ? MutationPredictor( - // DisplayListAttributeFlags::kDrawAtlasWithPaintFlags) - // : MutationPredictor( - // DisplayListAttributeFlags::kDrawAtlasFlags); - // RenderAll(cv_renderer, dl_renderer, predictor); - // TestingDrawAtlas = false; - // } - - // Used by the tests that call drawTextBlob so that we can skip the unique() - // tests on shared pointer attributes that get stored in an internal Skia - // cache. - // See: (TBD(flar) - file Skia bug) - // static void RenderTextBlob(CvRenderer& cv_renderer, DlRenderer& dl_renderer, - // ToleranceAdjuster& adjuster, - // const BoundsTolerance& tolerance) { - // TestingDrawTextBlob = true; - // MutationPredictor predictor = MutationPredictor( - // DisplayListAttributeFlags::kDrawTextBlobFlags); - // RenderAll(cv_renderer, dl_renderer, predictor, adjuster, tolerance); - // TestingDrawTextBlob = false; - // } - static void RenderWithSaveRestore(const TestParameters& testP, const RenderEnvironment& env, const BoundsTolerance& tolerance) { - SkRect clip = SkRect::MakeLTRB(0, 0, 10, 10); - SkRect rect = SkRect::MakeLTRB(5, 5, 15, 15); + SkRect clip = SkRect::MakeXYWH(RenderCenterX - 1, RenderCenterY - 1, 2, 2); + SkRect rect = SkRect::MakeXYWH(RenderCenterX, RenderCenterY, 10, 10); SkColor alpha_layer_color = SkColorSetARGB(0x7f, 0x00, 0xff, 0xff); SkColor default_color = SkPaint().getColor(); - CvRenderer cv_restore = [=](SkCanvas* cv, SkPaint& p) { + CvRenderer cv_restore = [=](SkCanvas* cv, const SkPaint& p) { // Draw more than one primitive to disable peephole optimizations cv->drawRect(RenderBounds.makeOffset(500, 500), p); // params.cv_renderer()(cv, p); @@ -627,75 +741,75 @@ class CanvasCompareTester { b.restore(); }; SkRect layer_bounds = RenderBounds.makeInset(15, 15); - RenderWith(testP, env, CaseParameters( - "With prior save/clip/restore", - [=](SkCanvas* cv, SkPaint& p) { - cv->save(); - cv->clipRect(clip, SkClipOp::kIntersect, false); - cv->drawRect(rect, p); - cv->restore(); - }, - [=](DisplayListBuilder& b) { - b.save(); - b.clipRect(clip, SkClipOp::kIntersect, false); - b.drawRect(rect); - b.restore(); - }), - tolerance, testP.over_other_rendering()); - RenderWith(testP, env, CaseParameters( - "saveLayer no paint, no bounds", - [=](SkCanvas* cv, SkPaint& p) { // - cv->saveLayer(nullptr, nullptr); - }, - [=](DisplayListBuilder& b) { // - b.saveLayer(nullptr, false); - }, - cv_restore, dl_restore), - tolerance, kPixelsMatchReference); - RenderWith(testP, env, + RenderWith(testP, env, tolerance, + CaseParameters( + "With prior save/clip/restore", + [=](SkCanvas* cv, SkPaint& p) { + cv->save(); + cv->clipRect(clip, SkClipOp::kIntersect, false); + SkPaint p2; + cv->drawRect(rect, p2); + p2.setBlendMode(SkBlendMode::kClear); + cv->drawRect(rect, p2); + cv->restore(); + }, + [=](DisplayListBuilder& b) { + b.save(); + b.clipRect(clip, SkClipOp::kIntersect, false); + b.drawRect(rect); + b.setBlendMode(SkBlendMode::kClear); + b.drawRect(rect); + b.setBlendMode(SkBlendMode::kSrcOver); + b.restore(); + })); + RenderWith(testP, env, tolerance, CaseParameters( - "saveLayer no paint, with bounds", - [=](SkCanvas* cv, SkPaint& p) { // - cv->saveLayer(layer_bounds, nullptr); - }, - [=](DisplayListBuilder& b) { // - b.saveLayer(&layer_bounds, false); - }, - cv_restore, dl_restore - ), - tolerance, kPixelsDoNotMatchReference); - RenderWith(testP, env, + "saveLayer no paint, no bounds", + [=](SkCanvas* cv, SkPaint& p) { // + cv->saveLayer(nullptr, nullptr); + }, + [=](DisplayListBuilder& b) { // + b.saveLayer(nullptr, false); + }) + .with_restore(cv_restore, dl_restore, false)); + RenderWith(testP, env, tolerance, CaseParameters( - "saveLayer with alpha, no bounds", - [=](SkCanvas* cv, SkPaint& p) { - SkPaint save_p; - save_p.setColor(alpha_layer_color); - cv->saveLayer(nullptr, &save_p); - }, - [=](DisplayListBuilder& b) { - b.setColor(alpha_layer_color); - b.saveLayer(nullptr, true); - b.setColor(default_color); - }, - cv_restore, dl_restore - ), - tolerance, kPixelsDoNotMatchReference); - RenderWith(testP, env, + "saveLayer no paint, with bounds", + [=](SkCanvas* cv, SkPaint& p) { // + cv->saveLayer(layer_bounds, nullptr); + }, + [=](DisplayListBuilder& b) { // + b.saveLayer(&layer_bounds, false); + }) + .with_restore(cv_restore, dl_restore, true)); + RenderWith(testP, env, tolerance, CaseParameters( - "saveLayer with alpha and bounds", - [=](SkCanvas* cv, SkPaint& p) { - SkPaint save_p; - save_p.setColor(alpha_layer_color); - cv->saveLayer(layer_bounds, &save_p); - }, - [=](DisplayListBuilder& b) { - b.setColor(alpha_layer_color); - b.saveLayer(&layer_bounds, true); - b.setColor(default_color); - }, - cv_restore, dl_restore - ), - tolerance, kPixelsDoNotMatchReference); + "saveLayer with alpha, no bounds", + [=](SkCanvas* cv, SkPaint& p) { + SkPaint save_p; + save_p.setColor(alpha_layer_color); + cv->saveLayer(nullptr, &save_p); + }, + [=](DisplayListBuilder& b) { + b.setColor(alpha_layer_color); + b.saveLayer(nullptr, true); + b.setColor(default_color); + }) + .with_restore(cv_restore, dl_restore, true)); + RenderWith(testP, env, tolerance, + CaseParameters( + "saveLayer with alpha and bounds", + [=](SkCanvas* cv, SkPaint& p) { + SkPaint save_p; + save_p.setColor(alpha_layer_color); + cv->saveLayer(layer_bounds, &save_p); + }, + [=](DisplayListBuilder& b) { + b.setColor(alpha_layer_color); + b.saveLayer(&layer_bounds, true); + b.setColor(default_color); + }) + .with_restore(cv_restore, dl_restore, true)); { // clang-format off @@ -706,96 +820,89 @@ class CanvasCompareTester { 0, 0, 0, 0.5, 0, }; // clang-format on - sk_sp filter = SkColorFilters::Matrix(rotate_alpha_color_matrix); + sk_sp filter = + SkColorFilters::Matrix(rotate_alpha_color_matrix); { - RenderWith(testP, env, + RenderWith(testP, env, tolerance, CaseParameters( - "saveLayer ColorFilter, no bounds", - [=](SkCanvas* cv, SkPaint& p) { - SkPaint save_p; - save_p.setColorFilter(filter); - cv->saveLayer(nullptr, &save_p); - p.setStrokeWidth(5.0); - }, - [=](DisplayListBuilder& b) { - b.setColorFilter(filter); - b.saveLayer(nullptr, true); - b.setColorFilter(nullptr); - b.setStrokeWidth(5.0); - }, - cv_restore, dl_restore - ), - tolerance, kPixelsDoNotMatchReference); + "saveLayer ColorFilter, no bounds", + [=](SkCanvas* cv, SkPaint& p) { + SkPaint save_p; + save_p.setColorFilter(filter); + cv->saveLayer(nullptr, &save_p); + p.setStrokeWidth(5.0); + }, + [=](DisplayListBuilder& b) { + b.setColorFilter(filter); + b.saveLayer(nullptr, true); + b.setColorFilter(nullptr); + b.setStrokeWidth(5.0); + }) + .with_restore(cv_restore, dl_restore, true)); } EXPECT_TRUE(filter->unique()) << "saveLayer ColorFilter, no bounds Cleanup"; { - RenderWith(testP, env, + RenderWith(testP, env, tolerance, CaseParameters( - "saveLayer ColorFilter and bounds", - [=](SkCanvas* cv, SkPaint& p) { - SkPaint save_p; - save_p.setColorFilter(filter); - cv->saveLayer(RenderBounds, &save_p); - p.setStrokeWidth(5.0); - }, - [=](DisplayListBuilder& b) { - b.setColorFilter(filter); - b.saveLayer(&RenderBounds, true); - b.setColorFilter(nullptr); - b.setStrokeWidth(5.0); - }, - cv_restore, dl_restore - ), - tolerance, kPixelsDoNotMatchReference); + "saveLayer ColorFilter and bounds", + [=](SkCanvas* cv, SkPaint& p) { + SkPaint save_p; + save_p.setColorFilter(filter); + cv->saveLayer(RenderBounds, &save_p); + p.setStrokeWidth(5.0); + }, + [=](DisplayListBuilder& b) { + b.setColorFilter(filter); + b.saveLayer(&RenderBounds, true); + b.setColorFilter(nullptr); + b.setStrokeWidth(5.0); + }) + .with_restore(cv_restore, dl_restore, true)); } EXPECT_TRUE(filter->unique()) << "saveLayer ColorFilter and bounds Cleanup"; } { - sk_sp filter = - SkImageFilters::Arithmetic(0.1, 0.1, 0.1, 0.25, true, nullptr, nullptr); + sk_sp filter = SkImageFilters::Arithmetic( + 0.1, 0.1, 0.1, 0.25, true, nullptr, nullptr); { - RenderWith(testP, env, + RenderWith(testP, env, tolerance, CaseParameters( - "saveLayer ImageFilter, no bounds", - [=](SkCanvas* cv, SkPaint& p) { - SkPaint save_p; - save_p.setImageFilter(filter); - cv->saveLayer(nullptr, &save_p); - p.setStrokeWidth(5.0); - }, - [=](DisplayListBuilder& b) { - b.setImageFilter(filter); - b.saveLayer(nullptr, true); - b.setImageFilter(nullptr); - b.setStrokeWidth(5.0); - }, - cv_restore, dl_restore - ), - tolerance, kPixelsDoNotMatchReference); + "saveLayer ImageFilter, no bounds", + [=](SkCanvas* cv, SkPaint& p) { + SkPaint save_p; + save_p.setImageFilter(filter); + cv->saveLayer(nullptr, &save_p); + p.setStrokeWidth(5.0); + }, + [=](DisplayListBuilder& b) { + b.setImageFilter(filter); + b.saveLayer(nullptr, true); + b.setImageFilter(nullptr); + b.setStrokeWidth(5.0); + }) + .with_restore(cv_restore, dl_restore, true)); } EXPECT_TRUE(filter->unique()) << "saveLayer ImageFilter, no bounds Cleanup"; { - RenderWith(testP, env, + RenderWith(testP, env, tolerance, CaseParameters( - "saveLayer ImageFilter and bounds", - [=](SkCanvas* cv, SkPaint& p) { - SkPaint save_p; - save_p.setImageFilter(filter); - cv->saveLayer(RenderBounds, &save_p); - p.setStrokeWidth(5.0); - }, - [=](DisplayListBuilder& b) { - b.setImageFilter(filter); - b.saveLayer(&RenderBounds, true); - b.setImageFilter(nullptr); - b.setStrokeWidth(5.0); - }, - cv_restore, dl_restore - ), - tolerance, kPixelsDoNotMatchReference); + "saveLayer ImageFilter and bounds", + [=](SkCanvas* cv, SkPaint& p) { + SkPaint save_p; + save_p.setImageFilter(filter); + cv->saveLayer(RenderBounds, &save_p); + p.setStrokeWidth(5.0); + }, + [=](DisplayListBuilder& b) { + b.setImageFilter(filter); + b.saveLayer(&RenderBounds, true); + b.setImageFilter(nullptr); + b.setStrokeWidth(5.0); + }) + .with_restore(cv_restore, dl_restore, true)); } EXPECT_TRUE(filter->unique()) << "saveLayer ImageFilter and bounds Cleanup"; @@ -805,8 +912,7 @@ class CanvasCompareTester { static void RenderWithAttributes(const TestParameters& testP, const RenderEnvironment& env, const BoundsTolerance& tolerance) { - RenderWith(testP, env, CaseParameters("Defaults Test"), - tolerance, kPixelsMatchReference); + RenderWith(testP, env, tolerance, CaseParameters("Defaults Test")); { // CPU renderer with default line width of 0 does not show antialiasing @@ -815,7 +921,7 @@ class CanvasCompareTester { RenderEnvironment aa_env = RenderEnvironment::MakeN32(); // Tweak the bounds tolerance for the displacement of 1/10 of a pixel const BoundsTolerance aa_tolerance = tolerance.addBoundsPadding(1, 1); - CvRenderer cv_aa_setup = [=](SkCanvas* cv, SkPaint& p) { + CvSetup cv_aa_setup = [=](SkCanvas* cv, SkPaint& p) { cv->translate(0.1, 0.1); p.setStrokeWidth(5.0); }; @@ -824,32 +930,28 @@ class CanvasCompareTester { b.setStrokeWidth(5.0); }; aa_env.init_ref(cv_aa_setup, testP.cv_renderer()); - RenderWith(testP, aa_env, + RenderWith(testP, aa_env, aa_tolerance, CaseParameters( - "AntiAlias == True", - [=](SkCanvas* cv, SkPaint& p) { - cv_aa_setup(cv, p); - p.setAntiAlias(true); - }, - [=](DisplayListBuilder& b) { - dl_aa_setup(b); - b.setAntiAlias(true); - } - ), - aa_tolerance, testP.with_anti_alias(true)); - RenderWith(testP, aa_env, + "AntiAlias == True", + [=](SkCanvas* cv, SkPaint& p) { + cv_aa_setup(cv, p); + p.setAntiAlias(true); + }, + [=](DisplayListBuilder& b) { + dl_aa_setup(b); + b.setAntiAlias(true); + })); + RenderWith(testP, aa_env, aa_tolerance, CaseParameters( - "AntiAlias == False", - [=](SkCanvas* cv, SkPaint& p) { - cv_aa_setup(cv, p); - p.setAntiAlias(false); - }, - [=](DisplayListBuilder& b) { - dl_aa_setup(b); - b.setAntiAlias(false); - } - ), - aa_tolerance, testP.with_anti_alias(false)); + "AntiAlias == False", + [=](SkCanvas* cv, SkPaint& p) { + cv_aa_setup(cv, p); + p.setAntiAlias(false); + }, + [=](DisplayListBuilder& b) { + dl_aa_setup(b); + b.setAntiAlias(false); + })); } { @@ -860,7 +962,7 @@ class CanvasCompareTester { // dithering so we use a non-trivial stroke width as well. RenderEnvironment dither_env = RenderEnvironment::Make565(); SkColor dither_bg = SK_ColorBLACK; - CvRenderer cv_dither_setup = [=](SkCanvas*, SkPaint& p) { + CvSetup cv_dither_setup = [=](SkCanvas*, SkPaint& p) { p.setShader(testImageShader); p.setAlpha(0xf0); p.setStrokeWidth(5.0); @@ -871,47 +973,43 @@ class CanvasCompareTester { b.setStrokeWidth(5.0); }; dither_env.init_ref(cv_dither_setup, testP.cv_renderer(), dither_bg); - RenderWith(testP, dither_env, + RenderWith(testP, dither_env, tolerance, CaseParameters( - "Dither == True", - [=](SkCanvas* cv, SkPaint& p) { - cv_dither_setup(cv, p); - p.setDither(true); - }, - [=](DisplayListBuilder& b) { - dl_dither_setup(b); - b.setDither(true); - }, - dither_bg), - tolerance, testP.with_dither(true)); - RenderWith(testP, dither_env, + "Dither == True", + [=](SkCanvas* cv, SkPaint& p) { + cv_dither_setup(cv, p); + p.setDither(true); + }, + [=](DisplayListBuilder& b) { + dl_dither_setup(b); + b.setDither(true); + }) + .with_bg(dither_bg)); + RenderWith(testP, dither_env, tolerance, CaseParameters( - "Dither = False", - [=](SkCanvas* cv, SkPaint& p) { - cv_dither_setup(cv, p); - p.setDither(false); - }, - [=](DisplayListBuilder& b) { - dl_dither_setup(b); - b.setDither(false); - }, - dither_bg), - tolerance, kPixelsMatchReference); - EXPECT_TRUE(testImageShader->unique()) << "Dither Cleanup"; + "Dither = False", + [=](SkCanvas* cv, SkPaint& p) { + cv_dither_setup(cv, p); + p.setDither(false); + }, + [=](DisplayListBuilder& b) { + dl_dither_setup(b); + b.setDither(false); + }) + .with_bg(dither_bg)); } + EXPECT_TRUE(testImageShader->unique()) << "Dither Cleanup"; - RenderWith(testP, env, + RenderWith(testP, env, tolerance, CaseParameters( - "Color == Blue", - [=](SkCanvas*, SkPaint& p) { p.setColor(SK_ColorBLUE); }, - [=](DisplayListBuilder& b) { b.setColor(SK_ColorBLUE); } - ), tolerance, testP.with_color(SK_ColorBLUE)); - RenderWith(testP, env, + "Color == Blue", + [=](SkCanvas*, SkPaint& p) { p.setColor(SK_ColorBLUE); }, + [=](DisplayListBuilder& b) { b.setColor(SK_ColorBLUE); })); + RenderWith(testP, env, tolerance, CaseParameters( - "Color == Green", - [=](SkCanvas*, SkPaint& p) { p.setColor(SK_ColorGREEN); }, - [=](DisplayListBuilder& b) { b.setColor(SK_ColorGREEN); } - ), tolerance, testP.with_color(SK_ColorGREEN)); + "Color == Green", + [=](SkCanvas*, SkPaint& p) { p.setColor(SK_ColorGREEN); }, + [=](DisplayListBuilder& b) { b.setColor(SK_ColorGREEN); })); RenderWithStrokes(testP, env, tolerance); @@ -920,54 +1018,50 @@ class CanvasCompareTester { SkColor blendableColor = SkColorSetARGB(0x7f, 0x00, 0xff, 0xff); SkColor bg = SK_ColorWHITE; - RenderWith(testP, env, + RenderWith(testP, env, tolerance, CaseParameters( - "Blend == SrcIn", - [=](SkCanvas*, SkPaint& p) { - p.setBlendMode(SkBlendMode::kSrcIn); - p.setColor(blendableColor); - }, - [=](DisplayListBuilder& b) { - b.setBlendMode(SkBlendMode::kSrcIn); - b.setColor(blendableColor); - }, - bg - ), tolerance, testP.with_blending(SkBlendMode::kSrcIn)); - RenderWith(testP, env, + "Blend == SrcIn", + [=](SkCanvas*, SkPaint& p) { + p.setBlendMode(SkBlendMode::kSrcIn); + p.setColor(blendableColor); + }, + [=](DisplayListBuilder& b) { + b.setBlendMode(SkBlendMode::kSrcIn); + b.setColor(blendableColor); + }) + .with_bg(bg)); + RenderWith(testP, env, tolerance, CaseParameters( - "Blend == DstIn", - [=](SkCanvas*, SkPaint& p) { - p.setBlendMode(SkBlendMode::kDstIn); - p.setColor(blendableColor); - }, - [=](DisplayListBuilder& b) { - b.setBlendMode(SkBlendMode::kDstIn); - b.setColor(blendableColor); - }, - bg - ), tolerance, testP.with_blending(SkBlendMode::kDstIn)); + "Blend == DstIn", + [=](SkCanvas*, SkPaint& p) { + p.setBlendMode(SkBlendMode::kDstIn); + p.setColor(blendableColor); + }, + [=](DisplayListBuilder& b) { + b.setBlendMode(SkBlendMode::kDstIn); + b.setColor(blendableColor); + }) + .with_bg(bg)); } if (!(testP.is_draw_atlas() || testP.is_draw_vertices())) { sk_sp blender = SkBlenders::Arithmetic(0.25, 0.25, 0.25, 0.25, false); { - RenderWith(testP, env, + RenderWith(testP, env, tolerance, CaseParameters( - "ImageFilter == Blender Arithmetic 0.25-false", - [=](SkCanvas*, SkPaint& p) { p.setBlender(blender); }, - [=](DisplayListBuilder& b) { b.setBlender(blender); } - ), tolerance, testP.with_blending(blender)); + "ImageFilter == Blender Arithmetic 0.25-false", + [=](SkCanvas*, SkPaint& p) { p.setBlender(blender); }, + [=](DisplayListBuilder& b) { b.setBlender(blender); })); } EXPECT_TRUE(blender->unique()) << "Blender Cleanup"; blender = SkBlenders::Arithmetic(0.25, 0.25, 0.25, 0.25, true); { - RenderWith(testP, env, + RenderWith(testP, env, tolerance, CaseParameters( - "ImageFilter == Blender Arithmetic 0.25-true", - [=](SkCanvas*, SkPaint& p) { p.setBlender(blender); }, - [=](DisplayListBuilder& b) { b.setBlender(blender); } - ), tolerance, testP.with_blending(blender)); + "ImageFilter == Blender Arithmetic 0.25-true", + [=](SkCanvas*, SkPaint& p) { p.setBlender(blender); }, + [=](DisplayListBuilder& b) { b.setBlender(blender); })); } EXPECT_TRUE(blender->unique()) << "Blender Cleanup"; } @@ -977,7 +1071,7 @@ class CanvasCompareTester { // like a non-trivial stroke width and a shader rather than a color // (for drawPaint) so we create a new environment for these tests. RenderEnvironment blur_env = RenderEnvironment::MakeN32(); - CvRenderer cv_blur_setup = [=](SkCanvas*, SkPaint& p) { + CvSetup cv_blur_setup = [=](SkCanvas*, SkPaint& p) { p.setShader(testImageShader); p.setStrokeWidth(5.0); }; @@ -990,35 +1084,33 @@ class CanvasCompareTester { SkImageFilters::Blur(5.0, 5.0, SkTileMode::kDecal, nullptr, nullptr); BoundsTolerance blur5Tolerance = tolerance.addBoundsPadding(4, 4); { - RenderWith(testP, blur_env, + RenderWith(testP, blur_env, blur5Tolerance, CaseParameters( - "ImageFilter == Decal Blur 5", - [=](SkCanvas* cv, SkPaint& p) { - cv_blur_setup(cv, p); - p.setImageFilter(filter); - }, - [=](DisplayListBuilder& b) { - dl_blur_setup(b); - b.setImageFilter(filter); - } - ), blur5Tolerance, testP.with_image_filter(filter)); + "ImageFilter == Decal Blur 5", + [=](SkCanvas* cv, SkPaint& p) { + cv_blur_setup(cv, p); + p.setImageFilter(filter); + }, + [=](DisplayListBuilder& b) { + dl_blur_setup(b); + b.setImageFilter(filter); + })); } EXPECT_TRUE(filter->unique()) << "ImageFilter Cleanup"; filter = SkImageFilters::Blur(5.0, 5.0, SkTileMode::kClamp, nullptr, nullptr); { - RenderWith(testP, blur_env, + RenderWith(testP, blur_env, blur5Tolerance, CaseParameters( - "ImageFilter == Clamp Blur 5", - [=](SkCanvas* cv, SkPaint& p) { - cv_blur_setup(cv, p); - p.setImageFilter(filter); - }, - [=](DisplayListBuilder& b) { - dl_blur_setup(b); - b.setImageFilter(filter); - } - ), blur5Tolerance, testP.with_image_filter(filter)); + "ImageFilter == Clamp Blur 5", + [=](SkCanvas* cv, SkPaint& p) { + cv_blur_setup(cv, p); + p.setImageFilter(filter); + }, + [=](DisplayListBuilder& b) { + dl_blur_setup(b); + b.setImageFilter(filter); + })); } EXPECT_TRUE(filter->unique()) << "ImageFilter Cleanup"; } @@ -1041,35 +1133,35 @@ class CanvasCompareTester { sk_sp filter = SkColorFilters::Matrix(rotate_color_matrix); { SkColor bg = SK_ColorWHITE; - RenderWith(testP, env, + RenderWith(testP, env, tolerance, CaseParameters( - "ColorFilter == RotateRGB", - [=](SkCanvas*, SkPaint& p) { - p.setColor(SK_ColorYELLOW); - p.setColorFilter(filter); - }, - [=](DisplayListBuilder& b) { - b.setColor(SK_ColorYELLOW); - b.setColorFilter(filter); - }, bg - ), tolerance, testP.with_color_filter(filter)); + "ColorFilter == RotateRGB", + [=](SkCanvas*, SkPaint& p) { + p.setColor(SK_ColorYELLOW); + p.setColorFilter(filter); + }, + [=](DisplayListBuilder& b) { + b.setColor(SK_ColorYELLOW); + b.setColorFilter(filter); + }) + .with_bg(bg)); } EXPECT_TRUE(filter->unique()) << "ColorFilter == RotateRGB Cleanup"; filter = SkColorFilters::Matrix(invert_color_matrix); { SkColor bg = SK_ColorWHITE; - RenderWith(testP, env, + RenderWith(testP, env, tolerance, CaseParameters( - "ColorFilter == Invert", - [=](SkCanvas*, SkPaint& p) { - p.setColor(SK_ColorYELLOW); - p.setColorFilter(filter); - }, - [=](DisplayListBuilder& b) { - b.setColor(SK_ColorYELLOW); - b.setInvertColors(true); - }, bg - ), tolerance, testP.with_color_filter(filter)); + "ColorFilter == Invert", + [=](SkCanvas*, SkPaint& p) { + p.setColor(SK_ColorYELLOW); + p.setColorFilter(filter); + }, + [=](DisplayListBuilder& b) { + b.setColor(SK_ColorYELLOW); + b.setInvertColors(true); + }) + .with_bg(bg)); } EXPECT_TRUE(filter->unique()) << "ColorFilter == Invert Cleanup"; } @@ -1081,26 +1173,26 @@ class CanvasCompareTester { // to do something realistic // And a Discrete(3, 5) effect produces miters that are near // maximal for a miter limit of 3.0. - RenderWith(testP, env, + BoundsTolerance discrete_tolerance = + tolerance + // register the discrete offset so adjusters can compensate + .addDiscreteOffset(5) + // the miters in the 3-5 discrete effect don't always fill + // their conservative bounds, so tolerate a couple of pixels + .addBoundsPadding(2, 2); + RenderWith(testP, env, discrete_tolerance, CaseParameters( - "PathEffect == Discrete-3-5", - [=](SkCanvas*, SkPaint& p) { - p.setStrokeWidth(5.0); - p.setStrokeMiter(3.0); - p.setPathEffect(effect); - }, - [=](DisplayListBuilder& b) { - b.setStrokeWidth(5.0); - b.setStrokeMiter(3.0); - b.setPathEffect(effect); - } - ), tolerance - // register the discrete offset so adjusters can compensate - .addDiscreteOffset(5) - // the miters in the 3-5 discrete effect don't always fill - // their conservative bounds, so tolerate a couple of pixels - .addBoundsPadding(2, 2), - testP.with_path_effect(effect)); + "PathEffect == Discrete-3-5", + [=](SkCanvas*, SkPaint& p) { + p.setStrokeWidth(5.0); + p.setStrokeMiter(3.0); + p.setPathEffect(effect); + }, + [=](DisplayListBuilder& b) { + b.setStrokeWidth(5.0); + b.setStrokeMiter(3.0); + b.setPathEffect(effect); + })); } EXPECT_TRUE(testP.is_draw_text_blob() || effect->unique()) << "PathEffect == Discrete-3-5 Cleanup"; @@ -1110,26 +1202,26 @@ class CanvasCompareTester { // to do something realistic // And a Discrete(2, 3) effect produces miters that are near // maximal for a miter limit of 2.5. - RenderWith(testP, env, + BoundsTolerance discrete_tolerance = + tolerance + // register the discrete offset so adjusters can compensate + .addDiscreteOffset(3) + // the miters in the 3-5 discrete effect don't always fill + // their conservative bounds, so tolerate a couple of pixels + .addBoundsPadding(2, 2); + RenderWith(testP, env, discrete_tolerance, CaseParameters( - "PathEffect == Discrete-2-3", - [=](SkCanvas*, SkPaint& p) { - p.setStrokeWidth(5.0); - p.setStrokeMiter(2.5); - p.setPathEffect(effect); - }, - [=](DisplayListBuilder& b) { - b.setStrokeWidth(5.0); - b.setStrokeMiter(2.5); - b.setPathEffect(effect); - } - ), tolerance - // register the discrete offset so adjusters can compensate - .addDiscreteOffset(3) - // the miters in the 3-5 discrete effect don't always fill - // their conservative bounds, so tolerate a couple of pixels - .addBoundsPadding(2, 2), - testP.with_path_effect(effect)); + "PathEffect == Discrete-2-3", + [=](SkCanvas*, SkPaint& p) { + p.setStrokeWidth(5.0); + p.setStrokeMiter(2.5); + p.setPathEffect(effect); + }, + [=](DisplayListBuilder& b) { + b.setStrokeWidth(5.0); + b.setStrokeMiter(2.5); + b.setPathEffect(effect); + })); } EXPECT_TRUE(testP.is_draw_text_blob() || effect->unique()) << "PathEffect == Discrete-2-3 Cleanup"; @@ -1140,37 +1232,33 @@ class CanvasCompareTester { SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, 5.0); BoundsTolerance blur5Tolerance = tolerance.addBoundsPadding(4, 4); { - // Stroked primitives require some non-trivial stroke size to get blurred - RenderWith(testP, env, + // Stroked primitives need some non-trivial stroke size to be blurred + RenderWith(testP, env, blur5Tolerance, CaseParameters( - "MaskFilter == Blur 5", - [=](SkCanvas*, SkPaint& p) { - p.setStrokeWidth(5.0); - p.setMaskFilter(filter); - }, - [=](DisplayListBuilder& b) { - b.setStrokeWidth(5.0); - b.setMaskFilter(filter); - } - ), blur5Tolerance, testP.with_mask_filter(filter)); + "MaskFilter == Blur 5", + [=](SkCanvas*, SkPaint& p) { + p.setStrokeWidth(5.0); + p.setMaskFilter(filter); + }, + [=](DisplayListBuilder& b) { + b.setStrokeWidth(5.0); + b.setMaskFilter(filter); + })); } EXPECT_TRUE(testP.is_draw_text_blob() || filter->unique()) << "MaskFilter == Blur 5 Cleanup"; { - RenderWith(testP, env, + RenderWith(testP, env, blur5Tolerance, CaseParameters( - "MaskFilter == Blur(Normal, 5.0)", - [=](SkCanvas*, SkPaint& p) { - // Provide some non-trivial stroke size to get blurred - p.setStrokeWidth(5.0); - p.setMaskFilter(filter); - }, - [=](DisplayListBuilder& b) { - // Provide some non-trivial stroke size to get blurred - b.setStrokeWidth(5.0); - b.setMaskBlurFilter(kNormal_SkBlurStyle, 5.0); - } - ), blur5Tolerance, testP.with_mask_filter(filter)); + "MaskFilter == Blur(Normal, 5.0)", + [=](SkCanvas*, SkPaint& p) { + p.setStrokeWidth(5.0); + p.setMaskFilter(filter); + }, + [=](DisplayListBuilder& b) { + b.setStrokeWidth(5.0); + b.setMaskBlurFilter(kNormal_SkBlurStyle, 5.0); + })); } EXPECT_TRUE(testP.is_draw_text_blob() || filter->unique()) << "MaskFilter == Blur(Normal, 5.0) Cleanup"; @@ -1194,12 +1282,11 @@ class CanvasCompareTester { sk_sp shader = SkGradientShader::MakeLinear( end_points, colors, stops, 3, SkTileMode::kMirror, 0, nullptr); { - RenderWith(testP, env, + RenderWith(testP, env, tolerance, CaseParameters( - "LinearGradient GYB", - [=](SkCanvas*, SkPaint& p) { p.setShader(shader); }, - [=](DisplayListBuilder& b) { b.setShader(shader); } - ), tolerance, testP.with_shader(shader)); + "LinearGradient GYB", + [=](SkCanvas*, SkPaint& p) { p.setShader(shader); }, + [=](DisplayListBuilder& b) { b.setShader(shader); })); } EXPECT_TRUE(shader->unique()) << "LinearGradient GYB Cleanup"; } @@ -1213,202 +1300,197 @@ class CanvasCompareTester { // by a couple of pixels so we will relax bounds testing for strokes by // a couple of pixels. BoundsTolerance tolerance = tolerance_in.addBoundsPadding(2, 2); - ReferencePixelExpectation stroke_expect = testP.with_stroke_style(SkPaint::kStroke_Style); - RenderWith(testP, env, + RenderWith(testP, env, tolerance, CaseParameters( - "Fill", - [=](SkCanvas*, SkPaint& p) { p.setStyle(SkPaint::kFill_Style); }, - [=](DisplayListBuilder& b) { b.setStyle(SkPaint::kFill_Style); } - ), tolerance, kPixelsMatchReference); - RenderWith(testP, env, + "Fill", + [=](SkCanvas*, SkPaint& p) { // + p.setStyle(SkPaint::kFill_Style); + }, + [=](DisplayListBuilder& b) { // + b.setStyle(SkPaint::kFill_Style); + })); + RenderWith(testP, env, tolerance, CaseParameters( - "Stroke + defaults", - [=](SkCanvas*, SkPaint& p) { p.setStyle(SkPaint::kStroke_Style); }, - [=](DisplayListBuilder& b) { b.setStyle(SkPaint::kStroke_Style); } - ), tolerance, stroke_expect); + "Stroke + defaults", + [=](SkCanvas*, SkPaint& p) { // + p.setStyle(SkPaint::kStroke_Style); + }, + [=](DisplayListBuilder& b) { // + b.setStyle(SkPaint::kStroke_Style); + })); - RenderWith(testP, env, + RenderWith(testP, env, tolerance, CaseParameters( - "Fill + unnecessary StrokeWidth 10", - [=](SkCanvas*, SkPaint& p) { - p.setStyle(SkPaint::kFill_Style); - p.setStrokeWidth(10.0); - }, - [=](DisplayListBuilder& b) { - b.setStyle(SkPaint::kFill_Style); - b.setStrokeWidth(10.0); - } - ), tolerance, testP.with_stroke_style(SkPaint::kFill_Style)); - - RenderWith(testP, env, + "Fill + unnecessary StrokeWidth 10", + [=](SkCanvas*, SkPaint& p) { + p.setStyle(SkPaint::kFill_Style); + p.setStrokeWidth(10.0); + }, + [=](DisplayListBuilder& b) { + b.setStyle(SkPaint::kFill_Style); + b.setStrokeWidth(10.0); + })); + + RenderEnvironment stroke_base_env = RenderEnvironment::MakeN32(); + CvSetup cv_stroke_setup = [=](SkCanvas*, SkPaint& p) { + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeWidth(5.0); + }; + stroke_base_env.init_ref(cv_stroke_setup, testP.cv_renderer()); + + RenderWith(testP, stroke_base_env, tolerance, CaseParameters( - "Stroke Width 10", - [=](SkCanvas*, SkPaint& p) { - p.setStyle(SkPaint::kStroke_Style); - p.setStrokeWidth(10.0); - }, - [=](DisplayListBuilder& b) { - b.setStyle(SkPaint::kStroke_Style); - b.setStrokeWidth(10.0); - } - ), tolerance, stroke_expect); - RenderWith(testP, env, + "Stroke Width 10", + [=](SkCanvas*, SkPaint& p) { + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeWidth(10.0); + }, + [=](DisplayListBuilder& b) { + b.setStyle(SkPaint::kStroke_Style); + b.setStrokeWidth(10.0); + })); + RenderWith(testP, stroke_base_env, tolerance, CaseParameters( - "Stroke Width 5", - [=](SkCanvas*, SkPaint& p) { - p.setStyle(SkPaint::kStroke_Style); - p.setStrokeWidth(5.0); - }, - [=](DisplayListBuilder& b) { - b.setStyle(SkPaint::kStroke_Style); - b.setStrokeWidth(5.0); - } - ), tolerance, stroke_expect); - - RenderWith(testP, env, + "Stroke Width 5", + [=](SkCanvas*, SkPaint& p) { + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeWidth(5.0); + }, + [=](DisplayListBuilder& b) { + b.setStyle(SkPaint::kStroke_Style); + b.setStrokeWidth(5.0); + })); + + RenderWith(testP, stroke_base_env, tolerance, CaseParameters( - "Stroke Width 5, Butt Cap", - [=](SkCanvas*, SkPaint& p) { - p.setStyle(SkPaint::kStroke_Style); - p.setStrokeWidth(5.0); - p.setStrokeCap(SkPaint::kButt_Cap); - }, - [=](DisplayListBuilder& b) { - b.setStyle(SkPaint::kStroke_Style); - b.setStrokeWidth(5.0); - b.setStrokeCap(SkPaint::kButt_Cap); - } - ), tolerance, stroke_expect); - RenderWith(testP, env, + "Stroke Width 5, Square Cap", + [=](SkCanvas*, SkPaint& p) { + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeWidth(5.0); + p.setStrokeCap(SkPaint::kSquare_Cap); + }, + [=](DisplayListBuilder& b) { + b.setStyle(SkPaint::kStroke_Style); + b.setStrokeWidth(5.0); + b.setStrokeCap(SkPaint::kSquare_Cap); + })); + RenderWith(testP, stroke_base_env, tolerance, CaseParameters( - "Stroke Width 5, Round Cap", - [=](SkCanvas*, SkPaint& p) { - p.setStyle(SkPaint::kStroke_Style); - p.setStrokeWidth(5.0); - p.setStrokeCap(SkPaint::kRound_Cap); - }, - [=](DisplayListBuilder& b) { - b.setStyle(SkPaint::kStroke_Style); - b.setStrokeWidth(5.0); - b.setStrokeCap(SkPaint::kRound_Cap); - } - ), tolerance, stroke_expect); - - RenderWith(testP, env, + "Stroke Width 5, Round Cap", + [=](SkCanvas*, SkPaint& p) { + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeWidth(5.0); + p.setStrokeCap(SkPaint::kRound_Cap); + }, + [=](DisplayListBuilder& b) { + b.setStyle(SkPaint::kStroke_Style); + b.setStrokeWidth(5.0); + b.setStrokeCap(SkPaint::kRound_Cap); + })); + + RenderWith(testP, stroke_base_env, tolerance, CaseParameters( - "Stroke Width 5, Bevel Join", - [=](SkCanvas*, SkPaint& p) { - p.setStyle(SkPaint::kStroke_Style); - p.setStrokeWidth(5.0); - p.setStrokeJoin(SkPaint::kBevel_Join); - }, - [=](DisplayListBuilder& b) { - b.setStyle(SkPaint::kStroke_Style); - b.setStrokeWidth(5.0); - b.setStrokeJoin(SkPaint::kBevel_Join); - } - ), tolerance, stroke_expect); - RenderWith(testP, env, + "Stroke Width 5, Bevel Join", + [=](SkCanvas*, SkPaint& p) { + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeWidth(5.0); + p.setStrokeJoin(SkPaint::kBevel_Join); + }, + [=](DisplayListBuilder& b) { + b.setStyle(SkPaint::kStroke_Style); + b.setStrokeWidth(5.0); + b.setStrokeJoin(SkPaint::kBevel_Join); + })); + RenderWith(testP, stroke_base_env, tolerance, CaseParameters( - "Stroke Width 5, Round Join", - [=](SkCanvas*, SkPaint& p) { - p.setStyle(SkPaint::kStroke_Style); - p.setStrokeWidth(5.0); - p.setStrokeJoin(SkPaint::kRound_Join); - }, - [=](DisplayListBuilder& b) { - b.setStyle(SkPaint::kStroke_Style); - b.setStrokeWidth(5.0); - b.setStrokeJoin(SkPaint::kRound_Join); - } - ), tolerance, stroke_expect); - - RenderWith(testP, env, + "Stroke Width 5, Round Join", + [=](SkCanvas*, SkPaint& p) { + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeWidth(5.0); + p.setStrokeJoin(SkPaint::kRound_Join); + }, + [=](DisplayListBuilder& b) { + b.setStyle(SkPaint::kStroke_Style); + b.setStrokeWidth(5.0); + b.setStrokeJoin(SkPaint::kRound_Join); + })); + + RenderWith(testP, stroke_base_env, tolerance, CaseParameters( - "Stroke Width 5, Miter 10", - [=](SkCanvas*, SkPaint& p) { - p.setStyle(SkPaint::kStroke_Style); - p.setStrokeWidth(5.0); - p.setStrokeMiter(10.0); - p.setStrokeJoin(SkPaint::kMiter_Join); - // AA helps fill in the peaks of the really thin miters better - // for bounds accuracy testing - // p.setAntiAlias(true); - }, - [=](DisplayListBuilder& b) { - b.setStyle(SkPaint::kStroke_Style); - b.setStrokeWidth(5.0); - b.setStrokeMiter(10.0); - b.setStrokeJoin(SkPaint::kMiter_Join); - // AA helps fill in the peaks of the really thin miters better - // for bounds accuracy testing - // b.setAntiAlias(true); - } - ), tolerance, stroke_expect); - - RenderWith(testP, env, + "Stroke Width 5, Miter 10", + [=](SkCanvas*, SkPaint& p) { + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeWidth(5.0); + p.setStrokeMiter(10.0); + p.setStrokeJoin(SkPaint::kMiter_Join); + }, + [=](DisplayListBuilder& b) { + b.setStyle(SkPaint::kStroke_Style); + b.setStrokeWidth(5.0); + b.setStrokeMiter(10.0); + b.setStrokeJoin(SkPaint::kMiter_Join); + })); + + RenderWith(testP, stroke_base_env, tolerance, CaseParameters( - "Stroke Width 5, Miter 0", - [=](SkCanvas*, SkPaint& p) { - p.setStyle(SkPaint::kStroke_Style); - p.setStrokeWidth(5.0); - p.setStrokeMiter(0.0); - p.setStrokeJoin(SkPaint::kMiter_Join); - }, - [=](DisplayListBuilder& b) { - b.setStyle(SkPaint::kStroke_Style); - b.setStrokeWidth(5.0); - b.setStrokeMiter(0.0); - b.setStrokeJoin(SkPaint::kMiter_Join); - } - ), tolerance, stroke_expect); + "Stroke Width 5, Miter 0", + [=](SkCanvas*, SkPaint& p) { + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeWidth(5.0); + p.setStrokeMiter(0.0); + p.setStrokeJoin(SkPaint::kMiter_Join); + }, + [=](DisplayListBuilder& b) { + b.setStyle(SkPaint::kStroke_Style); + b.setStrokeWidth(5.0); + b.setStrokeMiter(0.0); + b.setStrokeJoin(SkPaint::kMiter_Join); + })); { const SkScalar TestDashes1[] = {29.0, 2.0}; const SkScalar TestDashes2[] = {17.0, 1.5}; sk_sp effect = SkDashPathEffect::Make(TestDashes1, 2, 0.0f); { - RenderWith(testP, env, + RenderWith(testP, stroke_base_env, tolerance, CaseParameters( - "PathEffect == Dash-29-2", - [=](SkCanvas*, SkPaint& p) { - // Need stroke style to see dashing properly - p.setStyle(SkPaint::kStroke_Style); - // Provide some non-trivial stroke size to get dashed - p.setStrokeWidth(5.0); - p.setPathEffect(effect); - }, - [=](DisplayListBuilder& b) { - // Need stroke style to see dashing properly - b.setStyle(SkPaint::kStroke_Style); - // Provide some non-trivial stroke size to get dashed - b.setStrokeWidth(5.0); - b.setPathEffect(effect); - } - ), tolerance, stroke_expect); + "PathEffect == Dash-29-2", + [=](SkCanvas*, SkPaint& p) { + // Need stroke style to see dashing properly + p.setStyle(SkPaint::kStroke_Style); + // Provide some non-trivial stroke size to get dashed + p.setStrokeWidth(5.0); + p.setPathEffect(effect); + }, + [=](DisplayListBuilder& b) { + // Need stroke style to see dashing properly + b.setStyle(SkPaint::kStroke_Style); + // Provide some non-trivial stroke size to get dashed + b.setStrokeWidth(5.0); + b.setPathEffect(effect); + })); } EXPECT_TRUE(testP.is_draw_text_blob() || effect->unique()) << "PathEffect == Dash-29-2 Cleanup"; effect = SkDashPathEffect::Make(TestDashes2, 2, 0.0f); { - RenderWith(testP, env, + RenderWith(testP, stroke_base_env, tolerance, CaseParameters( - "PathEffect == Dash-17-1.5", - [=](SkCanvas*, SkPaint& p) { - // Need stroke style to see dashing properly - p.setStyle(SkPaint::kStroke_Style); - // Provide some non-trivial stroke size to get dashed - p.setStrokeWidth(5.0); - p.setPathEffect(effect); - }, - [=](DisplayListBuilder& b) { - // Need stroke style to see dashing properly - b.setStyle(SkPaint::kStroke_Style); - // Provide some non-trivial stroke size to get dashed - b.setStrokeWidth(5.0); - b.setPathEffect(effect); - } - ), tolerance, stroke_expect); + "PathEffect == Dash-17-1.5", + [=](SkCanvas*, SkPaint& p) { + // Need stroke style to see dashing properly + p.setStyle(SkPaint::kStroke_Style); + // Provide some non-trivial stroke size to get dashed + p.setStrokeWidth(5.0); + p.setPathEffect(effect); + }, + [=](DisplayListBuilder& b) { + // Need stroke style to see dashing properly + b.setStyle(SkPaint::kStroke_Style); + // Provide some non-trivial stroke size to get dashed + b.setStrokeWidth(5.0); + b.setPathEffect(effect); + })); } EXPECT_TRUE(testP.is_draw_text_blob() || effect->unique()) << "PathEffect == Dash-17-1.5 Cleanup"; @@ -1422,43 +1504,38 @@ class CanvasCompareTester { // bounds, then the estimate under rotation or skewing will be off // so we scale the padding by about 5% to compensate. BoundsTolerance skewed_tolerance = tolerance.mulScale(1.05, 1.05); - RenderWith(testP, env, + RenderWith(testP, env, tolerance, CaseParameters( - "Translate 5, 10", - [=](SkCanvas* c, SkPaint&) { c->translate(5, 10); }, - [=](DisplayListBuilder& b) { b.translate(5, 10); } - ), tolerance, testP.under_transform()); - RenderWith(testP, env, + "Translate 5, 10", // + [=](SkCanvas* c, SkPaint&) { c->translate(5, 10); }, + [=](DisplayListBuilder& b) { b.translate(5, 10); })); + RenderWith(testP, env, tolerance, CaseParameters( - "Scale +5%", - [=](SkCanvas* c, SkPaint&) { c->scale(1.05, 1.05); }, - [=](DisplayListBuilder& b) { b.scale(1.05, 1.05); } - ), tolerance, testP.under_transform()); - RenderWith(testP, env, + "Scale +5%", // + [=](SkCanvas* c, SkPaint&) { c->scale(1.05, 1.05); }, + [=](DisplayListBuilder& b) { b.scale(1.05, 1.05); })); + RenderWith(testP, env, skewed_tolerance, CaseParameters( - "Rotate 5 degrees", - [=](SkCanvas* c, SkPaint&) { c->rotate(5); }, - [=](DisplayListBuilder& b) { b.rotate(5); } - ), skewed_tolerance, testP.under_transform()); - RenderWith(testP, env, + "Rotate 5 degrees", // + [=](SkCanvas* c, SkPaint&) { c->rotate(5); }, + [=](DisplayListBuilder& b) { b.rotate(5); })); + RenderWith(testP, env, skewed_tolerance, CaseParameters( - "Skew 5%", - [=](SkCanvas* c, SkPaint&) { c->skew(0.05, 0.05); }, - [=](DisplayListBuilder& b) { b.skew(0.05, 0.05); } - ), skewed_tolerance, testP.under_transform()); + "Skew 5%", // + [=](SkCanvas* c, SkPaint&) { c->skew(0.05, 0.05); }, + [=](DisplayListBuilder& b) { b.skew(0.05, 0.05); })); { SkMatrix tx = SkMatrix::MakeAll(1.10, 0.10, 5, // 0.05, 1.05, 10, // 0, 0, 1); - RenderWith(testP, env, + RenderWith(testP, env, skewed_tolerance, CaseParameters( - "Transform 2D Affine", - [=](SkCanvas* c, SkPaint&) { c->concat(tx); }, - [=](DisplayListBuilder& b) { - b.transform2DAffine(tx[0], tx[1], tx[2], // - tx[3], tx[4], tx[5]); - } - ), skewed_tolerance, testP.under_transform()); + "Transform 2D Affine", + [=](SkCanvas* c, SkPaint&) { c->concat(tx); }, + [=](DisplayListBuilder& b) { + b.transform2DAffine(tx[0], tx[1], tx[2], // + tx[3], tx[4], tx[5]); + })); } { SkM44 m44 = SkM44(1, 0, 0, RenderCenterX, // @@ -1468,18 +1545,18 @@ class CanvasCompareTester { m44.preConcat(SkM44::Rotate({1, 0, 0}, M_PI / 60)); // 3 degrees around X m44.preConcat(SkM44::Rotate({0, 1, 0}, M_PI / 45)); // 4 degrees around Y m44.preTranslate(-RenderCenterX, -RenderCenterY); - RenderWith(testP, env, - CaseParameters( - "Transform Full Perspective", - [=](SkCanvas* c, SkPaint&) { c->concat(m44); }, // - [=](DisplayListBuilder& b) { - b.transformFullPerspective( - m44.rc(0, 0), m44.rc(0, 1), m44.rc(0, 2), m44.rc(0, 3), - m44.rc(1, 0), m44.rc(1, 1), m44.rc(1, 2), m44.rc(1, 3), - m44.rc(2, 0), m44.rc(2, 1), m44.rc(2, 2), m44.rc(2, 3), - m44.rc(3, 0), m44.rc(3, 1), m44.rc(3, 2), m44.rc(3, 3)); - } - ), skewed_tolerance, testP.under_transform()); + RenderWith( + testP, env, skewed_tolerance, + CaseParameters( + "Transform Full Perspective", + [=](SkCanvas* c, SkPaint&) { c->concat(m44); }, // + [=](DisplayListBuilder& b) { + b.transformFullPerspective( + m44.rc(0, 0), m44.rc(0, 1), m44.rc(0, 2), m44.rc(0, 3), + m44.rc(1, 0), m44.rc(1, 1), m44.rc(1, 2), m44.rc(1, 3), + m44.rc(2, 0), m44.rc(2, 1), m44.rc(2, 2), m44.rc(2, 3), + m44.rc(3, 0), m44.rc(3, 1), m44.rc(3, 2), m44.rc(3, 3)); + })); } } @@ -1488,101 +1565,95 @@ class CanvasCompareTester { const BoundsTolerance& diff_tolerance) { SkRect r_clip = RenderBounds.makeInset(15.5, 15.5); BoundsTolerance intersect_tolerance = diff_tolerance.clip(r_clip); - RenderWith(testP, env, + RenderWith(testP, env, intersect_tolerance, CaseParameters( - "Hard ClipRect inset by 15.5", - [=](SkCanvas* c, SkPaint&) { - c->clipRect(r_clip, SkClipOp::kIntersect, false); - }, - [=](DisplayListBuilder& b) { - b.clipRect(r_clip, SkClipOp::kIntersect, false); - } - ), intersect_tolerance, testP.with_clip()); - RenderWith(testP, env, + "Hard ClipRect inset by 15.5", + [=](SkCanvas* c, SkPaint&) { + c->clipRect(r_clip, SkClipOp::kIntersect, false); + }, + [=](DisplayListBuilder& b) { + b.clipRect(r_clip, SkClipOp::kIntersect, false); + })); + RenderWith(testP, env, intersect_tolerance, CaseParameters( - "AntiAlias ClipRect inset by 15.5", - [=](SkCanvas* c, SkPaint&) { - c->clipRect(r_clip, SkClipOp::kIntersect, true); - }, - [=](DisplayListBuilder& b) { - b.clipRect(r_clip, SkClipOp::kIntersect, true); - } - ), intersect_tolerance, testP.with_clip()); - RenderWith(testP, env, + "AntiAlias ClipRect inset by 15.5", + [=](SkCanvas* c, SkPaint&) { + c->clipRect(r_clip, SkClipOp::kIntersect, true); + }, + [=](DisplayListBuilder& b) { + b.clipRect(r_clip, SkClipOp::kIntersect, true); + })); + RenderWith(testP, env, diff_tolerance, CaseParameters( - "Hard ClipRect Diff, inset by 15.5", - [=](SkCanvas* c, SkPaint&) { - c->clipRect(r_clip, SkClipOp::kDifference, false); - }, - [=](DisplayListBuilder& b) { - b.clipRect(r_clip, SkClipOp::kDifference, false); - } - ), diff_tolerance, testP.with_clip()); + "Hard ClipRect Diff, inset by 15.5", + [=](SkCanvas* c, SkPaint&) { + c->clipRect(r_clip, SkClipOp::kDifference, false); + }, + [=](DisplayListBuilder& b) { + b.clipRect(r_clip, SkClipOp::kDifference, false); + }) + .with_diff_clip()); SkRRect rr_clip = SkRRect::MakeRectXY(r_clip, 1.8, 2.7); - RenderWith(testP, env, + RenderWith(testP, env, intersect_tolerance, CaseParameters( - "Hard ClipRRect inset by 15.5", - [=](SkCanvas* c, SkPaint&) { - c->clipRRect(rr_clip, SkClipOp::kIntersect, false); - }, - [=](DisplayListBuilder& b) { - b.clipRRect(rr_clip, SkClipOp::kIntersect, false); - } - ), intersect_tolerance, testP.with_clip()); - RenderWith(testP, env, + "Hard ClipRRect inset by 15.5", + [=](SkCanvas* c, SkPaint&) { + c->clipRRect(rr_clip, SkClipOp::kIntersect, false); + }, + [=](DisplayListBuilder& b) { + b.clipRRect(rr_clip, SkClipOp::kIntersect, false); + })); + RenderWith(testP, env, intersect_tolerance, CaseParameters( - "AntiAlias ClipRRect inset by 15.5", - [=](SkCanvas* c, SkPaint&) { - c->clipRRect(rr_clip, SkClipOp::kIntersect, true); - }, - [=](DisplayListBuilder& b) { - b.clipRRect(rr_clip, SkClipOp::kIntersect, true); - } - ), intersect_tolerance, testP.with_clip()); - RenderWith(testP, env, + "AntiAlias ClipRRect inset by 15.5", + [=](SkCanvas* c, SkPaint&) { + c->clipRRect(rr_clip, SkClipOp::kIntersect, true); + }, + [=](DisplayListBuilder& b) { + b.clipRRect(rr_clip, SkClipOp::kIntersect, true); + })); + RenderWith(testP, env, diff_tolerance, CaseParameters( - "Hard ClipRRect Diff, inset by 15.5", - [=](SkCanvas* c, SkPaint&) { - c->clipRRect(rr_clip, SkClipOp::kDifference, false); - }, - [=](DisplayListBuilder& b) { - b.clipRRect(rr_clip, SkClipOp::kDifference, false); - } - ), diff_tolerance, testP.with_clip()); + "Hard ClipRRect Diff, inset by 15.5", + [=](SkCanvas* c, SkPaint&) { + c->clipRRect(rr_clip, SkClipOp::kDifference, false); + }, + [=](DisplayListBuilder& b) { + b.clipRRect(rr_clip, SkClipOp::kDifference, false); + }) + .with_diff_clip()); SkPath path_clip = SkPath(); path_clip.setFillType(SkPathFillType::kEvenOdd); path_clip.addRect(r_clip); path_clip.addCircle(RenderCenterX, RenderCenterY, 1.0); - RenderWith(testP, env, + RenderWith(testP, env, intersect_tolerance, CaseParameters( - "Hard ClipPath inset by 15.5", - [=](SkCanvas* c, SkPaint&) { - c->clipPath(path_clip, SkClipOp::kIntersect, false); - }, - [=](DisplayListBuilder& b) { - b.clipPath(path_clip, SkClipOp::kIntersect, false); - } - ), intersect_tolerance, testP.with_clip()); - RenderWith(testP, env, + "Hard ClipPath inset by 15.5", + [=](SkCanvas* c, SkPaint&) { + c->clipPath(path_clip, SkClipOp::kIntersect, false); + }, + [=](DisplayListBuilder& b) { + b.clipPath(path_clip, SkClipOp::kIntersect, false); + })); + RenderWith(testP, env, intersect_tolerance, CaseParameters( - "AntiAlias ClipPath inset by 15.5", - [=](SkCanvas* c, SkPaint&) { - c->clipPath(path_clip, SkClipOp::kIntersect, true); - }, - [=](DisplayListBuilder& b) { - b.clipPath(path_clip, SkClipOp::kIntersect, true); - } - ), intersect_tolerance, testP.with_clip()); - RenderWith(testP, env, + "AntiAlias ClipPath inset by 15.5", + [=](SkCanvas* c, SkPaint&) { + c->clipPath(path_clip, SkClipOp::kIntersect, true); + }, + [=](DisplayListBuilder& b) { + b.clipPath(path_clip, SkClipOp::kIntersect, true); + })); + RenderWith(testP, env, diff_tolerance, CaseParameters( - "Hard ClipPath Diff, inset by 15.5", - [=](SkCanvas* c, SkPaint&) { - c->clipPath(path_clip, SkClipOp::kDifference, false); - }, - [=](DisplayListBuilder& b) { - b.clipPath(path_clip, SkClipOp::kDifference, false); - } - ), diff_tolerance, testP.with_clip()); + "Hard ClipPath Diff, inset by 15.5", + [=](SkCanvas* c, SkPaint&) { + c->clipPath(path_clip, SkClipOp::kDifference, false); + }, + [=](DisplayListBuilder& b) { + b.clipPath(path_clip, SkClipOp::kDifference, false); + }) + .with_diff_clip()); } static sk_sp getSkPicture(const TestParameters& testP, @@ -1596,9 +1667,8 @@ class CanvasCompareTester { static void RenderWith(const TestParameters& testP, const RenderEnvironment& env, - const CaseParameters& caseP, const BoundsTolerance& tolerance_in, - ReferencePixelExpectation ref_action) { + const CaseParameters& caseP) { // sk_surface is a direct rendering via SkCanvas to SkSurface // DisplayList mechanisms are not involved in this operation const std::string info = caseP.info(); @@ -1607,8 +1677,10 @@ class CanvasCompareTester { SkCanvas* sk_canvas = sk_surface.canvas(); SkPaint sk_paint; caseP.cv_setup()(sk_canvas, sk_paint); - const BoundsTolerance tolerance = testP.adjust(tolerance_in, sk_paint, - sk_canvas->getTotalMatrix()); + SkMatrix sk_matrix = sk_canvas->getTotalMatrix(); + SkIRect sk_clip = sk_canvas->getDeviceClipBounds(); + const BoundsTolerance tolerance = + testP.adjust(tolerance_in, sk_paint, sk_canvas->getTotalMatrix()); testP.render_to(sk_canvas, sk_paint); caseP.cv_restore()(sk_canvas, sk_paint); const sk_sp sk_picture = getSkPicture(testP, caseP); @@ -1619,17 +1691,14 @@ class CanvasCompareTester { ASSERT_EQ(sk_pixels->info().bytesPerPixel(), 4) << info; checkPixels(sk_pixels, sk_bounds, info + " (Skia reference)", bg); - switch (ref_action) { - case kPixelsMatchReference: - quickCompareToReference(env.ref_pixmap(), sk_pixels, true, - info + " (attribute has no effect)"); - break; - case kPixelsDoNotMatchReference: - quickCompareToReference(env.ref_pixmap(), sk_pixels, false, - info + " (attribute affects rendering)"); - break; - case kIgnoreReference: - break; + if (testP.should_match(env, sk_paint, sk_matrix, sk_clip, + caseP.has_diff_clip(), + caseP.has_mutating_save_layer())) { + quickCompareToReference(env.ref_pixmap(), sk_pixels, true, + info + " (attribute has no effect)"); + } else { + quickCompareToReference(env.ref_pixmap(), sk_pixels, false, + info + " (attribute affects rendering)"); } { @@ -1657,10 +1726,10 @@ class CanvasCompareTester { } } - // This sometimes triggers, but when it triggers and I examine - // the ref_bounds, they are always unnecessarily large and - // since the pixel OOB tests in the compare method do not - // trigger, we will trust the DL bounds. + // This EXPECT sometimes triggers, but when it triggers and I examine + // the ref_bounds, they are always unnecessarily large and since the + // pixel OOB tests in the compare method do not trigger, we will trust + // the DL bounds. // EXPECT_TRUE(dl_bounds.contains(ref_bounds)) << info; // When we are drawing a DisplayList, the display_list built above @@ -1708,8 +1777,10 @@ class CanvasCompareTester { SkCanvas* ref_canvas = sk_x2_recorder.beginRecording(TestBounds); SkPaint ref_paint; caseP.render_to(ref_canvas, testP); - sk_sp ref_x2_picture = sk_x2_recorder.finishRecordingAsPicture(); - RenderSurface ref_x2_surface = env.MakeSurface(bg, TestWidth2, TestHeight2); + sk_sp ref_x2_picture = + sk_x2_recorder.finishRecordingAsPicture(); + RenderSurface ref_x2_surface = + env.MakeSurface(bg, TestWidth2, TestHeight2); SkCanvas* ref_x2_canvas = ref_x2_surface.canvas(); ref_x2_canvas->scale(TestScale, TestScale); ref_x2_picture->playback(ref_x2_canvas); @@ -1721,7 +1792,8 @@ class CanvasCompareTester { DisplayListBuilder builder_x2(TestBounds); caseP.render_to(builder_x2, testP); sk_sp display_list_x2 = builder_x2.Build(); - RenderSurface test_x2_surface = env.MakeSurface(bg, TestWidth2, TestHeight2); + RenderSurface test_x2_surface = + env.MakeSurface(bg, TestWidth2, TestHeight2); SkCanvas* test_x2_canvas = test_x2_surface.canvas(); test_x2_canvas->scale(TestScale, TestScale); display_list_x2->RenderTo(test_x2_canvas); @@ -1856,7 +1928,8 @@ class CanvasCompareTester { int pad_top = std::max(0, pixTop - bounds.fTop); int pad_right = std::max(0, bounds.fRight - pixRight); int pad_bottom = std::max(0, bounds.fBottom - pixBottom); - SkIRect pix_bounds = SkIRect::MakeLTRB(pixLeft, pixTop, pixRight, pixBottom); + SkIRect pix_bounds = + SkIRect::MakeLTRB(pixLeft, pixTop, pixRight, pixBottom); SkISize pix_size = pix_bounds.size(); int pixWidth = pix_size.width(); int pixHeight = pix_size.height(); @@ -1892,7 +1965,7 @@ class CanvasCompareTester { SkCanvas* canvas = surface->getCanvas(); SkPaint p0, p1; p0.setStyle(SkPaint::kFill_Style); - p0.setColor(SkColorSetARGB(0xff, 0x00, 0xfe, 0x00)); // off-green + p0.setColor(SkColorSetARGB(0xff, 0x00, 0xfe, 0x00)); // off-green p1.setStyle(SkPaint::kFill_Style); p1.setColor(SK_ColorBLUE); // Some pixels need some transparency for DstIn testing @@ -1918,252 +1991,204 @@ class CanvasCompareTester { } }; -// bool CanvasCompareTester::TestingDrawShadows = false; -// bool CanvasCompareTester::TestingDrawVertices = false; -// bool CanvasCompareTester::TestingDrawAtlas = false; -// bool CanvasCompareTester::TestingDrawTextBlob = false; BoundsTolerance CanvasCompareTester::DefaultTolerance = BoundsTolerance().addAbsolutePadding(1, 1); const sk_sp CanvasCompareTester::testImage = makeTestImage(); const sk_sp CanvasCompareTester::testImageShader = - makeTestImage()->makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat, + makeTestImage()->makeShader(SkTileMode::kRepeat, + SkTileMode::kRepeat, SkSamplingOptions()); -TEST(DisplayListCanvas, DrawPaint) { - CanvasCompareTester::RenderAll(TestParameters( - [=](SkCanvas* canvas, SkPaint& paint) { - canvas->drawPaint(paint); - }, - [=](DisplayListBuilder& builder) { - builder.drawPaint(); - }, - DisplayListAttributeFlags::kDrawPaintFlags) - ); -} - -TEST(DisplayListCanvas, DrawColor) { - CanvasCompareTester::RenderAll(TestParameters( - [=](SkCanvas* canvas, SkPaint& paint) { - canvas->drawColor(SK_ColorMAGENTA); - }, - [=](DisplayListBuilder& builder) { - builder.drawColor(SK_ColorMAGENTA, SkBlendMode::kSrcOver); - }, - DisplayListAttributeFlags::kDrawColorFlags) - ); -} - -BoundsTolerance lineTolerance(const BoundsTolerance& tolerance, - const SkPaint& paint, - const SkMatrix& matrix, - bool is_horizontal, - bool is_vertical, - bool ignores_butt_cap) { - SkScalar adjust = 0.0; - SkScalar half_width = paint.getStrokeWidth() * 0.5f; - if (tolerance.discrete_offset() > 0) { - // When a discrete path effect is added, the bounds calculations must allow - // for miters in any direction, but a horizontal line will not have - // miters in the horizontal direction, similarly for vertical - // lines, and diagonal lines will have miters off at a "45 degree" angle - // that don't expand the bounds much at all. - // Also, the discrete offset will not move any points parallel with - // the line, so provide tolerance for both miters and offset. - adjust = half_width * paint.getStrokeMiter() + tolerance.discrete_offset(); - } - if (paint.getStrokeCap() == SkPaint::kButt_Cap && !ignores_butt_cap) { - adjust = std::max(adjust, half_width); - } - if (adjust == 0) { - return tolerance; - } - SkScalar hTolerance; - SkScalar vTolerance; - if (is_horizontal) { - FML_DCHECK(!is_vertical); - hTolerance = adjust; - vTolerance = 0; - } else if (is_vertical) { - hTolerance = 0; - vTolerance = adjust; - } else { - // The perpendicular miters just do not impact the bounds of - // diagonal lines at all as they are aimed in the wrong direction - // to matter. So allow tolerance in both axes. - hTolerance = vTolerance = adjust; - } - BoundsTolerance new_tolerance = - tolerance.addBoundsPadding(hTolerance, vTolerance); - return new_tolerance; -} - -// For drawing horizontal lines -BoundsTolerance hLineAdjuster(const BoundsTolerance& tolerance, - const SkPaint& paint, - const SkMatrix& matrix) { - return lineTolerance(tolerance, paint, matrix, true, false, false); -} - -// For drawing vertical lines -BoundsTolerance vLineAdjuster(const BoundsTolerance& tolerance, - const SkPaint& paint, - const SkMatrix& matrix) { - return lineTolerance(tolerance, paint, matrix, false, true, false); -} +// Eventually this bare bones testing::Test fixture will subsume the +// CanvasCompareTester and the TestParameters could then become just +// configuration calls made upon the fixture. +template +class DisplayListCanvasTestBase : public BaseT, protected DisplayListOpFlags { + public: + DisplayListCanvasTestBase() = default; -// For drawing diagonal lines -BoundsTolerance dLineAdjuster(const BoundsTolerance& tolerance, - const SkPaint& paint, - const SkMatrix& matrix) { - return lineTolerance(tolerance, paint, matrix, false, false, false); + private: + FML_DISALLOW_COPY_AND_ASSIGN(DisplayListCanvasTestBase); +}; +using DisplayListCanvas = DisplayListCanvasTestBase<::testing::Test>; + +TEST_F(DisplayListCanvas, DrawPaint) { + CanvasCompareTester::RenderAll( // + TestParameters( + [=](SkCanvas* canvas, const SkPaint& paint) { // + canvas->drawPaint(paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawPaint(); + }, + kDrawPaintFlags)); } -// For drawing individual points (drawPoints(Point_Mode)) -BoundsTolerance pointsAdjuster(const BoundsTolerance& tolerance, - const SkPaint& paint, - const SkMatrix& matrix) { - return lineTolerance(tolerance, paint, matrix, false, false, true); +TEST_F(DisplayListCanvas, DrawColor) { + CanvasCompareTester::RenderAll( // + TestParameters( + [=](SkCanvas* canvas, const SkPaint& paint) { + canvas->drawColor(SK_ColorMAGENTA); + }, + [=](DisplayListBuilder& builder) { + builder.drawColor(SK_ColorMAGENTA, SkBlendMode::kSrcOver); + }, + kDrawColorFlags)); } -TEST(DisplayListCanvas, DrawDiagonalLines) { +TEST_F(DisplayListCanvas, DrawDiagonalLines) { SkPoint p1 = SkPoint::Make(RenderLeft, RenderTop); SkPoint p2 = SkPoint::Make(RenderRight, RenderBottom); SkPoint p3 = SkPoint::Make(RenderLeft, RenderBottom); SkPoint p4 = SkPoint::Make(RenderRight, RenderTop); - CanvasCompareTester::RenderAll( - TestParameters( - [=](SkCanvas* canvas, SkPaint& paint) { // - // Skia requires kStroke style on horizontal and vertical - // lines to get the bounds correct. - // See https://bugs.chromium.org/p/skia/issues/detail?id=12446 - SkPaint p = paint; - p.setStyle(SkPaint::kStroke_Style); - canvas->drawLine(p1, p2, p); - canvas->drawLine(p3, p4, p); - }, - [=](DisplayListBuilder& builder) { // - builder.drawLine(p1, p2); - builder.drawLine(p3, p4); - }, - DisplayListAttributeFlags::kDrawLineFlags - )); + CanvasCompareTester::RenderAll( // + TestParameters( + [=](SkCanvas* canvas, const SkPaint& paint) { // + // Skia requires kStroke style on horizontal and vertical + // lines to get the bounds correct. + // See https://bugs.chromium.org/p/skia/issues/detail?id=12446 + SkPaint p = paint; + p.setStyle(SkPaint::kStroke_Style); + canvas->drawLine(p1, p2, p); + canvas->drawLine(p3, p4, p); + }, + [=](DisplayListBuilder& builder) { // + builder.drawLine(p1, p2); + builder.drawLine(p3, p4); + }, + kDrawLineFlags) + .set_draw_line()); } -// TEST(DisplayListCanvas, DrawHorizontalLine) { -// SkPoint p1 = SkPoint::Make(RenderLeft, RenderCenterY); -// SkPoint p2 = SkPoint::Make(RenderRight, RenderCenterY); - -// CanvasCompareTester::RenderAll( -// [=](SkCanvas* canvas, SkPaint& paint) { // -// // Skia requires kStroke style on horizontal and vertical -// // lines to get the bounds correct. -// // See https://bugs.chromium.org/p/skia/issues/detail?id=12446 -// SkPaint p = paint; -// p.setStyle(SkPaint::kStroke_Style); -// canvas->drawLine(p1, p2, p); -// }, -// [=](DisplayListBuilder& builder) { // -// builder.drawLine(p1, p2); -// }, -// MutationPredictor(DisplayListAttributeFlags::kDrawHVLineFlags), -// hLineAdjuster); -// } - -// TEST(DisplayListCanvas, DrawVerticalLine) { -// SkPoint p1 = SkPoint::Make(RenderCenterX, RenderTop); -// SkPoint p2 = SkPoint::Make(RenderCenterY, RenderBottom); - -// CanvasCompareTester::RenderAll( -// [=](SkCanvas* canvas, SkPaint& paint) { // -// // Skia requires kStroke style on horizontal and vertical -// // lines to get the bounds correct. -// // See https://bugs.chromium.org/p/skia/issues/detail?id=12446 -// SkPaint p = paint; -// p.setStyle(SkPaint::kStroke_Style); -// canvas->drawLine(p1, p2, p); -// }, -// [=](DisplayListBuilder& builder) { // -// builder.drawLine(p1, p2); -// }, -// MutationPredictor(DisplayListAttributeFlags::kDrawHVLineFlags), -// vLineAdjuster); -// } - -TEST(DisplayListCanvas, DrawRect) { +TEST_F(DisplayListCanvas, DrawHorizontalLine) { + SkPoint p1 = SkPoint::Make(RenderLeft, RenderCenterY); + SkPoint p2 = SkPoint::Make(RenderRight, RenderCenterY); + + CanvasCompareTester::RenderAll( // + TestParameters( + [=](SkCanvas* canvas, const SkPaint& paint) { // + // Skia requires kStroke style on horizontal and vertical + // lines to get the bounds correct. + // See https://bugs.chromium.org/p/skia/issues/detail?id=12446 + SkPaint p = paint; + p.setStyle(SkPaint::kStroke_Style); + canvas->drawLine(p1, p2, p); + }, + [=](DisplayListBuilder& builder) { // + builder.drawLine(p1, p2); + }, + kDrawHVLineFlags) + .set_draw_line() + .set_horizontal_line()); +} + +TEST_F(DisplayListCanvas, DrawVerticalLine) { + SkPoint p1 = SkPoint::Make(RenderCenterX, RenderTop); + SkPoint p2 = SkPoint::Make(RenderCenterY, RenderBottom); + + CanvasCompareTester::RenderAll( // + TestParameters( + [=](SkCanvas* canvas, const SkPaint& paint) { // + // Skia requires kStroke style on horizontal and vertical + // lines to get the bounds correct. + // See https://bugs.chromium.org/p/skia/issues/detail?id=12446 + SkPaint p = paint; + p.setStyle(SkPaint::kStroke_Style); + canvas->drawLine(p1, p2, p); + }, + [=](DisplayListBuilder& builder) { // + builder.drawLine(p1, p2); + }, + kDrawHVLineFlags) + .set_draw_line() + .set_vertical_line()); +} + +TEST_F(DisplayListCanvas, DrawRect) { // Bounds are offset by 0.5 pixels to induce AA - CanvasCompareTester::RenderAll(TestParameters( - [=](SkCanvas* canvas, SkPaint& paint) { // - canvas->drawRect(RenderBounds.makeOffset(0.5, 0.5), paint); - }, - [=](DisplayListBuilder& builder) { // - builder.drawRect(RenderBounds.makeOffset(0.5, 0.5)); - }, - DisplayListAttributeFlags::kDrawRectFlags) - ); + CanvasCompareTester::RenderAll( // + TestParameters( + [=](SkCanvas* canvas, const SkPaint& paint) { // + canvas->drawRect(RenderBounds.makeOffset(0.5, 0.5), paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawRect(RenderBounds.makeOffset(0.5, 0.5)); + }, + kDrawRectFlags) + .set_max_miter_limit(1.4)); } -TEST(DisplayListCanvas, DrawOval) { +TEST_F(DisplayListCanvas, DrawOval) { SkRect rect = RenderBounds.makeInset(0, 10); - CanvasCompareTester::RenderAll(TestParameters( - [=](SkCanvas* canvas, SkPaint& paint) { // - canvas->drawOval(rect, paint); - }, - [=](DisplayListBuilder& builder) { // - builder.drawOval(rect); - }, - DisplayListAttributeFlags::kDrawOvalFlags) - ); + CanvasCompareTester::RenderAll( // + TestParameters( + [=](SkCanvas* canvas, const SkPaint& paint) { // + canvas->drawOval(rect, paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawOval(rect); + }, + kDrawOvalFlags)); } -TEST(DisplayListCanvas, DrawCircle) { - CanvasCompareTester::RenderAll(TestParameters( - [=](SkCanvas* canvas, SkPaint& paint) { // - canvas->drawCircle(TestCenter, RenderRadius, paint); - }, - [=](DisplayListBuilder& builder) { // - builder.drawCircle(TestCenter, RenderRadius); - }, - DisplayListAttributeFlags::kDrawCircleFlags) - ); +TEST_F(DisplayListCanvas, DrawCircle) { + CanvasCompareTester::RenderAll( // + TestParameters( + [=](SkCanvas* canvas, const SkPaint& paint) { // + canvas->drawCircle(TestCenter, RenderRadius, paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawCircle(TestCenter, RenderRadius); + }, + kDrawCircleFlags)); } -TEST(DisplayListCanvas, DrawRRect) { +TEST_F(DisplayListCanvas, DrawRRect) { SkRRect rrect = SkRRect::MakeRectXY(RenderBounds, RenderCornerRadius, RenderCornerRadius); - CanvasCompareTester::RenderAll(TestParameters( - [=](SkCanvas* canvas, SkPaint& paint) { // - canvas->drawRRect(rrect, paint); - }, - [=](DisplayListBuilder& builder) { // - builder.drawRRect(rrect); - }, - DisplayListAttributeFlags::kDrawRRectFlags) - ); + CanvasCompareTester::RenderAll( // + TestParameters( + [=](SkCanvas* canvas, const SkPaint& paint) { // + canvas->drawRRect(rrect, paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawRRect(rrect); + }, + kDrawRRectFlags)); } -TEST(DisplayListCanvas, DrawDRRect) { +TEST_F(DisplayListCanvas, DrawDRRect) { SkRRect outer = SkRRect::MakeRectXY(RenderBounds, RenderCornerRadius, RenderCornerRadius); SkRect innerBounds = RenderBounds.makeInset(30.0, 30.0); SkRRect inner = SkRRect::MakeRectXY(innerBounds, RenderCornerRadius, RenderCornerRadius); - CanvasCompareTester::RenderAll(TestParameters( - [=](SkCanvas* canvas, SkPaint& paint) { // - canvas->drawDRRect(outer, inner, paint); - }, - [=](DisplayListBuilder& builder) { // - builder.drawDRRect(outer, inner); - }, - DisplayListAttributeFlags::kDrawDRRectFlags) - ); + CanvasCompareTester::RenderAll( // + TestParameters( + [=](SkCanvas* canvas, const SkPaint& paint) { // + canvas->drawDRRect(outer, inner, paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawDRRect(outer, inner); + }, + kDrawDRRectFlags)); } -TEST(DisplayListCanvas, DrawPath) { +TEST_F(DisplayListCanvas, DrawPath) { SkPath path; + + // unclosed lines to show some caps + path.moveTo(RenderLeft + 15, RenderTop + 15); + path.lineTo(RenderRight - 15, RenderBottom - 15); + path.moveTo(RenderLeft + 15, RenderBottom - 15); + path.lineTo(RenderRight - 15, RenderTop + 15); + path.addRect(RenderBounds); + + // miter diamonds horizontally and vertically to show miters path.moveTo(VerticalMiterDiamondPoints[0]); for (int i = 1; i < VerticalMiterDiamondPointCount; i++) { path.lineTo(VerticalMiterDiamondPoints[i]); @@ -2174,165 +2199,182 @@ TEST(DisplayListCanvas, DrawPath) { path.lineTo(HorizontalMiterDiamondPoints[i]); } path.close(); - CanvasCompareTester::RenderAll(TestParameters( - [=](SkCanvas* canvas, SkPaint& paint) { // - canvas->drawPath(path, paint); - }, - [=](DisplayListBuilder& builder) { // - builder.drawPath(path); - }, - DisplayListAttributeFlags::kDrawPathFlags) - ); + + CanvasCompareTester::RenderAll( // + TestParameters( + [=](SkCanvas* canvas, const SkPaint& paint) { // + canvas->drawPath(path, paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawPath(path); + }, + kDrawPathFlags)); +} + +TEST_F(DisplayListCanvas, DrawArc) { + CanvasCompareTester::RenderAll( // + TestParameters( + [=](SkCanvas* canvas, const SkPaint& paint) { // + canvas->drawArc(RenderBounds, 60, 330, false, paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawArc(RenderBounds, 60, 330, false); + }, + kDrawArcNoCenterFlags)); } -TEST(DisplayListCanvas, DrawArc) { - CanvasCompareTester::RenderAll(TestParameters( - [=](SkCanvas* canvas, SkPaint& paint) { // - canvas->drawArc(RenderBounds, 60, 330, false, paint); - }, - [=](DisplayListBuilder& builder) { // - builder.drawArc(RenderBounds, 60, 330, false); - }, - DisplayListAttributeFlags::kDrawArcFlags) - ); +TEST_F(DisplayListCanvas, DrawArcCenter) { + // Center arcs that inscribe nearly a whole circle except for a small + // arc extent gap have 2 angles that may appear or disappear at the + // various miter limits tested (0, 4, and 10). + // The center angle here is 12 degrees which shows a miter + // at limit=10, but not 0 or 4. + // The arcs at the corners where it turns in towards the + // center show miters at 4 and 10, but not 0. + // Limit == 0, neither corner does a miter + // Limit == 4, only the edge "turn-in" corners miter + // Limit == 10, edge and center corners all miter + CanvasCompareTester::RenderAll( // + TestParameters( + [=](SkCanvas* canvas, const SkPaint& paint) { // + canvas->drawArc(RenderBounds, 60, 360 - 12, true, paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawArc(RenderBounds, 60, 360 - 12, true); + }, + kDrawArcWithCenterFlags) + .set_draw_arc_center()); } -TEST(DisplayListCanvas, DrawArcCenter) { - CanvasCompareTester::RenderAll(TestParameters( - [=](SkCanvas* canvas, SkPaint& paint) { // - canvas->drawArc(RenderBounds, 60, 330, true, paint); - }, - [=](DisplayListBuilder& builder) { // - builder.drawArc(RenderBounds, 60, 330, true); - }, - DisplayListAttributeFlags::kDrawArcFlags) - ); +TEST_F(DisplayListCanvas, DrawPointsAsPoints) { + // The +/- 16 points are designed to fall just inside the clips + // that are tested against so we avoid lots of undrawn pixels + // in the accumulated bounds. + const SkScalar x0 = RenderLeft; + const SkScalar x1 = RenderLeft + 16; + const SkScalar x2 = (RenderLeft + RenderCenterX) * 0.5; + const SkScalar x3 = RenderCenterX + 0.1; + const SkScalar x4 = (RenderRight + RenderCenterX) * 0.5; + const SkScalar x5 = RenderRight - 16; + const SkScalar x6 = RenderRight; + + const SkScalar y0 = RenderTop; + const SkScalar y1 = RenderTop + 16; + const SkScalar y2 = (RenderTop + RenderCenterY) * 0.5; + const SkScalar y3 = RenderCenterY + 0.1; + const SkScalar y4 = (RenderBottom + RenderCenterY) * 0.5; + const SkScalar y5 = RenderBottom - 16; + const SkScalar y6 = RenderBottom; + + // clang-format off + const SkPoint points[] = { + {x0, y0}, {x1, y0}, {x2, y0}, {x3, y0}, {x4, y0}, {x5, y0}, {x6, y0}, + {x0, y1}, {x1, y1}, {x2, y1}, {x3, y1}, {x4, y1}, {x5, y1}, {x6, y1}, + {x0, y2}, {x1, y2}, {x2, y2}, {x3, y2}, {x4, y2}, {x5, y2}, {x6, y2}, + {x0, y3}, {x1, y3}, {x2, y3}, {x3, y3}, {x4, y3}, {x5, y3}, {x6, y3}, + {x0, y4}, {x1, y4}, {x2, y4}, {x3, y4}, {x4, y4}, {x5, y4}, {x6, y4}, + {x0, y5}, {x1, y5}, {x2, y5}, {x3, y5}, {x4, y5}, {x5, y5}, {x6, y5}, + {x0, y6}, {x1, y6}, {x2, y6}, {x3, y6}, {x4, y6}, {x5, y6}, {x6, y6}, + }; + // clang-format on + const int count = sizeof(points) / sizeof(points[0]); + + CanvasCompareTester::RenderAll( // + TestParameters( + [=](SkCanvas* canvas, const SkPaint& paint) { // + // Skia requires kStroke style on horizontal and vertical + // lines to get the bounds correct. + // See https://bugs.chromium.org/p/skia/issues/detail?id=12446 + SkPaint p = paint; + p.setStyle(SkPaint::kStroke_Style); + canvas->drawPoints(SkCanvas::kPoints_PointMode, count, points, p); + }, + [=](DisplayListBuilder& builder) { // + builder.drawPoints(SkCanvas::kPoints_PointMode, count, points); + }, + kDrawPointsAsPointsFlags) + .set_draw_line() + .set_ignores_dashes()); +} + +TEST_F(DisplayListCanvas, DrawPointsAsLines) { + const SkScalar x0 = RenderLeft + 1; + const SkScalar x1 = RenderLeft + 16; + const SkScalar x2 = RenderRight - 16; + const SkScalar x3 = RenderRight - 1; + + const SkScalar y0 = RenderTop; + const SkScalar y1 = RenderTop + 16; + const SkScalar y2 = RenderBottom - 16; + const SkScalar y3 = RenderBottom; + + // clang-format off + const SkPoint points[] = { + // Outer box + {x0, y0}, {x3, y0}, + {x3, y0}, {x3, y3}, + {x3, y3}, {x0, y3}, + {x0, y3}, {x0, y0}, + + // Diagonals + {x0, y0}, {x3, y3}, {x3, y0}, {x0, y3}, + + // Inner box + {x1, y1}, {x2, y1}, + {x2, y1}, {x2, y2}, + {x2, y2}, {x1, y2}, + {x1, y2}, {x1, y1}, + }; + // clang-format on + + const int count = sizeof(points) / sizeof(points[0]); + ASSERT_TRUE((count & 1) == 0); + CanvasCompareTester::RenderAll( // + TestParameters( + [=](SkCanvas* canvas, const SkPaint& paint) { // + // Skia requires kStroke style on horizontal and vertical + // lines to get the bounds correct. + // See https://bugs.chromium.org/p/skia/issues/detail?id=12446 + SkPaint p = paint; + p.setStyle(SkPaint::kStroke_Style); + canvas->drawPoints(SkCanvas::kLines_PointMode, count, points, p); + }, + [=](DisplayListBuilder& builder) { // + builder.drawPoints(SkCanvas::kLines_PointMode, count, points); + }, + kDrawPointsAsLinesFlags)); +} + +TEST_F(DisplayListCanvas, DrawPointsAsPolygon) { + const SkPoint points1[] = { + // RenderBounds box with a diagonal + SkPoint::Make(RenderLeft, RenderTop), + SkPoint::Make(RenderRight, RenderTop), + SkPoint::Make(RenderRight, RenderBottom), + SkPoint::Make(RenderLeft, RenderBottom), + SkPoint::Make(RenderLeft, RenderTop), + SkPoint::Make(RenderRight, RenderBottom), + }; + const int count1 = sizeof(points1) / sizeof(points1[0]); + + CanvasCompareTester::RenderAll( // + TestParameters( + [=](SkCanvas* canvas, const SkPaint& paint) { // + // Skia requires kStroke style on horizontal and vertical + // lines to get the bounds correct. + // See https://bugs.chromium.org/p/skia/issues/detail?id=12446 + SkPaint p = paint; + p.setStyle(SkPaint::kStroke_Style); + canvas->drawPoints(SkCanvas::kPolygon_PointMode, count1, points1, + p); + }, + [=](DisplayListBuilder& builder) { // + builder.drawPoints(SkCanvas::kPolygon_PointMode, count1, points1); + }, + kDrawPointsAsPolygonFlags)); } -// TEST(DisplayListCanvas, DrawPointsAsPoints) { -// // The +/- 16 points are designed to fall just inside the clips -// // that are tested against so we avoid lots of undrawn pixels -// // in the accumulated bounds. -// const SkScalar x0 = RenderLeft; -// const SkScalar x1 = RenderLeft + 16; -// const SkScalar x2 = (RenderLeft + RenderCenterX) * 0.5; -// const SkScalar x3 = RenderCenterX + 0.1; -// const SkScalar x4 = (RenderRight + RenderCenterX) * 0.5; -// const SkScalar x5 = RenderRight - 16; -// const SkScalar x6 = RenderRight; - -// const SkScalar y0 = RenderTop; -// const SkScalar y1 = RenderTop + 16; -// const SkScalar y2 = (RenderTop + RenderCenterY) * 0.5; -// const SkScalar y3 = RenderCenterY + 0.1; -// const SkScalar y4 = (RenderBottom + RenderCenterY) * 0.5; -// const SkScalar y5 = RenderBottom - 16; -// const SkScalar y6 = RenderBottom; - -// // clang-format off -// const SkPoint points[] = { -// {x0, y0}, {x1, y0}, {x2, y0}, {x3, y0}, {x4, y0}, {x5, y0}, {x6, y0}, -// {x0, y1}, {x1, y1}, {x2, y1}, {x3, y1}, {x4, y1}, {x5, y1}, {x6, y1}, -// {x0, y2}, {x1, y2}, {x2, y2}, {x3, y2}, {x4, y2}, {x5, y2}, {x6, y2}, -// {x0, y3}, {x1, y3}, {x2, y3}, {x3, y3}, {x4, y3}, {x5, y3}, {x6, y3}, -// {x0, y4}, {x1, y4}, {x2, y4}, {x3, y4}, {x4, y4}, {x5, y4}, {x6, y4}, -// {x0, y5}, {x1, y5}, {x2, y5}, {x3, y5}, {x4, y5}, {x5, y5}, {x6, y5}, -// {x0, y6}, {x1, y6}, {x2, y6}, {x3, y6}, {x4, y6}, {x5, y6}, {x6, y6}, -// }; -// // clang-format on -// const int count = sizeof(points) / sizeof(points[0]); - -// CanvasCompareTester::RenderAll( -// [=](SkCanvas* canvas, SkPaint& paint) { // -// // Skia requires kStroke style on horizontal and vertical -// // lines to get the bounds correct. -// // See https://bugs.chromium.org/p/skia/issues/detail?id=12446 -// SkPaint p = paint; -// p.setStyle(SkPaint::kStroke_Style); -// canvas->drawPoints(SkCanvas::kPoints_PointMode, count, points, p); -// }, -// [=](DisplayListBuilder& builder) { // -// builder.drawPoints(SkCanvas::kPoints_PointMode, count, points); -// }, -// MutationPredictor(DisplayListAttributeFlags::kDrawPointsAsPointsFlags), -// pointsAdjuster); -// } - -// TEST(DisplayListCanvas, DrawPointsAsLines) { -// const SkScalar x0 = RenderLeft + 1; -// const SkScalar x1 = RenderLeft + 16; -// const SkScalar x2 = RenderRight - 16; -// const SkScalar x3 = RenderRight - 1; - -// const SkScalar y0 = RenderTop; -// const SkScalar y1 = RenderTop + 16; -// const SkScalar y2 = RenderBottom - 16; -// const SkScalar y3 = RenderBottom; - -// // clang-format off -// const SkPoint points[] = { -// // Outer box -// {x0, y0}, {x3, y0}, -// {x3, y0}, {x3, y3}, -// {x3, y3}, {x0, y3}, -// {x0, y3}, {x0, y0}, - -// // Diagonals -// {x0, y0}, {x3, y3}, {x3, y0}, {x0, y3}, - -// // Inner box -// {x1, y1}, {x2, y1}, -// {x2, y1}, {x2, y2}, -// {x2, y2}, {x1, y2}, -// {x1, y2}, {x1, y1}, -// }; -// // clang-format on - -// const int count = sizeof(points) / sizeof(points[0]); -// ASSERT_TRUE((count & 1) == 0); -// CanvasCompareTester::RenderAll( -// [=](SkCanvas* canvas, SkPaint& paint) { // -// // Skia requires kStroke style on horizontal and vertical -// // lines to get the bounds correct. -// // See https://bugs.chromium.org/p/skia/issues/detail?id=12446 -// SkPaint p = paint; -// p.setStyle(SkPaint::kStroke_Style); -// canvas->drawPoints(SkCanvas::kLines_PointMode, count, points, p); -// }, -// [=](DisplayListBuilder& builder) { // -// builder.drawPoints(SkCanvas::kLines_PointMode, count, points); -// }, -// MutationPredictor(DisplayListAttributeFlags::kDrawPointsAsLinesFlags)); -// } - -// TEST(DisplayListCanvas, DrawPointsAsPolygon) { -// const SkPoint points1[] = { -// // RenderBounds box with a diagonal -// SkPoint::Make(RenderLeft, RenderTop), -// SkPoint::Make(RenderRight, RenderTop), -// SkPoint::Make(RenderRight, RenderBottom), -// SkPoint::Make(RenderLeft, RenderBottom), -// SkPoint::Make(RenderLeft, RenderTop), -// SkPoint::Make(RenderRight, RenderBottom), -// }; -// const int count1 = sizeof(points1) / sizeof(points1[0]); - -// CanvasCompareTester::RenderAll( -// [=](SkCanvas* canvas, SkPaint& paint) { // -// // Skia requires kStroke style on horizontal and vertical -// // lines to get the bounds correct. -// // See https://bugs.chromium.org/p/skia/issues/detail?id=12446 -// SkPaint p = paint; -// p.setStyle(SkPaint::kStroke_Style); -// canvas->drawPoints(SkCanvas::kPolygon_PointMode, count1, points1, p); -// }, -// [=](DisplayListBuilder& builder) { // -// builder.drawPoints(SkCanvas::kPolygon_PointMode, count1, points1); -// }, -// MutationPredictor(DisplayListAttributeFlags::kDrawPointsAsPolygonFlags)); -// } - -TEST(DisplayListCanvas, DrawVerticesWithColors) { +TEST_F(DisplayListCanvas, DrawVerticesWithColors) { // Cover as many sides of the box with only 6 vertices: // +----------+ // |xxxxxxxxxx| @@ -2359,21 +2401,20 @@ TEST(DisplayListCanvas, DrawVerticesWithColors) { const sk_sp vertices = SkVertices::MakeCopy( SkVertices::kTriangles_VertexMode, 6, pts, nullptr, colors); - CanvasCompareTester::RenderAll( - TestParameters( - [=](SkCanvas* canvas, SkPaint& paint) { // - canvas->drawVertices(vertices.get(), SkBlendMode::kSrcOver, paint); - }, - [=](DisplayListBuilder& builder) { // - builder.drawVertices(vertices, SkBlendMode::kSrcOver); - }, - DisplayListAttributeFlags::kDrawVerticesFlags - ).set_draw_vertices() - ); + CanvasCompareTester::RenderAll( // + TestParameters( + [=](SkCanvas* canvas, const SkPaint& paint) { // + canvas->drawVertices(vertices.get(), SkBlendMode::kSrcOver, paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawVertices(vertices, SkBlendMode::kSrcOver); + }, + kDrawVerticesFlags) + .set_draw_vertices()); EXPECT_TRUE(vertices->unique()); } -TEST(DisplayListCanvas, DrawVerticesWithImage) { +TEST_F(DisplayListCanvas, DrawVerticesWithImage) { // Cover as many sides of the box with only 6 vertices: // +----------+ // |xxxxxxxxxx| @@ -2404,173 +2445,179 @@ TEST(DisplayListCanvas, DrawVerticesWithImage) { const sk_sp vertices = SkVertices::MakeCopy( SkVertices::kTriangles_VertexMode, 6, pts, tex, nullptr); - CanvasCompareTester::RenderAll( - TestParameters( - [=](SkCanvas* canvas, SkPaint& paint) { // - if (paint.getShader() == nullptr) { - paint.setShader(CanvasCompareTester::testImageShader); - } - canvas->drawVertices(vertices.get(), SkBlendMode::kSrcOver, paint); - }, - [=](DisplayListBuilder& builder) { // - if (builder.getShader() == nullptr) { - builder.setShader(CanvasCompareTester::testImageShader); - } - builder.drawVertices(vertices, SkBlendMode::kSrcOver); - }, - DisplayListAttributeFlags::kDrawVerticesFlags - ).set_draw_vertices() - ); + CanvasCompareTester::RenderAll( // + TestParameters( + [=](SkCanvas* canvas, const SkPaint& paint) { // + SkPaint v_paint = paint; + if (v_paint.getShader() == nullptr) { + v_paint.setShader(CanvasCompareTester::testImageShader); + } + canvas->drawVertices(vertices.get(), SkBlendMode::kSrcOver, + v_paint); + }, + [=](DisplayListBuilder& builder) { // + if (builder.getShader() == nullptr) { + builder.setShader(CanvasCompareTester::testImageShader); + } + builder.drawVertices(vertices, SkBlendMode::kSrcOver); + }, + kDrawVerticesFlags) + .set_draw_vertices()); EXPECT_TRUE(vertices->unique()); EXPECT_TRUE(CanvasCompareTester::testImageShader->unique()); } -TEST(DisplayListCanvas, DrawImageNearest) { - CanvasCompareTester::RenderAll(TestParameters( - [=](SkCanvas* canvas, SkPaint& paint) { // - canvas->drawImage(CanvasCompareTester::testImage, RenderLeft, RenderTop, - DisplayList::NearestSampling, &paint); - }, - [=](DisplayListBuilder& builder) { // - builder.drawImage(CanvasCompareTester::testImage, - SkPoint::Make(RenderLeft, RenderTop), - DisplayList::NearestSampling, true); - }, - DisplayListAttributeFlags::kDrawImageWithPaintFlags) - ); +TEST_F(DisplayListCanvas, DrawImageNearest) { + CanvasCompareTester::RenderAll( // + TestParameters( + [=](SkCanvas* canvas, const SkPaint& paint) { // + canvas->drawImage(CanvasCompareTester::testImage, // + RenderLeft, RenderTop, + DisplayList::NearestSampling, &paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawImage(CanvasCompareTester::testImage, + SkPoint::Make(RenderLeft, RenderTop), + DisplayList::NearestSampling, true); + }, + kDrawImageWithPaintFlags)); } -TEST(DisplayListCanvas, DrawImageNearestNoPaint) { - CanvasCompareTester::RenderAll(TestParameters( - [=](SkCanvas* canvas, SkPaint& paint) { // - canvas->drawImage(CanvasCompareTester::testImage, RenderLeft, RenderTop, - DisplayList::NearestSampling, nullptr); - }, - [=](DisplayListBuilder& builder) { // - builder.drawImage(CanvasCompareTester::testImage, - SkPoint::Make(RenderLeft, RenderTop), - DisplayList::NearestSampling, false); - }, - DisplayListAttributeFlags::kDrawImageFlags) - ); +TEST_F(DisplayListCanvas, DrawImageNearestNoPaint) { + CanvasCompareTester::RenderAll( // + TestParameters( + [=](SkCanvas* canvas, const SkPaint& paint) { // + canvas->drawImage(CanvasCompareTester::testImage, // + RenderLeft, RenderTop, + DisplayList::NearestSampling, nullptr); + }, + [=](DisplayListBuilder& builder) { // + builder.drawImage(CanvasCompareTester::testImage, + SkPoint::Make(RenderLeft, RenderTop), + DisplayList::NearestSampling, false); + }, + kDrawImageFlags)); } -TEST(DisplayListCanvas, DrawImageLinear) { - CanvasCompareTester::RenderAll(TestParameters( - [=](SkCanvas* canvas, SkPaint& paint) { // - canvas->drawImage(CanvasCompareTester::testImage, RenderLeft, RenderTop, - DisplayList::LinearSampling, &paint); - }, - [=](DisplayListBuilder& builder) { // - builder.drawImage(CanvasCompareTester::testImage, - SkPoint::Make(RenderLeft, RenderTop), - DisplayList::LinearSampling, true); - }, - DisplayListAttributeFlags::kDrawImageWithPaintFlags) - ); +TEST_F(DisplayListCanvas, DrawImageLinear) { + CanvasCompareTester::RenderAll( // + TestParameters( + [=](SkCanvas* canvas, const SkPaint& paint) { // + canvas->drawImage(CanvasCompareTester::testImage, // + RenderLeft, RenderTop, + DisplayList::LinearSampling, &paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawImage(CanvasCompareTester::testImage, + SkPoint::Make(RenderLeft, RenderTop), + DisplayList::LinearSampling, true); + }, + kDrawImageWithPaintFlags)); } -TEST(DisplayListCanvas, DrawImageRectNearest) { +TEST_F(DisplayListCanvas, DrawImageRectNearest) { SkRect src = SkRect::MakeIWH(RenderWidth, RenderHeight).makeInset(5, 5); SkRect dst = RenderBounds.makeInset(10.5, 10.5); - CanvasCompareTester::RenderAll(TestParameters( - [=](SkCanvas* canvas, SkPaint& paint) { // - canvas->drawImageRect(CanvasCompareTester::testImage, src, dst, - DisplayList::NearestSampling, &paint, - SkCanvas::kFast_SrcRectConstraint); - }, - [=](DisplayListBuilder& builder) { // - builder.drawImageRect(CanvasCompareTester::testImage, src, dst, - DisplayList::NearestSampling, true); - }, - DisplayListAttributeFlags::kDrawImageRectWithPaintFlags) - ); + CanvasCompareTester::RenderAll( // + TestParameters( + [=](SkCanvas* canvas, const SkPaint& paint) { // + canvas->drawImageRect(CanvasCompareTester::testImage, src, dst, + DisplayList::NearestSampling, &paint, + SkCanvas::kFast_SrcRectConstraint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawImageRect(CanvasCompareTester::testImage, src, dst, + DisplayList::NearestSampling, true); + }, + kDrawImageRectWithPaintFlags)); } -TEST(DisplayListCanvas, DrawImageRectNearestNoPaint) { +TEST_F(DisplayListCanvas, DrawImageRectNearestNoPaint) { SkRect src = SkRect::MakeIWH(RenderWidth, RenderHeight).makeInset(5, 5); SkRect dst = RenderBounds.makeInset(10.5, 10.5); - CanvasCompareTester::RenderAll(TestParameters( - [=](SkCanvas* canvas, SkPaint& paint) { // - canvas->drawImageRect(CanvasCompareTester::testImage, src, dst, - DisplayList::NearestSampling, nullptr, - SkCanvas::kFast_SrcRectConstraint); - }, - [=](DisplayListBuilder& builder) { // - builder.drawImageRect(CanvasCompareTester::testImage, src, dst, - DisplayList::NearestSampling, false); - }, - DisplayListAttributeFlags::kDrawImageRectFlags) - ); + CanvasCompareTester::RenderAll( // + TestParameters( + [=](SkCanvas* canvas, const SkPaint& paint) { // + canvas->drawImageRect(CanvasCompareTester::testImage, src, dst, + DisplayList::NearestSampling, nullptr, + SkCanvas::kFast_SrcRectConstraint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawImageRect(CanvasCompareTester::testImage, src, dst, + DisplayList::NearestSampling, false); + }, + kDrawImageRectFlags)); } -TEST(DisplayListCanvas, DrawImageRectLinear) { +TEST_F(DisplayListCanvas, DrawImageRectLinear) { SkRect src = SkRect::MakeIWH(RenderWidth, RenderHeight).makeInset(5, 5); SkRect dst = RenderBounds.makeInset(10.5, 10.5); - CanvasCompareTester::RenderAll(TestParameters( - [=](SkCanvas* canvas, SkPaint& paint) { // - canvas->drawImageRect(CanvasCompareTester::testImage, src, dst, - DisplayList::LinearSampling, &paint, - SkCanvas::kFast_SrcRectConstraint); - }, - [=](DisplayListBuilder& builder) { // - builder.drawImageRect(CanvasCompareTester::testImage, src, dst, - DisplayList::LinearSampling, true); - }, - DisplayListAttributeFlags::kDrawImageRectWithPaintFlags) - ); + CanvasCompareTester::RenderAll( // + TestParameters( + [=](SkCanvas* canvas, const SkPaint& paint) { // + canvas->drawImageRect(CanvasCompareTester::testImage, src, dst, + DisplayList::LinearSampling, &paint, + SkCanvas::kFast_SrcRectConstraint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawImageRect(CanvasCompareTester::testImage, src, dst, + DisplayList::LinearSampling, true); + }, + kDrawImageRectWithPaintFlags)); } -TEST(DisplayListCanvas, DrawImageNineNearest) { +TEST_F(DisplayListCanvas, DrawImageNineNearest) { SkIRect src = SkIRect::MakeWH(RenderWidth, RenderHeight).makeInset(25, 25); SkRect dst = RenderBounds.makeInset(10.5, 10.5); - CanvasCompareTester::RenderAll(TestParameters( - [=](SkCanvas* canvas, SkPaint& paint) { // - canvas->drawImageNine(CanvasCompareTester::testImage.get(), src, dst, - SkFilterMode::kNearest, &paint); - }, - [=](DisplayListBuilder& builder) { // - builder.drawImageNine(CanvasCompareTester::testImage, src, dst, - SkFilterMode::kNearest, true); - }, - DisplayListAttributeFlags::kDrawImageNineWithPaintFlags) - ); + sk_sp image = CanvasCompareTester::testImage; + CanvasCompareTester::RenderAll( // + TestParameters( + [=](SkCanvas* canvas, const SkPaint& paint) { + canvas->drawImageNine(image.get(), src, dst, SkFilterMode::kNearest, + &paint); + }, + [=](DisplayListBuilder& builder) { + builder.drawImageNine(image, src, dst, SkFilterMode::kNearest, + true); + }, + kDrawImageNineWithPaintFlags)); } -TEST(DisplayListCanvas, DrawImageNineNearestNoPaint) { +TEST_F(DisplayListCanvas, DrawImageNineNearestNoPaint) { SkIRect src = SkIRect::MakeWH(RenderWidth, RenderHeight).makeInset(25, 25); SkRect dst = RenderBounds.makeInset(10.5, 10.5); - CanvasCompareTester::RenderAll(TestParameters( - [=](SkCanvas* canvas, SkPaint& paint) { // - canvas->drawImageNine(CanvasCompareTester::testImage.get(), src, dst, - SkFilterMode::kNearest, nullptr); - }, - [=](DisplayListBuilder& builder) { // - builder.drawImageNine(CanvasCompareTester::testImage, src, dst, - SkFilterMode::kNearest, false); - }, - DisplayListAttributeFlags::kDrawImageNineFlags) - ); + sk_sp image = CanvasCompareTester::testImage; + CanvasCompareTester::RenderAll( // + TestParameters( + [=](SkCanvas* canvas, const SkPaint& paint) { + canvas->drawImageNine(image.get(), src, dst, SkFilterMode::kNearest, + nullptr); + }, + [=](DisplayListBuilder& builder) { + builder.drawImageNine(image, src, dst, SkFilterMode::kNearest, + false); + }, + kDrawImageNineFlags)); } -TEST(DisplayListCanvas, DrawImageNineLinear) { +TEST_F(DisplayListCanvas, DrawImageNineLinear) { SkIRect src = SkIRect::MakeWH(RenderWidth, RenderHeight).makeInset(25, 25); SkRect dst = RenderBounds.makeInset(10.5, 10.5); - CanvasCompareTester::RenderAll(TestParameters( - [=](SkCanvas* canvas, SkPaint& paint) { // - canvas->drawImageNine(CanvasCompareTester::testImage.get(), src, dst, - SkFilterMode::kLinear, &paint); - }, - [=](DisplayListBuilder& builder) { // - builder.drawImageNine(CanvasCompareTester::testImage, src, dst, - SkFilterMode::kLinear, true); - }, - DisplayListAttributeFlags::kDrawImageNineWithPaintFlags) - ); + sk_sp image = CanvasCompareTester::testImage; + CanvasCompareTester::RenderAll( // + TestParameters( + [=](SkCanvas* canvas, const SkPaint& paint) { + canvas->drawImageNine(image.get(), src, dst, SkFilterMode::kLinear, + &paint); + }, + [=](DisplayListBuilder& builder) { + builder.drawImageNine(image, src, dst, SkFilterMode::kLinear, true); + }, + kDrawImageNineWithPaintFlags)); } -TEST(DisplayListCanvas, DrawImageLatticeNearest) { +TEST_F(DisplayListCanvas, DrawImageLatticeNearest) { const SkRect dst = RenderBounds.makeInset(10.5, 10.5); const int divX[] = { RenderWidth * 1 / 4, @@ -2585,20 +2632,21 @@ TEST(DisplayListCanvas, DrawImageLatticeNearest) { SkCanvas::Lattice lattice = { divX, divY, nullptr, 3, 3, nullptr, nullptr, }; - CanvasCompareTester::RenderAll(TestParameters( - [=](SkCanvas* canvas, SkPaint& paint) { // - canvas->drawImageLattice(CanvasCompareTester::testImage.get(), lattice, - dst, SkFilterMode::kNearest, &paint); - }, - [=](DisplayListBuilder& builder) { // - builder.drawImageLattice(CanvasCompareTester::testImage, lattice, // - dst, SkFilterMode::kNearest, true); - }, - DisplayListAttributeFlags::kDrawImageLatticeWithPaintFlags) - ); + sk_sp image = CanvasCompareTester::testImage; + CanvasCompareTester::RenderAll( // + TestParameters( + [=](SkCanvas* canvas, const SkPaint& paint) { + canvas->drawImageLattice(image.get(), lattice, dst, + SkFilterMode::kNearest, &paint); + }, + [=](DisplayListBuilder& builder) { + builder.drawImageLattice(image, lattice, dst, + SkFilterMode::kNearest, true); + }, + kDrawImageLatticeWithPaintFlags)); } -TEST(DisplayListCanvas, DrawImageLatticeNearestNoPaint) { +TEST_F(DisplayListCanvas, DrawImageLatticeNearestNoPaint) { const SkRect dst = RenderBounds.makeInset(10.5, 10.5); const int divX[] = { RenderWidth * 1 / 4, @@ -2613,20 +2661,21 @@ TEST(DisplayListCanvas, DrawImageLatticeNearestNoPaint) { SkCanvas::Lattice lattice = { divX, divY, nullptr, 3, 3, nullptr, nullptr, }; - CanvasCompareTester::RenderAll(TestParameters( - [=](SkCanvas* canvas, SkPaint& paint) { // - canvas->drawImageLattice(CanvasCompareTester::testImage.get(), lattice, - dst, SkFilterMode::kNearest, nullptr); - }, - [=](DisplayListBuilder& builder) { // - builder.drawImageLattice(CanvasCompareTester::testImage, lattice, // - dst, SkFilterMode::kNearest, false); - }, - DisplayListAttributeFlags::kDrawImageLatticeFlags) - ); + sk_sp image = CanvasCompareTester::testImage; + CanvasCompareTester::RenderAll( // + TestParameters( + [=](SkCanvas* canvas, const SkPaint& paint) { + canvas->drawImageLattice(image.get(), lattice, dst, + SkFilterMode::kNearest, nullptr); + }, + [=](DisplayListBuilder& builder) { + builder.drawImageLattice(image, lattice, dst, + SkFilterMode::kNearest, false); + }, + kDrawImageLatticeFlags)); } -TEST(DisplayListCanvas, DrawImageLatticeLinear) { +TEST_F(DisplayListCanvas, DrawImageLatticeLinear) { const SkRect dst = RenderBounds.makeInset(10.5, 10.5); const int divX[] = { RenderWidth / 4, @@ -2641,20 +2690,21 @@ TEST(DisplayListCanvas, DrawImageLatticeLinear) { SkCanvas::Lattice lattice = { divX, divY, nullptr, 3, 3, nullptr, nullptr, }; - CanvasCompareTester::RenderAll(TestParameters( - [=](SkCanvas* canvas, SkPaint& paint) { // - canvas->drawImageLattice(CanvasCompareTester::testImage.get(), lattice, - dst, SkFilterMode::kLinear, &paint); - }, - [=](DisplayListBuilder& builder) { // - builder.drawImageLattice(CanvasCompareTester::testImage, lattice, // - dst, SkFilterMode::kLinear, true); - }, - DisplayListAttributeFlags::kDrawImageLatticeWithPaintFlags) - ); + sk_sp image = CanvasCompareTester::testImage; + CanvasCompareTester::RenderAll( // + TestParameters( + [=](SkCanvas* canvas, const SkPaint& paint) { + canvas->drawImageLattice(image.get(), lattice, dst, + SkFilterMode::kLinear, &paint); + }, + [=](DisplayListBuilder& builder) { + builder.drawImageLattice(image, lattice, dst, SkFilterMode::kLinear, + true); + }, + kDrawImageLatticeWithPaintFlags)); } -TEST(DisplayListCanvas, DrawAtlasNearest) { +TEST_F(DisplayListCanvas, DrawAtlasNearest) { const SkRSXform xform[] = { // clang-format off { 1.2f, 0.0f, RenderLeft, RenderTop}, @@ -2678,23 +2728,22 @@ TEST(DisplayListCanvas, DrawAtlasNearest) { SK_ColorMAGENTA, }; const sk_sp image = CanvasCompareTester::testImage; - CanvasCompareTester::RenderAll( - TestParameters( - [=](SkCanvas* canvas, SkPaint& paint) { - canvas->drawAtlas(image.get(), xform, tex, colors, 4, - SkBlendMode::kSrcOver, DisplayList::NearestSampling, - nullptr, &paint); - }, - [=](DisplayListBuilder& builder) { - builder.drawAtlas(image, xform, tex, colors, 4, // - SkBlendMode::kSrcOver, DisplayList::NearestSampling, - nullptr, true); - }, - DisplayListAttributeFlags::kDrawAtlasWithPaintFlags - ).set_draw_atlas()); + const SkSamplingOptions sampling = DisplayList::NearestSampling; + CanvasCompareTester::RenderAll( // + TestParameters( + [=](SkCanvas* canvas, const SkPaint& paint) { + canvas->drawAtlas(image.get(), xform, tex, colors, 4, + SkBlendMode::kSrcOver, sampling, nullptr, &paint); + }, + [=](DisplayListBuilder& builder) { + builder.drawAtlas(image, xform, tex, colors, 4, // + SkBlendMode::kSrcOver, sampling, nullptr, true); + }, + kDrawAtlasWithPaintFlags) + .set_draw_atlas()); } -TEST(DisplayListCanvas, DrawAtlasNearestNoPaint) { +TEST_F(DisplayListCanvas, DrawAtlasNearestNoPaint) { const SkRSXform xform[] = { // clang-format off { 1.2f, 0.0f, RenderLeft, RenderTop}, @@ -2718,23 +2767,24 @@ TEST(DisplayListCanvas, DrawAtlasNearestNoPaint) { SK_ColorMAGENTA, }; const sk_sp image = CanvasCompareTester::testImage; - CanvasCompareTester::RenderAll( - TestParameters( - [=](SkCanvas* canvas, SkPaint& paint) { - canvas->drawAtlas(image.get(), xform, tex, colors, 4, - SkBlendMode::kSrcOver, DisplayList::NearestSampling, - nullptr, nullptr); - }, - [=](DisplayListBuilder& builder) { - builder.drawAtlas(image, xform, tex, colors, 4, // - SkBlendMode::kSrcOver, DisplayList::NearestSampling, - nullptr, false); - }, - DisplayListAttributeFlags::kDrawAtlasFlags - ).set_draw_atlas()); + const SkSamplingOptions sampling = DisplayList::NearestSampling; + CanvasCompareTester::RenderAll( // + TestParameters( + [=](SkCanvas* canvas, const SkPaint& paint) { + canvas->drawAtlas(image.get(), xform, tex, colors, 4, + SkBlendMode::kSrcOver, sampling, // + nullptr, nullptr); + }, + [=](DisplayListBuilder& builder) { + builder.drawAtlas(image, xform, tex, colors, 4, // + SkBlendMode::kSrcOver, sampling, // + nullptr, false); + }, + kDrawAtlasFlags) + .set_draw_atlas()); } -TEST(DisplayListCanvas, DrawAtlasLinear) { +TEST_F(DisplayListCanvas, DrawAtlasLinear) { const SkRSXform xform[] = { // clang-format off { 1.2f, 0.0f, RenderLeft, RenderTop}, @@ -2758,20 +2808,19 @@ TEST(DisplayListCanvas, DrawAtlasLinear) { SK_ColorMAGENTA, }; const sk_sp image = CanvasCompareTester::testImage; - CanvasCompareTester::RenderAll( - TestParameters( - [=](SkCanvas* canvas, SkPaint& paint) { - canvas->drawAtlas(image.get(), xform, tex, colors, 2, // - SkBlendMode::kSrcOver, DisplayList::LinearSampling, - nullptr, &paint); - }, - [=](DisplayListBuilder& builder) { - builder.drawAtlas(image, xform, tex, colors, 2, // - SkBlendMode::kSrcOver, DisplayList::LinearSampling, - nullptr, true); - }, - DisplayListAttributeFlags::kDrawAtlasWithPaintFlags - ).set_draw_atlas()); + const SkSamplingOptions sampling = DisplayList::LinearSampling; + CanvasCompareTester::RenderAll( // + TestParameters( + [=](SkCanvas* canvas, const SkPaint& paint) { + canvas->drawAtlas(image.get(), xform, tex, colors, 2, // + SkBlendMode::kSrcOver, sampling, nullptr, &paint); + }, + [=](DisplayListBuilder& builder) { + builder.drawAtlas(image, xform, tex, colors, 2, // + SkBlendMode::kSrcOver, sampling, nullptr, true); + }, + kDrawAtlasWithPaintFlags) + .set_draw_atlas()); } sk_sp makeTestPicture() { @@ -2790,44 +2839,44 @@ sk_sp makeTestPicture() { return recorder.finishRecordingAsPicture(); } -TEST(DisplayListCanvas, DrawPicture) { +TEST_F(DisplayListCanvas, DrawPicture) { sk_sp picture = makeTestPicture(); - CanvasCompareTester::RenderAll(TestParameters( - [=](SkCanvas* canvas, SkPaint& paint) { // - canvas->drawPicture(picture, nullptr, nullptr); - }, - [=](DisplayListBuilder& builder) { // - builder.drawPicture(picture, nullptr, false); - }, - DisplayListAttributeFlags::kDrawPictureFlags) - ); + CanvasCompareTester::RenderAll( // + TestParameters( + [=](SkCanvas* canvas, const SkPaint& paint) { // + canvas->drawPicture(picture, nullptr, nullptr); + }, + [=](DisplayListBuilder& builder) { // + builder.drawPicture(picture, nullptr, false); + }, + kDrawPictureFlags)); } -TEST(DisplayListCanvas, DrawPictureWithMatrix) { +TEST_F(DisplayListCanvas, DrawPictureWithMatrix) { sk_sp picture = makeTestPicture(); SkMatrix matrix = SkMatrix::Scale(0.95, 0.95); - CanvasCompareTester::RenderAll(TestParameters( - [=](SkCanvas* canvas, SkPaint& paint) { // - canvas->drawPicture(picture, &matrix, nullptr); - }, - [=](DisplayListBuilder& builder) { // - builder.drawPicture(picture, &matrix, false); - }, - DisplayListAttributeFlags::kDrawPictureFlags) - ); + CanvasCompareTester::RenderAll( // + TestParameters( + [=](SkCanvas* canvas, const SkPaint& paint) { // + canvas->drawPicture(picture, &matrix, nullptr); + }, + [=](DisplayListBuilder& builder) { // + builder.drawPicture(picture, &matrix, false); + }, + kDrawPictureFlags)); } -TEST(DisplayListCanvas, DrawPictureWithPaint) { +TEST_F(DisplayListCanvas, DrawPictureWithPaint) { sk_sp picture = makeTestPicture(); - CanvasCompareTester::RenderAll(TestParameters( - [=](SkCanvas* canvas, SkPaint& paint) { // - canvas->drawPicture(picture, nullptr, &paint); - }, - [=](DisplayListBuilder& builder) { // - builder.drawPicture(picture, nullptr, true); - }, - DisplayListAttributeFlags::kDrawPictureWithPaintFlags) - ); + CanvasCompareTester::RenderAll( // + TestParameters( + [=](SkCanvas* canvas, const SkPaint& paint) { // + canvas->drawPicture(picture, nullptr, &paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawPicture(picture, nullptr, true); + }, + kDrawPictureWithPaintFlags)); } sk_sp makeTestDisplayList() { @@ -2844,21 +2893,21 @@ sk_sp makeTestDisplayList() { return builder.Build(); } -TEST(DisplayListCanvas, DrawDisplayList) { +TEST_F(DisplayListCanvas, DrawDisplayList) { sk_sp display_list = makeTestDisplayList(); - CanvasCompareTester::RenderAll( - TestParameters( - [=](SkCanvas* canvas, SkPaint& paint) { // - display_list->RenderTo(canvas); - }, - [=](DisplayListBuilder& builder) { // - builder.drawDisplayList(display_list); - }, - DisplayListAttributeFlags::kDrawDisplayListFlags - ).set_draw_display_list()); + CanvasCompareTester::RenderAll( // + TestParameters( + [=](SkCanvas* canvas, const SkPaint& paint) { // + display_list->RenderTo(canvas); + }, + [=](DisplayListBuilder& builder) { // + builder.drawDisplayList(display_list); + }, + kDrawDisplayListFlags) + .set_draw_display_list()); } -TEST(DisplayListCanvas, DrawTextBlob) { +TEST_F(DisplayListCanvas, DrawTextBlob) { // TODO(https://github.com/flutter/flutter/issues/82202): Remove once the // performance overlay can use Fuchsia's font manager instead of the empty // default. @@ -2869,30 +2918,29 @@ TEST(DisplayListCanvas, DrawTextBlob) { CanvasCompareTester::MakeTextBlob("Testing", RenderHeight * 0.33f); SkScalar RenderY1_3 = RenderTop + RenderHeight * 0.3; SkScalar RenderY2_3 = RenderTop + RenderHeight * 0.6; - CanvasCompareTester::RenderAll( - TestParameters( - [=](SkCanvas* canvas, SkPaint& paint) { // - canvas->drawTextBlob(blob, RenderLeft, RenderY1_3, paint); - canvas->drawTextBlob(blob, RenderLeft, RenderY2_3, paint); - canvas->drawTextBlob(blob, RenderLeft, RenderBottom, paint); - }, - [=](DisplayListBuilder& builder) { // - builder.drawTextBlob(blob, RenderLeft, RenderY1_3); - builder.drawTextBlob(blob, RenderLeft, RenderY2_3); - builder.drawTextBlob(blob, RenderLeft, RenderBottom); - }, - DisplayListAttributeFlags::kDrawTextBlobFlags - ).set_draw_text_blob(), - // From examining the bounds differential for the "Default" case, the - // SkTextBlob adds a padding of ~32 on the left, ~30 on the right, - // ~12 on top and ~8 on the bottom, so we add 33h & 13v allowed - // padding to the tolerance - CanvasCompareTester::DefaultTolerance.addBoundsPadding(33, 13) - ); + CanvasCompareTester::RenderAll( // + TestParameters( + [=](SkCanvas* canvas, const SkPaint& paint) { // + canvas->drawTextBlob(blob, RenderLeft, RenderY1_3, paint); + canvas->drawTextBlob(blob, RenderLeft, RenderY2_3, paint); + canvas->drawTextBlob(blob, RenderLeft, RenderBottom, paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawTextBlob(blob, RenderLeft, RenderY1_3); + builder.drawTextBlob(blob, RenderLeft, RenderY2_3); + builder.drawTextBlob(blob, RenderLeft, RenderBottom); + }, + kDrawTextBlobFlags) + .set_draw_text_blob(), + // From examining the bounds differential for the "Default" case, the + // SkTextBlob adds a padding of ~32 on the left, ~30 on the right, + // ~12 on top and ~8 on the bottom, so we add 33h & 13v allowed + // padding to the tolerance + CanvasCompareTester::DefaultTolerance.addBoundsPadding(33, 13)); EXPECT_TRUE(blob->unique()); } -TEST(DisplayListCanvas, DrawShadow) { +TEST_F(DisplayListCanvas, DrawShadow) { SkPath path; path.addRoundRect( { @@ -2905,21 +2953,21 @@ TEST(DisplayListCanvas, DrawShadow) { const SkColor color = SK_ColorDKGRAY; const SkScalar elevation = 5; - CanvasCompareTester::RenderAll( - TestParameters( - [=](SkCanvas* canvas, SkPaint& paint) { // - PhysicalShapeLayer::DrawShadow(canvas, path, color, elevation, false, - 1.0); - }, - [=](DisplayListBuilder& builder) { // - builder.drawShadow(path, color, elevation, false, 1.0); - }, - DisplayListAttributeFlags::kDrawShadowFlags - ).set_draw_shadows(), - CanvasCompareTester::DefaultTolerance.addBoundsPadding(3, 3)); + CanvasCompareTester::RenderAll( // + TestParameters( + [=](SkCanvas* canvas, const SkPaint& paint) { // + PhysicalShapeLayer::DrawShadow(canvas, path, color, elevation, + false, 1.0); + }, + [=](DisplayListBuilder& builder) { // + builder.drawShadow(path, color, elevation, false, 1.0); + }, + kDrawShadowFlags) + .set_draw_shadows(), + CanvasCompareTester::DefaultTolerance.addBoundsPadding(3, 3)); } -TEST(DisplayListCanvas, DrawShadowTransparentOccluder) { +TEST_F(DisplayListCanvas, DrawShadowTransparentOccluder) { SkPath path; path.addRoundRect( { @@ -2932,21 +2980,21 @@ TEST(DisplayListCanvas, DrawShadowTransparentOccluder) { const SkColor color = SK_ColorDKGRAY; const SkScalar elevation = 5; - CanvasCompareTester::RenderAll( - TestParameters( - [=](SkCanvas* canvas, SkPaint& paint) { // - PhysicalShapeLayer::DrawShadow(canvas, path, color, elevation, true, - 1.0); - }, - [=](DisplayListBuilder& builder) { // - builder.drawShadow(path, color, elevation, true, 1.0); - }, - DisplayListAttributeFlags::kDrawShadowFlags - ).set_draw_shadows(), - CanvasCompareTester::DefaultTolerance.addBoundsPadding(3, 3)); + CanvasCompareTester::RenderAll( // + TestParameters( + [=](SkCanvas* canvas, const SkPaint& paint) { // + PhysicalShapeLayer::DrawShadow(canvas, path, color, elevation, true, + 1.0); + }, + [=](DisplayListBuilder& builder) { // + builder.drawShadow(path, color, elevation, true, 1.0); + }, + kDrawShadowFlags) + .set_draw_shadows(), + CanvasCompareTester::DefaultTolerance.addBoundsPadding(3, 3)); } -TEST(DisplayListCanvas, DrawShadowDpr) { +TEST_F(DisplayListCanvas, DrawShadowDpr) { SkPath path; path.addRoundRect( { @@ -2959,18 +3007,18 @@ TEST(DisplayListCanvas, DrawShadowDpr) { const SkColor color = SK_ColorDKGRAY; const SkScalar elevation = 5; - CanvasCompareTester::RenderAll( - TestParameters( - [=](SkCanvas* canvas, SkPaint& paint) { // - PhysicalShapeLayer::DrawShadow(canvas, path, color, elevation, false, - 1.5); - }, - [=](DisplayListBuilder& builder) { // - builder.drawShadow(path, color, elevation, false, 1.5); - }, - DisplayListAttributeFlags::kDrawShadowFlags - ).set_draw_shadows(), - CanvasCompareTester::DefaultTolerance.addBoundsPadding(3, 3)); + CanvasCompareTester::RenderAll( // + TestParameters( + [=](SkCanvas* canvas, const SkPaint& paint) { // + PhysicalShapeLayer::DrawShadow(canvas, path, color, elevation, + false, 1.5); + }, + [=](DisplayListBuilder& builder) { // + builder.drawShadow(path, color, elevation, false, 1.5); + }, + kDrawShadowFlags) + .set_draw_shadows(), + CanvasCompareTester::DefaultTolerance.addBoundsPadding(3, 3)); } } // namespace testing diff --git a/flow/display_list_utils.cc b/flow/display_list_utils.cc index 7681bc6db8c0c..d323f37d09e4d 100644 --- a/flow/display_list_utils.cc +++ b/flow/display_list_utils.cc @@ -312,7 +312,7 @@ void DisplayListBoundsCalculator::restore() { // modifications based on the attributes that were in place // when it was instantiated. Modifying it further base on the // current attributes would mix attribute states. - AccumulateRect(layer_bounds, DisplayListAttributeFlags::kSaveLayerFlags); + AccumulateRect(layer_bounds, kSaveLayerFlags); } if (layer_unbounded) { AccumulateUnbounded(); @@ -330,35 +330,34 @@ void DisplayListBoundsCalculator::drawLine(const SkPoint& p0, const SkPoint& p1) { SkRect bounds = SkRect::MakeLTRB(p0.fX, p0.fY, p1.fX, p1.fY).makeSorted(); DisplayListAttributeFlags flags = - (bounds.width() > 0.0f && bounds.height() > 0.0f) - ? DisplayListAttributeFlags::kDrawLineFlags - : DisplayListAttributeFlags::kDrawHVLineFlags; + (bounds.width() > 0.0f && bounds.height() > 0.0f) ? kDrawLineFlags + : kDrawHVLineFlags; AccumulateRect(bounds, flags); } void DisplayListBoundsCalculator::drawRect(const SkRect& rect) { - AccumulateRect(rect, DisplayListAttributeFlags::kDrawRectFlags); + AccumulateRect(rect, kDrawRectFlags); } void DisplayListBoundsCalculator::drawOval(const SkRect& bounds) { - AccumulateRect(bounds, DisplayListAttributeFlags::kDrawOvalFlags); + AccumulateRect(bounds, kDrawOvalFlags); } void DisplayListBoundsCalculator::drawCircle(const SkPoint& center, SkScalar radius) { AccumulateRect(SkRect::MakeLTRB(center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius), - DisplayListAttributeFlags::kDrawCircleFlags); + kDrawCircleFlags); } void DisplayListBoundsCalculator::drawRRect(const SkRRect& rrect) { - AccumulateRect(rrect.getBounds(), DisplayListAttributeFlags::kDrawRRectFlags); + AccumulateRect(rrect.getBounds(), kDrawRRectFlags); } void DisplayListBoundsCalculator::drawDRRect(const SkRRect& outer, const SkRRect& inner) { - AccumulateRect(outer.getBounds(), DisplayListAttributeFlags::kDrawDRRectFlags); + AccumulateRect(outer.getBounds(), kDrawDRRectFlags); } void DisplayListBoundsCalculator::drawPath(const SkPath& path) { if (path.isInverseFillType()) { AccumulateUnbounded(); } else { - AccumulateRect(path.getBounds(), DisplayListAttributeFlags::kDrawPathFlags); + AccumulateRect(path.getBounds(), kDrawPathFlags); } } void DisplayListBoundsCalculator::drawArc(const SkRect& bounds, @@ -368,7 +367,10 @@ void DisplayListBoundsCalculator::drawArc(const SkRect& bounds, // This could be tighter if we compute where the start and end // angles are and then also consider the quadrants swept and // the center if specified. - AccumulateRect(bounds, DisplayListAttributeFlags::kDrawArcFlags); + AccumulateRect(bounds, + useCenter // + ? kDrawArcWithCenterFlags + : kDrawArcNoCenterFlags); } void DisplayListBoundsCalculator::drawPoints(SkCanvas::PointMode mode, uint32_t count, @@ -381,20 +383,20 @@ void DisplayListBoundsCalculator::drawPoints(SkCanvas::PointMode mode, SkRect point_bounds = ptBounds.bounds(); switch (mode) { case SkCanvas::kPoints_PointMode: - AccumulateRect(point_bounds, DisplayListAttributeFlags::kDrawPointsAsPointsFlags); + AccumulateRect(point_bounds, kDrawPointsAsPointsFlags); break; case SkCanvas::kLines_PointMode: - AccumulateRect(point_bounds, DisplayListAttributeFlags::kDrawPointsAsLinesFlags); + AccumulateRect(point_bounds, kDrawPointsAsLinesFlags); break; case SkCanvas::kPolygon_PointMode: - AccumulateRect(point_bounds, DisplayListAttributeFlags::kDrawPointsAsPolygonFlags); + AccumulateRect(point_bounds, kDrawPointsAsPolygonFlags); break; } } } void DisplayListBoundsCalculator::drawVertices(const sk_sp vertices, SkBlendMode mode) { - AccumulateRect(vertices->bounds(), DisplayListAttributeFlags::kDrawVerticesFlags); + AccumulateRect(vertices->bounds(), kDrawVerticesFlags); } void DisplayListBoundsCalculator::drawImage(const sk_sp image, const SkPoint point, @@ -402,9 +404,9 @@ void DisplayListBoundsCalculator::drawImage(const sk_sp image, bool render_with_attributes) { SkRect bounds = SkRect::MakeXYWH(point.fX, point.fY, // image->width(), image->height()); - DisplayListAttributeFlags flags = render_with_attributes - ? DisplayListAttributeFlags::kDrawImageWithPaintFlags - : DisplayListAttributeFlags::kDrawImageFlags; + DisplayListAttributeFlags flags = render_with_attributes // + ? kDrawImageWithPaintFlags + : kDrawImageFlags; AccumulateRect(bounds, flags); } void DisplayListBoundsCalculator::drawImageRect( @@ -415,8 +417,8 @@ void DisplayListBoundsCalculator::drawImageRect( bool render_with_attributes, SkCanvas::SrcRectConstraint constraint) { DisplayListAttributeFlags flags = render_with_attributes - ? DisplayListAttributeFlags::kDrawImageRectWithPaintFlags - : DisplayListAttributeFlags::kDrawImageRectFlags; + ? kDrawImageRectWithPaintFlags + : kDrawImageRectFlags; AccumulateRect(dst, flags); } void DisplayListBoundsCalculator::drawImageNine(const sk_sp image, @@ -425,8 +427,8 @@ void DisplayListBoundsCalculator::drawImageNine(const sk_sp image, SkFilterMode filter, bool render_with_attributes) { DisplayListAttributeFlags flags = render_with_attributes - ? DisplayListAttributeFlags::kDrawImageNineWithPaintFlags - : DisplayListAttributeFlags::kDrawImageNineFlags; + ? kDrawImageNineWithPaintFlags + : kDrawImageNineFlags; AccumulateRect(dst, flags); } void DisplayListBoundsCalculator::drawImageLattice( @@ -436,8 +438,8 @@ void DisplayListBoundsCalculator::drawImageLattice( SkFilterMode filter, bool render_with_attributes) { DisplayListAttributeFlags flags = render_with_attributes - ? DisplayListAttributeFlags::kDrawImageLatticeWithPaintFlags - : DisplayListAttributeFlags::kDrawImageLatticeFlags; + ? kDrawImageLatticeWithPaintFlags + : kDrawImageLatticeFlags; AccumulateRect(dst, flags); } void DisplayListBoundsCalculator::drawAtlas(const sk_sp atlas, @@ -459,9 +461,9 @@ void DisplayListBoundsCalculator::drawAtlas(const sk_sp atlas, } } if (atlasBounds.is_not_empty()) { - DisplayListAttributeFlags flags = render_with_attributes - ? DisplayListAttributeFlags::kDrawAtlasWithPaintFlags - : DisplayListAttributeFlags::kDrawAtlasFlags; + DisplayListAttributeFlags flags = render_with_attributes // + ? kDrawAtlasWithPaintFlags + : kDrawAtlasFlags; AccumulateRect(atlasBounds.bounds(), flags); } } @@ -475,19 +477,19 @@ void DisplayListBoundsCalculator::drawPicture(const sk_sp picture, if (pic_matrix) { pic_matrix->mapRect(&bounds); } - DisplayListAttributeFlags flags = render_with_attributes - ? DisplayListAttributeFlags::kDrawPictureWithPaintFlags - : DisplayListAttributeFlags::kDrawPictureFlags; + DisplayListAttributeFlags flags = render_with_attributes // + ? kDrawPictureWithPaintFlags + : kDrawPictureFlags; AccumulateRect(bounds, flags); } void DisplayListBoundsCalculator::drawDisplayList( const sk_sp display_list) { - AccumulateRect(display_list->bounds(), DisplayListAttributeFlags::kDrawDisplayListFlags); + AccumulateRect(display_list->bounds(), kDrawDisplayListFlags); } void DisplayListBoundsCalculator::drawTextBlob(const sk_sp blob, SkScalar x, SkScalar y) { - AccumulateRect(blob->bounds().makeOffset(x, y), DisplayListAttributeFlags::kDrawTextBlobFlags); + AccumulateRect(blob->bounds().makeOffset(x, y), kDrawTextBlobFlags); } void DisplayListBoundsCalculator::drawShadow(const SkPath& path, const SkColor color, @@ -496,7 +498,7 @@ void DisplayListBoundsCalculator::drawShadow(const SkPath& path, SkScalar dpr) { SkRect shadow_bounds = PhysicalShapeLayer::ComputeShadowBounds(path, elevation, dpr, matrix()); - AccumulateRect(shadow_bounds, DisplayListAttributeFlags::kDrawShadowFlags); + AccumulateRect(shadow_bounds, kDrawShadowFlags); } bool DisplayListBoundsCalculator::ComputeFilteredBounds(SkRect& bounds, @@ -510,15 +512,17 @@ bool DisplayListBoundsCalculator::ComputeFilteredBounds(SkRect& bounds, return true; } -bool DisplayListBoundsCalculator::AdjustBoundsForPaint(SkRect& bounds, - DisplayListAttributeFlags flags) { +bool DisplayListBoundsCalculator::AdjustBoundsForPaint( + SkRect& bounds, + DisplayListAttributeFlags flags) { if (flags.ignores_paint()) { return true; } if (flags.is_geometric()) { // Path effect occurs before stroking... - DisplayListSpecialGeometryFlags special_flags = flags.WithPathEffect(path_effect_); + DisplayListSpecialGeometryFlags special_flags = + flags.WithPathEffect(path_effect_); if (path_effect_) { SkPaint p; p.setPathEffect(path_effect_); @@ -570,7 +574,9 @@ void DisplayListBoundsCalculator::AccumulateUnbounded() { layer_infos_.back()->set_unbounded(); } } -void DisplayListBoundsCalculator::AccumulateRect(SkRect& rect, DisplayListAttributeFlags flags) { +void DisplayListBoundsCalculator::AccumulateRect( + SkRect& rect, + DisplayListAttributeFlags flags) { if (AdjustBoundsForPaint(rect, flags)) { matrix().mapRect(&rect); if (!has_clip() || rect.intersect(clip_bounds())) { diff --git a/flow/display_list_utils.h b/flow/display_list_utils.h index dd2fcea8eb85c..779c48fd56583 100644 --- a/flow/display_list_utils.h +++ b/flow/display_list_utils.h @@ -267,7 +267,8 @@ class DisplayListBoundsCalculator final : public virtual Dispatcher, public virtual IgnoreAttributeDispatchHelper, public virtual SkMatrixDispatchHelper, - public virtual ClipBoundsDispatchHelper { + public virtual ClipBoundsDispatchHelper, + DisplayListOpFlags { public: // Construct a Calculator to determine the bounds of a list of // DisplayList dispatcher method calls. Since 2 of the method calls diff --git a/lib/ui/painting/canvas.cc b/lib/ui/painting/canvas.cc index bc6865740ae2c..cb6273e66bdb5 100644 --- a/lib/ui/painting/canvas.cc +++ b/lib/ui/painting/canvas.cc @@ -106,8 +106,7 @@ void Canvas::saveLayerWithoutBounds(const Paint& paint, FML_DCHECK(paint.isNotNull()); if (display_list_recorder_) { bool restore_with_paint = - paint.sync_to(builder(), - DisplayListAttributeFlags::kSaveLayerWithPaintFlags); + paint.sync_to(builder(), kSaveLayerWithPaintFlags); FML_DCHECK(restore_with_paint); TRACE_EVENT0("flutter", "ui.Canvas::saveLayer (Recorded)"); builder()->saveLayer(nullptr, restore_with_paint); @@ -128,8 +127,7 @@ void Canvas::saveLayer(double left, SkRect bounds = SkRect::MakeLTRB(left, top, right, bottom); if (display_list_recorder_) { bool restore_with_paint = - paint.sync_to(builder(), - DisplayListAttributeFlags::kSaveLayerWithPaintFlags); + paint.sync_to(builder(), kSaveLayerWithPaintFlags); FML_DCHECK(restore_with_paint); TRACE_EVENT0("flutter", "ui.Canvas::saveLayer (Recorded)"); builder()->saveLayer(&bounds, restore_with_paint); @@ -177,7 +175,7 @@ void Canvas::scale(double sx, double sy) { void Canvas::rotate(double radians) { if (display_list_recorder_) { builder()->rotate(radians * 180.0 / M_PI); - } if (canvas_) { + } else if (canvas_) { canvas_->rotate(radians * 180.0 / M_PI); } } @@ -192,11 +190,13 @@ void Canvas::skew(double sx, double sy) { void Canvas::transform(const tonic::Float64List& matrix4) { if (display_list_recorder_) { + // clang-format off builder()->transformFullPerspective( matrix4[ 0], matrix4[ 1], matrix4[ 2], matrix4[ 3], matrix4[ 4], matrix4[ 5], matrix4[ 6], matrix4[ 7], matrix4[ 8], matrix4[ 9], matrix4[10], matrix4[11], matrix4[12], matrix4[13], matrix4[14], matrix4[15]); + // clang-format on } else if (canvas_) { canvas_->concat(SkM44(matrix4[0], matrix4[4], matrix4[8], matrix4[12], matrix4[1], matrix4[5], matrix4[9], matrix4[13], @@ -257,7 +257,7 @@ void Canvas::drawLine(double x1, const PaintData& paint_data) { FML_DCHECK(paint.isNotNull()); if (display_list_recorder_) { - paint.sync_to(builder(), DisplayListAttributeFlags::kDrawLineFlags); + paint.sync_to(builder(), kDrawLineFlags); builder()->drawLine(SkPoint::Make(x1, y1), SkPoint::Make(x2, y2)); } else if (canvas_) { SkPaint sk_paint; @@ -268,7 +268,7 @@ void Canvas::drawLine(double x1, void Canvas::drawPaint(const Paint& paint, const PaintData& paint_data) { FML_DCHECK(paint.isNotNull()); if (display_list_recorder_) { - paint.sync_to(builder(), DisplayListAttributeFlags::kDrawPaintFlags); + paint.sync_to(builder(), kDrawPaintFlags); sk_sp filter = builder()->getImageFilter(); if (filter && !filter->asColorFilter(nullptr)) { // drawPaint does an implicit saveLayer if an SkImageFilter is @@ -297,11 +297,12 @@ void Canvas::drawRect(double left, const PaintData& paint_data) { FML_DCHECK(paint.isNotNull()); if (display_list_recorder_) { - paint.sync_to(builder(), DisplayListAttributeFlags::kDrawRectFlags); + paint.sync_to(builder(), kDrawRectFlags); builder()->drawRect(SkRect::MakeLTRB(left, top, right, bottom)); } else if (canvas_) { SkPaint sk_paint; - canvas_->drawRect(SkRect::MakeLTRB(left, top, right, bottom), *paint.paint(sk_paint)); + canvas_->drawRect(SkRect::MakeLTRB(left, top, right, bottom), + *paint.paint(sk_paint)); } } @@ -310,7 +311,7 @@ void Canvas::drawRRect(const RRect& rrect, const PaintData& paint_data) { FML_DCHECK(paint.isNotNull()); if (display_list_recorder_) { - paint.sync_to(builder(), DisplayListAttributeFlags::kDrawRRectFlags); + paint.sync_to(builder(), kDrawRRectFlags); builder()->drawRRect(rrect.sk_rrect); } else if (canvas_) { SkPaint sk_paint; @@ -324,7 +325,7 @@ void Canvas::drawDRRect(const RRect& outer, const PaintData& paint_data) { FML_DCHECK(paint.isNotNull()); if (display_list_recorder_) { - paint.sync_to(builder(), DisplayListAttributeFlags::kDrawDRRectFlags); + paint.sync_to(builder(), kDrawDRRectFlags); builder()->drawDRRect(outer.sk_rrect, inner.sk_rrect); } else if (canvas_) { SkPaint sk_paint; @@ -340,11 +341,12 @@ void Canvas::drawOval(double left, const PaintData& paint_data) { FML_DCHECK(paint.isNotNull()); if (display_list_recorder_) { - paint.sync_to(builder(), DisplayListAttributeFlags::kDrawOvalFlags); + paint.sync_to(builder(), kDrawOvalFlags); builder()->drawOval(SkRect::MakeLTRB(left, top, right, bottom)); } else if (canvas_) { SkPaint sk_paint; - canvas_->drawOval(SkRect::MakeLTRB(left, top, right, bottom), *paint.paint(sk_paint)); + canvas_->drawOval(SkRect::MakeLTRB(left, top, right, bottom), + *paint.paint(sk_paint)); } } @@ -355,7 +357,7 @@ void Canvas::drawCircle(double x, const PaintData& paint_data) { FML_DCHECK(paint.isNotNull()); if (display_list_recorder_) { - paint.sync_to(builder(), DisplayListAttributeFlags::kDrawCircleFlags); + paint.sync_to(builder(), kDrawCircleFlags); builder()->drawCircle(SkPoint::Make(x, y), radius); } else if (canvas_) { SkPaint sk_paint; @@ -374,15 +376,18 @@ void Canvas::drawArc(double left, const PaintData& paint_data) { FML_DCHECK(paint.isNotNull()); if (display_list_recorder_) { - paint.sync_to(builder(), DisplayListAttributeFlags::kDrawArcFlags); + paint.sync_to(builder(), + useCenter // + ? kDrawArcWithCenterFlags + : kDrawArcNoCenterFlags); builder()->drawArc(SkRect::MakeLTRB(left, top, right, bottom), startAngle * 180.0 / M_PI, sweepAngle * 180.0 / M_PI, useCenter); } else if (canvas_) { SkPaint sk_paint; canvas_->drawArc(SkRect::MakeLTRB(left, top, right, bottom), - startAngle * 180.0 / M_PI, sweepAngle * 180.0 / M_PI, - useCenter, *paint.paint(sk_paint)); + startAngle * 180.0 / M_PI, sweepAngle * 180.0 / M_PI, + useCenter, *paint.paint(sk_paint)); } } @@ -396,7 +401,7 @@ void Canvas::drawPath(const CanvasPath* path, return; } if (display_list_recorder_) { - paint.sync_to(builder(), DisplayListAttributeFlags::kDrawPathFlags); + paint.sync_to(builder(), kDrawPathFlags); builder()->drawPath(path->path()); } else if (canvas_) { SkPaint sk_paint; @@ -418,9 +423,9 @@ void Canvas::drawImage(const CanvasImage* image, } auto sampling = ImageFilter::SamplingFromIndex(filterQualityIndex); if (display_list_recorder_) { - bool with_attributes = - paint.sync_to(builder(), DisplayListAttributeFlags::kDrawImageWithPaintFlags); - builder()->drawImage(image->image(), SkPoint::Make(x, y), sampling, with_attributes); + bool with_attributes = paint.sync_to(builder(), kDrawImageWithPaintFlags); + builder()->drawImage(image->image(), SkPoint::Make(x, y), sampling, + with_attributes); } else if (canvas_) { SkPaint sk_paint; canvas_->drawImage(image->image(), x, y, sampling, paint.paint(sk_paint)); @@ -450,13 +455,15 @@ void Canvas::drawImageRect(const CanvasImage* image, auto sampling = ImageFilter::SamplingFromIndex(filterQualityIndex); if (display_list_recorder_) { bool with_attributes = - paint.sync_to(builder(), DisplayListAttributeFlags::kDrawImageRectWithPaintFlags); - builder()->drawImageRect(image->image(), src, dst, sampling, with_attributes, + paint.sync_to(builder(), kDrawImageRectWithPaintFlags); + builder()->drawImageRect(image->image(), src, dst, sampling, + with_attributes, SkCanvas::kFast_SrcRectConstraint); } else if (canvas_) { SkPaint sk_paint; - canvas_->drawImageRect(image->image(), src, dst, sampling, paint.paint(sk_paint), - SkCanvas::kFast_SrcRectConstraint); + canvas_->drawImageRect(image->image(), src, dst, sampling, + paint.paint(sk_paint), + SkCanvas::kFast_SrcRectConstraint); } } @@ -486,8 +493,9 @@ void Canvas::drawImageNine(const CanvasImage* image, auto filter = ImageFilter::FilterModeFromIndex(bitmapSamplingIndex); if (display_list_recorder_) { bool with_attributes = - paint.sync_to(builder(), DisplayListAttributeFlags::kDrawImageNineWithPaintFlags); - builder()->drawImageNine(image->image(), icenter, dst, filter, with_attributes); + paint.sync_to(builder(), kDrawImageNineWithPaintFlags); + builder()->drawImageNine(image->image(), icenter, dst, filter, + with_attributes); } else if (canvas_) { SkPaint sk_paint; canvas_->drawImageNine(image->image().get(), icenter, dst, filter, @@ -529,22 +537,22 @@ void Canvas::drawPoints(const Paint& paint, if (display_list_recorder_) { switch (point_mode) { case SkCanvas::kPoints_PointMode: - paint.sync_to(builder(), DisplayListAttributeFlags::kDrawPointsAsPointsFlags); + paint.sync_to(builder(), kDrawPointsAsPointsFlags); break; case SkCanvas::kLines_PointMode: - paint.sync_to(builder(), DisplayListAttributeFlags::kDrawPointsAsLinesFlags); + paint.sync_to(builder(), kDrawPointsAsLinesFlags); break; case SkCanvas::kPolygon_PointMode: - paint.sync_to(builder(), DisplayListAttributeFlags::kDrawPointsAsPolygonFlags); + paint.sync_to(builder(), kDrawPointsAsPolygonFlags); break; } builder()->drawPoints(point_mode, - points.num_elements() / 2, // SkPoints have two floats. + points.num_elements() / 2, // SkPoints have 2 floats reinterpret_cast(points.data())); } else if (canvas_) { SkPaint sk_paint; canvas_->drawPoints(point_mode, - points.num_elements() / 2, // SkPoints have two floats. + points.num_elements() / 2, // SkPoints have 2 floats reinterpret_cast(points.data()), *paint.paint(sk_paint)); } @@ -561,11 +569,12 @@ void Canvas::drawVertices(const Vertices* vertices, } FML_DCHECK(paint.isNotNull()); if (display_list_recorder_) { - paint.sync_to(builder(), DisplayListAttributeFlags::kDrawVerticesFlags); + paint.sync_to(builder(), kDrawVerticesFlags); builder()->drawVertices(vertices->vertices(), blend_mode); } else if (canvas_) { SkPaint sk_paint; - canvas_->drawVertices(vertices->vertices(), blend_mode, *paint.paint(sk_paint)); + canvas_->drawVertices(vertices->vertices(), blend_mode, + *paint.paint(sk_paint)); } } @@ -596,8 +605,7 @@ void Canvas::drawAtlas(const Paint& paint, FML_DCHECK(paint.isNotNull()); if (display_list_recorder_) { - bool with_attributes = - paint.sync_to(builder(), DisplayListAttributeFlags::kDrawAtlasWithPaintFlags); + bool with_attributes = paint.sync_to(builder(), kDrawAtlasWithPaintFlags); builder()->drawAtlas( skImage, reinterpret_cast(transforms.data()), reinterpret_cast(rects.data()), diff --git a/lib/ui/painting/canvas.h b/lib/ui/painting/canvas.h index f9314ba4ab72e..8e8fd107f9377 100644 --- a/lib/ui/painting/canvas.h +++ b/lib/ui/painting/canvas.h @@ -23,7 +23,7 @@ class DartLibraryNatives; namespace flutter { class CanvasImage; -class Canvas : public RefCountedDartWrappable { +class Canvas : public RefCountedDartWrappable, DisplayListOpFlags { DEFINE_WRAPPERTYPEINFO(); FML_FRIEND_MAKE_REF_COUNTED(Canvas); From 0c3a403d82fbafbc241619f209163c9642403a61 Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Mon, 1 Nov 2021 22:57:31 -0700 Subject: [PATCH 3/7] fix perspective transform recording --- flow/display_list_unittests.cc | 49 ++++++++++++++++++++++++++++++++++ lib/ui/painting/canvas.cc | 10 ++++--- 2 files changed, 55 insertions(+), 4 deletions(-) diff --git a/flow/display_list_unittests.cc b/flow/display_list_unittests.cc index 56b1eeec51b8f..e789c7e8990a5 100644 --- a/flow/display_list_unittests.cc +++ b/flow/display_list_unittests.cc @@ -1323,5 +1323,54 @@ TEST(DisplayList, DisplayListPathEffectRefHandling) { ASSERT_TRUE(tester.ref_is_unique()); } +TEST(DisplayList, DisplayListFullPerspectiveTransformHandling) { + // SkM44 constructor takes row-major order + SkM44 sk_matrix = SkM44( + // clang-format off + 1, 2, 3, 4, + 5, 6, 7, 8, + 9, 10, 11, 12, + 13, 14, 15, 16 + // clang-format on + ); + + { // First test == + DisplayListBuilder builder; + // builder.transformFullPerspective takes row-major order + builder.transformFullPerspective( + // clang-format off + 1, 2, 3, 4, + 5, 6, 7, 8, + 9, 10, 11, 12, + 13, 14, 15, 16 + // clang-format on + ); + sk_sp display_list = builder.Build(); + sk_sp surface = SkSurface::MakeRasterN32Premul(10, 10); + SkCanvas* canvas = surface->getCanvas(); + display_list->RenderTo(canvas); + SkM44 dl_matrix = canvas->getLocalToDevice(); + ASSERT_EQ(sk_matrix, dl_matrix); + } + { // Next test != + DisplayListBuilder builder; + // builder.transformFullPerspective takes row-major order + builder.transformFullPerspective( + // clang-format off + 1, 5, 9, 13, + 2, 6, 7, 11, + 3, 7, 11, 15, + 4, 8, 12, 16 + // clang-format on + ); + sk_sp display_list = builder.Build(); + sk_sp surface = SkSurface::MakeRasterN32Premul(10, 10); + SkCanvas* canvas = surface->getCanvas(); + display_list->RenderTo(canvas); + SkM44 dl_matrix = canvas->getLocalToDevice(); + ASSERT_NE(sk_matrix, dl_matrix); + } +} + } // namespace testing } // namespace flutter diff --git a/lib/ui/painting/canvas.cc b/lib/ui/painting/canvas.cc index cb6273e66bdb5..1e5a0f1c51901 100644 --- a/lib/ui/painting/canvas.cc +++ b/lib/ui/painting/canvas.cc @@ -189,13 +189,15 @@ void Canvas::skew(double sx, double sy) { } void Canvas::transform(const tonic::Float64List& matrix4) { + // The Float array stored by Dart Matrix4 is in column-major order + // Both DisplayList and SkM44 constructor take row-major matrix order if (display_list_recorder_) { // clang-format off builder()->transformFullPerspective( - matrix4[ 0], matrix4[ 1], matrix4[ 2], matrix4[ 3], - matrix4[ 4], matrix4[ 5], matrix4[ 6], matrix4[ 7], - matrix4[ 8], matrix4[ 9], matrix4[10], matrix4[11], - matrix4[12], matrix4[13], matrix4[14], matrix4[15]); + matrix4[ 0], matrix4[ 4], matrix4[ 8], matrix4[12], + matrix4[ 1], matrix4[ 5], matrix4[ 9], matrix4[13], + matrix4[ 2], matrix4[ 6], matrix4[10], matrix4[14], + matrix4[ 3], matrix4[ 7], matrix4[11], matrix4[15]); // clang-format on } else if (canvas_) { canvas_->concat(SkM44(matrix4[0], matrix4[4], matrix4[8], matrix4[12], From aaae4f087eb49b52dfa42c6e36a094425005a630 Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Tue, 2 Nov 2021 00:03:26 -0700 Subject: [PATCH 4/7] remove some dead code --- flow/display_list.h | 6 ----- flow/display_list_canvas_unittests.cc | 38 +++++++++------------------ 2 files changed, 12 insertions(+), 32 deletions(-) diff --git a/flow/display_list.h b/flow/display_list.h index afa73bea17bab..03252791cb168 100644 --- a/flow/display_list.h +++ b/flow/display_list.h @@ -674,12 +674,6 @@ class DisplayListAttributeFlags : DisplayListFlagsBase { bool is_flood() const { return has_any(kFloodsSurface_); } - // bool may_stroke_geometry() { - // return has_any(kIsDrawnGeometry_ | kIsStrokedGeometry_); - // } - // /// Only valid after calling |WithStyle| with the current |Style| - // bool does_stroke_geometry() { return has_any(kIsStrokedGeometry_); } - private: DisplayListAttributeFlags(int flags) : DisplayListFlagsBase(flags), diff --git a/flow/display_list_canvas_unittests.cc b/flow/display_list_canvas_unittests.cc index 613180b7c4e59..3bc178e6b5e71 100644 --- a/flow/display_list_canvas_unittests.cc +++ b/flow/display_list_canvas_unittests.cc @@ -323,8 +323,6 @@ class RenderEnvironment { const SkPixmap* ref_pixmap_ = nullptr; }; -class CaseParameters; - class TestParameters { public: TestParameters(const CvRenderer& cv_renderer, @@ -398,7 +396,7 @@ class TestParameters { SkPathEffect::kDash_DashType) { return false; } - if (!ignores_dashes_) { + if (!ignores_dashes()) { return false; } } @@ -447,15 +445,15 @@ class TestParameters { const BoundsTolerance adjust(const BoundsTolerance& tolerance, const SkPaint& paint, const SkMatrix& matrix) const { - if (is_draw_text_blob_ && tolerance.discrete_offset() > 0) { + if (is_draw_text_blob() && tolerance.discrete_offset() > 0) { // drawTextBlob needs just a little more leeway when using a // discrete path effect. return tolerance.addBoundsPadding(2, 2); } - if (is_draw_line_) { + if (is_draw_line()) { return lineAdjust(tolerance, paint, matrix); } - if (is_draw_arc_center_) { + if (is_draw_arc_center()) { if (paint.getStyle() != SkPaint::kFill_Style && paint.getStrokeJoin() == SkPaint::kMiter_Join) { // the miter join at the center of an arc does not really affect @@ -467,11 +465,6 @@ class TestParameters { return tolerance.addBoundsPadding(miter_pad, miter_pad); } } - // Shadow primitives could use just a little more horizontal bounds - // tolerance when drawn with a perspective transform. - // if (is_draw_shadows_ && matrix.hasPerspective()) { - // return tolerance.mulScale(1.04, 1.0); - // } return tolerance; } @@ -502,11 +495,11 @@ class TestParameters { } SkScalar hTolerance; SkScalar vTolerance; - if (is_horizontal_line_) { - FML_DCHECK(!is_vertical_line_); + if (is_horizontal_line()) { + FML_DCHECK(!is_vertical_line()); hTolerance = adjust; vTolerance = 0; - } else if (is_vertical_line_) { + } else if (is_vertical_line()) { hTolerance = 0; vTolerance = adjust; } else { @@ -551,8 +544,10 @@ class TestParameters { bool is_draw_text_blob() const { return is_draw_text_blob_; } bool is_draw_display_list() const { return is_draw_display_list_; } bool is_draw_line() const { return is_draw_line_; } + bool is_draw_arc_center() const { return is_draw_arc_center_; } bool is_horizontal_line() const { return is_horizontal_line_; } bool is_vertical_line() const { return is_vertical_line_; } + bool ignores_dashes() const { return ignores_dashes_; } TestParameters& set_draw_shadows() { is_draw_shadows_ = true; @@ -594,10 +589,6 @@ class TestParameters { is_vertical_line_ = true; return *this; } - TestParameters& set_max_miter_limit(SkScalar limit) { - max_miter_limit_ = limit; - return *this; - } private: const CvRenderer& cv_renderer_; @@ -614,8 +605,6 @@ class TestParameters { bool ignores_dashes_ = false; bool is_horizontal_line_ = false; bool is_vertical_line_ = false; - - SkScalar max_miter_limit_ = 1E9; }; class CaseParameters { @@ -729,15 +718,13 @@ class CanvasCompareTester { SkColor alpha_layer_color = SkColorSetARGB(0x7f, 0x00, 0xff, 0xff); SkColor default_color = SkPaint().getColor(); CvRenderer cv_restore = [=](SkCanvas* cv, const SkPaint& p) { - // Draw more than one primitive to disable peephole optimizations + // Draw another primitive to disable peephole optimizations cv->drawRect(RenderBounds.makeOffset(500, 500), p); - // params.cv_renderer()(cv, p); cv->restore(); }; DlRenderer dl_restore = [=](DisplayListBuilder& b) { - // Draw more than one primitive to disable peephole optimizations + // Draw another primitive to disable peephole optimizations b.drawRect(RenderBounds.makeOffset(500, 500)); - // params.dl_renderer()(b); b.restore(); }; SkRect layer_bounds = RenderBounds.makeInset(15, 15); @@ -2116,8 +2103,7 @@ TEST_F(DisplayListCanvas, DrawRect) { [=](DisplayListBuilder& builder) { // builder.drawRect(RenderBounds.makeOffset(0.5, 0.5)); }, - kDrawRectFlags) - .set_max_miter_limit(1.4)); + kDrawRectFlags)); } TEST_F(DisplayListCanvas, DrawOval) { From 5f8bb46bbdb4c7c9be25d1d30800f96bf8f0e408 Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Tue, 2 Nov 2021 02:32:45 -0700 Subject: [PATCH 5/7] minor streamlining of paragraph SkPaint construction --- lib/ui/text/paragraph_builder.cc | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/ui/text/paragraph_builder.cc b/lib/ui/text/paragraph_builder.cc index 793af6601c699..956856e4d1c2b 100644 --- a/lib/ui/text/paragraph_builder.cc +++ b/lib/ui/text/paragraph_builder.cc @@ -453,18 +453,16 @@ void ParagraphBuilder::pushStyle(tonic::Int32List& encoded, if (mask & tsBackgroundMask) { Paint background(background_objects, background_data); if (background.isNotNull()) { - SkPaint sk_paint; style.has_background = true; - style.background = *background.paint(sk_paint); + FML_CHECK(background.paint(style.background)); } } if (mask & tsForegroundMask) { Paint foreground(foreground_objects, foreground_data); if (foreground.isNotNull()) { - SkPaint sk_paint; style.has_foreground = true; - style.foreground = *foreground.paint(sk_paint); + FML_CHECK(foreground.paint(style.foreground)); } } From e58014da60b4ee0802a1d6829806cae8c0e7a8f7 Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Tue, 2 Nov 2021 14:36:32 -0700 Subject: [PATCH 6/7] revert 'minor streamlining of paragraph SkPaint construction' --- lib/ui/text/paragraph_builder.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/ui/text/paragraph_builder.cc b/lib/ui/text/paragraph_builder.cc index 956856e4d1c2b..793af6601c699 100644 --- a/lib/ui/text/paragraph_builder.cc +++ b/lib/ui/text/paragraph_builder.cc @@ -453,16 +453,18 @@ void ParagraphBuilder::pushStyle(tonic::Int32List& encoded, if (mask & tsBackgroundMask) { Paint background(background_objects, background_data); if (background.isNotNull()) { + SkPaint sk_paint; style.has_background = true; - FML_CHECK(background.paint(style.background)); + style.background = *background.paint(sk_paint); } } if (mask & tsForegroundMask) { Paint foreground(foreground_objects, foreground_data); if (foreground.isNotNull()) { + SkPaint sk_paint; style.has_foreground = true; - FML_CHECK(foreground.paint(style.foreground)); + style.foreground = *foreground.paint(sk_paint); } } From d998f8c38fe6c938479d1e42fb07cebf451c9500 Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Wed, 10 Nov 2021 11:52:34 -0800 Subject: [PATCH 7/7] review feedback --- flow/display_list.cc | 1 + flow/display_list.h | 11 ++++++----- flow/display_list_utils.cc | 1 - lib/ui/painting/canvas.cc | 9 +++++++++ 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/flow/display_list.cc b/flow/display_list.cc index 28fdde88a4a29..ac3af1ca4f7c0 100644 --- a/flow/display_list.cc +++ b/flow/display_list.cc @@ -1122,6 +1122,7 @@ void DisplayListBuilder::onSetBlender(sk_sp blender) { if (p.asBlendMode()) { setBlendMode(p.asBlendMode().value()); } else { + // |current_blender_| supersedes any value of |current_blend_mode_| (current_blender_ = blender) // ? Push(0, 0, std::move(blender)) : Push(0, 0); diff --git a/flow/display_list.h b/flow/display_list.h index 03252791cb168..a3a6b5c70a91a 100644 --- a/flow/display_list.h +++ b/flow/display_list.h @@ -480,22 +480,22 @@ class Dispatcher { class DisplayListFlags { protected: // A drawing operation that is not geometric in nature (but which - // may still apply a MaskFilter - see |kApplyMaskFilter| below). + // may still apply a MaskFilter - see |kUsesMaskFilter_| below). static constexpr int kIsNonGeometric_ = 0; // A geometric operation that is defined as a fill operation // regardless of what the current paint Style is set to. - // This flag will automatically assume |kApplyMaskFilter|. + // This flag will automatically assume |kUsesMaskFilter_|. static constexpr int kIsFilledGeometry_ = 1 << 0; // A geometric operation that is defined as a stroke operation // regardless of what the current paint Style is set to. - // This flag will automatically assume |kApplyMaskFilter|. + // This flag will automatically assume |kUsesMaskFilter_|. static constexpr int kIsStrokedGeometry_ = 1 << 1; // A geometric operation that may be a stroke or fill operation // depending on the current state of the paint Style attribute. - // This flag will automatically assume |kApplyMaskFilter|. + // This flag will automatically assume |kUsesMaskFilter_|. static constexpr int kIsDrawnGeometry_ = 1 << 2; static constexpr int kIsAnyGeometryMask_ = // @@ -1010,12 +1010,13 @@ class DisplayListBuilder final : public virtual Dispatcher, public SkRefCnt { bool current_dither_ = false; bool current_invert_colors_ = false; SkColor current_color_ = 0xFF000000; - SkBlendMode current_blend_mode_ = SkBlendMode::kSrcOver; SkPaint::Style current_style_ = SkPaint::Style::kFill_Style; SkScalar current_stroke_width_ = 0.0; SkScalar current_stroke_miter_ = 4.0; SkPaint::Cap current_stroke_cap_ = SkPaint::Cap::kButt_Cap; SkPaint::Join current_stroke_join_ = SkPaint::Join::kMiter_Join; + // If |current_blender_| is set then |current_blend_mode_| should be ignored + SkBlendMode current_blend_mode_ = SkBlendMode::kSrcOver; sk_sp current_blender_; sk_sp current_shader_; sk_sp current_color_filter_; diff --git a/flow/display_list_utils.cc b/flow/display_list_utils.cc index d323f37d09e4d..6cf86f16707ee 100644 --- a/flow/display_list_utils.cc +++ b/flow/display_list_utils.cc @@ -292,7 +292,6 @@ void DisplayListBoundsCalculator::saveLayer(const SkRect* bounds, if (bounds) { clipRect(*bounds, SkClipOp::kIntersect, false); } - // ClipBoundsDispatchHelper::reset(bounds); } void DisplayListBoundsCalculator::restore() { if (layer_infos_.size() > 1) { diff --git a/lib/ui/painting/canvas.cc b/lib/ui/painting/canvas.cc index 1e5a0f1c51901..6e31a060caaba 100644 --- a/lib/ui/painting/canvas.cc +++ b/lib/ui/painting/canvas.cc @@ -82,6 +82,15 @@ fml::RefPtr Canvas::Create(PictureRecorder* recorder, ToDart("Canvas constructor called with non-genuine PictureRecorder.")); return nullptr; } + + // This call will implicitly initialize the |canvas_| field with an SkCanvas + // whether or not we are using display_list. Now that all of the code here + // in canvas.cc will direct calls to the DisplayListBuilder we could almost + // stop initializing that field for the display list case. Unfortunately, + // the text code in paragraph.cc still needs to present its output to an + // SkCanvas* which means without significant work to the internals of the + // paragraph code, we are going to continue to need the canvas adapter and + // field and getter. fml::RefPtr canvas = fml::MakeRefCounted( recorder->BeginRecording(SkRect::MakeLTRB(left, top, right, bottom))); recorder->set_canvas(canvas);