blob: 51618e1e03d635fed500e8f6505a3ef6edf1efe9 [file] [log] [blame]
/*
* Copyright (C) 2013 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "platform/graphics/LoggingCanvas.h"
#include "platform/geometry/IntSize.h"
#include "platform/graphics/ImageBuffer.h"
#include "platform/graphics/skia/ImagePixelLocker.h"
#include "platform/graphics/skia/SkiaUtils.h"
#include "platform/image-encoders/PNGImageEncoder.h"
#include "third_party/skia/include/core/SkImage.h"
#include "third_party/skia/include/core/SkImageInfo.h"
#include "third_party/skia/include/core/SkPaint.h"
#include "third_party/skia/include/core/SkPath.h"
#include "third_party/skia/include/core/SkPicture.h"
#include "third_party/skia/include/core/SkRRect.h"
#include "third_party/skia/include/core/SkRect.h"
#include "wtf/HexNumber.h"
#include "wtf/text/Base64.h"
#include "wtf/text/TextEncoding.h"
namespace blink {
namespace {
struct VerbParams {
STACK_ALLOCATED();
String name;
unsigned pointCount;
unsigned pointOffset;
VerbParams(const String& name, unsigned pointCount, unsigned pointOffset)
: name(name), pointCount(pointCount), pointOffset(pointOffset) {}
};
std::unique_ptr<JSONObject> objectForSkRect(const SkRect& rect) {
std::unique_ptr<JSONObject> rectItem = JSONObject::create();
rectItem->setDouble("left", rect.left());
rectItem->setDouble("top", rect.top());
rectItem->setDouble("right", rect.right());
rectItem->setDouble("bottom", rect.bottom());
return rectItem;
}
std::unique_ptr<JSONObject> objectForSkIRect(const SkIRect& rect) {
std::unique_ptr<JSONObject> rectItem = JSONObject::create();
rectItem->setDouble("left", rect.left());
rectItem->setDouble("top", rect.top());
rectItem->setDouble("right", rect.right());
rectItem->setDouble("bottom", rect.bottom());
return rectItem;
}
String pointModeName(SkCanvas::PointMode mode) {
switch (mode) {
case SkCanvas::kPoints_PointMode:
return "Points";
case SkCanvas::kLines_PointMode:
return "Lines";
case SkCanvas::kPolygon_PointMode:
return "Polygon";
default:
ASSERT_NOT_REACHED();
return "?";
};
}
std::unique_ptr<JSONObject> objectForSkPoint(const SkPoint& point) {
std::unique_ptr<JSONObject> pointItem = JSONObject::create();
pointItem->setDouble("x", point.x());
pointItem->setDouble("y", point.y());
return pointItem;
}
std::unique_ptr<JSONArray> arrayForSkPoints(size_t count,
const SkPoint points[]) {
std::unique_ptr<JSONArray> pointsArrayItem = JSONArray::create();
for (size_t i = 0; i < count; ++i)
pointsArrayItem->pushObject(objectForSkPoint(points[i]));
return pointsArrayItem;
}
std::unique_ptr<JSONObject> objectForRadius(const SkRRect& rrect,
SkRRect::Corner corner) {
std::unique_ptr<JSONObject> radiusItem = JSONObject::create();
SkVector radius = rrect.radii(corner);
radiusItem->setDouble("xRadius", radius.x());
radiusItem->setDouble("yRadius", radius.y());
return radiusItem;
}
String rrectTypeName(SkRRect::Type type) {
switch (type) {
case SkRRect::kEmpty_Type:
return "Empty";
case SkRRect::kRect_Type:
return "Rect";
case SkRRect::kOval_Type:
return "Oval";
case SkRRect::kSimple_Type:
return "Simple";
case SkRRect::kNinePatch_Type:
return "Nine-patch";
case SkRRect::kComplex_Type:
return "Complex";
default:
ASSERT_NOT_REACHED();
return "?";
};
}
String radiusName(SkRRect::Corner corner) {
switch (corner) {
case SkRRect::kUpperLeft_Corner:
return "upperLeftRadius";
case SkRRect::kUpperRight_Corner:
return "upperRightRadius";
case SkRRect::kLowerRight_Corner:
return "lowerRightRadius";
case SkRRect::kLowerLeft_Corner:
return "lowerLeftRadius";
default:
ASSERT_NOT_REACHED();
return "?";
}
}
std::unique_ptr<JSONObject> objectForSkRRect(const SkRRect& rrect) {
std::unique_ptr<JSONObject> rrectItem = JSONObject::create();
rrectItem->setString("type", rrectTypeName(rrect.type()));
rrectItem->setDouble("left", rrect.rect().left());
rrectItem->setDouble("top", rrect.rect().top());
rrectItem->setDouble("right", rrect.rect().right());
rrectItem->setDouble("bottom", rrect.rect().bottom());
for (int i = 0; i < 4; ++i)
rrectItem->setObject(radiusName((SkRRect::Corner)i),
objectForRadius(rrect, (SkRRect::Corner)i));
return rrectItem;
}
String fillTypeName(SkPath::FillType type) {
switch (type) {
case SkPath::kWinding_FillType:
return "Winding";
case SkPath::kEvenOdd_FillType:
return "EvenOdd";
case SkPath::kInverseWinding_FillType:
return "InverseWinding";
case SkPath::kInverseEvenOdd_FillType:
return "InverseEvenOdd";
default:
ASSERT_NOT_REACHED();
return "?";
};
}
String convexityName(SkPath::Convexity convexity) {
switch (convexity) {
case SkPath::kUnknown_Convexity:
return "Unknown";
case SkPath::kConvex_Convexity:
return "Convex";
case SkPath::kConcave_Convexity:
return "Concave";
default:
ASSERT_NOT_REACHED();
return "?";
};
}
VerbParams segmentParams(SkPath::Verb verb) {
switch (verb) {
case SkPath::kMove_Verb:
return VerbParams("Move", 1, 0);
case SkPath::kLine_Verb:
return VerbParams("Line", 1, 1);
case SkPath::kQuad_Verb:
return VerbParams("Quad", 2, 1);
case SkPath::kConic_Verb:
return VerbParams("Conic", 2, 1);
case SkPath::kCubic_Verb:
return VerbParams("Cubic", 3, 1);
case SkPath::kClose_Verb:
return VerbParams("Close", 0, 0);
case SkPath::kDone_Verb:
return VerbParams("Done", 0, 0);
default:
ASSERT_NOT_REACHED();
return VerbParams("?", 0, 0);
};
}
std::unique_ptr<JSONObject> objectForSkPath(const SkPath& path) {
std::unique_ptr<JSONObject> pathItem = JSONObject::create();
pathItem->setString("fillType", fillTypeName(path.getFillType()));
pathItem->setString("convexity", convexityName(path.getConvexity()));
pathItem->setBoolean("isRect", path.isRect(0));
SkPath::Iter iter(path, false);
SkPoint points[4];
std::unique_ptr<JSONArray> pathPointsArray = JSONArray::create();
for (SkPath::Verb verb = iter.next(points, false); verb != SkPath::kDone_Verb;
verb = iter.next(points, false)) {
VerbParams verbParams = segmentParams(verb);
std::unique_ptr<JSONObject> pathPointItem = JSONObject::create();
pathPointItem->setString("verb", verbParams.name);
ASSERT(verbParams.pointCount + verbParams.pointOffset <=
WTF_ARRAY_LENGTH(points));
pathPointItem->setArray("points",
arrayForSkPoints(verbParams.pointCount,
points + verbParams.pointOffset));
if (SkPath::kConic_Verb == verb)
pathPointItem->setDouble("conicWeight", iter.conicWeight());
pathPointsArray->pushObject(std::move(pathPointItem));
}
pathItem->setArray("pathPoints", std::move(pathPointsArray));
pathItem->setObject("bounds", objectForSkRect(path.getBounds()));
return pathItem;
}
String colorTypeName(SkColorType colorType) {
switch (colorType) {
case kUnknown_SkColorType:
return "None";
case kAlpha_8_SkColorType:
return "A8";
case kIndex_8_SkColorType:
return "Index8";
case kRGB_565_SkColorType:
return "RGB565";
case kARGB_4444_SkColorType:
return "ARGB4444";
case kN32_SkColorType:
return "ARGB8888";
default:
ASSERT_NOT_REACHED();
return "?";
};
}
std::unique_ptr<JSONObject> objectForBitmapData(const SkBitmap& bitmap) {
Vector<unsigned char> output;
if (sk_sp<SkImage> image = SkImage::MakeFromBitmap(bitmap)) {
ImagePixelLocker pixelLocker(image, kUnpremul_SkAlphaType,
kRGBA_8888_SkColorType);
ImageDataBuffer imageData(
IntSize(image->width(), image->height()),
static_cast<const unsigned char*>(pixelLocker.pixels()));
PNGImageEncoder::encode(imageData, &output);
}
std::unique_ptr<JSONObject> dataItem = JSONObject::create();
dataItem->setString(
"base64",
WTF::base64Encode(reinterpret_cast<char*>(output.data()), output.size()));
dataItem->setString("mimeType", "image/png");
return dataItem;
}
std::unique_ptr<JSONObject> objectForSkBitmap(const SkBitmap& bitmap) {
std::unique_ptr<JSONObject> bitmapItem = JSONObject::create();
bitmapItem->setInteger("width", bitmap.width());
bitmapItem->setInteger("height", bitmap.height());
bitmapItem->setString("config", colorTypeName(bitmap.colorType()));
bitmapItem->setBoolean("opaque", bitmap.isOpaque());
bitmapItem->setBoolean("immutable", bitmap.isImmutable());
bitmapItem->setBoolean("volatile", bitmap.isVolatile());
bitmapItem->setInteger("genID", bitmap.getGenerationID());
bitmapItem->setObject("data", objectForBitmapData(bitmap));
return bitmapItem;
}
std::unique_ptr<JSONObject> objectForSkImage(const SkImage* image) {
std::unique_ptr<JSONObject> imageItem = JSONObject::create();
imageItem->setInteger("width", image->width());
imageItem->setInteger("height", image->height());
imageItem->setBoolean("opaque", image->isOpaque());
imageItem->setInteger("uniqueID", image->uniqueID());
return imageItem;
}
std::unique_ptr<JSONArray> arrayForSkMatrix(const SkMatrix& matrix) {
std::unique_ptr<JSONArray> matrixArray = JSONArray::create();
for (int i = 0; i < 9; ++i)
matrixArray->pushDouble(matrix[i]);
return matrixArray;
}
std::unique_ptr<JSONObject> objectForSkShader(const SkShader& shader) {
std::unique_ptr<JSONObject> shaderItem = JSONObject::create();
const SkMatrix localMatrix = shader.getLocalMatrix();
if (!localMatrix.isIdentity())
shaderItem->setArray("localMatrix", arrayForSkMatrix(localMatrix));
return shaderItem;
}
String stringForSkColor(const SkColor& color) {
// #AARRGGBB.
Vector<LChar, 9> result;
result.append('#');
appendUnsignedAsHex(color, result);
return String(result.data(), result.size());
}
void appendFlagToString(String* flagsString, bool isSet, const String& name) {
if (!isSet)
return;
if (flagsString->length())
flagsString->append("|");
flagsString->append(name);
}
String stringForSkPaintFlags(const SkPaint& paint) {
if (!paint.getFlags())
return "none";
String flagsString = "";
appendFlagToString(&flagsString, paint.isAntiAlias(), "AntiAlias");
appendFlagToString(&flagsString, paint.isDither(), "Dither");
appendFlagToString(&flagsString, paint.isUnderlineText(), "UnderlinText");
appendFlagToString(&flagsString, paint.isStrikeThruText(), "StrikeThruText");
appendFlagToString(&flagsString, paint.isFakeBoldText(), "FakeBoldText");
appendFlagToString(&flagsString, paint.isLinearText(), "LinearText");
appendFlagToString(&flagsString, paint.isSubpixelText(), "SubpixelText");
appendFlagToString(&flagsString, paint.isDevKernText(), "DevKernText");
appendFlagToString(&flagsString, paint.isLCDRenderText(), "LCDRenderText");
appendFlagToString(&flagsString, paint.isEmbeddedBitmapText(),
"EmbeddedBitmapText");
appendFlagToString(&flagsString, paint.isAutohinted(), "Autohinted");
appendFlagToString(&flagsString, paint.isVerticalText(), "VerticalText");
return flagsString;
}
String filterQualityName(SkFilterQuality filterQuality) {
switch (filterQuality) {
case kNone_SkFilterQuality:
return "None";
case kLow_SkFilterQuality:
return "Low";
case kMedium_SkFilterQuality:
return "Medium";
case kHigh_SkFilterQuality:
return "High";
default:
ASSERT_NOT_REACHED();
return "?";
};
}
String textAlignName(SkPaint::Align align) {
switch (align) {
case SkPaint::kLeft_Align:
return "Left";
case SkPaint::kCenter_Align:
return "Center";
case SkPaint::kRight_Align:
return "Right";
default:
ASSERT_NOT_REACHED();
return "?";
};
}
String strokeCapName(SkPaint::Cap cap) {
switch (cap) {
case SkPaint::kButt_Cap:
return "Butt";
case SkPaint::kRound_Cap:
return "Round";
case SkPaint::kSquare_Cap:
return "Square";
default:
ASSERT_NOT_REACHED();
return "?";
};
}
String strokeJoinName(SkPaint::Join join) {
switch (join) {
case SkPaint::kMiter_Join:
return "Miter";
case SkPaint::kRound_Join:
return "Round";
case SkPaint::kBevel_Join:
return "Bevel";
default:
ASSERT_NOT_REACHED();
return "?";
};
}
String styleName(SkPaint::Style style) {
switch (style) {
case SkPaint::kFill_Style:
return "Fill";
case SkPaint::kStroke_Style:
return "Stroke";
case SkPaint::kStrokeAndFill_Style:
return "StrokeAndFill";
default:
ASSERT_NOT_REACHED();
return "?";
};
}
String textEncodingName(SkPaint::TextEncoding encoding) {
switch (encoding) {
case SkPaint::kUTF8_TextEncoding:
return "UTF-8";
case SkPaint::kUTF16_TextEncoding:
return "UTF-16";
case SkPaint::kUTF32_TextEncoding:
return "UTF-32";
case SkPaint::kGlyphID_TextEncoding:
return "GlyphID";
default:
ASSERT_NOT_REACHED();
return "?";
};
}
String hintingName(SkPaint::Hinting hinting) {
switch (hinting) {
case SkPaint::kNo_Hinting:
return "None";
case SkPaint::kSlight_Hinting:
return "Slight";
case SkPaint::kNormal_Hinting:
return "Normal";
case SkPaint::kFull_Hinting:
return "Full";
default:
ASSERT_NOT_REACHED();
return "?";
};
}
std::unique_ptr<JSONObject> objectForSkPaint(const SkPaint& paint) {
std::unique_ptr<JSONObject> paintItem = JSONObject::create();
paintItem->setDouble("textSize", paint.getTextSize());
paintItem->setDouble("textScaleX", paint.getTextScaleX());
paintItem->setDouble("textSkewX", paint.getTextSkewX());
if (SkShader* shader = paint.getShader())
paintItem->setObject("shader", objectForSkShader(*shader));
paintItem->setString("color", stringForSkColor(paint.getColor()));
paintItem->setDouble("strokeWidth", paint.getStrokeWidth());
paintItem->setDouble("strokeMiter", paint.getStrokeMiter());
paintItem->setString("flags", stringForSkPaintFlags(paint));
paintItem->setString("filterLevel",
filterQualityName(paint.getFilterQuality()));
paintItem->setString("textAlign", textAlignName(paint.getTextAlign()));
paintItem->setString("strokeCap", strokeCapName(paint.getStrokeCap()));
paintItem->setString("strokeJoin", strokeJoinName(paint.getStrokeJoin()));
paintItem->setString("styleName", styleName(paint.getStyle()));
paintItem->setString("textEncoding",
textEncodingName(paint.getTextEncoding()));
paintItem->setString("hinting", hintingName(paint.getHinting()));
return paintItem;
}
std::unique_ptr<JSONArray> arrayForSkScalars(size_t n,
const SkScalar scalars[]) {
std::unique_ptr<JSONArray> scalarsArray = JSONArray::create();
for (size_t i = 0; i < n; ++i)
scalarsArray->pushDouble(scalars[i]);
return scalarsArray;
}
String regionOpName(SkRegion::Op op) {
switch (op) {
case SkRegion::kDifference_Op:
return "kDifference_Op";
case SkRegion::kIntersect_Op:
return "kIntersect_Op";
case SkRegion::kUnion_Op:
return "kUnion_Op";
case SkRegion::kXOR_Op:
return "kXOR_Op";
case SkRegion::kReverseDifference_Op:
return "kReverseDifference_Op";
case SkRegion::kReplace_Op:
return "kReplace_Op";
default:
return "Unknown type";
};
}
String saveLayerFlagsToString(SkCanvas::SaveLayerFlags flags) {
String flagsString = "";
if (flags & SkCanvas::kIsOpaque_SaveLayerFlag)
flagsString.append("kIsOpaque_SaveLayerFlag ");
if (flags & SkCanvas::kPreserveLCDText_SaveLayerFlag)
flagsString.append("kPreserveLCDText_SaveLayerFlag ");
return flagsString;
}
String textEncodingCanonicalName(SkPaint::TextEncoding encoding) {
String name = textEncodingName(encoding);
if (encoding == SkPaint::kUTF16_TextEncoding ||
encoding == SkPaint::kUTF32_TextEncoding)
name.append("LE");
return name;
}
String stringForUTFText(const void* text,
size_t length,
SkPaint::TextEncoding encoding) {
return WTF::TextEncoding(textEncodingCanonicalName(encoding))
.decode((const char*)text, length);
}
String stringForText(const void* text,
size_t byteLength,
const SkPaint& paint) {
SkPaint::TextEncoding encoding = paint.getTextEncoding();
switch (encoding) {
case SkPaint::kUTF8_TextEncoding:
case SkPaint::kUTF16_TextEncoding:
case SkPaint::kUTF32_TextEncoding:
return stringForUTFText(text, byteLength, encoding);
case SkPaint::kGlyphID_TextEncoding: {
WTF::Vector<SkUnichar> dataVector(byteLength / 2);
SkUnichar* textData = dataVector.data();
paint.glyphsToUnichars(static_cast<const uint16_t*>(text), byteLength / 2,
textData);
return WTF::UTF32LittleEndianEncoding().decode(
reinterpret_cast<const char*>(textData), byteLength * 2);
}
default:
ASSERT_NOT_REACHED();
return "?";
}
}
} // namespace
class AutoLogger
: InterceptingCanvasBase::CanvasInterceptorBase<LoggingCanvas> {
public:
explicit AutoLogger(LoggingCanvas* canvas)
: InterceptingCanvasBase::CanvasInterceptorBase<LoggingCanvas>(canvas) {}
JSONObject* logItem(const String& name);
JSONObject* logItemWithParams(const String& name);
~AutoLogger() {
if (topLevelCall())
canvas()->m_log->pushObject(std::move(m_logItem));
}
private:
std::unique_ptr<JSONObject> m_logItem;
};
JSONObject* AutoLogger::logItem(const String& name) {
std::unique_ptr<JSONObject> item = JSONObject::create();
item->setString("method", name);
m_logItem = std::move(item);
return m_logItem.get();
}
JSONObject* AutoLogger::logItemWithParams(const String& name) {
JSONObject* item = logItem(name);
std::unique_ptr<JSONObject> params = JSONObject::create();
item->setObject("params", std::move(params));
return item->getObject("params");
}
LoggingCanvas::LoggingCanvas(int width, int height)
: InterceptingCanvasBase(width, height), m_log(JSONArray::create()) {}
void LoggingCanvas::onDrawPaint(const SkPaint& paint) {
AutoLogger logger(this);
logger.logItemWithParams("drawPaint")
->setObject("paint", objectForSkPaint(paint));
this->SkCanvas::onDrawPaint(paint);
}
void LoggingCanvas::onDrawPoints(PointMode mode,
size_t count,
const SkPoint pts[],
const SkPaint& paint) {
AutoLogger logger(this);
JSONObject* params = logger.logItemWithParams("drawPoints");
params->setString("pointMode", pointModeName(mode));
params->setArray("points", arrayForSkPoints(count, pts));
params->setObject("paint", objectForSkPaint(paint));
this->SkCanvas::onDrawPoints(mode, count, pts, paint);
}
void LoggingCanvas::onDrawRect(const SkRect& rect, const SkPaint& paint) {
AutoLogger logger(this);
JSONObject* params = logger.logItemWithParams("drawRect");
params->setObject("rect", objectForSkRect(rect));
params->setObject("paint", objectForSkPaint(paint));
this->SkCanvas::onDrawRect(rect, paint);
}
void LoggingCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
AutoLogger logger(this);
JSONObject* params = logger.logItemWithParams("drawOval");
params->setObject("oval", objectForSkRect(oval));
params->setObject("paint", objectForSkPaint(paint));
this->SkCanvas::onDrawOval(oval, paint);
}
void LoggingCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
AutoLogger logger(this);
JSONObject* params = logger.logItemWithParams("drawRRect");
params->setObject("rrect", objectForSkRRect(rrect));
params->setObject("paint", objectForSkPaint(paint));
this->SkCanvas::onDrawRRect(rrect, paint);
}
void LoggingCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
AutoLogger logger(this);
JSONObject* params = logger.logItemWithParams("drawPath");
params->setObject("path", objectForSkPath(path));
params->setObject("paint", objectForSkPaint(paint));
this->SkCanvas::onDrawPath(path, paint);
}
void LoggingCanvas::onDrawBitmap(const SkBitmap& bitmap,
SkScalar left,
SkScalar top,
const SkPaint* paint) {
AutoLogger logger(this);
JSONObject* params = logger.logItemWithParams("drawBitmap");
params->setDouble("left", left);
params->setDouble("top", top);
params->setObject("bitmap", objectForSkBitmap(bitmap));
if (paint)
params->setObject("paint", objectForSkPaint(*paint));
this->SkCanvas::onDrawBitmap(bitmap, left, top, paint);
}
void LoggingCanvas::onDrawBitmapRect(const SkBitmap& bitmap,
const SkRect* src,
const SkRect& dst,
const SkPaint* paint,
SrcRectConstraint constraint) {
AutoLogger logger(this);
JSONObject* params = logger.logItemWithParams("drawBitmapRectToRect");
params->setObject("bitmap", objectForSkBitmap(bitmap));
if (src)
params->setObject("src", objectForSkRect(*src));
params->setObject("dst", objectForSkRect(dst));
if (paint)
params->setObject("paint", objectForSkPaint(*paint));
params->setInteger("flags", constraint);
this->SkCanvas::onDrawBitmapRect(bitmap, src, dst, paint, constraint);
}
void LoggingCanvas::onDrawBitmapNine(const SkBitmap& bitmap,
const SkIRect& center,
const SkRect& dst,
const SkPaint* paint) {
AutoLogger logger(this);
JSONObject* params = logger.logItemWithParams("drawBitmapNine");
params->setObject("bitmap", objectForSkBitmap(bitmap));
params->setObject("center", objectForSkIRect(center));
params->setObject("dst", objectForSkRect(dst));
if (paint)
params->setObject("paint", objectForSkPaint(*paint));
this->SkCanvas::onDrawBitmapNine(bitmap, center, dst, paint);
}
void LoggingCanvas::onDrawImage(const SkImage* image,
SkScalar left,
SkScalar top,
const SkPaint* paint) {
AutoLogger logger(this);
JSONObject* params = logger.logItemWithParams("drawImage");
params->setDouble("left", left);
params->setDouble("top", top);
params->setObject("image", objectForSkImage(image));
if (paint)
params->setObject("paint", objectForSkPaint(*paint));
this->SkCanvas::onDrawImage(image, left, top, paint);
}
void LoggingCanvas::onDrawImageRect(const SkImage* image,
const SkRect* src,
const SkRect& dst,
const SkPaint* paint,
SrcRectConstraint constraint) {
AutoLogger logger(this);
JSONObject* params = logger.logItemWithParams("drawImageRect");
params->setObject("image", objectForSkImage(image));
if (src)
params->setObject("src", objectForSkRect(*src));
params->setObject("dst", objectForSkRect(dst));
if (paint)
params->setObject("paint", objectForSkPaint(*paint));
this->SkCanvas::onDrawImageRect(image, src, dst, paint, constraint);
}
void LoggingCanvas::onDrawVertices(VertexMode vmode,
int vertexCount,
const SkPoint vertices[],
const SkPoint texs[],
const SkColor colors[],
SkBlendMode bmode,
const uint16_t indices[],
int indexCount,
const SkPaint& paint) {
AutoLogger logger(this);
JSONObject* params = logger.logItemWithParams("drawVertices");
params->setObject("paint", objectForSkPaint(paint));
this->SkCanvas::onDrawVertices(vmode, vertexCount, vertices, texs, colors,
bmode, indices, indexCount, paint);
}
void LoggingCanvas::onDrawDRRect(const SkRRect& outer,
const SkRRect& inner,
const SkPaint& paint) {
AutoLogger logger(this);
JSONObject* params = logger.logItemWithParams("drawDRRect");
params->setObject("outer", objectForSkRRect(outer));
params->setObject("inner", objectForSkRRect(inner));
params->setObject("paint", objectForSkPaint(paint));
this->SkCanvas::onDrawDRRect(outer, inner, paint);
}
void LoggingCanvas::onDrawText(const void* text,
size_t byteLength,
SkScalar x,
SkScalar y,
const SkPaint& paint) {
AutoLogger logger(this);
JSONObject* params = logger.logItemWithParams("drawText");
params->setString("text", stringForText(text, byteLength, paint));
params->setDouble("x", x);
params->setDouble("y", y);
params->setObject("paint", objectForSkPaint(paint));
this->SkCanvas::onDrawText(text, byteLength, x, y, paint);
}
void LoggingCanvas::onDrawPosText(const void* text,
size_t byteLength,
const SkPoint pos[],
const SkPaint& paint) {
AutoLogger logger(this);
JSONObject* params = logger.logItemWithParams("drawPosText");
params->setString("text", stringForText(text, byteLength, paint));
size_t pointsCount = paint.countText(text, byteLength);
params->setArray("pos", arrayForSkPoints(pointsCount, pos));
params->setObject("paint", objectForSkPaint(paint));
this->SkCanvas::onDrawPosText(text, byteLength, pos, paint);
}
void LoggingCanvas::onDrawPosTextH(const void* text,
size_t byteLength,
const SkScalar xpos[],
SkScalar constY,
const SkPaint& paint) {
AutoLogger logger(this);
JSONObject* params = logger.logItemWithParams("drawPosTextH");
params->setString("text", stringForText(text, byteLength, paint));
size_t pointsCount = paint.countText(text, byteLength);
params->setArray("xpos", arrayForSkScalars(pointsCount, xpos));
params->setDouble("constY", constY);
params->setObject("paint", objectForSkPaint(paint));
this->SkCanvas::onDrawPosTextH(text, byteLength, xpos, constY, paint);
}
void LoggingCanvas::onDrawTextOnPath(const void* text,
size_t byteLength,
const SkPath& path,
const SkMatrix* matrix,
const SkPaint& paint) {
AutoLogger logger(this);
JSONObject* params = logger.logItemWithParams("drawTextOnPath");
params->setString("text", stringForText(text, byteLength, paint));
params->setObject("path", objectForSkPath(path));
if (matrix)
params->setArray("matrix", arrayForSkMatrix(*matrix));
params->setObject("paint", objectForSkPaint(paint));
this->SkCanvas::onDrawTextOnPath(text, byteLength, path, matrix, paint);
}
void LoggingCanvas::onDrawTextBlob(const SkTextBlob* blob,
SkScalar x,
SkScalar y,
const SkPaint& paint) {
AutoLogger logger(this);
JSONObject* params = logger.logItemWithParams("drawTextBlob");
params->setDouble("x", x);
params->setDouble("y", y);
params->setObject("paint", objectForSkPaint(paint));
this->SkCanvas::onDrawTextBlob(blob, x, y, paint);
}
void LoggingCanvas::onClipRect(const SkRect& rect,
SkRegion::Op op,
ClipEdgeStyle style) {
AutoLogger logger(this);
JSONObject* params = logger.logItemWithParams("clipRect");
params->setObject("rect", objectForSkRect(rect));
params->setString("SkRegion::Op", regionOpName(op));
params->setBoolean("softClipEdgeStyle", kSoft_ClipEdgeStyle == style);
this->SkCanvas::onClipRect(rect, op, style);
}
void LoggingCanvas::onClipRRect(const SkRRect& rrect,
SkRegion::Op op,
ClipEdgeStyle style) {
AutoLogger logger(this);
JSONObject* params = logger.logItemWithParams("clipRRect");
params->setObject("rrect", objectForSkRRect(rrect));
params->setString("SkRegion::Op", regionOpName(op));
params->setBoolean("softClipEdgeStyle", kSoft_ClipEdgeStyle == style);
this->SkCanvas::onClipRRect(rrect, op, style);
}
void LoggingCanvas::onClipPath(const SkPath& path,
SkRegion::Op op,
ClipEdgeStyle style) {
AutoLogger logger(this);
JSONObject* params = logger.logItemWithParams("clipPath");
params->setObject("path", objectForSkPath(path));
params->setString("SkRegion::Op", regionOpName(op));
params->setBoolean("softClipEdgeStyle", kSoft_ClipEdgeStyle == style);
this->SkCanvas::onClipPath(path, op, style);
}
void LoggingCanvas::onClipRegion(const SkRegion& region, SkRegion::Op op) {
AutoLogger logger(this);
JSONObject* params = logger.logItemWithParams("clipRegion");
params->setString("op", regionOpName(op));
this->SkCanvas::onClipRegion(region, op);
}
void LoggingCanvas::onDrawPicture(const SkPicture* picture,
const SkMatrix* matrix,
const SkPaint* paint) {
this->unrollDrawPicture(picture, matrix, paint, nullptr);
}
void LoggingCanvas::didSetMatrix(const SkMatrix& matrix) {
AutoLogger logger(this);
JSONObject* params = logger.logItemWithParams("setMatrix");
params->setArray("matrix", arrayForSkMatrix(matrix));
this->SkCanvas::didSetMatrix(matrix);
}
void LoggingCanvas::didConcat(const SkMatrix& matrix) {
AutoLogger logger(this);
JSONObject* params;
switch (matrix.getType()) {
case SkMatrix::kTranslate_Mask:
params = logger.logItemWithParams("translate");
params->setDouble("dx", matrix.getTranslateX());
params->setDouble("dy", matrix.getTranslateY());
break;
case SkMatrix::kScale_Mask:
params = logger.logItemWithParams("scale");
params->setDouble("scaleX", matrix.getScaleX());
params->setDouble("scaleY", matrix.getScaleY());
break;
default:
params = logger.logItemWithParams("concat");
params->setArray("matrix", arrayForSkMatrix(matrix));
}
this->SkCanvas::didConcat(matrix);
}
void LoggingCanvas::willSave() {
AutoLogger logger(this);
logger.logItem("save");
this->SkCanvas::willSave();
}
SkCanvas::SaveLayerStrategy LoggingCanvas::getSaveLayerStrategy(
const SaveLayerRec& rec) {
AutoLogger logger(this);
JSONObject* params = logger.logItemWithParams("saveLayer");
if (rec.fBounds)
params->setObject("bounds", objectForSkRect(*rec.fBounds));
if (rec.fPaint)
params->setObject("paint", objectForSkPaint(*rec.fPaint));
params->setString("saveFlags", saveLayerFlagsToString(rec.fSaveLayerFlags));
return this->SkCanvas::getSaveLayerStrategy(rec);
}
void LoggingCanvas::willRestore() {
AutoLogger logger(this);
logger.logItem("restore");
this->SkCanvas::willRestore();
}
std::unique_ptr<JSONArray> LoggingCanvas::log() {
return JSONArray::from(m_log->clone());
}
#ifndef NDEBUG
String pictureAsDebugString(const SkPicture* picture) {
const SkIRect bounds = picture->cullRect().roundOut();
LoggingCanvas canvas(bounds.width(), bounds.height());
picture->playback(&canvas);
std::unique_ptr<JSONObject> pictureAsJSON = JSONObject::create();
pictureAsJSON->setObject("cullRect", objectForSkRect(picture->cullRect()));
pictureAsJSON->setArray("operations", canvas.log());
return pictureAsJSON->toPrettyJSONString();
}
void showSkPicture(const SkPicture* picture) {
WTFLogAlways("%s\n", pictureAsDebugString(picture).utf8().data());
}
#endif
} // namespace blink