diff options
Diffstat (limited to 'chromium/third_party/WebKit/Source/platform/graphics/GraphicsContextRecorder.cpp')
-rw-r--r-- | chromium/third_party/WebKit/Source/platform/graphics/GraphicsContextRecorder.cpp | 855 |
1 files changed, 851 insertions, 4 deletions
diff --git a/chromium/third_party/WebKit/Source/platform/graphics/GraphicsContextRecorder.cpp b/chromium/third_party/WebKit/Source/platform/graphics/GraphicsContextRecorder.cpp index ef7fb7e7268..4b7d9781afa 100644 --- a/chromium/third_party/WebKit/Source/platform/graphics/GraphicsContextRecorder.cpp +++ b/chromium/third_party/WebKit/Source/platform/graphics/GraphicsContextRecorder.cpp @@ -32,17 +32,27 @@ #include "platform/graphics/GraphicsContextRecorder.h" #include "platform/graphics/ImageBuffer.h" +#include "platform/graphics/ImageSource.h" +#include "platform/image-decoders/ImageDecoder.h" +#include "platform/image-decoders/ImageFrame.h" +#include "platform/image-encoders/skia/PNGImageEncoder.h" #include "third_party/skia/include/core/SkBitmapDevice.h" +#include "third_party/skia/include/core/SkPictureRecorder.h" +#include "third_party/skia/include/core/SkStream.h" +#include "wtf/HexNumber.h" +#include "wtf/text/Base64.h" +#include "wtf/text/TextEncoding.h" namespace WebCore { GraphicsContext* GraphicsContextRecorder::record(const IntSize& size, bool isCertainlyOpaque) { ASSERT(!m_picture); + ASSERT(!m_recorder); ASSERT(!m_context); - m_picture = adoptRef(new SkPicture()); m_isCertainlyOpaque = isCertainlyOpaque; - SkCanvas* canvas = m_picture->beginRecording(size.width(), size.height()); + m_recorder = adoptPtr(new SkPictureRecorder); + SkCanvas* canvas = m_recorder->beginRecording(size.width(), size.height(), 0, 0); m_context = adoptPtr(new GraphicsContext(canvas)); m_context->setTrackOpaqueRegion(isCertainlyOpaque); m_context->setCertainlyOpaque(isCertainlyOpaque); @@ -51,8 +61,9 @@ GraphicsContext* GraphicsContextRecorder::record(const IntSize& size, bool isCer PassRefPtr<GraphicsContextSnapshot> GraphicsContextRecorder::stop() { - m_picture->endRecording(); m_context.clear(); + m_picture = adoptRef(m_recorder->endRecording()); + m_recorder.clear(); return adoptRef(new GraphicsContextSnapshot(m_picture.release(), m_isCertainlyOpaque)); } @@ -153,10 +164,838 @@ private: Vector<double>* m_currentTimings; }; +class LoggingCanvas : public SkCanvas { +public: + LoggingCanvas(int width, int height) : SkCanvas(width, height) + { + m_log = JSONArray::create(); + } -PassOwnPtr<ImageBuffer> GraphicsContextSnapshot::replay(unsigned fromStep, unsigned toStep) const + void clear(SkColor color) OVERRIDE + { + addItemWithParams("clear")->setString("color", stringForSkColor(color)); + } + + void drawPaint(const SkPaint& paint) OVERRIDE + { + addItemWithParams("drawPaint")->setObject("paint", objectForSkPaint(paint)); + } + + void drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) OVERRIDE + { + RefPtr<JSONObject> params = addItemWithParams("drawPoints"); + params->setString("pointMode", pointModeName(mode)); + params->setArray("points", arrayForSkPoints(count, pts)); + params->setObject("paint", objectForSkPaint(paint)); + } + + void drawRect(const SkRect& rect, const SkPaint& paint) OVERRIDE + { + RefPtr<JSONObject> params = addItemWithParams("drawRect"); + params->setObject("rect", objectForSkRect(rect)); + params->setObject("paint", objectForSkPaint(paint)); + } + + void drawOval(const SkRect& oval, const SkPaint& paint) OVERRIDE + { + RefPtr<JSONObject> params = addItemWithParams("drawOval"); + params->setObject("oval", objectForSkRect(oval)); + params->setObject("paint", objectForSkPaint(paint)); + } + + void drawRRect(const SkRRect& rrect, const SkPaint& paint) OVERRIDE + { + RefPtr<JSONObject> params = addItemWithParams("drawRRect"); + params->setObject("rrect", objectForSkRRect(rrect)); + params->setObject("paint", objectForSkPaint(paint)); + } + + void drawPath(const SkPath& path, const SkPaint& paint) OVERRIDE + { + RefPtr<JSONObject> params = addItemWithParams("drawPath"); + params->setObject("path", objectForSkPath(path)); + params->setObject("paint", objectForSkPaint(paint)); + } + + void drawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top, const SkPaint* paint) OVERRIDE + { + RefPtr<JSONObject> params = addItemWithParams("drawBitmap"); + params->setNumber("left", left); + params->setNumber("top", top); + params->setObject("bitmap", objectForSkBitmap(bitmap)); + params->setObject("paint", objectForSkPaint(*paint)); + } + + void drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst, const SkPaint* paint, DrawBitmapRectFlags flags) OVERRIDE + { + RefPtr<JSONObject> params = addItemWithParams("drawBitmapRectToRect"); + params->setObject("bitmap", objectForSkBitmap(bitmap)); + params->setObject("src", objectForSkRect(*src)); + params->setObject("dst", objectForSkRect(dst)); + params->setObject("paint", objectForSkPaint(*paint)); + params->setNumber("flags", flags); + } + + void drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& m, const SkPaint* paint) OVERRIDE + { + RefPtr<JSONObject> params = addItemWithParams("drawBitmapMatrix"); + params->setObject("bitmap", objectForSkBitmap(bitmap)); + params->setArray("matrix", arrayForSkMatrix(m)); + params->setObject("paint", objectForSkPaint(*paint)); + } + + void drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst, const SkPaint* paint) OVERRIDE + { + RefPtr<JSONObject> params = addItemWithParams("drawBitmapNine"); + params->setObject("bitmap", objectForSkBitmap(bitmap)); + params->setObject("center", objectForSkIRect(center)); + params->setObject("dst", objectForSkRect(dst)); + params->setObject("paint", objectForSkPaint(*paint)); + } + + void drawSprite(const SkBitmap& bitmap, int left, int top, const SkPaint* paint) OVERRIDE + { + RefPtr<JSONObject> params = addItemWithParams("drawSprite"); + params->setObject("bitmap", objectForSkBitmap(bitmap)); + params->setNumber("left", left); + params->setNumber("top", top); + params->setObject("paint", objectForSkPaint(*paint)); + } + + void drawVertices(VertexMode vmode, int vertexCount, const SkPoint vertices[], const SkPoint texs[], const SkColor colors[], SkXfermode* xmode, + const uint16_t indices[], int indexCount, const SkPaint& paint) OVERRIDE + { + RefPtr<JSONObject> params = addItemWithParams("drawVertices"); + params->setObject("paint", objectForSkPaint(paint)); + } + + void drawData(const void* data, size_t length) OVERRIDE + { + RefPtr<JSONObject> params = addItemWithParams("drawData"); + params->setNumber("length", length); + } + + void beginCommentGroup(const char* description) OVERRIDE + { + RefPtr<JSONObject> params = addItemWithParams("beginCommentGroup"); + params->setString("description", description); + } + + void addComment(const char* keyword, const char* value) OVERRIDE + { + RefPtr<JSONObject> params = addItemWithParams("addComment"); + params->setString("key", keyword); + params->setString("value", value); + } + + void endCommentGroup() OVERRIDE + { + addItem("endCommentGroup"); + } + + void onDrawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint) OVERRIDE + { + RefPtr<JSONObject> params = addItemWithParams("drawDRRect"); + params->setObject("outer", objectForSkRRect(outer)); + params->setObject("inner", objectForSkRRect(inner)); + params->setObject("paint", objectForSkPaint(paint)); + } + + void onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y, const SkPaint& paint) OVERRIDE + { + RefPtr<JSONObject> params = addItemWithParams("drawText"); + params->setString("text", stringForText(text, byteLength, paint)); + params->setNumber("x", x); + params->setNumber("y", y); + params->setObject("paint", objectForSkPaint(paint)); + } + + void onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[], const SkPaint& paint) OVERRIDE + { + RefPtr<JSONObject> params = addItemWithParams("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)); + } + + void onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[], SkScalar constY, const SkPaint& paint) OVERRIDE + { + RefPtr<JSONObject> params = addItemWithParams("drawPosTextH"); + params->setString("text", stringForText(text, byteLength, paint)); + size_t pointsCount = paint.countText(text, byteLength); + params->setArray("xpos", arrayForSkScalars(pointsCount, xpos)); + params->setNumber("constY", constY); + params->setObject("paint", objectForSkPaint(paint)); + } + + void onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path, const SkMatrix* matrix, const SkPaint& paint) OVERRIDE + { + RefPtr<JSONObject> params = addItemWithParams("drawTextOnPath"); + params->setString("text", stringForText(text, byteLength, paint)); + params->setObject("path", objectForSkPath(path)); + params->setArray("matrix", arrayForSkMatrix(*matrix)); + params->setObject("paint", objectForSkPaint(paint)); + } + + void onPushCull(const SkRect& cullRect) OVERRIDE + { + RefPtr<JSONObject> params = addItemWithParams("pushCull"); + params->setObject("cullRect", objectForSkRect(cullRect)); + } + + void onPopCull() OVERRIDE + { + addItem("popCull"); + } + + void onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle style) OVERRIDE + { + RefPtr<JSONObject> params = addItemWithParams("clipRect"); + params->setObject("rect", objectForSkRect(rect)); + params->setString("SkRegion::Op", regionOpName(op)); + params->setBoolean("softClipEdgeStyle", kSoft_ClipEdgeStyle == style); + } + + void onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle style) OVERRIDE + { + RefPtr<JSONObject> params = addItemWithParams("clipRRect"); + params->setObject("rrect", objectForSkRRect(rrect)); + params->setString("SkRegion::Op", regionOpName(op)); + params->setBoolean("softClipEdgeStyle", kSoft_ClipEdgeStyle == style); + } + + void onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle style) OVERRIDE + { + RefPtr<JSONObject> params = addItemWithParams("clipPath"); + params->setObject("path", objectForSkPath(path)); + params->setString("SkRegion::Op", regionOpName(op)); + params->setBoolean("softClipEdgeStyle", kSoft_ClipEdgeStyle == style); + } + + void onClipRegion(const SkRegion& region, SkRegion::Op op) OVERRIDE + { + RefPtr<JSONObject> params = addItemWithParams("clipRegion"); + params->setString("op", regionOpName(op)); + } + + void onDrawPicture(const SkPicture* picture) OVERRIDE + { + addItemWithParams("drawPicture")->setObject("picture", objectForSkPicture(*picture)); + } + + void didSetMatrix(const SkMatrix& matrix) OVERRIDE + { + RefPtr<JSONObject> params = addItemWithParams("setMatrix"); + params->setArray("matrix", arrayForSkMatrix(matrix)); + this->SkCanvas::didSetMatrix(matrix); + } + + void didConcat(const SkMatrix& matrix) OVERRIDE + { + switch (matrix.getType()) { + case SkMatrix::kTranslate_Mask: + translate(matrix.getTranslateX(), matrix.getTranslateY()); + break; + case SkMatrix::kScale_Mask: + scale(matrix.getScaleX(), matrix.getScaleY()); + break; + default: + concat(matrix); + } + this->SkCanvas::didConcat(matrix); + } + + void willRestore() OVERRIDE + { + addItem("restore"); + this->SkCanvas::willRestore(); + } + + SaveLayerStrategy willSaveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags) OVERRIDE + { + RefPtr<JSONObject> params = addItemWithParams("saveLayer"); + if (bounds) + params->setObject("bounds", objectForSkRect(*bounds)); + params->setObject("paint", objectForSkPaint(*paint)); + params->setString("saveFlags", saveFlagsToString(flags)); + this->SkCanvas::willSaveLayer(bounds, paint, flags); + return kNoLayer_SaveLayerStrategy; + } + + void willSave() OVERRIDE + { + RefPtr<JSONObject> params = addItemWithParams("save"); + this->SkCanvas::willSave(); + } + + bool isClipEmpty() const OVERRIDE + { + return false; + } + + bool isClipRect() const OVERRIDE + { + return true; + } + +#ifdef SK_SUPPORT_LEGACY_GETCLIPTYPE + ClipType getClipType() const OVERRIDE + { + return kRect_ClipType; + } +#endif + + bool getClipBounds(SkRect* bounds) const OVERRIDE + { + if (bounds) + bounds->setXYWH(0, 0, SkIntToScalar(this->imageInfo().fWidth), SkIntToScalar(this->imageInfo().fHeight)); + return true; + } + + bool getClipDeviceBounds(SkIRect* bounds) const OVERRIDE + { + if (bounds) + bounds->setLargest(); + return true; + } + + PassRefPtr<JSONArray> log() + { + return m_log; + } + +private: + RefPtr<JSONArray> m_log; + + PassRefPtr<JSONObject> addItem(const String& name) + { + RefPtr<JSONObject> item = JSONObject::create(); + item->setString("method", name); + m_log->pushObject(item); + return item.release(); + } + + PassRefPtr<JSONObject> addItemWithParams(const String& name) + { + RefPtr<JSONObject> item = addItem(name); + RefPtr<JSONObject> params = JSONObject::create(); + item->setObject("params", params); + return params.release(); + } + + PassRefPtr<JSONObject> objectForSkRect(const SkRect& rect) + { + RefPtr<JSONObject> rectItem = JSONObject::create(); + rectItem->setNumber("left", rect.left()); + rectItem->setNumber("top", rect.top()); + rectItem->setNumber("right", rect.right()); + rectItem->setNumber("bottom", rect.bottom()); + return rectItem.release(); + } + + PassRefPtr<JSONObject> objectForSkIRect(const SkIRect& rect) + { + RefPtr<JSONObject> rectItem = JSONObject::create(); + rectItem->setNumber("left", rect.left()); + rectItem->setNumber("top", rect.top()); + rectItem->setNumber("right", rect.right()); + rectItem->setNumber("bottom", rect.bottom()); + return rectItem.release(); + } + + String pointModeName(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 "?"; + }; + } + + PassRefPtr<JSONObject> objectForSkPoint(const SkPoint& point) + { + RefPtr<JSONObject> pointItem = JSONObject::create(); + pointItem->setNumber("x", point.x()); + pointItem->setNumber("y", point.y()); + return pointItem.release(); + } + + PassRefPtr<JSONArray> arrayForSkPoints(size_t count, const SkPoint points[]) + { + RefPtr<JSONArray> pointsArrayItem = JSONArray::create(); + for (size_t i = 0; i < count; ++i) + pointsArrayItem->pushObject(objectForSkPoint(points[i])); + return pointsArrayItem.release(); + } + + PassRefPtr<JSONObject> objectForSkPicture(const SkPicture& picture) + { + RefPtr<JSONObject> pictureItem = JSONObject::create(); + pictureItem->setNumber("width", picture.width()); + pictureItem->setNumber("height", picture.height()); + return pictureItem.release(); + } + + PassRefPtr<JSONObject> objectForRadius(const SkRRect& rrect, SkRRect::Corner corner) + { + RefPtr<JSONObject> radiusItem = JSONObject::create(); + SkVector radius = rrect.radii(corner); + radiusItem->setNumber("xRadius", radius.x()); + radiusItem->setNumber("yRadius", radius.y()); + return radiusItem.release(); + } + + 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 "?"; + } + } + + PassRefPtr<JSONObject> objectForSkRRect(const SkRRect& rrect) + { + RefPtr<JSONObject> rrectItem = JSONObject::create(); + rrectItem->setString("type", rrectTypeName(rrect.type())); + rrectItem->setNumber("left", rrect.rect().left()); + rrectItem->setNumber("top", rrect.rect().top()); + rrectItem->setNumber("right", rrect.rect().right()); + rrectItem->setNumber("bottom", rrect.rect().bottom()); + for (int i = 0; i < 4; ++i) + rrectItem->setObject(radiusName((SkRRect::Corner) i), objectForRadius(rrect, (SkRRect::Corner) i)); + return rrectItem.release(); + } + + 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 "?"; + }; + } + + String verbName(SkPath::Verb verb) + { + switch (verb) { + case SkPath::kMove_Verb: return "Move"; + case SkPath::kLine_Verb: return "Line"; + case SkPath::kQuad_Verb: return "Quad"; + case SkPath::kConic_Verb: return "Conic"; + case SkPath::kCubic_Verb: return "Cubic"; + case SkPath::kClose_Verb: return "Close"; + case SkPath::kDone_Verb: return "Done"; + default: + ASSERT_NOT_REACHED(); + return "?"; + }; + } + + struct VerbParams { + String name; + unsigned pointCount; + unsigned pointOffset; + + VerbParams(const String& name, unsigned pointCount, unsigned pointOffset) + : name(name) + , pointCount(pointCount) + , pointOffset(pointOffset) { } + }; + + 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); + }; + } + + PassRefPtr<JSONObject> objectForSkPath(const SkPath& path) + { + RefPtr<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]; + RefPtr<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); + RefPtr<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->setNumber("conicWeight", iter.conicWeight()); + pathPointsArray->pushObject(pathPointItem); + } + pathItem->setArray("pathPoints", pathPointsArray); + pathItem->setObject("bounds", objectForSkRect(path.getBounds())); + return pathItem.release(); + } + + String configName(SkBitmap::Config config) + { + switch (config) { + case SkBitmap::kNo_Config: return "None"; + case SkBitmap::kA8_Config: return "A8"; + case SkBitmap::kIndex8_Config: return "Index8"; + case SkBitmap::kRGB_565_Config: return "RGB565"; + case SkBitmap::kARGB_4444_Config: return "ARGB4444"; + case SkBitmap::kARGB_8888_Config: return "ARGB8888"; + default: + ASSERT_NOT_REACHED(); + return "?"; + }; + } + + PassRefPtr<JSONObject> objectForBitmapData(const SkBitmap& bitmap) + { + RefPtr<JSONObject> dataItem = JSONObject::create(); + Vector<unsigned char> output; + WebCore::PNGImageEncoder::encode(bitmap, &output); + dataItem->setString("base64", WTF::base64Encode(reinterpret_cast<char*>(output.data()), output.size())); + dataItem->setString("mimeType", "image/png"); + return dataItem.release(); + } + + PassRefPtr<JSONObject> objectForSkBitmap(const SkBitmap& bitmap) + { + RefPtr<JSONObject> bitmapItem = JSONObject::create(); + bitmapItem->setNumber("width", bitmap.width()); + bitmapItem->setNumber("height", bitmap.height()); + bitmapItem->setString("config", configName(bitmap.config())); + bitmapItem->setBoolean("opaque", bitmap.isOpaque()); + bitmapItem->setBoolean("immutable", bitmap.isImmutable()); + bitmapItem->setBoolean("volatile", bitmap.isVolatile()); + bitmapItem->setNumber("genID", bitmap.getGenerationID()); + bitmapItem->setObject("data", objectForBitmapData(bitmap)); + return bitmapItem.release(); + } + + PassRefPtr<JSONObject> objectForSkShader(const SkShader& shader) + { + RefPtr<JSONObject> shaderItem = JSONObject::create(); + const SkMatrix localMatrix = shader.getLocalMatrix(); + if (!localMatrix.isIdentity()) + shaderItem->setArray("localMatrix", arrayForSkMatrix(localMatrix)); + return shaderItem.release(); + } + + String stringForSkColor(const SkColor& color) + { + String colorString = "#"; + appendUnsignedAsHex(color, colorString); + return colorString; + } + + 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"); + appendFlagToString(&flagsString, paint.getFlags() & SkPaint::kGenA8FromLCD_Flag, "GenA8FromLCD"); + return flagsString; + } + + String filterLevelName(SkPaint::FilterLevel filterLevel) + { + switch (filterLevel) { + case SkPaint::kNone_FilterLevel: return "None"; + case SkPaint::kLow_FilterLevel: return "Low"; + case SkPaint::kMedium_FilterLevel: return "Medium"; + case SkPaint::kHigh_FilterLevel: 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 "?"; + }; + } + + PassRefPtr<JSONObject> objectForSkPaint(const SkPaint& paint) + { + RefPtr<JSONObject> paintItem = JSONObject::create(); + paintItem->setNumber("textSize", paint.getTextSize()); + paintItem->setNumber("textScaleX", paint.getTextScaleX()); + paintItem->setNumber("textSkewX", paint.getTextSkewX()); + if (SkShader* shader = paint.getShader()) + paintItem->setObject("shader", objectForSkShader(*shader)); + paintItem->setString("color", stringForSkColor(paint.getColor())); + paintItem->setNumber("strokeWidth", paint.getStrokeWidth()); + paintItem->setNumber("strokeMiter", paint.getStrokeMiter()); + paintItem->setString("flags", stringForSkPaintFlags(paint)); + paintItem->setString("filterLevel", filterLevelName(paint.getFilterLevel())); + 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.release(); + } + + PassRefPtr<JSONArray> arrayForSkMatrix(const SkMatrix& matrix) + { + RefPtr<JSONArray> matrixArray = JSONArray::create(); + for (int i = 0; i < 9; ++i) + matrixArray->pushNumber(matrix[i]); + return matrixArray.release(); + } + + PassRefPtr<JSONArray> arrayForSkScalars(size_t n, const SkScalar scalars[]) + { + RefPtr<JSONArray> scalarsArray = JSONArray::create(); + for (size_t i = 0; i < n; ++i) + scalarsArray->pushNumber(scalars[i]); + return scalarsArray.release(); + } + + 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"; + }; + } + + void translate(SkScalar dx, SkScalar dy) + { + RefPtr<JSONObject> params = addItemWithParams("translate"); + params->setNumber("dx", dx); + params->setNumber("dy", dy); + } + + void scale(SkScalar scaleX, SkScalar scaleY) + { + RefPtr<JSONObject> params = addItemWithParams("scale"); + params->setNumber("scaleX", scaleX); + params->setNumber("scaleY", scaleY); + } + + void concat(const SkMatrix& matrix) + { + RefPtr<JSONObject> params = addItemWithParams("concat"); + params->setArray("matrix", arrayForSkMatrix(matrix)); + } + + String saveFlagsToString(SkCanvas::SaveFlags flags) + { + String flagsString = ""; + if (flags & SkCanvas::kHasAlphaLayer_SaveFlag) + flagsString.append("kHasAlphaLayer_SaveFlag "); + if (flags & SkCanvas::kFullColorLayer_SaveFlag) + flagsString.append("kFullColorLayer_SaveFlag "); + if (flags & SkCanvas::kClipToLayer_SaveFlag) + flagsString.append("kClipToLayer_SaveFlag "); + 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 "?"; + } + } +}; + +static bool decodeBitmap(const void* data, size_t length, SkBitmap* result) +{ + RefPtr<SharedBuffer> buffer = SharedBuffer::create(static_cast<const char*>(data), length); + OwnPtr<ImageDecoder> imageDecoder = ImageDecoder::create(*buffer, ImageSource::AlphaPremultiplied, ImageSource::GammaAndColorProfileIgnored); + if (!imageDecoder) + return false; + imageDecoder->setData(buffer.get(), true); + ImageFrame* frame = imageDecoder->frameBufferAtIndex(0); + if (!frame) + return true; + *result = frame->getSkBitmap(); + return true; +} + +PassRefPtr<GraphicsContextSnapshot> GraphicsContextSnapshot::load(const char* data, size_t size) { + SkMemoryStream stream(data, size); + RefPtr<SkPicture> picture = adoptRef(SkPicture::CreateFromStream(&stream, decodeBitmap)); + if (!picture) + return nullptr; + return adoptRef(new GraphicsContextSnapshot(picture, false)); +} +PassOwnPtr<ImageBuffer> GraphicsContextSnapshot::replay(unsigned fromStep, unsigned toStep) const +{ OwnPtr<ImageBuffer> imageBuffer = createImageBuffer(); FragmentSnapshotPlayer player(m_picture, imageBuffer->context()->canvas()); player.play(fromStep, toStep); @@ -177,4 +1016,12 @@ PassOwnPtr<ImageBuffer> GraphicsContextSnapshot::createImageBuffer() const return ImageBuffer::create(IntSize(m_picture->width(), m_picture->height()), m_isCertainlyOpaque ? Opaque : NonOpaque); } +PassRefPtr<JSONArray> GraphicsContextSnapshot::snapshotCommandLog() const +{ + LoggingCanvas canvas(m_picture->width(), m_picture->height()); + FragmentSnapshotPlayer player(m_picture, &canvas); + player.play(0, 0); + return canvas.log(); +} + } |