1 // Copyright 2017 The Chromium 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 #ifndef CC_TEST_PAINT_OP_HELPER_H_
6 #define CC_TEST_PAINT_OP_HELPER_H_
7 
8 #include <sstream>
9 #include <string>
10 
11 #include "base/strings/stringprintf.h"
12 #include "cc/paint/paint_filter.h"
13 #include "cc/paint/paint_op_buffer.h"
14 
15 namespace cc {
16 
17 // A helper class to help with debugging PaintOp/PaintOpBuffer.
18 // Note that this file is primarily used for debugging. As such, it isn't
19 // typically a part of BUILD.gn (except for self-testing), so all of the
20 // implementation should be limited ot the header.
21 class PaintOpHelper {
22  public:
ToString(const PaintOp * base_op)23   static std::string ToString(const PaintOp* base_op) {
24     std::ostringstream str;
25     str << std::boolalpha;
26     switch (base_op->GetType()) {
27       case PaintOpType::Annotate: {
28         const auto* op = static_cast<const AnnotateOp*>(base_op);
29         str << "AnnotateOp(type="
30             << PaintOpHelper::EnumToString(op->annotation_type)
31             << ", rect=" << PaintOpHelper::SkiaTypeToString(op->rect)
32             << ", data=" << PaintOpHelper::SkiaTypeToString(op->data) << ")";
33         break;
34       }
35       case PaintOpType::ClipPath: {
36         const auto* op = static_cast<const ClipPathOp*>(base_op);
37         str << "ClipPathOp(path=" << PaintOpHelper::SkiaTypeToString(op->path)
38             << ", op=" << PaintOpHelper::SkiaTypeToString(op->op)
39             << ", antialias=" << op->antialias << ")";
40         break;
41       }
42       case PaintOpType::ClipRect: {
43         const auto* op = static_cast<const ClipRectOp*>(base_op);
44         str << "ClipRectOp(rect=" << PaintOpHelper::SkiaTypeToString(op->rect)
45             << ", op=" << PaintOpHelper::SkiaTypeToString(op->op)
46             << ", antialias=" << op->antialias << ")";
47         break;
48       }
49       case PaintOpType::ClipRRect: {
50         const auto* op = static_cast<const ClipRRectOp*>(base_op);
51         str << "ClipRRectOp(rrect="
52             << PaintOpHelper::SkiaTypeToString(op->rrect)
53             << ", op=" << PaintOpHelper::SkiaTypeToString(op->op)
54             << ", antialias=" << op->antialias << ")";
55         break;
56       }
57       case PaintOpType::Concat: {
58         const auto* op = static_cast<const ConcatOp*>(base_op);
59         str << "ConcatOp(matrix=" << PaintOpHelper::SkiaTypeToString(op->matrix)
60             << ")";
61         break;
62       }
63       case PaintOpType::CustomData: {
64         const auto* op = static_cast<const CustomDataOp*>(base_op);
65         str << "CustomDataOp(id=" << PaintOpHelper::SkiaTypeToString(op->id)
66             << ")";
67         break;
68       }
69       case PaintOpType::DrawColor: {
70         const auto* op = static_cast<const DrawColorOp*>(base_op);
71         str << "DrawColorOp(color="
72             << PaintOpHelper::SkiaTypeToString(op->color)
73             << ", mode=" << PaintOpHelper::SkiaTypeToString(op->mode) << ")";
74         break;
75       }
76       case PaintOpType::DrawDRRect: {
77         const auto* op = static_cast<const DrawDRRectOp*>(base_op);
78         str << "DrawDRRectOp(outer="
79             << PaintOpHelper::SkiaTypeToString(op->outer)
80             << ", inner=" << PaintOpHelper::SkiaTypeToString(op->inner)
81             << ", flags=" << PaintOpHelper::FlagsToString(op->flags) << ")";
82         break;
83       }
84       case PaintOpType::DrawImage: {
85         const auto* op = static_cast<const DrawImageOp*>(base_op);
86         str << "DrawImageOp(image=" << PaintOpHelper::ImageToString(op->image)
87             << ", left=" << PaintOpHelper::SkiaTypeToString(op->left)
88             << ", top=" << PaintOpHelper::SkiaTypeToString(op->top)
89             << ", flags=" << PaintOpHelper::FlagsToString(op->flags) << ")";
90         break;
91       }
92       case PaintOpType::DrawImageRect: {
93         const auto* op = static_cast<const DrawImageRectOp*>(base_op);
94         str << "DrawImageRectOp(image="
95             << PaintOpHelper::ImageToString(op->image)
96             << ", src=" << PaintOpHelper::SkiaTypeToString(op->src)
97             << ", dst=" << PaintOpHelper::SkiaTypeToString(op->dst)
98             << ", constraint=" << PaintOpHelper::EnumToString(op->constraint)
99             << ", flags=" << PaintOpHelper::FlagsToString(op->flags) << ")";
100         break;
101       }
102       case PaintOpType::DrawIRect: {
103         const auto* op = static_cast<const DrawIRectOp*>(base_op);
104         str << "DrawIRectOp(rect=" << PaintOpHelper::SkiaTypeToString(op->rect)
105             << ", flags=" << PaintOpHelper::FlagsToString(op->flags) << ")";
106         break;
107       }
108       case PaintOpType::DrawLine: {
109         const auto* op = static_cast<const DrawLineOp*>(base_op);
110         str << "DrawLineOp(x0=" << PaintOpHelper::SkiaTypeToString(op->x0)
111             << ", y0=" << PaintOpHelper::SkiaTypeToString(op->y0)
112             << ", x1=" << PaintOpHelper::SkiaTypeToString(op->x1)
113             << ", y1=" << PaintOpHelper::SkiaTypeToString(op->y1)
114             << ", flags=" << PaintOpHelper::FlagsToString(op->flags) << ")";
115         break;
116       }
117       case PaintOpType::DrawOval: {
118         const auto* op = static_cast<const DrawOvalOp*>(base_op);
119         str << "DrawOvalOp(oval=" << PaintOpHelper::SkiaTypeToString(op->oval)
120             << ", flags=" << PaintOpHelper::FlagsToString(op->flags) << ")";
121         break;
122       }
123       case PaintOpType::DrawPath: {
124         const auto* op = static_cast<const DrawPathOp*>(base_op);
125         str << "DrawPathOp(path=" << PaintOpHelper::SkiaTypeToString(op->path)
126             << ", flags=" << PaintOpHelper::FlagsToString(op->flags) << ")";
127         break;
128       }
129       case PaintOpType::DrawRecord: {
130         const auto* op = static_cast<const DrawRecordOp*>(base_op);
131         str << "DrawRecordOp(record="
132             << PaintOpHelper::RecordToString(op->record) << ")";
133         break;
134       }
135       case PaintOpType::DrawRect: {
136         const auto* op = static_cast<const DrawRectOp*>(base_op);
137         str << "DrawRectOp(rect=" << PaintOpHelper::SkiaTypeToString(op->rect)
138             << ", flags=" << PaintOpHelper::FlagsToString(op->flags) << ")";
139         break;
140       }
141       case PaintOpType::DrawRRect: {
142         const auto* op = static_cast<const DrawRRectOp*>(base_op);
143         str << "DrawRRectOp(rrect="
144             << PaintOpHelper::SkiaTypeToString(op->rrect)
145             << ", flags=" << PaintOpHelper::FlagsToString(op->flags) << ")";
146         break;
147       }
148       case PaintOpType::DrawSkottie: {
149         const auto* op = static_cast<const DrawSkottieOp*>(base_op);
150         str << "DrawSkottieOp("
151             << "skottie=" << PaintOpHelper::SkottieToString(op->skottie)
152             << ", dst=" << PaintOpHelper::SkiaTypeToString(op->dst)
153             << ", t=" << op->t << ")";
154         break;
155       }
156       case PaintOpType::DrawTextBlob: {
157         const auto* op = static_cast<const DrawTextBlobOp*>(base_op);
158         str << "DrawTextBlobOp(blob="
159             << PaintOpHelper::TextBlobToString(op->blob)
160             << ", x=" << PaintOpHelper::SkiaTypeToString(op->x)
161             << ", y=" << PaintOpHelper::SkiaTypeToString(op->y)
162             << ", flags=" << PaintOpHelper::FlagsToString(op->flags) << ")";
163         break;
164       }
165       case PaintOpType::Noop: {
166         str << "NoopOp()";
167         break;
168       }
169       case PaintOpType::Restore: {
170         str << "RestoreOp()";
171         break;
172       }
173       case PaintOpType::Rotate: {
174         const auto* op = static_cast<const RotateOp*>(base_op);
175         str << "RotateOp(degrees="
176             << PaintOpHelper::SkiaTypeToString(op->degrees) << ")";
177         break;
178       }
179       case PaintOpType::Save: {
180         str << "SaveOp()";
181         break;
182       }
183       case PaintOpType::SaveLayer: {
184         const auto* op = static_cast<const SaveLayerOp*>(base_op);
185         str << "SaveLayerOp(bounds="
186             << PaintOpHelper::SkiaTypeToString(op->bounds)
187             << ", flags=" << PaintOpHelper::FlagsToString(op->flags) << ")";
188         break;
189       }
190       case PaintOpType::SaveLayerAlpha: {
191         const auto* op = static_cast<const SaveLayerAlphaOp*>(base_op);
192         str << "SaveLayerAlphaOp(bounds="
193             << PaintOpHelper::SkiaTypeToString(op->bounds)
194             << ", alpha=" << static_cast<uint32_t>(op->alpha)
195             << ")";
196         break;
197       }
198       case PaintOpType::Scale: {
199         const auto* op = static_cast<const ScaleOp*>(base_op);
200         str << "ScaleOp(sx=" << PaintOpHelper::SkiaTypeToString(op->sx)
201             << ", sy=" << PaintOpHelper::SkiaTypeToString(op->sy) << ")";
202         break;
203       }
204       case PaintOpType::SetMatrix: {
205         const auto* op = static_cast<const SetMatrixOp*>(base_op);
206         str << "SetMatrixOp(matrix="
207             << PaintOpHelper::SkiaTypeToString(op->matrix) << ")";
208         break;
209       }
210       case PaintOpType::Translate: {
211         const auto* op = static_cast<const TranslateOp*>(base_op);
212         str << "TranslateOp(dx=" << PaintOpHelper::SkiaTypeToString(op->dx)
213             << ", dy=" << PaintOpHelper::SkiaTypeToString(op->dy) << ")";
214         break;
215       }
216       case PaintOpType::SetNodeId: {
217         const auto* op = static_cast<const SetNodeIdOp*>(base_op);
218         str << "SetNodeIdOp(id=" << op->node_id << ")";
219         break;
220       }
221     }
222     return str.str();
223   }
224 
225   template <typename T>
SkiaTypeToString(const T &)226   static std::string SkiaTypeToString(const T&) {
227     return "<unknown skia type>";
228   }
229 
SkiaTypeToString(const SkScalar & scalar)230   static std::string SkiaTypeToString(const SkScalar& scalar) {
231     return base::StringPrintf("%.3f", scalar);
232   }
233 
SkiaTypeToString(const SkPoint & point)234   static std::string SkiaTypeToString(const SkPoint& point) {
235     return base::StringPrintf("[%.3f,%.3f]", point.fX, point.fY);
236   }
237 
SkiaTypeToString(const SkRect & rect)238   static std::string SkiaTypeToString(const SkRect& rect) {
239     return base::StringPrintf("[%.3f,%.3f %.3fx%.3f]", rect.x(), rect.y(),
240                               rect.width(), rect.height());
241   }
242 
SkiaTypeToString(const SkIRect & rect)243   static std::string SkiaTypeToString(const SkIRect& rect) {
244     return base::StringPrintf("[%d,%d %dx%d]", rect.x(), rect.y(), rect.width(),
245                               rect.height());
246   }
247 
SkiaTypeToString(const SkRRect & rect)248   static std::string SkiaTypeToString(const SkRRect& rect) {
249     return base::StringPrintf("[bounded by %.3f,%.3f %.3fx%.3f]",
250                               rect.rect().x(), rect.rect().y(),
251                               rect.rect().width(), rect.rect().height());
252   }
253 
SkiaTypeToString(const ThreadsafeMatrix & matrix)254   static std::string SkiaTypeToString(const ThreadsafeMatrix& matrix) {
255     return SkiaTypeToString(static_cast<const SkMatrix&>(matrix));
256   }
257 
SkiaTypeToString(const SkMatrix & matrix)258   static std::string SkiaTypeToString(const SkMatrix& matrix) {
259     return base::StringPrintf(
260         "[%8.4f %8.4f %8.4f][%8.4f %8.4f %8.4f][%8.4f %8.4f %8.4f]", matrix[0],
261         matrix[1], matrix[2], matrix[3], matrix[4], matrix[5], matrix[6],
262         matrix[7], matrix[8]);
263   }
264 
SkiaTypeToString(const SkColor & color)265   static std::string SkiaTypeToString(const SkColor& color) {
266     return base::StringPrintf("rgba(%d, %d, %d, %d)", SkColorGetR(color),
267                               SkColorGetG(color), SkColorGetB(color),
268                               SkColorGetA(color));
269   }
270 
SkiaTypeToString(const SkBlendMode & mode)271   static std::string SkiaTypeToString(const SkBlendMode& mode) {
272     switch (mode) {
273       default:
274         break;
275       case SkBlendMode::kClear:
276         return "kClear";
277       case SkBlendMode::kSrc:
278         return "kSrc";
279       case SkBlendMode::kDst:
280         return "kDst";
281       case SkBlendMode::kSrcOver:
282         return "kSrcOver";
283       case SkBlendMode::kDstOver:
284         return "kDstOver";
285       case SkBlendMode::kSrcIn:
286         return "kSrcIn";
287       case SkBlendMode::kDstIn:
288         return "kDstIn";
289       case SkBlendMode::kSrcOut:
290         return "kSrcOut";
291       case SkBlendMode::kDstOut:
292         return "kDstOut";
293       case SkBlendMode::kSrcATop:
294         return "kSrcATop";
295       case SkBlendMode::kDstATop:
296         return "kDstATop";
297       case SkBlendMode::kXor:
298         return "kXor";
299       case SkBlendMode::kPlus:
300         return "kPlus";
301       case SkBlendMode::kModulate:
302         return "kModulate";
303       case SkBlendMode::kScreen:
304         return "kScreen";
305       case SkBlendMode::kOverlay:
306         return "kOverlay";
307       case SkBlendMode::kDarken:
308         return "kDarken";
309       case SkBlendMode::kLighten:
310         return "kLighten";
311       case SkBlendMode::kColorDodge:
312         return "kColorDodge";
313       case SkBlendMode::kColorBurn:
314         return "kColorBurn";
315       case SkBlendMode::kHardLight:
316         return "kHardLight";
317       case SkBlendMode::kSoftLight:
318         return "kSoftLight";
319       case SkBlendMode::kDifference:
320         return "kDifference";
321       case SkBlendMode::kExclusion:
322         return "kExclusion";
323       case SkBlendMode::kMultiply:
324         return "kMultiply";
325       case SkBlendMode::kHue:
326         return "kHue";
327       case SkBlendMode::kSaturation:
328         return "kSaturation";
329       case SkBlendMode::kColor:
330         return "kColor";
331       case SkBlendMode::kLuminosity:
332         return "kLuminosity";
333     }
334     return "<unknown SkBlendMode>";
335   }
336 
SkiaTypeToString(const SkClipOp & op)337   static std::string SkiaTypeToString(const SkClipOp& op) {
338     switch (op) {
339       default:
340         break;
341       case SkClipOp::kDifference:
342         return "kDifference";
343       case SkClipOp::kIntersect:
344         return "kIntersect";
345     }
346     return "<unknown SkClipOp>";
347   }
348 
SkiaTypeToString(const sk_sp<SkData> data)349   static std::string SkiaTypeToString(const sk_sp<SkData> data) {
350     return data ? "<SkData>" : "(nil)";
351   }
352 
SkiaTypeToString(const ThreadsafePath & path)353   static std::string SkiaTypeToString(const ThreadsafePath& path) {
354     return SkiaTypeToString(static_cast<const SkPath&>(path));
355   }
356 
SkiaTypeToString(const SkPath & path)357   static std::string SkiaTypeToString(const SkPath& path) {
358     // TODO(vmpstr): SkPath has a dump function which we can use here?
359     return "<SkPath>";
360   }
361 
SkiaTypeToString(SkFilterQuality quality)362   static std::string SkiaTypeToString(SkFilterQuality quality) {
363     switch (quality) {
364       case kNone_SkFilterQuality:
365         return "kNone_SkFilterQuality";
366       case kLow_SkFilterQuality:
367         return "kLow_SkFilterQuality";
368       case kMedium_SkFilterQuality:
369         return "kMedium_SkFilterQuality";
370       case kHigh_SkFilterQuality:
371         return "kHigh_SkFilterQuality";
372     }
373     return "<unknown SkFilterQuality>";
374   }
375 
SkiaTypeToString(PaintFlags::Cap cap)376   static std::string SkiaTypeToString(PaintFlags::Cap cap) {
377     switch (cap) {
378       case PaintFlags::kButt_Cap:
379         return "kButt_Cap";
380       case PaintFlags::kRound_Cap:
381         return "kRound_Cap";
382       case PaintFlags::kSquare_Cap:
383         return "kSquare_Cap";
384     }
385     return "<unknown PaintFlags::Cap>";
386   }
387 
SkiaTypeToString(PaintFlags::Join join)388   static std::string SkiaTypeToString(PaintFlags::Join join) {
389     switch (join) {
390       case PaintFlags::kMiter_Join:
391         return "kMiter_Join";
392       case PaintFlags::kRound_Join:
393         return "kRound_Join";
394       case PaintFlags::kBevel_Join:
395         return "kBevel_Join";
396     }
397     return "<unknown PaintFlags::Join>";
398   }
399 
SkiaTypeToString(const sk_sp<SkColorFilter> & filter)400   static std::string SkiaTypeToString(const sk_sp<SkColorFilter>& filter) {
401     if (!filter)
402       return "(nil)";
403     return "SkColorFilter";
404   }
405 
SkiaTypeToString(const sk_sp<SkMaskFilter> & filter)406   static std::string SkiaTypeToString(const sk_sp<SkMaskFilter>& filter) {
407     if (!filter)
408       return "(nil)";
409     return "SkMaskFilter";
410   }
411 
SkiaTypeToString(const sk_sp<SkPathEffect> & effect)412   static std::string SkiaTypeToString(const sk_sp<SkPathEffect>& effect) {
413     if (!effect)
414       return "(nil)";
415     return "SkPathEffect";
416   }
417 
SkiaTypeToString(const sk_sp<SkDrawLooper> & looper)418   static std::string SkiaTypeToString(const sk_sp<SkDrawLooper>& looper) {
419     if (!looper)
420       return "(nil)";
421     return "SkDrawLooper";
422   }
423 
424   template <typename T>
EnumToString(T)425   static std::string EnumToString(T) {
426     return "<unknown enum type>";
427   }
428 
EnumToString(PaintCanvas::AnnotationType type)429   static std::string EnumToString(PaintCanvas::AnnotationType type) {
430     switch (type) {
431       default:
432         break;
433       case PaintCanvas::AnnotationType::URL:
434         return "URL";
435       case PaintCanvas::AnnotationType::NAMED_DESTINATION:
436         return "NAMED_DESTINATION";
437       case PaintCanvas::AnnotationType::LINK_TO_DESTINATION:
438         return "LINK_TO_DESTINATION";
439     }
440     return "<unknown AnnotationType>";
441   }
442 
EnumToString(const SkCanvas::SrcRectConstraint & constraint)443   static std::string EnumToString(
444       const SkCanvas::SrcRectConstraint& constraint) {
445     switch (constraint) {
446       default:
447         break;
448       case SkCanvas::kStrict_SrcRectConstraint:
449         return "kStrict_SrcRectConstraint";
450       case SkCanvas::kFast_SrcRectConstraint:
451         return "kFast_SrcRectConstraint";
452     }
453     return "<unknown SrcRectConstraint>";
454   }
455 
EnumToString(PaintShader::ScalingBehavior behavior)456   static std::string EnumToString(PaintShader::ScalingBehavior behavior) {
457     switch (behavior) {
458       case PaintShader::ScalingBehavior::kRasterAtScale:
459         return "kRasterAtScale";
460       case PaintShader::ScalingBehavior::kFixedScale:
461         return "kFixedScale";
462     }
463     return "<unknown ScalingBehavior>";
464   }
465 
EnumToString(PaintShader::Type type)466   static std::string EnumToString(PaintShader::Type type) {
467     switch (type) {
468       case PaintShader::Type::kEmpty:
469         return "kEmpty";
470       case PaintShader::Type::kColor:
471         return "kColor";
472       case PaintShader::Type::kLinearGradient:
473         return "kLinearGradient";
474       case PaintShader::Type::kRadialGradient:
475         return "kRadialGradient";
476       case PaintShader::Type::kTwoPointConicalGradient:
477         return "kTwoPointConicalGradient";
478       case PaintShader::Type::kSweepGradient:
479         return "kSweepGradient";
480       case PaintShader::Type::kImage:
481         return "kImage";
482       case PaintShader::Type::kPaintRecord:
483         return "kPaintRecord";
484       case PaintShader::Type::kShaderCount:
485         return "kShaderCount";
486     }
487     return "<unknown PaintShader::Type>";
488   }
489 
ImageToString(const PaintImage & image)490   static std::string ImageToString(const PaintImage& image) {
491     return "<paint image>";
492   }
493 
SkottieToString(scoped_refptr<SkottieWrapper> skottie)494   static std::string SkottieToString(scoped_refptr<SkottieWrapper> skottie) {
495     std::ostringstream str;
496     str << "<skottie [";
497     str << "duration=" << skottie->duration() << " seconds";
498     str << ", width="
499         << PaintOpHelper::SkiaTypeToString(skottie->size().width());
500     str << ", height="
501         << PaintOpHelper::SkiaTypeToString(skottie->size().height());
502     str << "]";
503     return str.str();
504   }
505 
RecordToString(const sk_sp<const PaintRecord> & record)506   static std::string RecordToString(const sk_sp<const PaintRecord>& record) {
507     return record ? "<paint record>" : "(nil)";
508   }
509 
TextBlobToString(const sk_sp<SkTextBlob> & blob)510   static std::string TextBlobToString(const sk_sp<SkTextBlob>& blob) {
511     return blob ? "<sk text blob>" : "(nil)";
512   }
513 
PaintShaderToString(const PaintShader * shader)514   static std::string PaintShaderToString(const PaintShader* shader) {
515     if (!shader)
516       return "(nil)";
517     std::ostringstream str;
518     str << "[type=" << EnumToString(shader->shader_type());
519     str << ", flags=" << shader->flags_;
520     str << ", end_radius=" << shader->end_radius_;
521     str << ", start_radius=" << shader->start_radius_;
522     str << ", tx=" << static_cast<unsigned>(shader->tx_);
523     str << ", ty=" << static_cast<unsigned>(shader->ty_);
524     str << ", fallback_color=" << shader->fallback_color_;
525     str << ", scaling_behavior=" << EnumToString(shader->scaling_behavior_);
526     if (shader->local_matrix_.has_value()) {
527       str << ", local_matrix=" << SkiaTypeToString(*shader->local_matrix_);
528     } else {
529       str << ", local_matrix=(nil)";
530     }
531     str << ", center=" << SkiaTypeToString(shader->center_);
532     str << ", tile=" << SkiaTypeToString(shader->tile_);
533     str << ", start_point=" << SkiaTypeToString(shader->start_point_);
534     str << ", end_point=" << SkiaTypeToString(shader->end_point_);
535     str << ", start_degrees=" << shader->start_degrees_;
536     str << ", end_degrees=" << shader->end_degrees_;
537     if (shader->shader_type() == PaintShader::Type::kImage)
538       str << ", image=" << ImageToString(shader->image_);
539     else
540       str << ", image=(nil)";
541     str << ", record=" << RecordToString(shader->record_);
542     str << ", id=" << shader->id_;
543     str << ", tile_scale=";
544     if (shader->tile_scale_) {
545       str << "[" << shader->tile_scale_->ToString() << "]";
546     } else {
547       str << "(nil)";
548     }
549     if (shader->colors_.size() > 0) {
550       str << ", colors=[" << shader->colors_[0];
551       for (size_t i = 1; i < shader->colors_.size(); ++i) {
552         str << ", " << shader->colors_[i];
553       }
554       str << "]";
555     } else {
556       str << ", colors=(nil)";
557     }
558     if (shader->positions_.size() > 0) {
559       str << ", positions=[" << shader->positions_[0];
560       for (size_t i = 1; i < shader->positions_.size(); ++i) {
561         str << ", " << shader->positions_[i];
562       }
563       str << "]";
564     } else {
565       str << ", positions=(nil)";
566     }
567     str << "]";
568 
569     return str.str();
570   }
571 
PaintFilterToString(const sk_sp<PaintFilter> filter)572   static std::string PaintFilterToString(const sk_sp<PaintFilter> filter) {
573     return filter ? "<PaintFilter>" : "(nil)";
574   }
575 
FlagsToString(const PaintFlags & flags)576   static std::string FlagsToString(const PaintFlags& flags) {
577     std::ostringstream str;
578     str << std::boolalpha;
579     str << "[color=" << PaintOpHelper::SkiaTypeToString(flags.getColor());
580     str << ", blendMode="
581         << PaintOpHelper::SkiaTypeToString(flags.getBlendMode());
582     str << ", isAntiAlias=" << flags.isAntiAlias();
583     str << ", isDither=" << flags.isDither();
584     str << ", filterQuality="
585         << PaintOpHelper::SkiaTypeToString(flags.getFilterQuality());
586     str << ", strokeWidth="
587         << PaintOpHelper::SkiaTypeToString(flags.getStrokeWidth());
588     str << ", strokeMiter="
589         << PaintOpHelper::SkiaTypeToString(flags.getStrokeMiter());
590     str << ", strokeCap="
591         << PaintOpHelper::SkiaTypeToString(flags.getStrokeCap());
592     str << ", strokeJoin="
593         << PaintOpHelper::SkiaTypeToString(flags.getStrokeJoin());
594     str << ", colorFilter="
595         << PaintOpHelper::SkiaTypeToString(flags.getColorFilter());
596     str << ", maskFilter="
597         << PaintOpHelper::SkiaTypeToString(flags.getMaskFilter());
598     str << ", shader=" << PaintOpHelper::PaintShaderToString(flags.getShader());
599     str << ", hasShader=" << flags.HasShader();
600     str << ", shaderIsOpaque=" << (flags.HasShader() && flags.ShaderIsOpaque());
601     str << ", pathEffect="
602         << PaintOpHelper::SkiaTypeToString(flags.getPathEffect());
603     str << ", imageFilter="
604         << PaintOpHelper::PaintFilterToString(flags.getImageFilter());
605     str << ", drawLooper="
606         << PaintOpHelper::SkiaTypeToString(flags.getLooper());
607     str << ", isSimpleOpacity=" << flags.IsSimpleOpacity();
608     str << ", supportsFoldingAlpha=" << flags.SupportsFoldingAlpha();
609     str << ", isValid=" << flags.IsValid();
610     str << ", hasDiscardableImages=" << flags.HasDiscardableImages();
611     str << "]";
612     return str.str();
613   }
614 };
615 
616 }  // namespace cc
617 
618 inline ::std::ostream& operator<<(::std::ostream& os, const cc::PaintOp& op) {
619   return os << cc::PaintOpHelper::ToString(&op);
620 }
621 
622 #endif  // CC_TEST_PAINT_OP_HELPER_H_
623