Flutter Engine
display_list_canvas_unittests.cc
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "flutter/display_list/display_list.h"
6 #include "flutter/display_list/display_list_canvas_dispatcher.h"
7 #include "flutter/display_list/display_list_canvas_recorder.h"
8 #include "flutter/display_list/display_list_flags.h"
9 #include "flutter/fml/math.h"
10 #include "flutter/testing/testing.h"
11 #include "third_party/skia/include/core/SkPictureRecorder.h"
12 #include "third_party/skia/include/core/SkSurface.h"
13 #include "third_party/skia/include/effects/SkBlenders.h"
14 #include "third_party/skia/include/effects/SkDashPathEffect.h"
15 #include "third_party/skia/include/effects/SkDiscretePathEffect.h"
16 #include "third_party/skia/include/effects/SkGradientShader.h"
17 #include "third_party/skia/include/effects/SkImageFilters.h"
18 
19 namespace flutter {
20 namespace testing {
21 
22 constexpr int TestWidth = 200;
23 constexpr int TestHeight = 200;
24 constexpr int RenderWidth = 100;
25 constexpr int RenderHeight = 100;
26 constexpr int RenderHalfWidth = 50;
27 constexpr int RenderHalfHeight = 50;
28 constexpr int RenderLeft = (TestWidth - RenderWidth) / 2;
29 constexpr int RenderTop = (TestHeight - RenderHeight) / 2;
30 constexpr int RenderRight = RenderLeft + RenderWidth;
31 constexpr int RenderBottom = RenderTop + RenderHeight;
32 constexpr int RenderCenterX = (RenderLeft + RenderRight) / 2;
33 constexpr int RenderCenterY = (RenderTop + RenderBottom) / 2;
34 constexpr SkScalar RenderRadius = std::min(RenderWidth, RenderHeight) / 2.0;
35 constexpr SkScalar RenderCornerRadius = RenderRadius / 5.0;
36 
37 constexpr SkPoint TestCenter = SkPoint::Make(TestWidth / 2, TestHeight / 2);
38 constexpr SkRect TestBounds = SkRect::MakeWH(TestWidth, TestHeight);
39 constexpr SkRect RenderBounds =
40  SkRect::MakeLTRB(RenderLeft, RenderTop, RenderRight, RenderBottom);
41 
42 // The tests try 3 miter limit values, 0.0, 4.0 (the default), and 10.0
43 // These values will allow us to construct a diamond that spans the
44 // width or height of the render box and still show the miter for 4.0
45 // and 10.0.
46 // These values were discovered by drawing a diamond path in Skia fiddle
47 // and then playing with the cross-axis size until the miter was about
48 // as large as it could get before it got cut off.
49 
50 // The X offsets which will be used for tall vertical diamonds are
51 // expressed in terms of the rendering height to obtain the proper angle
52 constexpr SkScalar MiterExtremeDiamondOffsetX = RenderHeight * 0.04;
53 constexpr SkScalar Miter10DiamondOffsetX = RenderHeight * 0.051;
54 constexpr SkScalar Miter4DiamondOffsetX = RenderHeight * 0.14;
55 
56 // The Y offsets which will be used for long horizontal diamonds are
57 // expressed in terms of the rendering width to obtain the proper angle
58 constexpr SkScalar MiterExtremeDiamondOffsetY = RenderWidth * 0.04;
59 constexpr SkScalar Miter10DiamondOffsetY = RenderWidth * 0.051;
60 constexpr SkScalar Miter4DiamondOffsetY = RenderWidth * 0.14;
61 
62 // Render 3 vertical and horizontal diamonds each
63 // designed to break at the tested miter limits
64 // 0.0, 4.0 and 10.0
65 // Center is biased by 0.5 to include more pixel centers in the
66 // thin miters
67 constexpr SkScalar x_off_0 = RenderCenterX + 0.5;
68 constexpr SkScalar x_off_l1 = x_off_0 - Miter4DiamondOffsetX;
69 constexpr SkScalar x_off_l2 = x_off_l1 - Miter10DiamondOffsetX;
70 constexpr SkScalar x_off_l3 = x_off_l2 - Miter10DiamondOffsetX;
71 constexpr SkScalar x_off_r1 = x_off_0 + Miter4DiamondOffsetX;
72 constexpr SkScalar x_off_r2 = x_off_r1 + MiterExtremeDiamondOffsetX;
73 constexpr SkScalar x_off_r3 = x_off_r2 + MiterExtremeDiamondOffsetX;
74 constexpr SkPoint VerticalMiterDiamondPoints[] = {
75  // Vertical diamonds:
76  // M10 M4 Mextreme
77  // /\ /|\ /\ top of RenderBounds
78  // / \ / | \ / \ to
79  // <----X--+--X----> RenderCenter
80  // \ / \ | / \ / to
81  // \/ \|/ \/ bottom of RenderBounds
82  // clang-format off
83  SkPoint::Make(x_off_l3, RenderCenterY),
84  SkPoint::Make(x_off_l2, RenderTop),
85  SkPoint::Make(x_off_l1, RenderCenterY),
86  SkPoint::Make(x_off_0, RenderTop),
87  SkPoint::Make(x_off_r1, RenderCenterY),
88  SkPoint::Make(x_off_r2, RenderTop),
89  SkPoint::Make(x_off_r3, RenderCenterY),
90  SkPoint::Make(x_off_r2, RenderBottom),
91  SkPoint::Make(x_off_r1, RenderCenterY),
92  SkPoint::Make(x_off_0, RenderBottom),
93  SkPoint::Make(x_off_l1, RenderCenterY),
94  SkPoint::Make(x_off_l2, RenderBottom),
95  SkPoint::Make(x_off_l3, RenderCenterY),
96  // clang-format on
97 };
99  sizeof(VerticalMiterDiamondPoints) / sizeof(VerticalMiterDiamondPoints[0]);
100 
101 constexpr SkScalar y_off_0 = RenderCenterY + 0.5;
102 constexpr SkScalar y_off_u1 = x_off_0 - Miter4DiamondOffsetY;
103 constexpr SkScalar y_off_u2 = y_off_u1 - Miter10DiamondOffsetY;
104 constexpr SkScalar y_off_u3 = y_off_u2 - Miter10DiamondOffsetY;
105 constexpr SkScalar y_off_d1 = x_off_0 + Miter4DiamondOffsetY;
106 constexpr SkScalar y_off_d2 = y_off_d1 + MiterExtremeDiamondOffsetY;
107 constexpr SkScalar y_off_d3 = y_off_d2 + MiterExtremeDiamondOffsetY;
108 const SkPoint HorizontalMiterDiamondPoints[] = {
109  // Horizontal diamonds
110  // Same configuration as Vertical diamonds above but
111  // rotated 90 degrees
112  // clang-format off
113  SkPoint::Make(RenderCenterX, y_off_u3),
114  SkPoint::Make(RenderLeft, y_off_u2),
115  SkPoint::Make(RenderCenterX, y_off_u1),
116  SkPoint::Make(RenderLeft, y_off_0),
117  SkPoint::Make(RenderCenterX, y_off_d1),
118  SkPoint::Make(RenderLeft, y_off_d2),
119  SkPoint::Make(RenderCenterX, y_off_d3),
120  SkPoint::Make(RenderRight, y_off_d2),
121  SkPoint::Make(RenderCenterX, y_off_d1),
122  SkPoint::Make(RenderRight, y_off_0),
123  SkPoint::Make(RenderCenterX, y_off_u1),
124  SkPoint::Make(RenderRight, y_off_u2),
125  SkPoint::Make(RenderCenterX, y_off_u3),
126  // clang-format on
127 };
130  sizeof(HorizontalMiterDiamondPoints[0]));
131 
132 // A class to specify how much tolerance to allow in bounds estimates.
133 // For some attributes, the machinery must make some conservative
134 // assumptions as to the extent of the bounds, but some of our test
135 // parameters do not produce bounds that expand by the full conservative
136 // estimates. This class provides a number of tweaks to apply to the
137 // pixel bounds to account for the conservative factors.
138 //
139 // An instance is passed along through the methods and if any test adds
140 // a paint attribute or other modifier that will cause a more conservative
141 // estimate for bounds, it can modify the factors here to account for it.
142 // Ideally, all tests will be executed with geometry that will trigger
143 // the conservative cases anyway and all attributes will be combined with
144 // other attributes that make their output more predictable, but in those
145 // cases where a given test sequence cannot really provide attributes to
146 // demonstrate the worst case scenario, they can modify these factors to
147 // avoid false bounds overflow notifications.
149  public:
150  BoundsTolerance() = default;
151  BoundsTolerance(const BoundsTolerance&) = default;
152 
153  BoundsTolerance addBoundsPadding(SkScalar bounds_pad_x,
154  SkScalar bounds_pad_y) const {
155  BoundsTolerance copy = BoundsTolerance(*this);
156  copy.bounds_pad_.offset(bounds_pad_x, bounds_pad_y);
157  return copy;
158  }
159 
160  BoundsTolerance mulScale(SkScalar scale_x, SkScalar scale_y) const {
161  BoundsTolerance copy = BoundsTolerance(*this);
162  copy.scale_.fX *= scale_x;
163  copy.scale_.fY *= scale_y;
164  return copy;
165  }
166 
167  BoundsTolerance addAbsolutePadding(SkScalar absolute_pad_x,
168  SkScalar absolute_pad_y) const {
169  BoundsTolerance copy = BoundsTolerance(*this);
170  copy.absolute_pad_.offset(absolute_pad_x, absolute_pad_y);
171  return copy;
172  }
173 
175  BoundsTolerance copy = BoundsTolerance(*this);
176  copy.discrete_offset_ += discrete_offset;
177  return copy;
178  }
179 
180  BoundsTolerance clip(SkRect clip) const {
181  BoundsTolerance copy = BoundsTolerance(*this);
182  if (!copy.clip_.intersect(clip)) {
183  copy.clip_.setEmpty();
184  }
185  return copy;
186  }
187 
188  static SkRect Scale(const SkRect& rect, const SkPoint& scales) {
189  SkScalar outset_x = rect.width() * (scales.fX - 1);
190  SkScalar outset_y = rect.height() * (scales.fY - 1);
191  return rect.makeOutset(outset_x, outset_y);
192  }
193 
194  bool overflows(SkIRect pix_bounds,
195  int worst_bounds_pad_x,
196  int worst_bounds_pad_y) const {
197  SkRect allowed = SkRect::Make(pix_bounds);
198  allowed.outset(bounds_pad_.fX, bounds_pad_.fY);
199  allowed = Scale(allowed, scale_);
200  allowed.outset(absolute_pad_.fX, absolute_pad_.fY);
201  if (!allowed.intersect(clip_)) {
202  allowed.setEmpty();
203  }
204  SkIRect rounded = allowed.roundOut();
205  int padLeft = std::max(0, pix_bounds.fLeft - rounded.fLeft);
206  int padTop = std::max(0, pix_bounds.fTop - rounded.fTop);
207  int padRight = std::max(0, pix_bounds.fRight - rounded.fRight);
208  int padBottom = std::max(0, pix_bounds.fBottom - rounded.fBottom);
209  int allowed_pad_x = std::max(padLeft, padRight);
210  int allowed_pad_y = std::max(padTop, padBottom);
211  if (worst_bounds_pad_x > allowed_pad_x ||
212  worst_bounds_pad_y > allowed_pad_y) {
213  FML_LOG(ERROR) << "allowed pad: " //
214  << allowed_pad_x << ", " << allowed_pad_y;
215  }
216  return (worst_bounds_pad_x > allowed_pad_x ||
217  worst_bounds_pad_y > allowed_pad_y);
218  }
219 
220  SkScalar discrete_offset() const { return discrete_offset_; }
221 
222  private:
223  SkPoint bounds_pad_ = {0, 0};
224  SkPoint scale_ = {1, 1};
225  SkPoint absolute_pad_ = {0, 0};
226  SkRect clip_ = {-1E9, -1E9, 1E9, 1E9};
227 
228  SkScalar discrete_offset_ = 0;
229 };
230 
231 typedef const std::function<void(SkCanvas*, SkPaint&)> CvSetup;
232 typedef const std::function<void(SkCanvas*, const SkPaint&)> CvRenderer;
233 typedef const std::function<void(DisplayListBuilder&)> DlRenderer;
234 static void EmptyCvRenderer(SkCanvas*, const SkPaint&) {}
236 
238  public:
239  explicit RenderSurface(sk_sp<SkSurface> surface) : surface_(surface) {
240  EXPECT_EQ(canvas()->save(), 1);
241  }
242  ~RenderSurface() { sk_free(addr_); }
243 
244  SkCanvas* canvas() { return surface_->getCanvas(); }
245 
246  const SkPixmap* pixmap() {
247  if (!pixmap_.addr()) {
248  canvas()->restoreToCount(1);
249  SkImageInfo info = surface_->imageInfo();
250  if (info.colorType() != kN32_SkColorType ||
251  !surface_->peekPixels(&pixmap_)) {
252  info = SkImageInfo::MakeN32Premul(info.dimensions());
253  addr_ = malloc(info.computeMinByteSize() * info.height());
254  pixmap_.reset(info, addr_, info.minRowBytes());
255  EXPECT_TRUE(surface_->readPixels(pixmap_, 0, 0));
256  }
257  }
258  return &pixmap_;
259  }
260 
261  private:
262  sk_sp<SkSurface> surface_;
263  SkPixmap pixmap_;
264  void* addr_ = nullptr;
265 };
266 
268  public:
270  return RenderEnvironment(SkImageInfo::Make({1, 1}, kRGB_565_SkColorType,
271  kOpaque_SkAlphaType, nullptr));
272  }
273 
275  return RenderEnvironment(SkImageInfo::MakeN32Premul(1, 1));
276  }
277 
278  std::unique_ptr<RenderSurface> MakeSurface(
279  const SkColor bg = SK_ColorTRANSPARENT,
280  int width = TestWidth,
281  int height = TestHeight) const {
282  sk_sp<SkSurface> surface =
283  SkSurface::MakeRaster(info_.makeWH(width, height));
284  surface->getCanvas()->clear(bg);
285  return std::make_unique<RenderSurface>(surface);
286  }
287 
288  void init_ref(CvRenderer& cv_renderer, SkColor bg = SK_ColorTRANSPARENT) {
289  init_ref([=](SkCanvas*, SkPaint&) {}, cv_renderer, bg);
290  }
291 
292  void init_ref(CvSetup& cv_setup,
293  CvRenderer& cv_renderer,
294  SkColor bg = SK_ColorTRANSPARENT) {
295  ref_canvas()->clear(bg);
296  cv_setup(ref_canvas(), ref_paint_);
297  ref_matrix_ = ref_canvas()->getTotalMatrix();
298  ref_clip_ = ref_canvas()->getDeviceClipBounds();
299  cv_renderer(ref_canvas(), ref_paint_);
300  ref_pixmap_ = ref_surface_->pixmap();
301  }
302 
303  const SkImageInfo& info() const { return info_; }
304  SkCanvas* ref_canvas() { return ref_surface_->canvas(); }
305  const SkPaint& ref_paint() const { return ref_paint_; }
306  const SkMatrix& ref_matrix() const { return ref_matrix_; }
307  const SkIRect& ref_clip_bounds() const { return ref_clip_; }
308  const SkPixmap* ref_pixmap() const { return ref_pixmap_; }
309 
310  private:
311  explicit RenderEnvironment(const SkImageInfo& info) : info_(info) {
312  ref_surface_ = MakeSurface();
313  }
314 
315  const SkImageInfo info_;
316 
317  SkPaint ref_paint_;
318  SkMatrix ref_matrix_;
319  SkIRect ref_clip_;
320  std::unique_ptr<RenderSurface> ref_surface_;
321  const SkPixmap* ref_pixmap_ = nullptr;
322 };
323 
325  public:
326  TestParameters(const CvRenderer& cv_renderer,
327  const DlRenderer& dl_renderer,
329  : cv_renderer_(cv_renderer), dl_renderer_(dl_renderer), flags_(flags) {}
330 
331  bool uses_paint() const { return !flags_.ignores_paint(); }
332 
334  const SkPaint& paint,
335  const SkMatrix& matrix,
336  const SkIRect& device_clip,
337  bool has_diff_clip,
338  bool has_mutating_save_layer) const {
339  if (has_mutating_save_layer) {
340  return false;
341  }
342  if (env.ref_clip_bounds() != device_clip || has_diff_clip) {
343  return false;
344  }
345  if (env.ref_matrix() != matrix && !flags_.is_flood()) {
346  return false;
347  }
348  if (flags_.ignores_paint()) {
349  return true;
350  }
351  const SkPaint& ref_paint = env.ref_paint();
352  if (flags_.applies_anti_alias() && //
353  ref_paint.isAntiAlias() != paint.isAntiAlias()) {
354  return false;
355  }
356  if (flags_.applies_dither() && //
357  ref_paint.isDither() != paint.isDither()) {
358  return false;
359  }
360  if (flags_.applies_color() && //
361  ref_paint.getColor() != paint.getColor()) {
362  return false;
363  }
364  if (flags_.applies_alpha() && //
365  ref_paint.getAlpha() != paint.getAlpha()) {
366  return false;
367  }
368  if (flags_.applies_blend() && //
369  ref_paint.getBlender() != paint.getBlender()) {
370  return false;
371  }
372  if (flags_.applies_color_filter() && //
373  ref_paint.getColorFilter() != paint.getColorFilter()) {
374  return false;
375  }
376  if (flags_.applies_mask_filter() && //
377  ref_paint.getMaskFilter() != paint.getMaskFilter()) {
378  return false;
379  }
380  if (flags_.applies_image_filter() && //
381  ref_paint.getImageFilter() != paint.getImageFilter()) {
382  return false;
383  }
384  if (flags_.applies_shader() && //
385  ref_paint.getShader() != paint.getShader()) {
386  return false;
387  }
389  flags_.WithPathEffect(paint.refPathEffect());
390  if (flags_.applies_path_effect() && //
391  ref_paint.getPathEffect() != paint.getPathEffect()) {
392  SkPathEffect::DashInfo info;
393  if (paint.getPathEffect()->asADash(&info) !=
394  SkPathEffect::kDash_DashType) {
395  return false;
396  }
397  if (!ignores_dashes()) {
398  return false;
399  }
400  }
401  bool is_stroked = flags_.is_stroked(ref_paint.getStyle());
402  if (flags_.is_stroked(paint.getStyle()) != is_stroked) {
403  return false;
404  }
405  if (!is_stroked) {
406  return true;
407  }
408  if (ref_paint.getStrokeWidth() != paint.getStrokeWidth()) {
409  return false;
410  }
411  if (geo_flags.may_have_end_caps() && //
412  getCap(ref_paint, geo_flags) != getCap(paint, geo_flags)) {
413  return false;
414  }
415  if (geo_flags.may_have_joins()) {
416  if (ref_paint.getStrokeJoin() != paint.getStrokeJoin()) {
417  return false;
418  }
419  if (ref_paint.getStrokeJoin() == SkPaint::kMiter_Join) {
420  SkScalar ref_miter = ref_paint.getStrokeMiter();
421  SkScalar test_miter = paint.getStrokeMiter();
422  // miter limit < 1.4 affects right angles
423  if (geo_flags.may_have_acute_joins() || //
424  ref_miter < 1.4 || test_miter < 1.4) {
425  if (ref_miter != test_miter) {
426  return false;
427  }
428  }
429  }
430  }
431  return true;
432  }
433 
434  SkPaint::Cap getCap(const SkPaint& paint,
435  DisplayListSpecialGeometryFlags geo_flags) const {
436  SkPaint::Cap cap = paint.getStrokeCap();
437  if (geo_flags.butt_cap_becomes_square() && cap == SkPaint::kButt_Cap) {
438  return SkPaint::kSquare_Cap;
439  }
440  return cap;
441  }
442 
443  const BoundsTolerance adjust(const BoundsTolerance& tolerance,
444  const SkPaint& paint,
445  const SkMatrix& matrix) const {
446  if (is_draw_text_blob() && tolerance.discrete_offset() > 0) {
447  // drawTextBlob needs just a little more leeway when using a
448  // discrete path effect.
449  return tolerance.addBoundsPadding(2, 2);
450  }
451  if (is_draw_line()) {
452  return lineAdjust(tolerance, paint, matrix);
453  }
454  if (is_draw_arc_center()) {
455  if (paint.getStyle() != SkPaint::kFill_Style &&
456  paint.getStrokeJoin() == SkPaint::kMiter_Join) {
457  // the miter join at the center of an arc does not really affect
458  // its bounds in any of our test cases, but the bounds code needs
459  // to take it into account for the cases where it might, so we
460  // relax our tolerance to reflect the miter bounds padding.
461  SkScalar miter_pad =
462  paint.getStrokeMiter() * paint.getStrokeWidth() * 0.5f;
463  return tolerance.addBoundsPadding(miter_pad, miter_pad);
464  }
465  }
466  return tolerance;
467  }
468 
470  const SkPaint& paint,
471  const SkMatrix& matrix) const {
472  SkScalar adjust = 0.0;
473  SkScalar half_width = paint.getStrokeWidth() * 0.5f;
474  if (tolerance.discrete_offset() > 0) {
475  // When a discrete path effect is added, the bounds calculations must
476  // allow for miters in any direction, but a horizontal line will not
477  // have miters in the horizontal direction, similarly for vertical
478  // lines, and diagonal lines will have miters off at a "45 degree"
479  // angle that don't expand the bounds much at all.
480  // Also, the discrete offset will not move any points parallel with
481  // the line, so provide tolerance for both miters and offset.
482  adjust =
483  half_width * paint.getStrokeMiter() + tolerance.discrete_offset();
484  }
486  flags_.WithPathEffect(paint.refPathEffect());
487  if (paint.getStrokeCap() == SkPaint::kButt_Cap &&
488  !geo_flags.butt_cap_becomes_square()) {
489  adjust = std::max(adjust, half_width);
490  }
491  if (adjust == 0) {
492  return tolerance;
493  }
494  SkScalar hTolerance;
495  SkScalar vTolerance;
496  if (is_horizontal_line()) {
497  FML_DCHECK(!is_vertical_line());
498  hTolerance = adjust;
499  vTolerance = 0;
500  } else if (is_vertical_line()) {
501  hTolerance = 0;
502  vTolerance = adjust;
503  } else {
504  // The perpendicular miters just do not impact the bounds of
505  // diagonal lines at all as they are aimed in the wrong direction
506  // to matter. So allow tolerance in both axes.
507  hTolerance = vTolerance = adjust;
508  }
509  BoundsTolerance new_tolerance =
510  tolerance.addBoundsPadding(hTolerance, vTolerance);
511  return new_tolerance;
512  }
513 
514  const CvRenderer& cv_renderer() const { return cv_renderer_; }
515  void render_to(SkCanvas* canvas, SkPaint& paint) const {
516  cv_renderer_(canvas, paint);
517  }
518 
519  const DlRenderer& dl_renderer() const { return dl_renderer_; }
520  void render_to(DisplayListBuilder& builder) const { //
521  dl_renderer_(builder);
522  }
523 
524  // If a test is using any shadow operations then we cannot currently
525  // record those in an SkCanvas and play it back into a DisplayList
526  // because internally the operation gets encapsulated in a Skia
527  // ShadowRec which is not exposed by their headers. For operations
528  // that use shadows, we can perform a lot of tests, but not the tests
529  // that require SkCanvas->DisplayList transfers.
530  // See: https://bugs.chromium.org/p/skia/issues/detail?id=12125
531  bool is_draw_shadows() const { return is_draw_shadows_; }
532  // The CPU renders nothing for drawVertices with a Blender.
533  // See: https://bugs.chromium.org/p/skia/issues/detail?id=12200
534  bool is_draw_vertices() const { return is_draw_vertices_; }
535  // The CPU renders nothing for drawAtlas with a Blender.
536  // See: https://bugs.chromium.org/p/skia/issues/detail?id=12199
537  bool is_draw_atlas() const { return is_draw_atlas_; }
538  // Tests that call drawTextBlob with an sk_ref paint attribute will cause
539  // those attributes to be stored in an internal Skia cache so we need
540  // to expect that the |sk_ref.unique()| call will fail in those cases.
541  // See: (TBD(flar) - file Skia bug)
542  bool is_draw_text_blob() const { return is_draw_text_blob_; }
543  bool is_draw_display_list() const { return is_draw_display_list_; }
544  bool is_draw_line() const { return is_draw_line_; }
545  bool is_draw_arc_center() const { return is_draw_arc_center_; }
546  bool is_horizontal_line() const { return is_horizontal_line_; }
547  bool is_vertical_line() const { return is_vertical_line_; }
548  bool ignores_dashes() const { return ignores_dashes_; }
549 
551  is_draw_shadows_ = true;
552  return *this;
553  }
555  is_draw_vertices_ = true;
556  return *this;
557  }
559  is_draw_text_blob_ = true;
560  return *this;
561  }
563  is_draw_atlas_ = true;
564  return *this;
565  }
567  is_draw_display_list_ = true;
568  return *this;
569  }
571  is_draw_line_ = true;
572  return *this;
573  }
575  is_draw_arc_center_ = true;
576  return *this;
577  }
579  ignores_dashes_ = true;
580  return *this;
581  }
583  is_horizontal_line_ = true;
584  return *this;
585  }
587  is_vertical_line_ = true;
588  return *this;
589  }
590 
591  private:
592  const CvRenderer& cv_renderer_;
593  const DlRenderer& dl_renderer_;
594  const DisplayListAttributeFlags& flags_;
595 
596  bool is_draw_shadows_ = false;
597  bool is_draw_vertices_ = false;
598  bool is_draw_text_blob_ = false;
599  bool is_draw_atlas_ = false;
600  bool is_draw_display_list_ = false;
601  bool is_draw_line_ = false;
602  bool is_draw_arc_center_ = false;
603  bool ignores_dashes_ = false;
604  bool is_horizontal_line_ = false;
605  bool is_vertical_line_ = false;
606 };
607 
609  public:
610  explicit CaseParameters(std::string info)
612 
613  CaseParameters(std::string info, CvSetup cv_setup, DlRenderer dl_setup)
614  : CaseParameters(info,
615  cv_setup,
616  dl_setup,
619  SK_ColorTRANSPARENT,
620  false,
621  false) {}
622 
623  CaseParameters(std::string info,
624  CvSetup cv_setup,
625  DlRenderer dl_setup,
626  CvRenderer cv_restore,
627  DlRenderer dl_restore,
628  SkColor bg,
629  bool has_diff_clip,
630  bool has_mutating_save_layer)
631  : info_(info),
632  bg_(bg),
633  cv_setup_(cv_setup),
634  dl_setup_(dl_setup),
635  cv_restore_(cv_restore),
636  dl_restore_(dl_restore),
637  has_diff_clip_(has_diff_clip),
638  has_mutating_save_layer_(has_mutating_save_layer) {}
639 
640  CaseParameters with_restore(CvRenderer cv_restore,
641  DlRenderer dl_restore,
642  bool mutating_layer) {
643  return CaseParameters(info_, cv_setup_, dl_setup_, cv_restore, dl_restore,
644  bg_, has_diff_clip_, mutating_layer);
645  }
646 
647  CaseParameters with_bg(SkColor bg) {
648  return CaseParameters(info_, cv_setup_, dl_setup_, cv_restore_, dl_restore_,
649  bg, has_diff_clip_, has_mutating_save_layer_);
650  }
651 
653  return CaseParameters(info_, cv_setup_, dl_setup_, cv_restore_, dl_restore_,
654  bg_, true, has_mutating_save_layer_);
655  }
656 
657  std::string info() const { return info_; }
658  SkColor bg() const { return bg_; }
659  bool has_diff_clip() const { return has_diff_clip_; }
660  bool has_mutating_save_layer() const { return has_mutating_save_layer_; }
661 
662  CvSetup cv_setup() const { return cv_setup_; }
663  DlRenderer dl_setup() const { return dl_setup_; }
664  CvRenderer cv_restore() const { return cv_restore_; }
665  DlRenderer dl_restore() const { return dl_restore_; }
666 
667  const SkPaint render_to(SkCanvas* canvas, //
668  const TestParameters& testP) const {
669  SkPaint paint;
670  cv_setup_(canvas, paint);
671  testP.render_to(canvas, paint);
672  cv_restore_(canvas, paint);
673  return paint;
674  }
675 
677  const TestParameters& testP) const {
678  dl_setup_(builder);
679  testP.render_to(builder);
680  dl_restore_(builder);
681  }
682 
683  private:
684  const std::string info_;
685  const SkColor bg_;
686  const CvSetup cv_setup_;
687  const DlRenderer dl_setup_;
688  const CvRenderer cv_restore_;
689  const DlRenderer dl_restore_;
690  const bool has_diff_clip_;
691  const bool has_mutating_save_layer_;
692 };
693 
695  public:
697 
698  static void RenderAll(const TestParameters& params,
699  const BoundsTolerance& tolerance = DefaultTolerance) {
701  env.init_ref(params.cv_renderer());
702  RenderWithTransforms(params, env, tolerance);
703  RenderWithClips(params, env, tolerance);
704  RenderWithSaveRestore(params, env, tolerance);
705  // Only test attributes if the canvas version uses the paint object
706  if (params.uses_paint()) {
707  RenderWithAttributes(params, env, tolerance);
708  }
709  }
710 
711  static void RenderWithSaveRestore(const TestParameters& testP,
712  const RenderEnvironment& env,
713  const BoundsTolerance& tolerance) {
714  SkRect clip = SkRect::MakeXYWH(RenderCenterX - 1, RenderCenterY - 1, 2, 2);
715  SkRect rect = SkRect::MakeXYWH(RenderCenterX, RenderCenterY, 10, 10);
716  SkColor alpha_layer_color = SkColorSetARGB(0x7f, 0x00, 0xff, 0xff);
717  SkColor default_color = SkPaint().getColor();
718  CvRenderer cv_restore = [=](SkCanvas* cv, const SkPaint& p) {
719  // Draw another primitive to disable peephole optimizations
720  cv->drawRect(RenderBounds.makeOffset(500, 500), p);
721  cv->restore();
722  };
723  DlRenderer dl_restore = [=](DisplayListBuilder& b) {
724  // Draw another primitive to disable peephole optimizations
725  b.drawRect(RenderBounds.makeOffset(500, 500));
726  b.restore();
727  };
728  SkRect layer_bounds = RenderBounds.makeInset(15, 15);
729  RenderWith(testP, env, tolerance,
731  "With prior save/clip/restore",
732  [=](SkCanvas* cv, SkPaint& p) {
733  cv->save();
734  cv->clipRect(clip, SkClipOp::kIntersect, false);
735  SkPaint p2;
736  cv->drawRect(rect, p2);
737  p2.setBlendMode(SkBlendMode::kClear);
738  cv->drawRect(rect, p2);
739  cv->restore();
740  },
741  [=](DisplayListBuilder& b) {
742  b.save();
743  b.clipRect(clip, SkClipOp::kIntersect, false);
744  b.drawRect(rect);
745  b.setBlendMode(SkBlendMode::kClear);
746  b.drawRect(rect);
747  b.setBlendMode(SkBlendMode::kSrcOver);
748  b.restore();
749  }));
750  RenderWith(testP, env, tolerance,
752  "saveLayer no paint, no bounds",
753  [=](SkCanvas* cv, SkPaint& p) { //
754  cv->saveLayer(nullptr, nullptr);
755  },
756  [=](DisplayListBuilder& b) { //
757  b.saveLayer(nullptr, false);
758  })
759  .with_restore(cv_restore, dl_restore, false));
760  RenderWith(testP, env, tolerance,
762  "saveLayer no paint, with bounds",
763  [=](SkCanvas* cv, SkPaint& p) { //
764  cv->saveLayer(layer_bounds, nullptr);
765  },
766  [=](DisplayListBuilder& b) { //
767  b.saveLayer(&layer_bounds, false);
768  })
769  .with_restore(cv_restore, dl_restore, true));
770  RenderWith(testP, env, tolerance,
772  "saveLayer with alpha, no bounds",
773  [=](SkCanvas* cv, SkPaint& p) {
774  SkPaint save_p;
775  save_p.setColor(alpha_layer_color);
776  cv->saveLayer(nullptr, &save_p);
777  },
778  [=](DisplayListBuilder& b) {
779  b.setColor(alpha_layer_color);
780  b.saveLayer(nullptr, true);
781  b.setColor(default_color);
782  })
783  .with_restore(cv_restore, dl_restore, true));
784  RenderWith(testP, env, tolerance,
786  "saveLayer with alpha and bounds",
787  [=](SkCanvas* cv, SkPaint& p) {
788  SkPaint save_p;
789  save_p.setColor(alpha_layer_color);
790  cv->saveLayer(layer_bounds, &save_p);
791  },
792  [=](DisplayListBuilder& b) {
793  b.setColor(alpha_layer_color);
794  b.saveLayer(&layer_bounds, true);
795  b.setColor(default_color);
796  })
797  .with_restore(cv_restore, dl_restore, true));
798 
799  {
800  // clang-format off
801  constexpr float rotate_alpha_color_matrix[20] = {
802  0, 1, 0, 0 , 0,
803  0, 0, 1, 0 , 0,
804  1, 0, 0, 0 , 0,
805  0, 0, 0, 0.5, 0,
806  };
807  // clang-format on
808  sk_sp<SkColorFilter> filter =
809  SkColorFilters::Matrix(rotate_alpha_color_matrix);
810  {
811  RenderWith(testP, env, tolerance,
813  "saveLayer ColorFilter, no bounds",
814  [=](SkCanvas* cv, SkPaint& p) {
815  SkPaint save_p;
816  save_p.setColorFilter(filter);
817  cv->saveLayer(nullptr, &save_p);
818  p.setStrokeWidth(5.0);
819  },
820  [=](DisplayListBuilder& b) {
821  b.setColorFilter(filter);
822  b.saveLayer(nullptr, true);
823  b.setColorFilter(nullptr);
824  b.setStrokeWidth(5.0);
825  })
826  .with_restore(cv_restore, dl_restore, true));
827  }
828  EXPECT_TRUE(filter->unique())
829  << "saveLayer ColorFilter, no bounds Cleanup";
830  {
831  RenderWith(testP, env, tolerance,
833  "saveLayer ColorFilter and bounds",
834  [=](SkCanvas* cv, SkPaint& p) {
835  SkPaint save_p;
836  save_p.setColorFilter(filter);
837  cv->saveLayer(RenderBounds, &save_p);
838  p.setStrokeWidth(5.0);
839  },
840  [=](DisplayListBuilder& b) {
841  b.setColorFilter(filter);
842  b.saveLayer(&RenderBounds, true);
843  b.setColorFilter(nullptr);
844  b.setStrokeWidth(5.0);
845  })
846  .with_restore(cv_restore, dl_restore, true));
847  }
848  EXPECT_TRUE(filter->unique())
849  << "saveLayer ColorFilter and bounds Cleanup";
850  }
851  {
852  sk_sp<SkImageFilter> filter = SkImageFilters::Arithmetic(
853  0.1, 0.1, 0.1, 0.25, true, nullptr, nullptr);
854  {
855  RenderWith(testP, env, tolerance,
857  "saveLayer ImageFilter, no bounds",
858  [=](SkCanvas* cv, SkPaint& p) {
859  SkPaint save_p;
860  save_p.setImageFilter(filter);
861  cv->saveLayer(nullptr, &save_p);
862  p.setStrokeWidth(5.0);
863  },
864  [=](DisplayListBuilder& b) {
865  b.setImageFilter(filter);
866  b.saveLayer(nullptr, true);
867  b.setImageFilter(nullptr);
868  b.setStrokeWidth(5.0);
869  })
870  .with_restore(cv_restore, dl_restore, true));
871  }
872  EXPECT_TRUE(filter->unique())
873  << "saveLayer ImageFilter, no bounds Cleanup";
874  {
875  RenderWith(testP, env, tolerance,
877  "saveLayer ImageFilter and bounds",
878  [=](SkCanvas* cv, SkPaint& p) {
879  SkPaint save_p;
880  save_p.setImageFilter(filter);
881  cv->saveLayer(RenderBounds, &save_p);
882  p.setStrokeWidth(5.0);
883  },
884  [=](DisplayListBuilder& b) {
885  b.setImageFilter(filter);
886  b.saveLayer(&RenderBounds, true);
887  b.setImageFilter(nullptr);
888  b.setStrokeWidth(5.0);
889  })
890  .with_restore(cv_restore, dl_restore, true));
891  }
892  EXPECT_TRUE(filter->unique())
893  << "saveLayer ImageFilter and bounds Cleanup";
894  }
895  }
896 
897  static void RenderWithAttributes(const TestParameters& testP,
898  const RenderEnvironment& env,
899  const BoundsTolerance& tolerance) {
900  RenderWith(testP, env, tolerance, CaseParameters("Defaults Test"));
901 
902  {
903  // CPU renderer with default line width of 0 does not show antialiasing
904  // for stroked primitives, so we make a new reference with a non-trivial
905  // stroke width to demonstrate the differences
907  // Tweak the bounds tolerance for the displacement of 1/10 of a pixel
908  const BoundsTolerance aa_tolerance = tolerance.addBoundsPadding(1, 1);
909  CvSetup cv_aa_setup = [=](SkCanvas* cv, SkPaint& p) {
910  cv->translate(0.1, 0.1);
911  p.setStrokeWidth(5.0);
912  };
913  DlRenderer dl_aa_setup = [=](DisplayListBuilder& b) {
914  b.translate(0.1, 0.1);
915  b.setStrokeWidth(5.0);
916  };
917  aa_env.init_ref(cv_aa_setup, testP.cv_renderer());
918  RenderWith(testP, aa_env, aa_tolerance,
920  "AntiAlias == True",
921  [=](SkCanvas* cv, SkPaint& p) {
922  cv_aa_setup(cv, p);
923  p.setAntiAlias(true);
924  },
925  [=](DisplayListBuilder& b) {
926  dl_aa_setup(b);
927  b.setAntiAlias(true);
928  }));
929  RenderWith(testP, aa_env, aa_tolerance,
931  "AntiAlias == False",
932  [=](SkCanvas* cv, SkPaint& p) {
933  cv_aa_setup(cv, p);
934  p.setAntiAlias(false);
935  },
936  [=](DisplayListBuilder& b) {
937  dl_aa_setup(b);
938  b.setAntiAlias(false);
939  }));
940  }
941 
942  {
943  // The CPU renderer does not always dither for solid colors and we
944  // need to use a non-default color (default is black) on an opaque
945  // surface, so we use a shader instead of a color. Also, thin stroked
946  // primitives (mainly drawLine and drawPoints) do not show much
947  // dithering so we use a non-trivial stroke width as well.
949  SkColor dither_bg = SK_ColorBLACK;
950  CvSetup cv_dither_setup = [=](SkCanvas*, SkPaint& p) {
951  p.setShader(testImageShader);
952  p.setAlpha(0xf0);
953  p.setStrokeWidth(5.0);
954  };
955  DlRenderer dl_dither_setup = [=](DisplayListBuilder& b) {
956  b.setShader(testImageShader);
957  b.setColor(SkColor(0xf0000000));
958  b.setStrokeWidth(5.0);
959  };
960  dither_env.init_ref(cv_dither_setup, testP.cv_renderer(), dither_bg);
961  RenderWith(testP, dither_env, tolerance,
963  "Dither == True",
964  [=](SkCanvas* cv, SkPaint& p) {
965  cv_dither_setup(cv, p);
966  p.setDither(true);
967  },
968  [=](DisplayListBuilder& b) {
969  dl_dither_setup(b);
970  b.setDither(true);
971  })
972  .with_bg(dither_bg));
973  RenderWith(testP, dither_env, tolerance,
975  "Dither = False",
976  [=](SkCanvas* cv, SkPaint& p) {
977  cv_dither_setup(cv, p);
978  p.setDither(false);
979  },
980  [=](DisplayListBuilder& b) {
981  dl_dither_setup(b);
982  b.setDither(false);
983  })
984  .with_bg(dither_bg));
985  }
986  EXPECT_TRUE(testImageShader->unique()) << "Dither Cleanup";
987 
988  RenderWith(testP, env, tolerance,
990  "Color == Blue",
991  [=](SkCanvas*, SkPaint& p) { p.setColor(SK_ColorBLUE); },
992  [=](DisplayListBuilder& b) { b.setColor(SK_ColorBLUE); }));
993  RenderWith(testP, env, tolerance,
995  "Color == Green",
996  [=](SkCanvas*, SkPaint& p) { p.setColor(SK_ColorGREEN); },
997  [=](DisplayListBuilder& b) { b.setColor(SK_ColorGREEN); }));
998 
999  RenderWithStrokes(testP, env, tolerance);
1000 
1001  {
1002  // half opaque cyan
1003  SkColor blendableColor = SkColorSetARGB(0x7f, 0x00, 0xff, 0xff);
1004  SkColor bg = SK_ColorWHITE;
1005 
1006  RenderWith(testP, env, tolerance,
1008  "Blend == SrcIn",
1009  [=](SkCanvas*, SkPaint& p) {
1010  p.setBlendMode(SkBlendMode::kSrcIn);
1011  p.setColor(blendableColor);
1012  },
1013  [=](DisplayListBuilder& b) {
1014  b.setBlendMode(SkBlendMode::kSrcIn);
1015  b.setColor(blendableColor);
1016  })
1017  .with_bg(bg));
1018  RenderWith(testP, env, tolerance,
1020  "Blend == DstIn",
1021  [=](SkCanvas*, SkPaint& p) {
1022  p.setBlendMode(SkBlendMode::kDstIn);
1023  p.setColor(blendableColor);
1024  },
1025  [=](DisplayListBuilder& b) {
1026  b.setBlendMode(SkBlendMode::kDstIn);
1027  b.setColor(blendableColor);
1028  })
1029  .with_bg(bg));
1030  }
1031 
1032  if (!(testP.is_draw_atlas() || testP.is_draw_vertices())) {
1033  sk_sp<SkBlender> blender =
1034  SkBlenders::Arithmetic(0.25, 0.25, 0.25, 0.25, false);
1035  {
1036  RenderWith(testP, env, tolerance,
1038  "Blender == Arithmetic 0.25-false",
1039  [=](SkCanvas*, SkPaint& p) { p.setBlender(blender); },
1040  [=](DisplayListBuilder& b) { b.setBlender(blender); }));
1041  }
1042  EXPECT_TRUE(blender->unique()) << "Blender Cleanup";
1043  blender = SkBlenders::Arithmetic(0.25, 0.25, 0.25, 0.25, true);
1044  {
1045  RenderWith(testP, env, tolerance,
1047  "Blender == Arithmetic 0.25-true",
1048  [=](SkCanvas*, SkPaint& p) { p.setBlender(blender); },
1049  [=](DisplayListBuilder& b) { b.setBlender(blender); }));
1050  }
1051  EXPECT_TRUE(blender->unique()) << "Blender Cleanup";
1052  }
1053 
1054  {
1055  // Being able to see a blur requires some non-default attributes,
1056  // like a non-trivial stroke width and a shader rather than a color
1057  // (for drawPaint) so we create a new environment for these tests.
1059  CvSetup cv_blur_setup = [=](SkCanvas*, SkPaint& p) {
1060  p.setShader(testImageShader);
1061  p.setStrokeWidth(5.0);
1062  };
1063  DlRenderer dl_blur_setup = [=](DisplayListBuilder& b) {
1064  b.setShader(testImageShader);
1065  b.setStrokeWidth(5.0);
1066  };
1067  blur_env.init_ref(cv_blur_setup, testP.cv_renderer());
1068  sk_sp<SkImageFilter> filter =
1069  SkImageFilters::Blur(5.0, 5.0, SkTileMode::kDecal, nullptr, nullptr);
1070  BoundsTolerance blur5Tolerance = tolerance.addBoundsPadding(4, 4);
1071  {
1072  RenderWith(testP, blur_env, blur5Tolerance,
1074  "ImageFilter == Decal Blur 5",
1075  [=](SkCanvas* cv, SkPaint& p) {
1076  cv_blur_setup(cv, p);
1077  p.setImageFilter(filter);
1078  },
1079  [=](DisplayListBuilder& b) {
1080  dl_blur_setup(b);
1081  b.setImageFilter(filter);
1082  }));
1083  }
1084  EXPECT_TRUE(filter->unique()) << "ImageFilter Cleanup";
1085  filter =
1086  SkImageFilters::Blur(5.0, 5.0, SkTileMode::kClamp, nullptr, nullptr);
1087  {
1088  RenderWith(testP, blur_env, blur5Tolerance,
1090  "ImageFilter == Clamp Blur 5",
1091  [=](SkCanvas* cv, SkPaint& p) {
1092  cv_blur_setup(cv, p);
1093  p.setImageFilter(filter);
1094  },
1095  [=](DisplayListBuilder& b) {
1096  dl_blur_setup(b);
1097  b.setImageFilter(filter);
1098  }));
1099  }
1100  EXPECT_TRUE(filter->unique()) << "ImageFilter Cleanup";
1101  }
1102 
1103  {
1104  // clang-format off
1105  constexpr float rotate_color_matrix[20] = {
1106  0, 1, 0, 0, 0,
1107  0, 0, 1, 0, 0,
1108  1, 0, 0, 0, 0,
1109  0, 0, 0, 1, 0,
1110  };
1111  constexpr float invert_color_matrix[20] = {
1112  -1.0, 0, 0, 1.0, 0,
1113  0, -1.0, 0, 1.0, 0,
1114  0, 0, -1.0, 1.0, 0,
1115  1.0, 1.0, 1.0, 1.0, 0,
1116  };
1117  // clang-format on
1118  sk_sp<SkColorFilter> filter = SkColorFilters::Matrix(rotate_color_matrix);
1119  {
1120  SkColor bg = SK_ColorWHITE;
1121  RenderWith(testP, env, tolerance,
1123  "ColorFilter == RotateRGB",
1124  [=](SkCanvas*, SkPaint& p) {
1125  p.setColor(SK_ColorYELLOW);
1126  p.setColorFilter(filter);
1127  },
1128  [=](DisplayListBuilder& b) {
1129  b.setColor(SK_ColorYELLOW);
1130  b.setColorFilter(filter);
1131  })
1132  .with_bg(bg));
1133  }
1134  EXPECT_TRUE(filter->unique()) << "ColorFilter == RotateRGB Cleanup";
1135  filter = SkColorFilters::Matrix(invert_color_matrix);
1136  {
1137  SkColor bg = SK_ColorWHITE;
1138  RenderWith(testP, env, tolerance,
1140  "ColorFilter == Invert",
1141  [=](SkCanvas*, SkPaint& p) {
1142  p.setColor(SK_ColorYELLOW);
1143  p.setColorFilter(filter);
1144  },
1145  [=](DisplayListBuilder& b) {
1146  b.setColor(SK_ColorYELLOW);
1147  b.setInvertColors(true);
1148  })
1149  .with_bg(bg));
1150  }
1151  EXPECT_TRUE(filter->unique()) << "ColorFilter == Invert Cleanup";
1152  }
1153 
1154  {
1155  sk_sp<SkPathEffect> effect = SkDiscretePathEffect::Make(3, 5);
1156  {
1157  // Discrete path effects need a stroke width for drawPointsAsPoints
1158  // to do something realistic
1159  // And a Discrete(3, 5) effect produces miters that are near
1160  // maximal for a miter limit of 3.0.
1161  BoundsTolerance discrete_tolerance =
1162  tolerance
1163  // register the discrete offset so adjusters can compensate
1164  .addDiscreteOffset(5)
1165  // the miters in the 3-5 discrete effect don't always fill
1166  // their conservative bounds, so tolerate a couple of pixels
1167  .addBoundsPadding(2, 2);
1168  RenderWith(testP, env, discrete_tolerance,
1170  "PathEffect == Discrete-3-5",
1171  [=](SkCanvas*, SkPaint& p) {
1172  p.setStrokeWidth(5.0);
1173  p.setStrokeMiter(3.0);
1174  p.setPathEffect(effect);
1175  },
1176  [=](DisplayListBuilder& b) {
1177  b.setStrokeWidth(5.0);
1178  b.setStrokeMiter(3.0);
1179  b.setPathEffect(effect);
1180  }));
1181  }
1182  EXPECT_TRUE(testP.is_draw_text_blob() || effect->unique())
1183  << "PathEffect == Discrete-3-5 Cleanup";
1184  effect = SkDiscretePathEffect::Make(2, 3);
1185  {
1186  // Discrete path effects need a stroke width for drawPointsAsPoints
1187  // to do something realistic
1188  // And a Discrete(2, 3) effect produces miters that are near
1189  // maximal for a miter limit of 2.5.
1190  BoundsTolerance discrete_tolerance =
1191  tolerance
1192  // register the discrete offset so adjusters can compensate
1193  .addDiscreteOffset(3)
1194  // the miters in the 3-5 discrete effect don't always fill
1195  // their conservative bounds, so tolerate a couple of pixels
1196  .addBoundsPadding(2, 2);
1197  RenderWith(testP, env, discrete_tolerance,
1199  "PathEffect == Discrete-2-3",
1200  [=](SkCanvas*, SkPaint& p) {
1201  p.setStrokeWidth(5.0);
1202  p.setStrokeMiter(2.5);
1203  p.setPathEffect(effect);
1204  },
1205  [=](DisplayListBuilder& b) {
1206  b.setStrokeWidth(5.0);
1207  b.setStrokeMiter(2.5);
1208  b.setPathEffect(effect);
1209  }));
1210  }
1211  EXPECT_TRUE(testP.is_draw_text_blob() || effect->unique())
1212  << "PathEffect == Discrete-2-3 Cleanup";
1213  }
1214 
1215  {
1216  sk_sp<SkMaskFilter> filter =
1217  SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, 5.0);
1218  BoundsTolerance blur5Tolerance = tolerance.addBoundsPadding(4, 4);
1219  {
1220  // Stroked primitives need some non-trivial stroke size to be blurred
1221  RenderWith(testP, env, blur5Tolerance,
1223  "MaskFilter == Blur 5",
1224  [=](SkCanvas*, SkPaint& p) {
1225  p.setStrokeWidth(5.0);
1226  p.setMaskFilter(filter);
1227  },
1228  [=](DisplayListBuilder& b) {
1229  b.setStrokeWidth(5.0);
1230  b.setMaskFilter(filter);
1231  }));
1232  }
1233  EXPECT_TRUE(testP.is_draw_text_blob() || filter->unique())
1234  << "MaskFilter == Blur 5 Cleanup";
1235  {
1236  RenderWith(testP, env, blur5Tolerance,
1238  "MaskFilter == Blur(Normal, 5.0)",
1239  [=](SkCanvas*, SkPaint& p) {
1240  p.setStrokeWidth(5.0);
1241  p.setMaskFilter(filter);
1242  },
1243  [=](DisplayListBuilder& b) {
1244  b.setStrokeWidth(5.0);
1245  b.setMaskBlurFilter(kNormal_SkBlurStyle, 5.0);
1246  }));
1247  }
1248  EXPECT_TRUE(testP.is_draw_text_blob() || filter->unique())
1249  << "MaskFilter == Blur(Normal, 5.0) Cleanup";
1250  }
1251 
1252  {
1253  SkPoint end_points[] = {
1254  SkPoint::Make(RenderBounds.fLeft, RenderBounds.fTop),
1255  SkPoint::Make(RenderBounds.fRight, RenderBounds.fBottom),
1256  };
1257  SkColor colors[] = {
1258  SK_ColorGREEN,
1259  SkColorSetA(SK_ColorYELLOW, 0x7f),
1260  SK_ColorBLUE,
1261  };
1262  float stops[] = {
1263  0.0,
1264  0.5,
1265  1.0,
1266  };
1267  sk_sp<SkShader> shader = SkGradientShader::MakeLinear(
1268  end_points, colors, stops, 3, SkTileMode::kMirror, 0, nullptr);
1269  {
1270  RenderWith(testP, env, tolerance,
1272  "LinearGradient GYB",
1273  [=](SkCanvas*, SkPaint& p) { p.setShader(shader); },
1274  [=](DisplayListBuilder& b) { b.setShader(shader); }));
1275  }
1276  EXPECT_TRUE(shader->unique()) << "LinearGradient GYB Cleanup";
1277  }
1278  }
1279 
1280  static void RenderWithStrokes(const TestParameters& testP,
1281  const RenderEnvironment& env,
1282  const BoundsTolerance& tolerance_in) {
1283  // The test cases were generated with geometry that will try to fill
1284  // out the various miter limits used for testing, but they can be off
1285  // by a couple of pixels so we will relax bounds testing for strokes by
1286  // a couple of pixels.
1287  BoundsTolerance tolerance = tolerance_in.addBoundsPadding(2, 2);
1288  RenderWith(testP, env, tolerance,
1290  "Fill",
1291  [=](SkCanvas*, SkPaint& p) { //
1292  p.setStyle(SkPaint::kFill_Style);
1293  },
1294  [=](DisplayListBuilder& b) { //
1295  b.setStyle(SkPaint::kFill_Style);
1296  }));
1297  RenderWith(testP, env, tolerance,
1299  "Stroke + defaults",
1300  [=](SkCanvas*, SkPaint& p) { //
1301  p.setStyle(SkPaint::kStroke_Style);
1302  },
1303  [=](DisplayListBuilder& b) { //
1304  b.setStyle(SkPaint::kStroke_Style);
1305  }));
1306 
1307  RenderWith(testP, env, tolerance,
1309  "Fill + unnecessary StrokeWidth 10",
1310  [=](SkCanvas*, SkPaint& p) {
1311  p.setStyle(SkPaint::kFill_Style);
1312  p.setStrokeWidth(10.0);
1313  },
1314  [=](DisplayListBuilder& b) {
1315  b.setStyle(SkPaint::kFill_Style);
1316  b.setStrokeWidth(10.0);
1317  }));
1318 
1319  RenderEnvironment stroke_base_env = RenderEnvironment::MakeN32();
1320  CvSetup cv_stroke_setup = [=](SkCanvas*, SkPaint& p) {
1321  p.setStyle(SkPaint::kStroke_Style);
1322  p.setStrokeWidth(5.0);
1323  };
1324  stroke_base_env.init_ref(cv_stroke_setup, testP.cv_renderer());
1325 
1326  RenderWith(testP, stroke_base_env, tolerance,
1328  "Stroke Width 10",
1329  [=](SkCanvas*, SkPaint& p) {
1330  p.setStyle(SkPaint::kStroke_Style);
1331  p.setStrokeWidth(10.0);
1332  },
1333  [=](DisplayListBuilder& b) {
1334  b.setStyle(SkPaint::kStroke_Style);
1335  b.setStrokeWidth(10.0);
1336  }));
1337  RenderWith(testP, stroke_base_env, tolerance,
1339  "Stroke Width 5",
1340  [=](SkCanvas*, SkPaint& p) {
1341  p.setStyle(SkPaint::kStroke_Style);
1342  p.setStrokeWidth(5.0);
1343  },
1344  [=](DisplayListBuilder& b) {
1345  b.setStyle(SkPaint::kStroke_Style);
1346  b.setStrokeWidth(5.0);
1347  }));
1348 
1349  RenderWith(testP, stroke_base_env, tolerance,
1351  "Stroke Width 5, Square Cap",
1352  [=](SkCanvas*, SkPaint& p) {
1353  p.setStyle(SkPaint::kStroke_Style);
1354  p.setStrokeWidth(5.0);
1355  p.setStrokeCap(SkPaint::kSquare_Cap);
1356  },
1357  [=](DisplayListBuilder& b) {
1358  b.setStyle(SkPaint::kStroke_Style);
1359  b.setStrokeWidth(5.0);
1360  b.setStrokeCap(SkPaint::kSquare_Cap);
1361  }));
1362  RenderWith(testP, stroke_base_env, tolerance,
1364  "Stroke Width 5, Round Cap",
1365  [=](SkCanvas*, SkPaint& p) {
1366  p.setStyle(SkPaint::kStroke_Style);
1367  p.setStrokeWidth(5.0);
1368  p.setStrokeCap(SkPaint::kRound_Cap);
1369  },
1370  [=](DisplayListBuilder& b) {
1371  b.setStyle(SkPaint::kStroke_Style);
1372  b.setStrokeWidth(5.0);
1373  b.setStrokeCap(SkPaint::kRound_Cap);
1374  }));
1375 
1376  RenderWith(testP, stroke_base_env, tolerance,
1378  "Stroke Width 5, Bevel Join",
1379  [=](SkCanvas*, SkPaint& p) {
1380  p.setStyle(SkPaint::kStroke_Style);
1381  p.setStrokeWidth(5.0);
1382  p.setStrokeJoin(SkPaint::kBevel_Join);
1383  },
1384  [=](DisplayListBuilder& b) {
1385  b.setStyle(SkPaint::kStroke_Style);
1386  b.setStrokeWidth(5.0);
1387  b.setStrokeJoin(SkPaint::kBevel_Join);
1388  }));
1389  RenderWith(testP, stroke_base_env, tolerance,
1391  "Stroke Width 5, Round Join",
1392  [=](SkCanvas*, SkPaint& p) {
1393  p.setStyle(SkPaint::kStroke_Style);
1394  p.setStrokeWidth(5.0);
1395  p.setStrokeJoin(SkPaint::kRound_Join);
1396  },
1397  [=](DisplayListBuilder& b) {
1398  b.setStyle(SkPaint::kStroke_Style);
1399  b.setStrokeWidth(5.0);
1400  b.setStrokeJoin(SkPaint::kRound_Join);
1401  }));
1402 
1403  RenderWith(testP, stroke_base_env, tolerance,
1405  "Stroke Width 5, Miter 10",
1406  [=](SkCanvas*, SkPaint& p) {
1407  p.setStyle(SkPaint::kStroke_Style);
1408  p.setStrokeWidth(5.0);
1409  p.setStrokeMiter(10.0);
1410  p.setStrokeJoin(SkPaint::kMiter_Join);
1411  },
1412  [=](DisplayListBuilder& b) {
1413  b.setStyle(SkPaint::kStroke_Style);
1414  b.setStrokeWidth(5.0);
1415  b.setStrokeMiter(10.0);
1416  b.setStrokeJoin(SkPaint::kMiter_Join);
1417  }));
1418 
1419  RenderWith(testP, stroke_base_env, tolerance,
1421  "Stroke Width 5, Miter 0",
1422  [=](SkCanvas*, SkPaint& p) {
1423  p.setStyle(SkPaint::kStroke_Style);
1424  p.setStrokeWidth(5.0);
1425  p.setStrokeMiter(0.0);
1426  p.setStrokeJoin(SkPaint::kMiter_Join);
1427  },
1428  [=](DisplayListBuilder& b) {
1429  b.setStyle(SkPaint::kStroke_Style);
1430  b.setStrokeWidth(5.0);
1431  b.setStrokeMiter(0.0);
1432  b.setStrokeJoin(SkPaint::kMiter_Join);
1433  }));
1434 
1435  {
1436  const SkScalar TestDashes1[] = {29.0, 2.0};
1437  const SkScalar TestDashes2[] = {17.0, 1.5};
1438  sk_sp<SkPathEffect> effect = SkDashPathEffect::Make(TestDashes1, 2, 0.0f);
1439  {
1440  RenderWith(testP, stroke_base_env, tolerance,
1442  "PathEffect == Dash-29-2",
1443  [=](SkCanvas*, SkPaint& p) {
1444  // Need stroke style to see dashing properly
1445  p.setStyle(SkPaint::kStroke_Style);
1446  // Provide some non-trivial stroke size to get dashed
1447  p.setStrokeWidth(5.0);
1448  p.setPathEffect(effect);
1449  },
1450  [=](DisplayListBuilder& b) {
1451  // Need stroke style to see dashing properly
1452  b.setStyle(SkPaint::kStroke_Style);
1453  // Provide some non-trivial stroke size to get dashed
1454  b.setStrokeWidth(5.0);
1455  b.setPathEffect(effect);
1456  }));
1457  }
1458  EXPECT_TRUE(testP.is_draw_text_blob() || effect->unique())
1459  << "PathEffect == Dash-29-2 Cleanup";
1460  effect = SkDashPathEffect::Make(TestDashes2, 2, 0.0f);
1461  {
1462  RenderWith(testP, stroke_base_env, tolerance,
1464  "PathEffect == Dash-17-1.5",
1465  [=](SkCanvas*, SkPaint& p) {
1466  // Need stroke style to see dashing properly
1467  p.setStyle(SkPaint::kStroke_Style);
1468  // Provide some non-trivial stroke size to get dashed
1469  p.setStrokeWidth(5.0);
1470  p.setPathEffect(effect);
1471  },
1472  [=](DisplayListBuilder& b) {
1473  // Need stroke style to see dashing properly
1474  b.setStyle(SkPaint::kStroke_Style);
1475  // Provide some non-trivial stroke size to get dashed
1476  b.setStrokeWidth(5.0);
1477  b.setPathEffect(effect);
1478  }));
1479  }
1480  EXPECT_TRUE(testP.is_draw_text_blob() || effect->unique())
1481  << "PathEffect == Dash-17-1.5 Cleanup";
1482  }
1483  }
1484 
1485  static void RenderWithTransforms(const TestParameters& testP,
1486  const RenderEnvironment& env,
1487  const BoundsTolerance& tolerance) {
1488  // If the rendering method does not fill the corners of the original
1489  // bounds, then the estimate under rotation or skewing will be off
1490  // so we scale the padding by about 5% to compensate.
1491  BoundsTolerance skewed_tolerance = tolerance.mulScale(1.05, 1.05);
1492  RenderWith(testP, env, tolerance,
1494  "Translate 5, 10", //
1495  [=](SkCanvas* c, SkPaint&) { c->translate(5, 10); },
1496  [=](DisplayListBuilder& b) { b.translate(5, 10); }));
1497  RenderWith(testP, env, tolerance,
1499  "Scale +5%", //
1500  [=](SkCanvas* c, SkPaint&) { c->scale(1.05, 1.05); },
1501  [=](DisplayListBuilder& b) { b.scale(1.05, 1.05); }));
1502  RenderWith(testP, env, skewed_tolerance,
1504  "Rotate 5 degrees", //
1505  [=](SkCanvas* c, SkPaint&) { c->rotate(5); },
1506  [=](DisplayListBuilder& b) { b.rotate(5); }));
1507  RenderWith(testP, env, skewed_tolerance,
1509  "Skew 5%", //
1510  [=](SkCanvas* c, SkPaint&) { c->skew(0.05, 0.05); },
1511  [=](DisplayListBuilder& b) { b.skew(0.05, 0.05); }));
1512  {
1513  // This rather odd transform can cause slight differences in
1514  // computing in-bounds samples depending on which base rendering
1515  // routine Skia uses. Making sure our matrix values are powers
1516  // of 2 reduces, but does not eliminate, these slight differences
1517  // in calculation when we are comparing rendering with an alpha
1518  // to rendering opaque colors in the group opacity tests, for
1519  // example.
1520  SkScalar tweak = 1.0 / 16.0;
1521  SkMatrix tx = SkMatrix::MakeAll(1.0 + tweak, tweak, 5, //
1522  tweak, 1.0 + tweak, 10, //
1523  0, 0, 1);
1524  RenderWith(testP, env, skewed_tolerance,
1526  "Transform 2D Affine",
1527  [=](SkCanvas* c, SkPaint&) { c->concat(tx); },
1528  [=](DisplayListBuilder& b) {
1529  b.transform2DAffine(tx[0], tx[1], tx[2], //
1530  tx[3], tx[4], tx[5]);
1531  }));
1532  }
1533  {
1534  SkM44 m44 = SkM44(1, 0, 0, RenderCenterX, //
1535  0, 1, 0, RenderCenterY, //
1536  0, 0, 1, 0, //
1537  0, 0, .001, 1);
1538  m44.preConcat(
1539  SkM44::Rotate({1, 0, 0}, math::kPi / 60)); // 3 degrees around X
1540  m44.preConcat(
1541  SkM44::Rotate({0, 1, 0}, math::kPi / 45)); // 4 degrees around Y
1542  m44.preTranslate(-RenderCenterX, -RenderCenterY);
1543  RenderWith(
1544  testP, env, skewed_tolerance,
1546  "Transform Full Perspective",
1547  [=](SkCanvas* c, SkPaint&) { c->concat(m44); }, //
1548  [=](DisplayListBuilder& b) {
1549  b.transformFullPerspective(
1550  m44.rc(0, 0), m44.rc(0, 1), m44.rc(0, 2), m44.rc(0, 3),
1551  m44.rc(1, 0), m44.rc(1, 1), m44.rc(1, 2), m44.rc(1, 3),
1552  m44.rc(2, 0), m44.rc(2, 1), m44.rc(2, 2), m44.rc(2, 3),
1553  m44.rc(3, 0), m44.rc(3, 1), m44.rc(3, 2), m44.rc(3, 3));
1554  }));
1555  }
1556  }
1557 
1558  static void RenderWithClips(const TestParameters& testP,
1559  const RenderEnvironment& env,
1560  const BoundsTolerance& diff_tolerance) {
1561  SkRect r_clip = RenderBounds.makeInset(15.5, 15.5);
1562  BoundsTolerance intersect_tolerance = diff_tolerance.clip(r_clip);
1563  RenderWith(testP, env, intersect_tolerance,
1565  "Hard ClipRect inset by 15.5",
1566  [=](SkCanvas* c, SkPaint&) {
1567  c->clipRect(r_clip, SkClipOp::kIntersect, false);
1568  },
1569  [=](DisplayListBuilder& b) {
1570  b.clipRect(r_clip, SkClipOp::kIntersect, false);
1571  }));
1572  RenderWith(testP, env, intersect_tolerance,
1574  "AntiAlias ClipRect inset by 15.5",
1575  [=](SkCanvas* c, SkPaint&) {
1576  c->clipRect(r_clip, SkClipOp::kIntersect, true);
1577  },
1578  [=](DisplayListBuilder& b) {
1579  b.clipRect(r_clip, SkClipOp::kIntersect, true);
1580  }));
1581  RenderWith(testP, env, diff_tolerance,
1583  "Hard ClipRect Diff, inset by 15.5",
1584  [=](SkCanvas* c, SkPaint&) {
1585  c->clipRect(r_clip, SkClipOp::kDifference, false);
1586  },
1587  [=](DisplayListBuilder& b) {
1588  b.clipRect(r_clip, SkClipOp::kDifference, false);
1589  })
1590  .with_diff_clip());
1591  SkRRect rr_clip = SkRRect::MakeRectXY(r_clip, 1.8, 2.7);
1592  RenderWith(testP, env, intersect_tolerance,
1594  "Hard ClipRRect inset by 15.5",
1595  [=](SkCanvas* c, SkPaint&) {
1596  c->clipRRect(rr_clip, SkClipOp::kIntersect, false);
1597  },
1598  [=](DisplayListBuilder& b) {
1599  b.clipRRect(rr_clip, SkClipOp::kIntersect, false);
1600  }));
1601  RenderWith(testP, env, intersect_tolerance,
1603  "AntiAlias ClipRRect inset by 15.5",
1604  [=](SkCanvas* c, SkPaint&) {
1605  c->clipRRect(rr_clip, SkClipOp::kIntersect, true);
1606  },
1607  [=](DisplayListBuilder& b) {
1608  b.clipRRect(rr_clip, SkClipOp::kIntersect, true);
1609  }));
1610  RenderWith(testP, env, diff_tolerance,
1612  "Hard ClipRRect Diff, inset by 15.5",
1613  [=](SkCanvas* c, SkPaint&) {
1614  c->clipRRect(rr_clip, SkClipOp::kDifference, false);
1615  },
1616  [=](DisplayListBuilder& b) {
1617  b.clipRRect(rr_clip, SkClipOp::kDifference, false);
1618  })
1619  .with_diff_clip());
1620  SkPath path_clip = SkPath();
1621  path_clip.setFillType(SkPathFillType::kEvenOdd);
1622  path_clip.addRect(r_clip);
1623  path_clip.addCircle(RenderCenterX, RenderCenterY, 1.0);
1624  RenderWith(testP, env, intersect_tolerance,
1626  "Hard ClipPath inset by 15.5",
1627  [=](SkCanvas* c, SkPaint&) {
1628  c->clipPath(path_clip, SkClipOp::kIntersect, false);
1629  },
1630  [=](DisplayListBuilder& b) {
1631  b.clipPath(path_clip, SkClipOp::kIntersect, false);
1632  }));
1633  RenderWith(testP, env, intersect_tolerance,
1635  "AntiAlias ClipPath inset by 15.5",
1636  [=](SkCanvas* c, SkPaint&) {
1637  c->clipPath(path_clip, SkClipOp::kIntersect, true);
1638  },
1639  [=](DisplayListBuilder& b) {
1640  b.clipPath(path_clip, SkClipOp::kIntersect, true);
1641  }));
1642  RenderWith(testP, env, diff_tolerance,
1644  "Hard ClipPath Diff, inset by 15.5",
1645  [=](SkCanvas* c, SkPaint&) {
1646  c->clipPath(path_clip, SkClipOp::kDifference, false);
1647  },
1648  [=](DisplayListBuilder& b) {
1649  b.clipPath(path_clip, SkClipOp::kDifference, false);
1650  })
1651  .with_diff_clip());
1652  }
1653 
1654  static sk_sp<SkPicture> getSkPicture(const TestParameters& testP,
1655  const CaseParameters& caseP) {
1656  SkPictureRecorder recorder;
1657  SkRTreeFactory rtree_factory;
1658  SkCanvas* cv = recorder.beginRecording(TestBounds, &rtree_factory);
1659  caseP.render_to(cv, testP);
1660  return recorder.finishRecordingAsPicture();
1661  }
1662 
1663  static void RenderWith(const TestParameters& testP,
1664  const RenderEnvironment& env,
1665  const BoundsTolerance& tolerance_in,
1666  const CaseParameters& caseP) {
1667  // sk_surface is a direct rendering via SkCanvas to SkSurface
1668  // DisplayList mechanisms are not involved in this operation
1669  const std::string info = caseP.info();
1670  const SkColor bg = caseP.bg();
1671  std::unique_ptr<RenderSurface> sk_surface = env.MakeSurface(bg);
1672  SkCanvas* sk_canvas = sk_surface->canvas();
1673  SkPaint sk_paint;
1674  caseP.cv_setup()(sk_canvas, sk_paint);
1675  SkMatrix sk_matrix = sk_canvas->getTotalMatrix();
1676  SkIRect sk_clip = sk_canvas->getDeviceClipBounds();
1677  const BoundsTolerance tolerance =
1678  testP.adjust(tolerance_in, sk_paint, sk_canvas->getTotalMatrix());
1679  testP.render_to(sk_canvas, sk_paint);
1680  caseP.cv_restore()(sk_canvas, sk_paint);
1681  const sk_sp<SkPicture> sk_picture = getSkPicture(testP, caseP);
1682  SkRect sk_bounds = sk_picture->cullRect();
1683  const SkPixmap* sk_pixels = sk_surface->pixmap();
1684  ASSERT_EQ(sk_pixels->width(), TestWidth) << info;
1685  ASSERT_EQ(sk_pixels->height(), TestHeight) << info;
1686  ASSERT_EQ(sk_pixels->info().bytesPerPixel(), 4) << info;
1687  checkPixels(sk_pixels, sk_bounds, info + " (Skia reference)", bg);
1688 
1689  if (testP.should_match(env, sk_paint, sk_matrix, sk_clip,
1690  caseP.has_diff_clip(),
1691  caseP.has_mutating_save_layer())) {
1692  quickCompareToReference(env.ref_pixmap(), sk_pixels, true,
1693  info + " (attribute has no effect)");
1694  } else {
1695  quickCompareToReference(env.ref_pixmap(), sk_pixels, false,
1696  info + " (attribute affects rendering)");
1697  }
1698 
1699  {
1700  // This sequence plays the provided equivalently constructed
1701  // DisplayList onto the SkCanvas of the surface
1702  // DisplayList => direct rendering
1703  DisplayListBuilder builder(TestBounds);
1704  caseP.render_to(builder, testP);
1705  sk_sp<DisplayList> display_list = builder.Build();
1706  SkRect dl_bounds = display_list->bounds();
1707  if (!sk_bounds.roundOut().contains(dl_bounds)) {
1708  FML_LOG(ERROR) << "For " << info;
1709  FML_LOG(ERROR) << "sk ref: " //
1710  << sk_bounds.fLeft << ", " << sk_bounds.fTop << " => "
1711  << sk_bounds.fRight << ", " << sk_bounds.fBottom;
1712  FML_LOG(ERROR) << "dl: " //
1713  << dl_bounds.fLeft << ", " << dl_bounds.fTop << " => "
1714  << dl_bounds.fRight << ", " << dl_bounds.fBottom;
1715  if (!dl_bounds.contains(sk_bounds)) {
1716  FML_LOG(ERROR) << "DisplayList bounds are too small!";
1717  }
1718  if (!sk_bounds.roundOut().contains(dl_bounds.roundOut())) {
1719  FML_LOG(ERROR) << "###### DisplayList bounds larger than reference!";
1720  }
1721  }
1722 
1723  // This EXPECT sometimes triggers, but when it triggers and I examine
1724  // the ref_bounds, they are always unnecessarily large and since the
1725  // pixel OOB tests in the compare method do not trigger, we will trust
1726  // the DL bounds.
1727  // EXPECT_TRUE(dl_bounds.contains(ref_bounds)) << info;
1728 
1729  // When we are drawing a DisplayList, the display_list built above
1730  // will contain just a single drawDisplayList call plus the case
1731  // attribute. The sk_picture will, however, contain a list of all
1732  // of the embedded calls in the display list and so the op counts
1733  // will not be equal between the two.
1734  if (!testP.is_draw_display_list()) {
1735  EXPECT_EQ(display_list->op_count(), sk_picture->approximateOpCount())
1736  << info;
1737  }
1738 
1739  std::unique_ptr<RenderSurface> dl_surface = env.MakeSurface(bg);
1740  display_list->RenderTo(dl_surface->canvas());
1741  compareToReference(dl_surface->pixmap(), sk_pixels,
1742  info + " (DisplayList built directly -> surface)",
1743  &dl_bounds, &tolerance, bg);
1744 
1745  if (display_list->can_apply_group_opacity()) {
1746  checkGroupOpacity(env, display_list, dl_surface->pixmap(),
1747  info + " with Group Opacity", bg);
1748  }
1749  }
1750 
1751  // This test cannot work if the rendering is using shadows until
1752  // we can access the Skia ShadowRec via public headers.
1753  if (!testP.is_draw_shadows()) {
1754  // This sequence renders SkCanvas calls to a DisplayList and then
1755  // plays them back on SkCanvas to SkSurface
1756  // SkCanvas calls => DisplayList => rendering
1757  std::unique_ptr<RenderSurface> cv_dl_surface = env.MakeSurface(bg);
1758  DisplayListCanvasRecorder dl_recorder(TestBounds);
1759  caseP.render_to(&dl_recorder, testP);
1760  dl_recorder.builder()->Build()->RenderTo(cv_dl_surface->canvas());
1761  compareToReference(cv_dl_surface->pixmap(), sk_pixels,
1762  info + " (Skia calls -> DisplayList -> surface)",
1763  nullptr, nullptr, bg);
1764  }
1765 
1766  {
1767  // This sequence renders the SkCanvas calls to an SkPictureRecorder and
1768  // renders the DisplayList calls to a DisplayListBuilder and then
1769  // renders both back under a transform (scale(2x)) to see if their
1770  // rendering is affected differently by a change of matrix between
1771  // recording time and rendering time.
1772  const int TestWidth2 = TestWidth * 2;
1773  const int TestHeight2 = TestHeight * 2;
1774  const SkScalar TestScale = 2.0;
1775 
1776  SkPictureRecorder sk_x2_recorder;
1777  SkCanvas* ref_canvas = sk_x2_recorder.beginRecording(TestBounds);
1778  SkPaint ref_paint;
1779  caseP.render_to(ref_canvas, testP);
1780  sk_sp<SkPicture> ref_x2_picture =
1781  sk_x2_recorder.finishRecordingAsPicture();
1782  std::unique_ptr<RenderSurface> ref_x2_surface =
1783  env.MakeSurface(bg, TestWidth2, TestHeight2);
1784  SkCanvas* ref_x2_canvas = ref_x2_surface->canvas();
1785  ref_x2_canvas->scale(TestScale, TestScale);
1786  ref_x2_picture->playback(ref_x2_canvas);
1787  const SkPixmap* ref_x2_pixels = ref_x2_surface->pixmap();
1788  ASSERT_EQ(ref_x2_pixels->width(), TestWidth2) << info;
1789  ASSERT_EQ(ref_x2_pixels->height(), TestHeight2) << info;
1790  ASSERT_EQ(ref_x2_pixels->info().bytesPerPixel(), 4) << info;
1791 
1792  DisplayListBuilder builder_x2(TestBounds);
1793  caseP.render_to(builder_x2, testP);
1794  sk_sp<DisplayList> display_list_x2 = builder_x2.Build();
1795  std::unique_ptr<RenderSurface> test_x2_surface =
1796  env.MakeSurface(bg, TestWidth2, TestHeight2);
1797  SkCanvas* test_x2_canvas = test_x2_surface->canvas();
1798  test_x2_canvas->scale(TestScale, TestScale);
1799  display_list_x2->RenderTo(test_x2_canvas);
1800  compareToReference(test_x2_surface->pixmap(), ref_x2_pixels,
1801  info + " (Both rendered scaled 2x)", nullptr, nullptr,
1802  bg, TestWidth2, TestHeight2, false);
1803  }
1804  }
1805 
1806  static void checkGroupOpacity(const RenderEnvironment& env,
1807  sk_sp<DisplayList> display_list,
1808  const SkPixmap* ref_pixmap,
1809  const std::string info,
1810  SkColor bg) {
1811  SkScalar opacity = 128.0 / 255.0;
1812 
1813  std::unique_ptr<RenderSurface> group_opacity_surface = env.MakeSurface(bg);
1814  SkCanvas* group_opacity_canvas = group_opacity_surface->canvas();
1815  display_list->RenderTo(group_opacity_canvas, opacity);
1816  const SkPixmap* group_opacity_pixmap = group_opacity_surface->pixmap();
1817 
1818  ASSERT_EQ(group_opacity_pixmap->width(), TestWidth) << info;
1819  ASSERT_EQ(group_opacity_pixmap->height(), TestHeight) << info;
1820  ASSERT_EQ(group_opacity_pixmap->info().bytesPerPixel(), 4) << info;
1821 
1822  ASSERT_EQ(ref_pixmap->width(), TestWidth) << info;
1823  ASSERT_EQ(ref_pixmap->height(), TestHeight) << info;
1824  ASSERT_EQ(ref_pixmap->info().bytesPerPixel(), 4) << info;
1825 
1826  int pixels_touched = 0;
1827  int pixels_different = 0;
1828  // We need to allow some slight differences per component due to the
1829  // fact that rearranging discrete calculations can compound round off
1830  // errors. Off-by-2 is enough for 8 bit components, but for the 565
1831  // tests we allow at least 9 which is the maximum distance between
1832  // samples when converted to 8 bits. (You might think it would be a
1833  // max step of 8 converting 5 bits to 8 bits, but it is really
1834  // converting 31 steps to 255 steps with an average step size of
1835  // 8.23 - 24 of the steps are by 8, but 7 of them are by 9.)
1836  int fudge = env.info().bytesPerPixel() < 4 ? 9 : 2;
1837  for (int y = 0; y < TestHeight; y++) {
1838  const uint32_t* ref_row = ref_pixmap->addr32(0, y);
1839  const uint32_t* test_row = group_opacity_pixmap->addr32(0, y);
1840  for (int x = 0; x < TestWidth; x++) {
1841  uint32_t ref_pixel = ref_row[x];
1842  uint32_t test_pixel = test_row[x];
1843  if (ref_pixel != bg || test_pixel != bg) {
1844  pixels_touched++;
1845  for (int i = 0; i < 32; i += 8) {
1846  int ref_comp = (ref_pixel >> i) & 0xff;
1847  int bg_comp = (bg >> i) & 0xff;
1848  SkScalar faded_comp = bg_comp + (ref_comp - bg_comp) * opacity;
1849  int test_comp = (test_pixel >> i) & 0xff;
1850  if (std::abs(faded_comp - test_comp) > fudge) {
1851  pixels_different++;
1852  break;
1853  }
1854  }
1855  }
1856  }
1857  }
1858  ASSERT_GT(pixels_touched, 20) << info;
1859  ASSERT_LE(pixels_different, 1) << info;
1860  }
1861 
1862  static void checkPixels(const SkPixmap* ref_pixels,
1863  const SkRect ref_bounds,
1864  const std::string info,
1865  const SkColor bg) {
1866  SkPMColor untouched = SkPreMultiplyColor(bg);
1867  int pixels_touched = 0;
1868  int pixels_oob = 0;
1869  SkIRect i_bounds = ref_bounds.roundOut();
1870  for (int y = 0; y < TestHeight; y++) {
1871  const uint32_t* ref_row = ref_pixels->addr32(0, y);
1872  for (int x = 0; x < TestWidth; x++) {
1873  if (ref_row[x] != untouched) {
1874  pixels_touched++;
1875  if (!i_bounds.contains(x, y)) {
1876  pixels_oob++;
1877  }
1878  }
1879  }
1880  }
1881  ASSERT_EQ(pixels_oob, 0) << info;
1882  ASSERT_GT(pixels_touched, 0) << info;
1883  }
1884 
1885  static void quickCompareToReference(const SkPixmap* ref_pixels,
1886  const SkPixmap* test_pixels,
1887  bool should_match,
1888  const std::string info) {
1889  ASSERT_EQ(test_pixels->width(), ref_pixels->width()) << info;
1890  ASSERT_EQ(test_pixels->height(), ref_pixels->height()) << info;
1891  ASSERT_EQ(test_pixels->info().bytesPerPixel(), 4) << info;
1892  ASSERT_EQ(ref_pixels->info().bytesPerPixel(), 4) << info;
1893  int pixels_different = 0;
1894  for (int y = 0; y < test_pixels->height(); y++) {
1895  const uint32_t* ref_row = ref_pixels->addr32(0, y);
1896  const uint32_t* test_row = test_pixels->addr32(0, y);
1897  for (int x = 0; x < test_pixels->width(); x++) {
1898  if (ref_row[x] != test_row[x]) {
1899  pixels_different++;
1900  }
1901  }
1902  }
1903  if (should_match) {
1904  ASSERT_EQ(pixels_different, 0) << info;
1905  } else {
1906  ASSERT_NE(pixels_different, 0) << info;
1907  }
1908  }
1909 
1910  static void compareToReference(const SkPixmap* test_pixels,
1911  const SkPixmap* ref_pixels,
1912  const std::string info,
1913  SkRect* bounds,
1914  const BoundsTolerance* tolerance,
1915  const SkColor bg,
1916  int width = TestWidth,
1917  int height = TestHeight,
1918  bool printMismatches = false) {
1919  SkPMColor untouched = SkPreMultiplyColor(bg);
1920  ASSERT_EQ(test_pixels->width(), width) << info;
1921  ASSERT_EQ(test_pixels->height(), height) << info;
1922  ASSERT_EQ(test_pixels->info().bytesPerPixel(), 4) << info;
1923  ASSERT_EQ(ref_pixels->info().bytesPerPixel(), 4) << info;
1924  SkIRect i_bounds =
1925  bounds ? bounds->roundOut() : SkIRect::MakeWH(width, height);
1926 
1927  int pixels_different = 0;
1928  int pixels_oob = 0;
1929  int minX = width;
1930  int minY = height;
1931  int maxX = 0;
1932  int maxY = 0;
1933  for (int y = 0; y < height; y++) {
1934  const uint32_t* ref_row = ref_pixels->addr32(0, y);
1935  const uint32_t* test_row = test_pixels->addr32(0, y);
1936  for (int x = 0; x < width; x++) {
1937  if (bounds && test_row[x] != untouched) {
1938  if (minX > x) {
1939  minX = x;
1940  }
1941  if (minY > y) {
1942  minY = y;
1943  }
1944  if (maxX <= x) {
1945  maxX = x + 1;
1946  }
1947  if (maxY <= y) {
1948  maxY = y + 1;
1949  }
1950  if (!i_bounds.contains(x, y)) {
1951  pixels_oob++;
1952  }
1953  }
1954  if (test_row[x] != ref_row[x]) {
1955  if (printMismatches) {
1956  FML_LOG(ERROR) << "pix[" << x << ", " << y
1957  << "] mismatch: " << std::hex << test_row[x]
1958  << "(test) != (ref)" << ref_row[x] << std::dec;
1959  }
1960  pixels_different++;
1961  }
1962  }
1963  }
1964  if (pixels_oob > 0) {
1965  FML_LOG(ERROR) << "pix bounds[" //
1966  << minX << ", " << minY << " => " << maxX << ", " << maxY
1967  << "]";
1968  FML_LOG(ERROR) << "dl_bounds[" //
1969  << bounds->fLeft << ", " << bounds->fTop //
1970  << " => " //
1971  << bounds->fRight << ", " << bounds->fBottom //
1972  << "]";
1973  } else if (bounds) {
1974  showBoundsOverflow(info, i_bounds, tolerance, minX, minY, maxX, maxY);
1975  }
1976  ASSERT_EQ(pixels_oob, 0) << info;
1977  ASSERT_EQ(pixels_different, 0) << info;
1978  }
1979 
1980  static void showBoundsOverflow(std::string info,
1981  SkIRect& bounds,
1982  const BoundsTolerance* tolerance,
1983  int pixLeft,
1984  int pixTop,
1985  int pixRight,
1986  int pixBottom) {
1987  int pad_left = std::max(0, pixLeft - bounds.fLeft);
1988  int pad_top = std::max(0, pixTop - bounds.fTop);
1989  int pad_right = std::max(0, bounds.fRight - pixRight);
1990  int pad_bottom = std::max(0, bounds.fBottom - pixBottom);
1991  SkIRect pix_bounds =
1992  SkIRect::MakeLTRB(pixLeft, pixTop, pixRight, pixBottom);
1993  SkISize pix_size = pix_bounds.size();
1994  int pixWidth = pix_size.width();
1995  int pixHeight = pix_size.height();
1996  int worst_pad_x = std::max(pad_left, pad_right);
1997  int worst_pad_y = std::max(pad_top, pad_bottom);
1998  if (tolerance->overflows(pix_bounds, worst_pad_x, worst_pad_y)) {
1999  FML_LOG(ERROR) << "Overflow for " << info;
2000  FML_LOG(ERROR) << "pix bounds[" //
2001  << pixLeft << ", " << pixTop << " => " //
2002  << pixRight << ", " << pixBottom //
2003  << "]";
2004  FML_LOG(ERROR) << "dl_bounds[" //
2005  << bounds.fLeft << ", " << bounds.fTop //
2006  << " => " //
2007  << bounds.fRight << ", " << bounds.fBottom //
2008  << "]";
2009  FML_LOG(ERROR) << "Bounds overflowed by up to " //
2010  << worst_pad_x << ", " << worst_pad_y //
2011  << " (" << (worst_pad_x * 100.0 / pixWidth) //
2012  << "%, " << (worst_pad_y * 100.0 / pixHeight) << "%)";
2013  int pix_area = pix_size.area();
2014  int dl_area = bounds.width() * bounds.height();
2015  FML_LOG(ERROR) << "Total overflow area: " << (dl_area - pix_area) //
2016  << " (+" << (dl_area * 100.0 / pix_area - 100.0) << "%)";
2017  FML_LOG(ERROR);
2018  }
2019  }
2020 
2021  static const sk_sp<SkImage> testImage;
2022  static const sk_sp<SkImage> makeTestImage() {
2023  sk_sp<SkSurface> surface =
2024  SkSurface::MakeRasterN32Premul(RenderWidth, RenderHeight);
2025  SkCanvas* canvas = surface->getCanvas();
2026  SkPaint p0, p1;
2027  p0.setStyle(SkPaint::kFill_Style);
2028  p0.setColor(SkColorSetARGB(0xff, 0x00, 0xfe, 0x00)); // off-green
2029  p1.setStyle(SkPaint::kFill_Style);
2030  p1.setColor(SK_ColorBLUE);
2031  // Some pixels need some transparency for DstIn testing
2032  p1.setAlpha(128);
2033  int cbdim = 5;
2034  for (int y = 0; y < RenderHeight; y += cbdim) {
2035  for (int x = 0; x < RenderWidth; x += cbdim) {
2036  SkPaint& cellp = ((x + y) & 1) == 0 ? p0 : p1;
2037  canvas->drawRect(SkRect::MakeXYWH(x, y, cbdim, cbdim), cellp);
2038  }
2039  }
2040  return surface->makeImageSnapshot();
2041  }
2042 
2043  static const sk_sp<SkShader> testImageShader;
2044 
2045  static sk_sp<SkTextBlob> MakeTextBlob(std::string string,
2046  SkScalar font_height) {
2047  SkFont font(SkTypeface::MakeFromName("ahem", SkFontStyle::Normal()),
2048  font_height);
2049  return SkTextBlob::MakeFromText(string.c_str(), string.size(), font,
2050  SkTextEncoding::kUTF8);
2051  }
2052 };
2053 
2055  BoundsTolerance().addAbsolutePadding(1, 1);
2056 
2057 const sk_sp<SkImage> CanvasCompareTester::testImage = makeTestImage();
2058 const sk_sp<SkShader> CanvasCompareTester::testImageShader =
2059  makeTestImage()->makeShader(SkTileMode::kRepeat,
2061  SkSamplingOptions());
2062 
2063 // Eventually this bare bones testing::Test fixture will subsume the
2064 // CanvasCompareTester and the TestParameters could then become just
2065 // configuration calls made upon the fixture.
2066 template <typename BaseT>
2067 class DisplayListCanvasTestBase : public BaseT, protected DisplayListOpFlags {
2068  public:
2069  DisplayListCanvasTestBase() = default;
2070 
2071  private:
2073 };
2075 
2079  [=](SkCanvas* canvas, const SkPaint& paint) { //
2080  canvas->drawPaint(paint);
2081  },
2082  [=](DisplayListBuilder& builder) { //
2083  builder.drawPaint();
2084  },
2085  kDrawPaintFlags));
2086 }
2087 
2091  [=](SkCanvas* canvas, const SkPaint& paint) {
2092  canvas->drawColor(SK_ColorMAGENTA);
2093  },
2094  [=](DisplayListBuilder& builder) {
2095  builder.drawColor(SK_ColorMAGENTA, SkBlendMode::kSrcOver);
2096  },
2097  kDrawColorFlags));
2098 }
2099 
2100 TEST_F(DisplayListCanvas, DrawDiagonalLines) {
2101  SkPoint p1 = SkPoint::Make(RenderLeft, RenderTop);
2102  SkPoint p2 = SkPoint::Make(RenderRight, RenderBottom);
2103  SkPoint p3 = SkPoint::Make(RenderLeft, RenderBottom);
2104  SkPoint p4 = SkPoint::Make(RenderRight, RenderTop);
2105 
2108  [=](SkCanvas* canvas, const SkPaint& paint) { //
2109  // Skia requires kStroke style on horizontal and vertical
2110  // lines to get the bounds correct.
2111  // See https://bugs.chromium.org/p/skia/issues/detail?id=12446
2112  SkPaint p = paint;
2113  p.setStyle(SkPaint::kStroke_Style);
2114  canvas->drawLine(p1, p2, p);
2115  canvas->drawLine(p3, p4, p);
2116  },
2117  [=](DisplayListBuilder& builder) { //
2118  builder.drawLine(p1, p2);
2119  builder.drawLine(p3, p4);
2120  },
2121  kDrawLineFlags)
2122  .set_draw_line());
2123 }
2124 
2125 TEST_F(DisplayListCanvas, DrawHorizontalLine) {
2126  SkPoint p1 = SkPoint::Make(RenderLeft, RenderCenterY);
2127  SkPoint p2 = SkPoint::Make(RenderRight, RenderCenterY);
2128 
2131  [=](SkCanvas* canvas, const SkPaint& paint) { //
2132  // Skia requires kStroke style on horizontal and vertical
2133  // lines to get the bounds correct.
2134  // See https://bugs.chromium.org/p/skia/issues/detail?id=12446
2135  SkPaint p = paint;
2136  p.setStyle(SkPaint::kStroke_Style);
2137  canvas->drawLine(p1, p2, p);
2138  },
2139  [=](DisplayListBuilder& builder) { //
2140  builder.drawLine(p1, p2);
2141  },
2142  kDrawHVLineFlags)
2143  .set_draw_line()
2144  .set_horizontal_line());
2145 }
2146 
2147 TEST_F(DisplayListCanvas, DrawVerticalLine) {
2148  SkPoint p1 = SkPoint::Make(RenderCenterX, RenderTop);
2149  SkPoint p2 = SkPoint::Make(RenderCenterY, RenderBottom);
2150 
2153  [=](SkCanvas* canvas, const SkPaint& paint) { //
2154  // Skia requires kStroke style on horizontal and vertical
2155  // lines to get the bounds correct.
2156  // See https://bugs.chromium.org/p/skia/issues/detail?id=12446
2157  SkPaint p = paint;
2158  p.setStyle(SkPaint::kStroke_Style);
2159  canvas->drawLine(p1, p2, p);
2160  },
2161  [=](DisplayListBuilder& builder) { //
2162  builder.drawLine(p1, p2);
2163  },
2164  kDrawHVLineFlags)
2165  .set_draw_line()
2166  .set_vertical_line());
2167 }
2168 
2170  // Bounds are offset by 0.5 pixels to induce AA
2173  [=](SkCanvas* canvas, const SkPaint& paint) { //
2174  canvas->drawRect(RenderBounds.makeOffset(0.5, 0.5), paint);
2175  },
2176  [=](DisplayListBuilder& builder) { //
2177  builder.drawRect(RenderBounds.makeOffset(0.5, 0.5));
2178  },
2179  kDrawRectFlags));
2180 }
2181 
2183  SkRect rect = RenderBounds.makeInset(0, 10);
2184 
2187  [=](SkCanvas* canvas, const SkPaint& paint) { //
2188  canvas->drawOval(rect, paint);
2189  },
2190  [=](DisplayListBuilder& builder) { //
2191  builder.drawOval(rect);
2192  },
2193  kDrawOvalFlags));
2194 }
2195 
2199  [=](SkCanvas* canvas, const SkPaint& paint) { //
2200  canvas->drawCircle(TestCenter, RenderRadius, paint);
2201  },
2202  [=](DisplayListBuilder& builder) { //
2203  builder.drawCircle(TestCenter, RenderRadius);
2204  },
2205  kDrawCircleFlags));
2206 }
2207 
2209  SkRRect rrect =
2210  SkRRect::MakeRectXY(RenderBounds, RenderCornerRadius, RenderCornerRadius);
2213  [=](SkCanvas* canvas, const SkPaint& paint) { //
2214  canvas->drawRRect(rrect, paint);
2215  },
2216  [=](DisplayListBuilder& builder) { //
2217  builder.drawRRect(rrect);
2218  },
2219  kDrawRRectFlags));
2220 }
2221 
2223  SkRRect outer =
2224  SkRRect::MakeRectXY(RenderBounds, RenderCornerRadius, RenderCornerRadius);
2225  SkRect innerBounds = RenderBounds.makeInset(30.0, 30.0);
2226  SkRRect inner =
2227  SkRRect::MakeRectXY(innerBounds, RenderCornerRadius, RenderCornerRadius);
2230  [=](SkCanvas* canvas, const SkPaint& paint) { //
2231  canvas->drawDRRect(outer, inner, paint);
2232  },
2233  [=](DisplayListBuilder& builder) { //
2234  builder.drawDRRect(outer, inner);
2235  },
2236  kDrawDRRectFlags));
2237 }
2238 
2240  SkPath path;
2241 
2242  // unclosed lines to show some caps
2243  path.moveTo(RenderLeft + 15, RenderTop + 15);
2244  path.lineTo(RenderRight - 15, RenderBottom - 15);
2245  path.moveTo(RenderLeft + 15, RenderBottom - 15);
2246  path.lineTo(RenderRight - 15, RenderTop + 15);
2247 
2248  path.addRect(RenderBounds);
2249 
2250  // miter diamonds horizontally and vertically to show miters
2251  path.moveTo(VerticalMiterDiamondPoints[0]);
2252  for (int i = 1; i < VerticalMiterDiamondPointCount; i++) {
2253  path.lineTo(VerticalMiterDiamondPoints[i]);
2254  }
2255  path.close();
2256  path.moveTo(HorizontalMiterDiamondPoints[0]);
2257  for (int i = 1; i < HorizontalMiterDiamondPointCount; i++) {
2258  path.lineTo(HorizontalMiterDiamondPoints[i]);
2259  }
2260  path.close();
2261 
2264  [=](SkCanvas* canvas, const SkPaint& paint) { //
2265  canvas->drawPath(path, paint);
2266  },
2267  [=](DisplayListBuilder& builder) { //
2268  builder.drawPath(path);
2269  },
2270  kDrawPathFlags));
2271 }
2272 
2276  [=](SkCanvas* canvas, const SkPaint& paint) { //
2277  canvas->drawArc(RenderBounds, 60, 330, false, paint);
2278  },
2279  [=](DisplayListBuilder& builder) { //
2280  builder.drawArc(RenderBounds, 60, 330, false);
2281  },
2282  kDrawArcNoCenterFlags));
2283 }
2284 
2285 TEST_F(DisplayListCanvas, DrawArcCenter) {
2286  // Center arcs that inscribe nearly a whole circle except for a small
2287  // arc extent gap have 2 angles that may appear or disappear at the
2288  // various miter limits tested (0, 4, and 10).
2289  // The center angle here is 12 degrees which shows a miter
2290  // at limit=10, but not 0 or 4.
2291  // The arcs at the corners where it turns in towards the
2292  // center show miters at 4 and 10, but not 0.
2293  // Limit == 0, neither corner does a miter
2294  // Limit == 4, only the edge "turn-in" corners miter
2295  // Limit == 10, edge and center corners all miter
2298  [=](SkCanvas* canvas, const SkPaint& paint) { //
2299  canvas->drawArc(RenderBounds, 60, 360 - 12, true, paint);
2300  },
2301  [=](DisplayListBuilder& builder) { //
2302  builder.drawArc(RenderBounds, 60, 360 - 12, true);
2303  },
2304  kDrawArcWithCenterFlags)
2305  .set_draw_arc_center());
2306 }
2307 
2308 TEST_F(DisplayListCanvas, DrawPointsAsPoints) {
2309  // The +/- 16 points are designed to fall just inside the clips
2310  // that are tested against so we avoid lots of undrawn pixels
2311  // in the accumulated bounds.
2312  const SkScalar x0 = RenderLeft;
2313  const SkScalar x1 = RenderLeft + 16;
2314  const SkScalar x2 = (RenderLeft + RenderCenterX) * 0.5;
2315  const SkScalar x3 = RenderCenterX + 0.1;
2316  const SkScalar x4 = (RenderRight + RenderCenterX) * 0.5;
2317  const SkScalar x5 = RenderRight - 16;
2318  const SkScalar x6 = RenderRight;
2319 
2320  const SkScalar y0 = RenderTop;
2321  const SkScalar y1 = RenderTop + 16;
2322  const SkScalar y2 = (RenderTop + RenderCenterY) * 0.5;
2323  const SkScalar y3 = RenderCenterY + 0.1;
2324  const SkScalar y4 = (RenderBottom + RenderCenterY) * 0.5;
2325  const SkScalar y5 = RenderBottom - 16;
2326  const SkScalar y6 = RenderBottom;
2327 
2328  // clang-format off
2329  const SkPoint points[] = {
2330  {x0, y0}, {x1, y0}, {x2, y0}, {x3, y0}, {x4, y0}, {x5, y0}, {x6, y0},
2331  {x0, y1}, {x1, y1}, {x2, y1}, {x3, y1}, {x4, y1}, {x5, y1}, {x6, y1},
2332  {x0, y2}, {x1, y2}, {x2, y2}, {x3, y2}, {x4, y2}, {x5, y2}, {x6, y2},
2333  {x0, y3}, {x1, y3}, {x2, y3}, {x3, y3}, {x4, y3}, {x5, y3}, {x6, y3},
2334  {x0, y4}, {x1, y4}, {x2, y4}, {x3, y4}, {x4, y4}, {x5, y4}, {x6, y4},
2335  {x0, y5}, {x1, y5}, {x2, y5}, {x3, y5}, {x4, y5}, {x5, y5}, {x6, y5},
2336  {x0, y6}, {x1, y6}, {x2, y6}, {x3, y6}, {x4, y6}, {x5, y6}, {x6, y6},
2337  };
2338  // clang-format on
2339  const int count = sizeof(points) / sizeof(points[0]);
2340 
2343  [=](SkCanvas* canvas, const SkPaint& paint) { //
2344  // Skia requires kStroke style on horizontal and vertical
2345  // lines to get the bounds correct.
2346  // See https://bugs.chromium.org/p/skia/issues/detail?id=12446
2347  SkPaint p = paint;
2348  p.setStyle(SkPaint::kStroke_Style);
2349  canvas->drawPoints(SkCanvas::kPoints_PointMode, count, points, p);
2350  },
2351  [=](DisplayListBuilder& builder) { //
2352  builder.drawPoints(SkCanvas::kPoints_PointMode, count, points);
2353  },
2354  kDrawPointsAsPointsFlags)
2355  .set_draw_line()
2356  .set_ignores_dashes());
2357 }
2358 
2359 TEST_F(DisplayListCanvas, DrawPointsAsLines) {
2360  const SkScalar x0 = RenderLeft + 1;
2361  const SkScalar x1 = RenderLeft + 16;
2362  const SkScalar x2 = RenderRight - 16;
2363  const SkScalar x3 = RenderRight - 1;
2364 
2365  const SkScalar y0 = RenderTop;
2366  const SkScalar y1 = RenderTop + 16;
2367  const SkScalar y2 = RenderBottom - 16;
2368  const SkScalar y3 = RenderBottom;
2369 
2370  // clang-format off
2371  const SkPoint points[] = {
2372  // Outer box
2373  {x0, y0}, {x3, y0},
2374  {x3, y0}, {x3, y3},
2375  {x3, y3}, {x0, y3},
2376  {x0, y3}, {x0, y0},
2377 
2378  // Diagonals
2379  {x0, y0}, {x3, y3}, {x3, y0}, {x0, y3},
2380 
2381  // Inner box
2382  {x1, y1}, {x2, y1},
2383  {x2, y1}, {x2, y2},
2384  {x2, y2}, {x1, y2},
2385  {x1, y2}, {x1, y1},
2386  };
2387  // clang-format on
2388 
2389  const int count = sizeof(points) / sizeof(points[0]);
2390  ASSERT_TRUE((count & 1) == 0);
2393  [=](SkCanvas* canvas, const SkPaint& paint) { //
2394  // Skia requires kStroke style on horizontal and vertical
2395  // lines to get the bounds correct.
2396  // See https://bugs.chromium.org/p/skia/issues/detail?id=12446
2397  SkPaint p = paint;
2398  p.setStyle(SkPaint::kStroke_Style);
2399  canvas->drawPoints(SkCanvas::kLines_PointMode, count, points, p);
2400  },
2401  [=](DisplayListBuilder& builder) { //
2402  builder.drawPoints(SkCanvas::kLines_PointMode, count, points);
2403  },
2404  kDrawPointsAsLinesFlags));
2405 }
2406 
2407 TEST_F(DisplayListCanvas, DrawPointsAsPolygon) {
2408  const SkPoint points1[] = {
2409  // RenderBounds box with a diagonal
2410  SkPoint::Make(RenderLeft, RenderTop),
2411  SkPoint::Make(RenderRight, RenderTop),
2412  SkPoint::Make(RenderRight, RenderBottom),
2413  SkPoint::Make(RenderLeft, RenderBottom),
2414  SkPoint::Make(RenderLeft, RenderTop),
2415  SkPoint::Make(RenderRight, RenderBottom),
2416  };
2417  const int count1 = sizeof(points1) / sizeof(points1[0]);
2418 
2421  [=](SkCanvas* canvas, const SkPaint& paint) { //
2422  // Skia requires kStroke style on horizontal and vertical
2423  // lines to get the bounds correct.
2424  // See https://bugs.chromium.org/p/skia/issues/detail?id=12446
2425  SkPaint p = paint;
2426  p.setStyle(SkPaint::kStroke_Style);
2427  canvas->drawPoints(SkCanvas::kPolygon_PointMode, count1, points1,
2428  p);
2429  },
2430  [=](DisplayListBuilder& builder) { //
2431  builder.drawPoints(SkCanvas::kPolygon_PointMode, count1, points1);
2432  },
2433  kDrawPointsAsPolygonFlags));
2434 }
2435 
2436 TEST_F(DisplayListCanvas, DrawVerticesWithColors) {
2437  // Cover as many sides of the box with only 6 vertices:
2438  // +----------+
2439  // |xxxxxxxxxx|
2440  // | xxxxxx|
2441  // | xxx|
2442  // |xxx |
2443  // |xxxxxx |
2444  // |xxxxxxxxxx|
2445  // +----------|
2446  const SkPoint pts[6] = {
2447  // Upper-Right corner, full top, half right coverage
2448  SkPoint::Make(RenderLeft, RenderTop),
2449  SkPoint::Make(RenderRight, RenderTop),
2450  SkPoint::Make(RenderRight, RenderCenterY),
2451  // Lower-Left corner, full bottom, half left coverage
2452  SkPoint::Make(RenderLeft, RenderBottom),
2453  SkPoint::Make(RenderLeft, RenderCenterY),
2454  SkPoint::Make(RenderRight, RenderBottom),
2455  };
2456  const SkColor colors[6] = {
2457  SK_ColorRED, SK_ColorBLUE, SK_ColorGREEN,
2458  SK_ColorCYAN, SK_ColorYELLOW, SK_ColorMAGENTA,
2459  };
2460  const sk_sp<SkVertices> vertices = SkVertices::MakeCopy(
2461  SkVertices::kTriangles_VertexMode, 6, pts, nullptr, colors);
2462 
2465  [=](SkCanvas* canvas, const SkPaint& paint) { //
2466  canvas->drawVertices(vertices.get(), SkBlendMode::kSrcOver, paint);
2467  },
2468  [=](DisplayListBuilder& builder) { //
2469  builder.drawVertices(vertices, SkBlendMode::kSrcOver);
2470  },
2471  kDrawVerticesFlags)
2472  .set_draw_vertices());
2473  EXPECT_TRUE(vertices->unique());
2474 }
2475 
2476 TEST_F(DisplayListCanvas, DrawVerticesWithImage) {
2477  // Cover as many sides of the box with only 6 vertices:
2478  // +----------+
2479  // |xxxxxxxxxx|
2480  // | xxxxxx|
2481  // | xxx|
2482  // |xxx |
2483  // |xxxxxx |
2484  // |xxxxxxxxxx|
2485  // +----------|
2486  const SkPoint pts[6] = {
2487  // Upper-Right corner, full top, half right coverage
2488  SkPoint::Make(RenderLeft, RenderTop),
2489  SkPoint::Make(RenderRight, RenderTop),
2490  SkPoint::Make(RenderRight, RenderCenterY),
2491  // Lower-Left corner, full bottom, half left coverage
2492  SkPoint::Make(RenderLeft, RenderBottom),
2493  SkPoint::Make(RenderLeft, RenderCenterY),
2494  SkPoint::Make(RenderRight, RenderBottom),
2495  };
2496  const SkPoint tex[6] = {
2497  SkPoint::Make(RenderWidth / 2.0, 0),
2498  SkPoint::Make(0, RenderHeight),
2499  SkPoint::Make(RenderWidth, RenderHeight),
2500  SkPoint::Make(RenderWidth / 2, RenderHeight),
2501  SkPoint::Make(0, 0),
2502  SkPoint::Make(RenderWidth, 0),
2503  };
2504  const sk_sp<SkVertices> vertices = SkVertices::MakeCopy(
2505  SkVertices::kTriangles_VertexMode, 6, pts, tex, nullptr);
2506 
2509  [=](SkCanvas* canvas, const SkPaint& paint) { //
2510  SkPaint v_paint = paint;
2511  if (v_paint.getShader() == nullptr) {
2512  v_paint.setShader(CanvasCompareTester::testImageShader);
2513  }
2514  canvas->drawVertices(vertices.get(), SkBlendMode::kSrcOver,
2515  v_paint);
2516  },
2517  [=](DisplayListBuilder& builder) { //
2518  if (builder.getShader() == nullptr) {
2519  builder.setShader(CanvasCompareTester::testImageShader);
2520  }
2521  builder.drawVertices(vertices, SkBlendMode::kSrcOver);
2522  },
2523  kDrawVerticesFlags)
2524  .set_draw_vertices());
2525 
2526  EXPECT_TRUE(vertices->unique());
2527  EXPECT_TRUE(CanvasCompareTester::testImageShader->unique());
2528 }
2529 
2530 TEST_F(DisplayListCanvas, DrawImageNearest) {
2533  [=](SkCanvas* canvas, const SkPaint& paint) { //
2534  canvas->drawImage(CanvasCompareTester::testImage, //
2535  RenderLeft, RenderTop,
2537  },
2538  [=](DisplayListBuilder& builder) { //
2539  builder.drawImage(CanvasCompareTester::testImage,
2540  SkPoint::Make(RenderLeft, RenderTop),
2542  },
2543  kDrawImageWithPaintFlags));
2544 }
2545 
2546 TEST_F(DisplayListCanvas, DrawImageNearestNoPaint) {
2549  [=](SkCanvas* canvas, const SkPaint& paint) { //
2550  canvas->drawImage(CanvasCompareTester::testImage, //
2551  RenderLeft, RenderTop,
2552  DisplayList::NearestSampling, nullptr);
2553  },
2554  [=](DisplayListBuilder& builder) { //
2555  builder.drawImage(CanvasCompareTester::testImage,
2556  SkPoint::Make(RenderLeft, RenderTop),
2558  },
2559  kDrawImageFlags));
2560 }
2561 
2562 TEST_F(DisplayListCanvas, DrawImageLinear) {
2565  [=](SkCanvas* canvas, const SkPaint& paint) { //
2566  canvas->drawImage(CanvasCompareTester::testImage, //
2567  RenderLeft, RenderTop,
2568  DisplayList::LinearSampling, &paint);
2569  },
2570  [=](DisplayListBuilder& builder) { //
2571  builder.drawImage(CanvasCompareTester::testImage,
2572  SkPoint::Make(RenderLeft, RenderTop),
2574  },
2575  kDrawImageWithPaintFlags));
2576 }
2577 
2578 TEST_F(DisplayListCanvas, DrawImageRectNearest) {
2579  SkRect src = SkRect::MakeIWH(RenderWidth, RenderHeight).makeInset(5, 5);
2580  SkRect dst = RenderBounds.makeInset(10.5, 10.5);
2583  [=](SkCanvas* canvas, const SkPaint& paint) { //
2584  canvas->drawImageRect(CanvasCompareTester::testImage, src, dst,
2586  SkCanvas::kFast_SrcRectConstraint);
2587  },
2588  [=](DisplayListBuilder& builder) { //
2589  builder.drawImageRect(CanvasCompareTester::testImage, src, dst,
2591  },
2592  kDrawImageRectWithPaintFlags));
2593 }
2594 
2595 TEST_F(DisplayListCanvas, DrawImageRectNearestNoPaint) {
2596  SkRect src = SkRect::MakeIWH(RenderWidth, RenderHeight).makeInset(5, 5);
2597  SkRect dst = RenderBounds.makeInset(10.5, 10.5);
2600  [=](SkCanvas* canvas, const SkPaint& paint) { //
2601  canvas->drawImageRect(CanvasCompareTester::testImage, src, dst,
2603  SkCanvas::kFast_SrcRectConstraint);
2604  },
2605  [=](DisplayListBuilder& builder) { //
2606  builder.drawImageRect(CanvasCompareTester::testImage, src, dst,
2608  },
2609  kDrawImageRectFlags));
2610 }
2611 
2612 TEST_F(DisplayListCanvas, DrawImageRectLinear) {
2613  SkRect src = SkRect::MakeIWH(RenderWidth, RenderHeight).makeInset(5, 5);
2614  SkRect dst = RenderBounds.makeInset(10.5, 10.5);
2617  [=](SkCanvas* canvas, const SkPaint& paint) { //
2618  canvas->drawImageRect(CanvasCompareTester::testImage, src, dst,
2620  SkCanvas::kFast_SrcRectConstraint);
2621  },
2622  [=](DisplayListBuilder& builder) { //
2623  builder.drawImageRect(CanvasCompareTester::testImage, src, dst,
2625  },
2626  kDrawImageRectWithPaintFlags));
2627 }
2628 
2629 TEST_F(DisplayListCanvas, DrawImageNineNearest) {
2630  SkIRect src = SkIRect::MakeWH(RenderWidth, RenderHeight).makeInset(25, 25);
2631  SkRect dst = RenderBounds.makeInset(10.5, 10.5);
2632  sk_sp<SkImage> image = CanvasCompareTester::testImage;
2635  [=](SkCanvas* canvas, const SkPaint& paint) {
2636  canvas->drawImageNine(image.get(), src, dst, SkFilterMode::kNearest,
2637  &paint);
2638  },
2639  [=](DisplayListBuilder& builder) {
2640  builder.drawImageNine(image, src, dst, SkFilterMode::kNearest,
2641  true);
2642  },
2643  kDrawImageNineWithPaintFlags));
2644 }
2645 
2646 TEST_F(DisplayListCanvas, DrawImageNineNearestNoPaint) {
2647  SkIRect src = SkIRect::MakeWH(RenderWidth, RenderHeight).makeInset(25, 25);
2648  SkRect dst = RenderBounds.makeInset(10.5, 10.5);
2649  sk_sp<SkImage> image = CanvasCompareTester::testImage;
2652  [=](SkCanvas* canvas, const SkPaint& paint) {
2653  canvas->drawImageNine(image.get(), src, dst, SkFilterMode::kNearest,
2654  nullptr);
2655  },
2656  [=](DisplayListBuilder& builder) {
2657  builder.drawImageNine(image, src, dst, SkFilterMode::kNearest,
2658  false);
2659  },
2660  kDrawImageNineFlags));
2661 }
2662 
2663 TEST_F(DisplayListCanvas, DrawImageNineLinear) {
2664  SkIRect src = SkIRect::MakeWH(RenderWidth, RenderHeight).makeInset(25, 25);
2665  SkRect dst = RenderBounds.makeInset(10.5, 10.5);
2666  sk_sp<SkImage> image = CanvasCompareTester::testImage;
2669  [=](SkCanvas* canvas, const SkPaint& paint) {
2670  canvas->drawImageNine(image.get(), src, dst, SkFilterMode::kLinear,
2671  &paint);
2672  },
2673  [=](DisplayListBuilder& builder) {
2674  builder.drawImageNine(image, src, dst, SkFilterMode::kLinear, true);
2675  },
2676  kDrawImageNineWithPaintFlags));
2677 }
2678 
2679 TEST_F(DisplayListCanvas, DrawImageLatticeNearest) {
2680  const SkRect dst = RenderBounds.makeInset(10.5, 10.5);
2681  const int divX[] = {
2682  RenderWidth * 1 / 4,
2683  RenderWidth * 2 / 4,
2684  RenderWidth * 3 / 4,
2685  };
2686  const int divY[] = {
2687  RenderHeight * 1 / 4,
2688  RenderHeight * 2 / 4,
2689  RenderHeight * 3 / 4,
2690  };
2691  SkCanvas::Lattice lattice = {
2692  divX, divY, nullptr, 3, 3, nullptr, nullptr,
2693  };
2694  sk_sp<SkImage> image = CanvasCompareTester::testImage;
2697  [=](SkCanvas* canvas, const SkPaint& paint) {
2698  canvas->drawImageLattice(image.get(), lattice, dst,
2699  SkFilterMode::kNearest, &paint);
2700  },
2701  [=](DisplayListBuilder& builder) {
2702  builder.drawImageLattice(image, lattice, dst,
2703  SkFilterMode::kNearest, true);
2704  },
2705  kDrawImageLatticeWithPaintFlags));
2706 }
2707 
2708 TEST_F(DisplayListCanvas, DrawImageLatticeNearestNoPaint) {
2709  const SkRect dst = RenderBounds.makeInset(10.5, 10.5);
2710  const int divX[] = {
2711  RenderWidth * 1 / 4,
2712  RenderWidth * 2 / 4,
2713  RenderWidth * 3 / 4,
2714  };
2715  const int divY[] = {
2716  RenderHeight * 1 / 4,
2717  RenderHeight * 2 / 4,
2718  RenderHeight * 3 / 4,
2719  };
2720  SkCanvas::Lattice lattice = {
2721  divX, divY, nullptr, 3, 3, nullptr, nullptr,
2722  };
2723  sk_sp<SkImage> image = CanvasCompareTester::testImage;
2726  [=](SkCanvas* canvas, const SkPaint& paint) {
2727  canvas->drawImageLattice(image.get(), lattice, dst,
2728  SkFilterMode::kNearest, nullptr);
2729  },
2730  [=](DisplayListBuilder& builder) {
2731  builder.drawImageLattice(image, lattice, dst,
2732  SkFilterMode::kNearest, false);
2733  },
2734  kDrawImageLatticeFlags));
2735 }
2736 
2737 TEST_F(DisplayListCanvas, DrawImageLatticeLinear) {
2738  const SkRect dst = RenderBounds.makeInset(10.5, 10.5);
2739  const int divX[] = {
2740  RenderWidth / 4,
2741  RenderWidth / 2,
2742  RenderWidth * 3 / 4,
2743  };
2744  const int divY[] = {
2745  RenderHeight / 4,
2746  RenderHeight / 2,
2747  RenderHeight * 3 / 4,
2748  };
2749  SkCanvas::Lattice lattice = {
2750  divX, divY, nullptr, 3, 3, nullptr, nullptr,
2751  };
2752  sk_sp<SkImage> image = CanvasCompareTester::testImage;
2755  [=](SkCanvas* canvas, const SkPaint& paint) {
2756  canvas->drawImageLattice(image.get(), lattice, dst,
2757  SkFilterMode::kLinear, &paint);
2758  },
2759  [=](DisplayListBuilder& builder) {
2760  builder.drawImageLattice(image, lattice, dst, SkFilterMode::kLinear,
2761  true);
2762  },
2763  kDrawImageLatticeWithPaintFlags));
2764 }
2765 
2766 TEST_F(DisplayListCanvas, DrawAtlasNearest) {
2767  const SkRSXform xform[] = {
2768  // clang-format off
2769  { 1.2f, 0.0f, RenderLeft, RenderTop},
2770  { 0.0f, 1.2f, RenderRight, RenderTop},
2771  {-1.2f, 0.0f, RenderRight, RenderBottom},
2772  { 0.0f, -1.2f, RenderLeft, RenderBottom},
2773  // clang-format on
2774  };
2775  const SkRect tex[] = {
2776  // clang-format off
2777  {0, 0, RenderHalfWidth, RenderHalfHeight},
2778  {RenderHalfWidth, 0, RenderWidth, RenderHalfHeight},
2779  {RenderHalfWidth, RenderHalfHeight, RenderWidth, RenderHeight},
2780  {0, RenderHalfHeight, RenderHalfWidth, RenderHeight},
2781  // clang-format on
2782  };
2783  const SkColor colors[] = {
2784  SK_ColorBLUE,
2785  SK_ColorGREEN,
2786  SK_ColorYELLOW,
2787  SK_ColorMAGENTA,
2788  };
2789  const sk_sp<SkImage> image = CanvasCompareTester::testImage;
2790  const SkSamplingOptions sampling = DisplayList::NearestSampling;
2793  [=](SkCanvas* canvas, const SkPaint& paint) {
2794  canvas->drawAtlas(image.get(), xform, tex, colors, 4,
2795  SkBlendMode::kSrcOver, sampling, nullptr, &paint);
2796  },
2797  [=](DisplayListBuilder& builder) {
2798  builder.drawAtlas(image, xform, tex, colors, 4, //
2799  SkBlendMode::kSrcOver, sampling, nullptr, true);
2800  },
2801  kDrawAtlasWithPaintFlags)
2802  .set_draw_atlas());
2803 }
2804 
2805 TEST_F(DisplayListCanvas, DrawAtlasNearestNoPaint) {
2806  const SkRSXform xform[] = {
2807  // clang-format off
2808  { 1.2f, 0.0f, RenderLeft, RenderTop},
2809  { 0.0f, 1.2f, RenderRight, RenderTop},
2810  {-1.2f, 0.0f, RenderRight, RenderBottom},
2811  { 0.0f, -1.2f, RenderLeft, RenderBottom},
2812  // clang-format on
2813  };
2814  const SkRect tex[] = {
2815  // clang-format off
2816  {0, 0, RenderHalfWidth, RenderHalfHeight},
2817  {RenderHalfWidth, 0, RenderWidth, RenderHalfHeight},
2818  {RenderHalfWidth, RenderHalfHeight, RenderWidth, RenderHeight},
2819  {0, RenderHalfHeight, RenderHalfWidth, RenderHeight},
2820  // clang-format on
2821  };
2822  const SkColor colors[] = {
2823  SK_ColorBLUE,
2824  SK_ColorGREEN,
2825  SK_ColorYELLOW,
2826  SK_ColorMAGENTA,
2827  };
2828  const sk_sp<SkImage> image = CanvasCompareTester::testImage;
2829  const SkSamplingOptions sampling = DisplayList::NearestSampling;
2832  [=](SkCanvas* canvas, const SkPaint& paint) {
2833  canvas->drawAtlas(image.get(), xform, tex, colors, 4,
2834  SkBlendMode::kSrcOver, sampling, //
2835  nullptr, nullptr);
2836  },
2837  [=](DisplayListBuilder& builder) {
2838  builder.drawAtlas(image, xform, tex, colors, 4, //
2839  SkBlendMode::kSrcOver, sampling, //
2840  nullptr, false);
2841  },
2842  kDrawAtlasFlags)
2843  .set_draw_atlas());
2844 }
2845 
2846 TEST_F(DisplayListCanvas, DrawAtlasLinear) {
2847  const SkRSXform xform[] = {
2848  // clang-format off
2849  { 1.2f, 0.0f, RenderLeft, RenderTop},
2850  { 0.0f, 1.2f, RenderRight, RenderTop},
2851  {-1.2f, 0.0f, RenderRight, RenderBottom},
2852  { 0.0f, -1.2f, RenderLeft, RenderBottom},
2853  // clang-format on
2854  };
2855  const SkRect tex[] = {
2856  // clang-format off
2857  {0, 0, RenderHalfWidth, RenderHalfHeight},
2858  {RenderHalfWidth, 0, RenderWidth, RenderHalfHeight},
2859  {RenderHalfWidth, RenderHalfHeight, RenderWidth, RenderHeight},
2860  {0, RenderHalfHeight, RenderHalfWidth, RenderHeight},
2861  // clang-format on
2862  };
2863  const SkColor colors[] = {
2864  SK_ColorBLUE,
2865  SK_ColorGREEN,
2866  SK_ColorYELLOW,
2867  SK_ColorMAGENTA,
2868  };
2869  const sk_sp<SkImage> image = CanvasCompareTester::testImage;
2870  const SkSamplingOptions sampling = DisplayList::LinearSampling;
2873  [=](SkCanvas* canvas, const SkPaint& paint) {
2874  canvas->drawAtlas(image.get(), xform, tex, colors, 2, //
2875  SkBlendMode::kSrcOver, sampling, nullptr, &paint);
2876  },
2877  [=](DisplayListBuilder& builder) {
2878  builder.drawAtlas(image, xform, tex, colors, 2, //
2879  SkBlendMode::kSrcOver, sampling, nullptr, true);
2880  },
2881  kDrawAtlasWithPaintFlags)
2882  .set_draw_atlas());
2883 }
2884 
2885 sk_sp<SkPicture> makeTestPicture() {
2886  SkPictureRecorder recorder;
2887  SkCanvas* cv = recorder.beginRecording(RenderBounds);
2888  SkPaint p;
2889  p.setStyle(SkPaint::kFill_Style);
2890  p.setColor(SK_ColorRED);
2891  cv->drawRect({RenderLeft, RenderTop, RenderCenterX, RenderCenterY}, p);
2892  p.setColor(SK_ColorBLUE);
2893  cv->drawRect({RenderCenterX, RenderTop, RenderRight, RenderCenterY}, p);
2894  p.setColor(SK_ColorGREEN);
2895  cv->drawRect({RenderLeft, RenderCenterY, RenderCenterX, RenderBottom}, p);
2896  p.setColor(SK_ColorYELLOW);
2897  cv->drawRect({RenderCenterX, RenderCenterY, RenderRight, RenderBottom}, p);
2898  return recorder.finishRecordingAsPicture();
2899 }
2900 
2901 TEST_F(DisplayListCanvas, DrawPicture) {
2902  sk_sp<SkPicture> picture = makeTestPicture();
2905  [=](SkCanvas* canvas, const SkPaint& paint) { //
2906  canvas->drawPicture(picture, nullptr, nullptr);
2907  },
2908  [=](DisplayListBuilder& builder) { //
2909  builder.drawPicture(picture, nullptr, false);
2910  },
2911  kDrawPictureFlags));
2912 }
2913 
2914 TEST_F(DisplayListCanvas, DrawPictureWithMatrix) {
2915  sk_sp<SkPicture> picture = makeTestPicture();
2916  SkMatrix matrix = SkMatrix::Scale(0.9, 0.9);
2919  [=](SkCanvas* canvas, const SkPaint& paint) { //
2920  canvas->drawPicture(picture, &matrix, nullptr);
2921  },
2922  [=](DisplayListBuilder& builder) { //
2923  builder.drawPicture(picture, &matrix, false);
2924  },
2925  kDrawPictureFlags));
2926 }
2927 
2928 TEST_F(DisplayListCanvas, DrawPictureWithPaint) {
2929  sk_sp<SkPicture> picture = makeTestPicture();
2932  [=](SkCanvas* canvas, const SkPaint& paint) { //
2933  canvas->drawPicture(picture, nullptr, &paint);
2934  },
2935  [=](DisplayListBuilder& builder) { //
2936  builder.drawPicture(picture, nullptr, true);
2937  },
2938  kDrawPictureWithPaintFlags));
2939 }
2940 
2941 sk_sp<DisplayList> makeTestDisplayList() {
2942  DisplayListBuilder builder;
2943  builder.setStyle(SkPaint::kFill_Style);
2944  builder.setColor(SK_ColorRED);
2945  builder.drawRect({RenderLeft, RenderTop, RenderCenterX, RenderCenterY});
2946  builder.setColor(SK_ColorBLUE);
2947  builder.drawRect({RenderCenterX, RenderTop, RenderRight, RenderCenterY});
2948  builder.setColor(SK_ColorGREEN);
2949  builder.drawRect({RenderLeft, RenderCenterY, RenderCenterX, RenderBottom});
2950  builder.setColor(SK_ColorYELLOW);
2951  builder.drawRect({RenderCenterX, RenderCenterY, RenderRight, RenderBottom});
2952  return builder.Build();
2953 }
2954 
2955 TEST_F(DisplayListCanvas, DrawDisplayList) {
2956  sk_sp<DisplayList> display_list = makeTestDisplayList();
2959  [=](SkCanvas* canvas, const SkPaint& paint) { //
2960  display_list->RenderTo(canvas);
2961  },
2962  [=](DisplayListBuilder& builder) { //
2963  builder.drawDisplayList(display_list);
2964  },
2965  kDrawDisplayListFlags)
2966  .set_draw_display_list());
2967 }
2968 
2969 TEST_F(DisplayListCanvas, DrawTextBlob) {
2970  // TODO(https://github.com/flutter/flutter/issues/82202): Remove once the
2971  // performance overlay can use Fuchsia's font manager instead of the empty
2972  // default.
2973 #if defined(OS_FUCHSIA)
2974  GTEST_SKIP() << "Rendering comparisons require a valid default font manager";
2975 #endif // OS_FUCHSIA
2976  sk_sp<SkTextBlob> blob =
2977  CanvasCompareTester::MakeTextBlob("Testing", RenderHeight * 0.33f);
2978  SkScalar RenderY1_3 = RenderTop + RenderHeight * 0.3;
2979  SkScalar RenderY2_3 = RenderTop + RenderHeight * 0.6;
2982  [=](SkCanvas* canvas, const SkPaint& paint) { //
2983  canvas->drawTextBlob(blob, RenderLeft, RenderY1_3, paint);
2984  canvas->drawTextBlob(blob, RenderLeft, RenderY2_3, paint);
2985  canvas->drawTextBlob(blob, RenderLeft, RenderBottom, paint);
2986  },
2987  [=](DisplayListBuilder& builder) { //
2988  builder.drawTextBlob(blob, RenderLeft, RenderY1_3);
2989  builder.drawTextBlob(blob, RenderLeft, RenderY2_3);
2990  builder.drawTextBlob(blob, RenderLeft, RenderBottom);
2991  },
2992  kDrawTextBlobFlags)
2993  .set_draw_text_blob(),
2994  // From examining the bounds differential for the "Default" case, the
2995  // SkTextBlob adds a padding of ~32 on the left, ~30 on the right,
2996  // ~12 on top and ~8 on the bottom, so we add 33h & 13v allowed
2997  // padding to the tolerance
2998  CanvasCompareTester::DefaultTolerance.addBoundsPadding(33, 13));
2999  EXPECT_TRUE(blob->unique());
3000 }
3001 
3003  SkPath path;
3004  path.addRoundRect(
3005  {
3006  RenderLeft + 10,
3007  RenderTop,
3008  RenderRight - 10,
3009  RenderBottom - 20,
3010  },
3012  const SkColor color = SK_ColorDKGRAY;
3013  const SkScalar elevation = 5;
3014 
3017  [=](SkCanvas* canvas, const SkPaint& paint) { //
3018  DisplayListCanvasDispatcher::DrawShadow(canvas, path, color,
3019  elevation, false, 1.0);
3020  },
3021  [=](DisplayListBuilder& builder) { //
3022  builder.drawShadow(path, color, elevation, false, 1.0);
3023  },
3024  kDrawShadowFlags)
3025  .set_draw_shadows(),
3026  CanvasCompareTester::DefaultTolerance.addBoundsPadding(3, 3));
3027 }
3028 
3029 TEST_F(DisplayListCanvas, DrawShadowTransparentOccluder) {
3030  SkPath path;
3031  path.addRoundRect(
3032  {
3033  RenderLeft + 10,
3034  RenderTop,
3035  RenderRight - 10,
3036  RenderBottom - 20,
3037  },
3039  const SkColor color = SK_ColorDKGRAY;
3040  const SkScalar elevation = 5;
3041 
3044  [=](SkCanvas* canvas, const SkPaint& paint) { //
3045  DisplayListCanvasDispatcher::DrawShadow(canvas, path, color,
3046  elevation, true, 1.0);
3047  },
3048  [=](DisplayListBuilder& builder) { //
3049  builder.drawShadow(path, color, elevation, true, 1.0);
3050  },
3051  kDrawShadowFlags)
3052  .set_draw_shadows(),
3053  CanvasCompareTester::DefaultTolerance.addBoundsPadding(3, 3));
3054 }
3055 
3056 TEST_F(DisplayListCanvas, DrawShadowDpr) {
3057  SkPath path;
3058  path.addRoundRect(
3059  {
3060  RenderLeft + 10,
3061  RenderTop,
3062  RenderRight - 10,
3063  RenderBottom - 20,
3064  },
3066  const SkColor color = SK_ColorDKGRAY;
3067  const SkScalar elevation = 5;
3068 
3071  [=](SkCanvas* canvas, const SkPaint& paint) { //
3072  DisplayListCanvasDispatcher::DrawShadow(canvas, path, color,
3073  elevation, false, 1.5);
3074  },
3075  [=](DisplayListBuilder& builder) { //
3076  builder.drawShadow(path, color, elevation, false, 1.5);
3077  },
3078  kDrawShadowFlags)
3079  .set_draw_shadows(),
3080  CanvasCompareTester::DefaultTolerance.addBoundsPadding(3, 3));
3081 }
3082 
3083 } // namespace testing
3084 } // namespace flutter
constexpr SkPoint end_points[]
const std::function< void(DisplayListBuilder &)> DlRenderer
BoundsTolerance mulScale(SkScalar scale_x, SkScalar scale_y) const
constexpr float kPi
Definition: math.h:24
#define FML_DCHECK(condition)
Definition: logging.h:86
TestParameters(const CvRenderer &cv_renderer, const DlRenderer &dl_renderer, const DisplayListAttributeFlags &flags)
constexpr SkScalar Miter10DiamondOffsetX
const SkPoint HorizontalMiterDiamondPoints[]
const SkScalar TestDashes1[]
static void checkPixels(const SkPixmap *ref_pixels, const SkRect ref_bounds, const std::string info, const SkColor bg)
static sk_sp< SkPicture > getSkPicture(const TestParameters &testP, const CaseParameters &caseP)
const BoundsTolerance lineAdjust(const BoundsTolerance &tolerance, const SkPaint &paint, const SkMatrix &matrix) const
const std::function< void(SkCanvas *, const SkPaint &)> CvRenderer
std::unique_ptr< RenderSurface > MakeSurface(const SkColor bg=SK_ColorTRANSPARENT, int width=TestWidth, int height=TestHeight) const
void render_to(SkCanvas *canvas, SkPaint &paint) const
static void compareToReference(const SkPixmap *test_pixels, const SkPixmap *ref_pixels, const std::string info, SkRect *bounds, const BoundsTolerance *tolerance, const SkColor bg, int width=TestWidth, int height=TestHeight, bool printMismatches=false)
constexpr SkScalar MiterExtremeDiamondOffsetY
const std::function< void(SkCanvas *, SkPaint &)> CvSetup
static void RenderWithSaveRestore(const TestParameters &testP, const RenderEnvironment &env, const BoundsTolerance &tolerance)
constexpr float invert_color_matrix[20]
BoundsTolerance addBoundsPadding(SkScalar bounds_pad_x, SkScalar bounds_pad_y) const
static void DrawShadow(SkCanvas *canvas, const SkPath &path, SkColor color, float elevation, bool transparentOccluder, SkScalar dpr)
BoundsTolerance clip(SkRect clip) const
constexpr std::size_t size(T(&array)[N])
Definition: size.h:13
constexpr SkScalar Miter10DiamondOffsetY
#define FML_LOG(severity)
Definition: logging.h:65
void setStyle(SkPaint::Style style) override
CaseParameters(std::string info, CvSetup cv_setup, DlRenderer dl_setup, CvRenderer cv_restore, DlRenderer dl_restore, SkColor bg, bool has_diff_clip, bool has_mutating_save_layer)
static void RenderWithStrokes(const TestParameters &testP, const RenderEnvironment &env, const BoundsTolerance &tolerance_in)
void render_to(DisplayListBuilder &builder) const
static void RenderWithTransforms(const TestParameters &testP, const RenderEnvironment &env, const BoundsTolerance &tolerance)
const BoundsTolerance adjust(const BoundsTolerance &tolerance, const SkPaint &paint, const SkMatrix &matrix) const
FlutterSemanticsFlag flags
static void RenderAll(const TestParameters &params, const BoundsTolerance &tolerance=DefaultTolerance)
static void showBoundsOverflow(std::string info, SkIRect &bounds, const BoundsTolerance *tolerance, int pixLeft, int pixTop, int pixRight, int pixBottom)
static SkRect Scale(const SkRect &rect, const SkPoint &scales)
const SkPaint render_to(SkCanvas *canvas, const TestParameters &testP) const
static void EmptyCvRenderer(SkCanvas *, const SkPaint &)
constexpr SkScalar MiterExtremeDiamondOffsetX
constexpr SkScalar Miter4DiamondOffsetY
CaseParameters with_restore(CvRenderer cv_restore, DlRenderer dl_restore, bool mutating_layer)
static void checkGroupOpacity(const RenderEnvironment &env, sk_sp< DisplayList > display_list, const SkPixmap *ref_pixmap, const std::string info, SkColor bg)
SkPaint::Cap getCap(const SkPaint &paint, DisplayListSpecialGeometryFlags geo_flags) const
const SkScalar TestDashes2[]
bool butt_cap_becomes_square() const
Mainly for drawPoints(PointMode) where Butt caps are rendered as squares.
constexpr float rotate_color_matrix[20]
void setColor(SkColor color) override
static void RenderWithAttributes(const TestParameters &testP, const RenderEnvironment &env, const BoundsTolerance &tolerance)
static const SkSamplingOptions LinearSampling
Definition: display_list.h:159
void drawRect(const SkRect &rect) override
TEST_F(DisplayListCanvas, DrawPaint)
const sk_sp< DisplayListBuilder > builder()
CaseParameters(std::string info, CvSetup cv_setup, DlRenderer dl_setup)
bool may_have_end_caps() const
The geometry may have segments that end without closing the path.
BoundsTolerance addAbsolutePadding(SkScalar absolute_pad_x, SkScalar absolute_pad_y) const
constexpr SkScalar RenderCornerRadius
int32_t width
void init_ref(CvSetup &cv_setup, CvRenderer &cv_renderer, SkColor bg=SK_ColorTRANSPARENT)
int32_t height
void render_to(DisplayListBuilder &builder, const TestParameters &testP) const
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
sk_sp< DisplayList > Build()
constexpr float stops[]
static void RenderWith(const TestParameters &testP, const RenderEnvironment &env, const BoundsTolerance &tolerance_in, const CaseParameters &caseP)
sk_sp< DisplayList > makeTestDisplayList()
static const SkSamplingOptions NearestSampling
Definition: display_list.h:158
BoundsTolerance addDiscreteOffset(SkScalar discrete_offset) const
bool may_have_joins() const
The geometry may have segments connect non-continuously.
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition: switches.h:57
sk_sp< SkPicture > makeTestPicture()
constexpr SkScalar Miter4DiamondOffsetX
static sk_sp< SkTextBlob > MakeTextBlob(std::string string, SkScalar font_height)
#define FML_DISALLOW_COPY_AND_ASSIGN(TypeName)
Definition: macros.h:27
constexpr SkColor colors[]
static void RenderWithClips(const TestParameters &testP, const RenderEnvironment &env, const BoundsTolerance &diff_tolerance)
static void EmptyDlRenderer(DisplayListBuilder &)
bool overflows(SkIRect pix_bounds, int worst_bounds_pad_x, int worst_bounds_pad_y) const
constexpr SkPoint VerticalMiterDiamondPoints[]
void init_ref(CvRenderer &cv_renderer, SkColor bg=SK_ColorTRANSPARENT)
static void quickCompareToReference(const SkPixmap *ref_pixels, const SkPixmap *test_pixels, bool should_match, const std::string info)