// Copyright (c) 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/logging.h" #include "base/strings/stringprintf.h" #include "base/time/time.h" #include "skia/ext/benchmarking_canvas.h" #include "third_party/skia/include/core/SkColorFilter.h" #include "third_party/skia/include/core/SkImageFilter.h" #include "third_party/skia/include/core/SkTLazy.h" #include "third_party/skia/include/core/SkPicture.h" #include "third_party/skia/include/core/SkRegion.h" #include "third_party/skia/include/core/SkString.h" #include "third_party/skia/include/core/SkTextBlob.h" #include "third_party/skia/include/core/SkXfermode.h" namespace { class FlagsBuilder { public: FlagsBuilder(char separator) : separator_(separator) {} void addFlag(bool flag_val, const char flag_name[]) { if (!flag_val) return; if (!oss_.str().empty()) oss_ << separator_; oss_ << flag_name; } std::string str() const { return oss_.str(); } private: char separator_; std::ostringstream oss_; }; WARN_UNUSED_RESULT scoped_ptr AsValue(bool b) { scoped_ptr val(new base::FundamentalValue(b)); return val.Pass(); } WARN_UNUSED_RESULT scoped_ptr AsValue(SkScalar scalar) { scoped_ptr val(new base::FundamentalValue(scalar)); return val.Pass(); } WARN_UNUSED_RESULT scoped_ptr AsValue(const SkSize& size) { scoped_ptr val(new base::DictionaryValue()); val->Set("width", AsValue(size.width())); val->Set("height", AsValue(size.height())); return val.Pass(); } WARN_UNUSED_RESULT scoped_ptr AsValue(const SkPoint& point) { scoped_ptr val(new base::DictionaryValue()); val->Set("x", AsValue(point.x())); val->Set("y", AsValue(point.y())); return val.Pass(); } WARN_UNUSED_RESULT scoped_ptr AsValue(const SkRect& rect) { scoped_ptr val(new base::DictionaryValue()); val->Set("left", AsValue(rect.fLeft)); val->Set("top", AsValue(rect.fTop)); val->Set("right", AsValue(rect.fRight)); val->Set("bottom", AsValue(rect.fBottom)); return val.Pass(); } WARN_UNUSED_RESULT scoped_ptr AsValue(const SkRRect& rrect) { scoped_ptr radii_val(new base::DictionaryValue()); radii_val->Set("upper-left", AsValue(rrect.radii(SkRRect::kUpperLeft_Corner))); radii_val->Set("upper-right", AsValue(rrect.radii(SkRRect::kUpperRight_Corner))); radii_val->Set("lower-right", AsValue(rrect.radii(SkRRect::kLowerRight_Corner))); radii_val->Set("lower-left", AsValue(rrect.radii(SkRRect::kLowerLeft_Corner))); scoped_ptr val(new base::DictionaryValue()); val->Set("rect", AsValue(rrect.rect())); val->Set("radii", radii_val.Pass()); return val.Pass(); } WARN_UNUSED_RESULT scoped_ptr AsValue(const SkMatrix& matrix) { scoped_ptr val(new base::ListValue()); for (int i = 0; i < 9; ++i) val->Append(AsValue(matrix[i]).release()); // no scoped_ptr-aware Append() variant return val.Pass(); } WARN_UNUSED_RESULT scoped_ptr AsValue(SkColor color) { scoped_ptr val(new base::DictionaryValue()); val->SetInteger("a", SkColorGetA(color)); val->SetInteger("r", SkColorGetR(color)); val->SetInteger("g", SkColorGetG(color)); val->SetInteger("b", SkColorGetB(color)); return val.Pass(); } WARN_UNUSED_RESULT scoped_ptr AsValue(SkXfermode::Mode mode) { scoped_ptr val( new base::StringValue(SkXfermode::ModeName(mode))); return val.Pass(); } WARN_UNUSED_RESULT scoped_ptr AsValue(SkCanvas::PointMode mode) { static const char* gModeStrings[] = { "Points", "Lines", "Polygon" }; DCHECK_LT(static_cast(mode), SK_ARRAY_COUNT(gModeStrings)); scoped_ptr val(new base::StringValue(gModeStrings[mode])); return val.Pass(); } WARN_UNUSED_RESULT scoped_ptr AsValue(const SkXfermode& xfermode) { SkXfermode::Mode mode; if (xfermode.asMode(&mode)) return AsValue(mode); scoped_ptr val(new base::StringValue("unknown")); return val.Pass(); } WARN_UNUSED_RESULT scoped_ptr AsValue(const SkColorFilter& filter) { scoped_ptr val(new base::DictionaryValue()); if (unsigned flags = filter.getFlags()) { FlagsBuilder builder('|'); builder.addFlag(flags & SkColorFilter::kAlphaUnchanged_Flag, "kAlphaUnchanged_Flag"); val->SetString("flags", builder.str()); } SkScalar color_matrix[20]; if (filter.asColorMatrix(color_matrix)) { scoped_ptr color_matrix_val(new base::ListValue()); for (unsigned i = 0; i < 20; ++i) color_matrix_val->Append(AsValue(color_matrix[i]).release()); val->Set("color_matrix", color_matrix_val.Pass()); } SkColor color; SkXfermode::Mode mode; if (filter.asColorMode(&color, &mode)) { scoped_ptr color_mode_val( new base::DictionaryValue()); color_mode_val->Set("color", AsValue(color)); color_mode_val->Set("mode", AsValue(mode)); val->Set("color_mode", color_mode_val.Pass()); } if (filter.asComponentTable(nullptr)) { scoped_ptr component_table_val( new base::DictionaryValue()); // use this as a marker for now val->Set("component_table", component_table_val.Pass()); } return val.Pass(); } WARN_UNUSED_RESULT scoped_ptr AsValue(const SkImageFilter& filter) { scoped_ptr val(new base::DictionaryValue()); val->SetInteger("inputs", filter.countInputs()); SkColorFilter* color_filter; if (filter.asColorFilter(&color_filter)) { val->Set("color_filter", AsValue(*color_filter)); SkSafeUnref(color_filter); // ref'd in asColorFilter } return val.Pass(); } WARN_UNUSED_RESULT scoped_ptr AsValue(const SkPaint& paint) { scoped_ptr val(new base::DictionaryValue()); SkPaint default_paint; if (paint.getColor() != default_paint.getColor()) val->Set("Color", AsValue(paint.getColor())); if (paint.getStyle() != default_paint.getStyle()) { static const char* gStyleStrings[] = { "Fill", "Stroke", "StrokeFill" }; DCHECK_LT(static_cast(paint.getStyle()), SK_ARRAY_COUNT(gStyleStrings)); val->SetString("Style", gStyleStrings[paint.getStyle()]); } if (paint.getXfermode() != default_paint.getXfermode()) { DCHECK(paint.getXfermode()); val->Set("Xfermode", AsValue(*paint.getXfermode())); } if (paint.getFlags()) { FlagsBuilder builder('|'); builder.addFlag(paint.isAntiAlias(), "AntiAlias"); builder.addFlag(paint.isDither(), "Dither"); builder.addFlag(paint.isUnderlineText(), "UnderlineText"); builder.addFlag(paint.isStrikeThruText(), "StrikeThruText"); builder.addFlag(paint.isFakeBoldText(), "FakeBoldText"); builder.addFlag(paint.isLinearText(), "LinearText"); builder.addFlag(paint.isSubpixelText(), "SubpixelText"); builder.addFlag(paint.isDevKernText(), "DevKernText"); builder.addFlag(paint.isLCDRenderText(), "LCDRenderText"); builder.addFlag(paint.isEmbeddedBitmapText(), "EmbeddedBitmapText"); builder.addFlag(paint.isAutohinted(), "Autohinted"); builder.addFlag(paint.isVerticalText(), "VerticalText"); builder.addFlag(paint.getFlags() & SkPaint::kGenA8FromLCD_Flag, "GenA8FromLCD"); val->SetString("Flags", builder.str()); } if (paint.getFilterQuality() != default_paint.getFilterQuality()) { static const char* gFilterQualityStrings[] = { "None", "Low", "Medium", "High"}; DCHECK_LT(static_cast(paint.getFilterQuality()), SK_ARRAY_COUNT(gFilterQualityStrings)); val->SetString("FilterLevel", gFilterQualityStrings[paint.getFilterQuality()]); } if (paint.getTextSize() != default_paint.getTextSize()) val->SetDouble("TextSize", paint.getTextSize()); if (paint.getTextScaleX() != default_paint.getTextScaleX()) val->SetDouble("TextScaleX", paint.getTextScaleX()); if (paint.getTextSkewX() != default_paint.getTextSkewX()) val->SetDouble("TextSkewX", paint.getTextSkewX()); if (paint.getColorFilter()) val->Set("ColorFilter", AsValue(*paint.getColorFilter())); if (paint.getImageFilter()) val->Set("ImageFilter", AsValue(*paint.getImageFilter())); return val.Pass(); } WARN_UNUSED_RESULT scoped_ptr AsValue(SkCanvas::SaveFlags flags) { FlagsBuilder builder('|'); builder.addFlag(flags & SkCanvas::kHasAlphaLayer_SaveFlag, "kHasAlphaLayer"); builder.addFlag(flags & SkCanvas::kFullColorLayer_SaveFlag, "kFullColorLayer"); builder.addFlag(flags & SkCanvas::kClipToLayer_SaveFlag, "kClipToLayer"); scoped_ptr val(new base::StringValue(builder.str())); return val.Pass(); } WARN_UNUSED_RESULT scoped_ptr AsValue(SkRegion::Op op) { static const char* gOpStrings[] = { "Difference", "Intersect", "Union", "XOR", "ReverseDifference", "Replace" }; DCHECK_LT(static_cast(op), SK_ARRAY_COUNT(gOpStrings)); scoped_ptr val(new base::StringValue(gOpStrings[op])); return val.Pass(); } WARN_UNUSED_RESULT scoped_ptr AsValue(const SkRegion& region) { scoped_ptr val(new base::DictionaryValue()); val->Set("bounds", AsValue(SkRect::Make(region.getBounds()))); return val.Pass(); } WARN_UNUSED_RESULT scoped_ptr AsValue(const SkPicture& picture) { scoped_ptr val(new base::DictionaryValue()); val->Set("cull-rect", AsValue(picture.cullRect())); return val.Pass(); } WARN_UNUSED_RESULT scoped_ptr AsValue(const SkBitmap& bitmap) { scoped_ptr val(new base::DictionaryValue()); val->Set("size", AsValue(SkSize::Make(bitmap.width(), bitmap.height()))); return val.Pass(); } WARN_UNUSED_RESULT scoped_ptr AsValue(const SkImage& image) { scoped_ptr val(new base::DictionaryValue()); val->Set("size", AsValue(SkSize::Make(image.width(), image.height()))); return val.Pass(); } WARN_UNUSED_RESULT scoped_ptr AsValue(const SkTextBlob& blob) { scoped_ptr val(new base::DictionaryValue()); val->Set("bounds", AsValue(blob.bounds())); return val.Pass(); } WARN_UNUSED_RESULT scoped_ptr AsValue(const SkPath& path) { scoped_ptr val(new base::DictionaryValue()); static const char* gFillStrings[] = { "winding", "even-odd", "inverse-winding", "inverse-even-odd" }; DCHECK_LT(static_cast(path.getFillType()), SK_ARRAY_COUNT(gFillStrings)); val->SetString("fill-type", gFillStrings[path.getFillType()]); static const char* gConvexityStrings[] = { "Unknown", "Convex", "Concave" }; DCHECK_LT(static_cast(path.getConvexity()), SK_ARRAY_COUNT(gConvexityStrings)); val->SetString("convexity", gConvexityStrings[path.getConvexity()]); val->SetBoolean("is-rect", path.isRect(nullptr)); val->Set("bounds", AsValue(path.getBounds())); static const char* gVerbStrings[] = { "move", "line", "quad", "conic", "cubic", "close", "done" }; static const int gPtsPerVerb[] = { 1, 1, 2, 2, 3, 0, 0 }; static const int gPtOffsetPerVerb[] = { 0, 1, 1, 1, 1, 0, 0 }; SK_COMPILE_ASSERT( SK_ARRAY_COUNT(gVerbStrings) == static_cast(SkPath::kDone_Verb + 1), gVerbStrings_size_mismatch); SK_COMPILE_ASSERT( SK_ARRAY_COUNT(gVerbStrings) == SK_ARRAY_COUNT(gPtsPerVerb), gPtsPerVerb_size_mismatch); SK_COMPILE_ASSERT( SK_ARRAY_COUNT(gVerbStrings) == SK_ARRAY_COUNT(gPtOffsetPerVerb), gPtOffsetPerVerb_size_mismatch); scoped_ptr verbs_val(new base::ListValue()); SkPath::Iter iter(const_cast(path), false); SkPoint points[4]; for(SkPath::Verb verb = iter.next(points, false); verb != SkPath::kDone_Verb; verb = iter.next(points, false)) { DCHECK_LT(static_cast(verb), SK_ARRAY_COUNT(gVerbStrings)); scoped_ptr verb_val(new base::DictionaryValue()); scoped_ptr pts_val(new base::ListValue()); for (int i = 0; i < gPtsPerVerb[verb]; ++i) pts_val->Append(AsValue(points[i + gPtOffsetPerVerb[verb]]).release()); verb_val->Set(gVerbStrings[verb], pts_val.Pass()); if (SkPath::kConic_Verb == verb) verb_val->Set("weight", AsValue(iter.conicWeight())); verbs_val->Append(verb_val.release()); } val->Set("verbs", verbs_val.Pass()); return val.Pass(); } template WARN_UNUSED_RESULT scoped_ptr AsListValue(const T array[], size_t count) { scoped_ptr val(new base::ListValue()); for (size_t i = 0; i < count; ++i) val->Append(AsValue(array[i]).release()); return val.Pass(); } class OverdrawXfermode : public SkXfermode { public: SkPMColor xferColor(SkPMColor src, SkPMColor dst) const override { // This table encodes the color progression of the overdraw visualization static const SkPMColor gTable[] = { SkPackARGB32(0x00, 0x00, 0x00, 0x00), SkPackARGB32(0xFF, 128, 158, 255), SkPackARGB32(0xFF, 170, 185, 212), SkPackARGB32(0xFF, 213, 195, 170), SkPackARGB32(0xFF, 255, 192, 127), SkPackARGB32(0xFF, 255, 185, 85), SkPackARGB32(0xFF, 255, 165, 42), SkPackARGB32(0xFF, 255, 135, 0), SkPackARGB32(0xFF, 255, 95, 0), SkPackARGB32(0xFF, 255, 50, 0), SkPackARGB32(0xFF, 255, 0, 0) }; size_t idx; if (SkColorGetR(dst) < 64) { // 0 idx = 0; } else if (SkColorGetG(dst) < 25) { // 10 idx = 9; // cap at 9 for upcoming increment } else if ((SkColorGetB(dst) + 21) / 42 > 0) { // 1-6 idx = 7 - (SkColorGetB(dst) + 21) / 42; } else { // 7-9 idx = 10 - (SkColorGetG(dst) + 22) / 45; } ++idx; SkASSERT(idx < SK_ARRAY_COUNT(gTable)); return gTable[idx]; } Factory getFactory() const override { return NULL; } #ifndef SK_IGNORE_TO_STRING void toString(SkString* str) const override { str->set("OverdrawXfermode"); } #endif }; } // namespace namespace skia { class BenchmarkingCanvas::AutoOp { public: AutoOp(BenchmarkingCanvas* canvas, const char op_name[], const SkPaint* paint = nullptr) : canvas_(canvas) , op_record_(new base::DictionaryValue()) , op_params_(new base::ListValue()) // AutoOp objects are always scoped within draw call frames, // so the paint is guaranteed to be valid for their lifetime. , paint_(paint) { DCHECK(canvas); DCHECK(op_name); op_record_->SetString("cmd_string", op_name); op_record_->Set("info", op_params_); if (paint) this->addParam("paint", AsValue(*paint)); if (canvas->flags_ & kOverdrawVisualization_Flag) { DCHECK(canvas->overdraw_xfermode_); paint_ = paint ? filtered_paint_.set(*paint) : filtered_paint_.init(); filtered_paint_.get()->setXfermode(canvas->overdraw_xfermode_.get()); filtered_paint_.get()->setAntiAlias(false); } start_ticks_ = base::TimeTicks::Now(); } ~AutoOp() { base::TimeDelta ticks = base::TimeTicks::Now() - start_ticks_; op_record_->SetDouble("cmd_time", ticks.InMillisecondsF()); canvas_->op_records_.Append(op_record_); } void addParam(const char name[], scoped_ptr value) { scoped_ptr param(new base::DictionaryValue()); param->Set(name, value.Pass()); op_params_->Append(param.release()); } const SkPaint* paint() const { return paint_; } private: BenchmarkingCanvas* canvas_; base::DictionaryValue* op_record_; base::ListValue* op_params_; base::TimeTicks start_ticks_; const SkPaint* paint_; SkTLazy filtered_paint_; }; BenchmarkingCanvas::BenchmarkingCanvas(SkCanvas* canvas, unsigned flags) : INHERITED(canvas->imageInfo().width(), canvas->imageInfo().height()) , flags_(flags) { addCanvas(canvas); if (flags & kOverdrawVisualization_Flag) overdraw_xfermode_ = AdoptRef(new OverdrawXfermode); } BenchmarkingCanvas::~BenchmarkingCanvas() { } size_t BenchmarkingCanvas::CommandCount() const { return op_records_.GetSize(); } const base::ListValue& BenchmarkingCanvas::Commands() const { return op_records_; } double BenchmarkingCanvas::GetTime(size_t index) { const base::DictionaryValue* op; if (!op_records_.GetDictionary(index, &op)) return 0; double t; if (!op->GetDouble("cmd_time", &t)) return 0; return t; } void BenchmarkingCanvas::willSave() { AutoOp op(this, "Save"); INHERITED::willSave(); } SkCanvas::SaveLayerStrategy BenchmarkingCanvas::willSaveLayer(const SkRect* rect, const SkPaint* paint, SaveFlags flags) { AutoOp op(this, "SaveLayer", paint); if (rect) op.addParam("bounds", AsValue(*rect)); if (flags) op.addParam("flags", AsValue(flags)); return INHERITED::willSaveLayer(rect, op.paint(), flags); } void BenchmarkingCanvas::willRestore() { AutoOp op(this, "Restore"); INHERITED::willRestore(); } void BenchmarkingCanvas::didConcat(const SkMatrix& m) { AutoOp op(this, "Concat"); op.addParam("matrix", AsValue(m)); INHERITED::didConcat(m); } void BenchmarkingCanvas::didSetMatrix(const SkMatrix& m) { AutoOp op(this, "SetMatrix"); op.addParam("matrix", AsValue(m)); INHERITED::didSetMatrix(m); } void BenchmarkingCanvas::onClipRect(const SkRect& rect, SkRegion::Op region_op, SkCanvas::ClipEdgeStyle style) { AutoOp op(this, "ClipRect"); op.addParam("rect", AsValue(rect)); op.addParam("op", AsValue(region_op)); op.addParam("anti-alias", AsValue(style == kSoft_ClipEdgeStyle)); INHERITED::onClipRect(rect, region_op, style); } void BenchmarkingCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op region_op, SkCanvas::ClipEdgeStyle style) { AutoOp op(this, "ClipRRect"); op.addParam("rrect", AsValue(rrect)); op.addParam("op", AsValue(region_op)); op.addParam("anti-alias", AsValue(style == kSoft_ClipEdgeStyle)); INHERITED::onClipRRect(rrect, region_op, style); } void BenchmarkingCanvas::onClipPath(const SkPath& path, SkRegion::Op region_op, SkCanvas::ClipEdgeStyle style) { AutoOp op(this, "ClipPath"); op.addParam("path", AsValue(path)); op.addParam("op", AsValue(region_op)); op.addParam("anti-alias", AsValue(style == kSoft_ClipEdgeStyle)); INHERITED::onClipPath(path, region_op, style); } void BenchmarkingCanvas::onClipRegion(const SkRegion& region, SkRegion::Op region_op) { AutoOp op(this, "ClipRegion"); op.addParam("region", AsValue(region)); op.addParam("op", AsValue(region_op)); INHERITED::onClipRegion(region, region_op); } void BenchmarkingCanvas::onDrawPaint(const SkPaint& paint) { AutoOp op(this, "DrawPaint", &paint); INHERITED::onDrawPaint(*op.paint()); } void BenchmarkingCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) { AutoOp op(this, "DrawPoints", &paint); op.addParam("mode", AsValue(mode)); op.addParam("points", AsListValue(pts, count)); INHERITED::onDrawPoints(mode, count, pts, *op.paint()); } void BenchmarkingCanvas::onDrawRect(const SkRect& rect, const SkPaint& paint) { AutoOp op(this, "DrawRect", &paint); op.addParam("rect", AsValue(rect)); INHERITED::onDrawRect(rect, *op.paint()); } void BenchmarkingCanvas::onDrawOval(const SkRect& rect, const SkPaint& paint) { AutoOp op(this, "DrawOval", &paint); op.addParam("rect", AsValue(rect)); INHERITED::onDrawOval(rect, *op.paint()); } void BenchmarkingCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) { AutoOp op(this, "DrawRRect", &paint); op.addParam("rrect", AsValue(rrect)); INHERITED::onDrawRRect(rrect, *op.paint()); } void BenchmarkingCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint) { AutoOp op(this, "DrawDRRect", &paint); op.addParam("outer", AsValue(outer)); op.addParam("inner", AsValue(inner)); INHERITED::onDrawDRRect(outer, inner, *op.paint()); } void BenchmarkingCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) { AutoOp op(this, "DrawPath", &paint); op.addParam("path", AsValue(path)); INHERITED::onDrawPath(path, *op.paint()); } void BenchmarkingCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) { DCHECK(picture); AutoOp op(this, "DrawPicture", paint); op.addParam("picture", AsValue(picture)); if (matrix) op.addParam("matrix", AsValue(*matrix)); INHERITED::onDrawPicture(picture, matrix, op.paint()); } void BenchmarkingCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top, const SkPaint* paint) { AutoOp op(this, "DrawBitmap", paint); op.addParam("bitmap", AsValue(bitmap)); op.addParam("left", AsValue(left)); op.addParam("top", AsValue(top)); INHERITED::onDrawBitmap(bitmap, left, top, op.paint()); } void BenchmarkingCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst, const SkPaint* paint, DrawBitmapRectFlags flags) { AutoOp op(this, "DrawBitmapRect", paint); op.addParam("bitmap", AsValue(bitmap)); if (src) op.addParam("src", AsValue(*src)); op.addParam("dst", AsValue(dst)); INHERITED::onDrawBitmapRect(bitmap, src, dst, op.paint(), flags); } void BenchmarkingCanvas::onDrawImage(const SkImage* image, SkScalar left, SkScalar top, const SkPaint* paint) { DCHECK(image); AutoOp op(this, "DrawImage", paint); op.addParam("image", AsValue(*image)); op.addParam("left", AsValue(left)); op.addParam("top", AsValue(top)); INHERITED::onDrawImage(image, left, top, op.paint()); } void BenchmarkingCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst, const SkPaint* paint) { DCHECK(image); AutoOp op(this, "DrawImageRect", paint); op.addParam("image", AsValue(*image)); if (src) op.addParam("src", AsValue(*src)); op.addParam("dst", AsValue(dst)); INHERITED::onDrawImageRect(image, src, dst, op.paint()); } void BenchmarkingCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst, const SkPaint* paint) { AutoOp op(this, "DrawBitmapNine", paint); op.addParam("bitmap", AsValue(bitmap)); op.addParam("center", AsValue(SkRect::Make(center))); op.addParam("dst", AsValue(dst)); INHERITED::onDrawBitmapNine(bitmap, center, dst, op.paint()); } void BenchmarkingCanvas::onDrawSprite(const SkBitmap& bitmap, int left, int top, const SkPaint* paint) { AutoOp op(this, "DrawSprite", paint); op.addParam("bitmap", AsValue(bitmap)); op.addParam("left", AsValue(SkIntToScalar(left))); op.addParam("top", AsValue(SkIntToScalar(top))); INHERITED::onDrawSprite(bitmap, left, top, op.paint()); } void BenchmarkingCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y, const SkPaint& paint) { AutoOp op(this, "DrawText", &paint); op.addParam("count", AsValue(SkIntToScalar(paint.countText(text, byteLength)))); op.addParam("x", AsValue(x)); op.addParam("y", AsValue(y)); INHERITED::onDrawText(text, byteLength, x, y, *op.paint()); } void BenchmarkingCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[], const SkPaint& paint) { AutoOp op(this, "DrawPosText", &paint); int count = paint.countText(text, byteLength); op.addParam("count", AsValue(SkIntToScalar(count))); op.addParam("pos", AsListValue(pos, count)); INHERITED::onDrawPosText(text, byteLength, pos, *op.paint()); } void BenchmarkingCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[], SkScalar constY, const SkPaint& paint) { AutoOp op(this, "DrawPosTextH", &paint); op.addParam("constY", AsValue(constY)); int count = paint.countText(text, byteLength); op.addParam("count", AsValue(SkIntToScalar(count))); op.addParam("pos", AsListValue(xpos, count)); INHERITED::onDrawPosTextH(text, byteLength, xpos, constY, *op.paint()); } void BenchmarkingCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path, const SkMatrix* matrix, const SkPaint& paint) { AutoOp op(this, "DrawTextOnPath", &paint); op.addParam("count", AsValue(SkIntToScalar(paint.countText(text, byteLength)))); op.addParam("path", AsValue(path)); if (matrix) op.addParam("matrix", AsValue(*matrix)); INHERITED::onDrawTextOnPath(text, byteLength, path, matrix, *op.paint()); } void BenchmarkingCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, const SkPaint& paint) { DCHECK(blob); AutoOp op(this, "DrawTextBlob", &paint); op.addParam("blob", AsValue(*blob)); op.addParam("x", AsValue(x)); op.addParam("y", AsValue(y)); INHERITED::onDrawTextBlob(blob, x, y, *op.paint()); } } // namespace skia