diff options
author | fmalita <fmalita@chromium.org> | 2015-02-06 12:51:10 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-02-06 12:51:10 -0800 |
commit | 2aafe6f427f6d98aa4d173efc6195fe245add91d (patch) | |
tree | e54ed2c567cafcc6ca76b5d424d0b70eda00fece /src | |
parent | 23e619cf462b2a8a500f3ca750e099f79601f508 (diff) |
Add SkSVGCanvas
Add a public SVG canvas factory + move the SVG files from
experimental -> src/svg/.
Update current clients to the new API.
R=reed@google.com, mtklein@google.com, halcanary@google.com
Review URL: https://codereview.chromium.org/902583006
Diffstat (limited to 'src')
-rw-r--r-- | src/svg/SkSVGCanvas.cpp | 17 | ||||
-rw-r--r-- | src/svg/SkSVGDevice.cpp | 651 | ||||
-rw-r--r-- | src/svg/SkSVGDevice.h | 73 | ||||
-rw-r--r-- | src/svg/skp2svg.cpp | 70 |
4 files changed, 811 insertions, 0 deletions
diff --git a/src/svg/SkSVGCanvas.cpp b/src/svg/SkSVGCanvas.cpp new file mode 100644 index 0000000000..22a6e0bd9c --- /dev/null +++ b/src/svg/SkSVGCanvas.cpp @@ -0,0 +1,17 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkSVGCanvas.h" +#include "SkSVGDevice.h" + +SkCanvas* SkSVGCanvas::Create(const SkRect& bounds, SkXMLWriter* writer) { + // TODO: pass full bounds to the device + SkISize size = bounds.roundOut().size(); + SkAutoTUnref<SkBaseDevice> device(SkSVGDevice::Create(size, writer)); + + return SkNEW_ARGS(SkCanvas, (device)); +} diff --git a/src/svg/SkSVGDevice.cpp b/src/svg/SkSVGDevice.cpp new file mode 100644 index 0000000000..bf19a81a1d --- /dev/null +++ b/src/svg/SkSVGDevice.cpp @@ -0,0 +1,651 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkSVGDevice.h" + +#include "SkBitmap.h" +#include "SkDraw.h" +#include "SkPaint.h" +#include "SkParsePath.h" +#include "SkPathOps.h" +#include "SkShader.h" +#include "SkStream.h" +#include "SkTypeface.h" +#include "SkUtils.h" +#include "SkXMLWriter.h" + +namespace { + +static SkString svg_color(SkColor color) { + return SkStringPrintf("rgb(%u,%u,%u)", + SkColorGetR(color), + SkColorGetG(color), + SkColorGetB(color)); +} + +static SkScalar svg_opacity(SkColor color) { + return SkIntToScalar(SkColorGetA(color)) / SK_AlphaOPAQUE; +} + +// Keep in sync with SkPaint::Cap +static const char* cap_map[] = { + NULL, // kButt_Cap (default) + "round", // kRound_Cap + "square" // kSquare_Cap +}; +SK_COMPILE_ASSERT(SK_ARRAY_COUNT(cap_map) == SkPaint::kCapCount, missing_cap_map_entry); + +static const char* svg_cap(SkPaint::Cap cap) { + SkASSERT(cap < SK_ARRAY_COUNT(cap_map)); + return cap_map[cap]; +} + +// Keep in sync with SkPaint::Join +static const char* join_map[] = { + NULL, // kMiter_Join (default) + "round", // kRound_Join + "bevel" // kBevel_Join +}; +SK_COMPILE_ASSERT(SK_ARRAY_COUNT(join_map) == SkPaint::kJoinCount, missing_join_map_entry); + +static const char* svg_join(SkPaint::Join join) { + SkASSERT(join < SK_ARRAY_COUNT(join_map)); + return join_map[join]; +} + +// Keep in sync with SkPaint::Align +static const char* text_align_map[] = { + NULL, // kLeft_Align (default) + "middle", // kCenter_Align + "end" // kRight_Align +}; +SK_COMPILE_ASSERT(SK_ARRAY_COUNT(text_align_map) == SkPaint::kAlignCount, + missing_text_align_map_entry); +static const char* svg_text_align(SkPaint::Align align) { + SkASSERT(align < SK_ARRAY_COUNT(text_align_map)); + return text_align_map[align]; +} + +static SkString svg_transform(const SkMatrix& t) { + SkASSERT(!t.isIdentity()); + + SkString tstr; + switch (t.getType()) { + case SkMatrix::kPerspective_Mask: + SkDebugf("Can't handle perspective matrices."); + break; + case SkMatrix::kTranslate_Mask: + tstr.printf("translate(%g %g)", t.getTranslateX(), t.getTranslateY()); + break; + case SkMatrix::kScale_Mask: + tstr.printf("scale(%g %g)", t.getScaleX(), t.getScaleY()); + break; + default: + // http://www.w3.org/TR/SVG/coords.html#TransformMatrixDefined + // | a c e | + // | b d f | + // | 0 0 1 | + tstr.printf("matrix(%g %g %g %g %g %g)", + t.getScaleX(), t.getSkewY(), + t.getSkewX(), t.getScaleY(), + t.getTranslateX(), t.getTranslateY()); + break; + } + + return tstr; +} + +static void append_escaped_unichar(SkUnichar c, SkString* text) { + switch(c) { + case '&': + text->append("&"); + break; + case '"': + text->append("""); + break; + case '\'': + text->append("'"); + break; + case '<': + text->append("<"); + break; + case '>': + text->append(">"); + break; + default: + text->appendUnichar(c); + break; + } +} + +static SkString svg_text(const void* text, size_t byteLen, const SkPaint& paint) { + SkString svgText; + int count = paint.countText(text, byteLen); + + switch(paint.getTextEncoding()) { + case SkPaint::kGlyphID_TextEncoding: { + SkASSERT(count * sizeof(uint16_t) == byteLen); + SkAutoSTArray<64, SkUnichar> unichars(count); + paint.glyphsToUnichars((const uint16_t*)text, count, unichars.get()); + for (int i = 0; i < count; ++i) { + append_escaped_unichar(unichars[i], &svgText); + } + } break; + case SkPaint::kUTF8_TextEncoding: { + const char* c8 = reinterpret_cast<const char*>(text); + for (int i = 0; i < count; ++i) { + append_escaped_unichar(SkUTF8_NextUnichar(&c8), &svgText); + } + SkASSERT(reinterpret_cast<const char*>(text) + byteLen == c8); + } break; + case SkPaint::kUTF16_TextEncoding: { + const uint16_t* c16 = reinterpret_cast<const uint16_t*>(text); + for (int i = 0; i < count; ++i) { + append_escaped_unichar(SkUTF16_NextUnichar(&c16), &svgText); + } + SkASSERT(SkIsAlign2(byteLen)); + SkASSERT(reinterpret_cast<const uint16_t*>(text) + (byteLen / 2) == c16); + } break; + case SkPaint::kUTF32_TextEncoding: { + SkASSERT(count * sizeof(uint32_t) == byteLen); + const uint32_t* c32 = reinterpret_cast<const uint32_t*>(text); + for (int i = 0; i < count; ++i) { + append_escaped_unichar(c32[i], &svgText); + } + } break; + default: + SkFAIL("unknown text encoding"); + } + + return svgText; +} + +struct Resources { + Resources(const SkPaint& paint) + : fPaintServer(svg_color(paint.getColor())) {} + + SkString fPaintServer; + SkString fClip; +}; + +} + +// For now all this does is serve unique serial IDs, but it will eventually evolve to track +// and deduplicate resources. +class SkSVGDevice::ResourceBucket : ::SkNoncopyable { +public: + ResourceBucket() : fGradientCount(0), fClipCount(0), fPathCount(0) {} + + SkString addLinearGradient() { + return SkStringPrintf("gradient_%d", fGradientCount++); + } + + SkString addClip() { + return SkStringPrintf("clip_%d", fClipCount++); + } + + SkString addPath() { + return SkStringPrintf("path_%d", fPathCount++); + } + +private: + uint32_t fGradientCount; + uint32_t fClipCount; + uint32_t fPathCount; +}; + +class SkSVGDevice::AutoElement : ::SkNoncopyable { +public: + AutoElement(const char name[], SkXMLWriter* writer) + : fWriter(writer) + , fResourceBucket(NULL) { + fWriter->startElement(name); + } + + AutoElement(const char name[], SkXMLWriter* writer, ResourceBucket* bucket, + const SkDraw& draw, const SkPaint& paint) + : fWriter(writer) + , fResourceBucket(bucket) { + + Resources res = this->addResources(draw, paint); + + fWriter->startElement(name); + + this->addPaint(paint, res); + + if (!draw.fMatrix->isIdentity()) { + this->addAttribute("transform", svg_transform(*draw.fMatrix)); + } + } + + ~AutoElement() { + fWriter->endElement(); + } + + void addAttribute(const char name[], const char val[]) { + fWriter->addAttribute(name, val); + } + + void addAttribute(const char name[], const SkString& val) { + fWriter->addAttribute(name, val.c_str()); + } + + void addAttribute(const char name[], int32_t val) { + fWriter->addS32Attribute(name, val); + } + + void addAttribute(const char name[], SkScalar val) { + fWriter->addScalarAttribute(name, val); + } + + void addText(const SkString& text) { + fWriter->addText(text.c_str(), text.size()); + } + + void addRectAttributes(const SkRect&); + void addPathAttributes(const SkPath&); + void addTextAttributes(const SkPaint&); + +private: + Resources addResources(const SkDraw& draw, const SkPaint& paint); + void addClipResources(const SkDraw& draw, Resources* resources); + void addShaderResources(const SkPaint& paint, Resources* resources); + + void addPaint(const SkPaint& paint, const Resources& resources); + + SkString addLinearGradientDef(const SkShader::GradientInfo& info, const SkShader* shader); + + SkXMLWriter* fWriter; + ResourceBucket* fResourceBucket; +}; + +void SkSVGDevice::AutoElement::addPaint(const SkPaint& paint, const Resources& resources) { + SkPaint::Style style = paint.getStyle(); + if (style == SkPaint::kFill_Style || style == SkPaint::kStrokeAndFill_Style) { + this->addAttribute("fill", resources.fPaintServer); + + if (SK_AlphaOPAQUE != SkColorGetA(paint.getColor())) { + this->addAttribute("fill-opacity", svg_opacity(paint.getColor())); + } + } else { + SkASSERT(style == SkPaint::kStroke_Style); + this->addAttribute("fill", "none"); + } + + if (style == SkPaint::kStroke_Style || style == SkPaint::kStrokeAndFill_Style) { + this->addAttribute("stroke", resources.fPaintServer); + + SkScalar strokeWidth = paint.getStrokeWidth(); + if (strokeWidth == 0) { + // Hairline stroke + strokeWidth = 1; + this->addAttribute("vector-effect", "non-scaling-stroke"); + } + this->addAttribute("stroke-width", strokeWidth); + + if (const char* cap = svg_cap(paint.getStrokeCap())) { + this->addAttribute("stroke-linecap", cap); + } + + if (const char* join = svg_join(paint.getStrokeJoin())) { + this->addAttribute("stroke-linejoin", join); + } + + if (paint.getStrokeJoin() == SkPaint::kMiter_Join) { + this->addAttribute("stroke-miterlimit", paint.getStrokeMiter()); + } + + if (SK_AlphaOPAQUE != SkColorGetA(paint.getColor())) { + this->addAttribute("stroke-opacity", svg_opacity(paint.getColor())); + } + } else { + SkASSERT(style == SkPaint::kFill_Style); + this->addAttribute("stroke", "none"); + } + + if (!resources.fClip.isEmpty()) { + this->addAttribute("clip-path", resources.fClip); + } +} + +Resources SkSVGDevice::AutoElement::addResources(const SkDraw& draw, const SkPaint& paint) { + Resources resources(paint); + + // FIXME: this is a weak heuristic and we end up with LOTS of redundant clips. + bool hasClip = !draw.fClipStack->isWideOpen(); + bool hasShader = SkToBool(paint.getShader()); + + if (hasClip || hasShader) { + AutoElement defs("defs", fWriter); + + if (hasClip) { + this->addClipResources(draw, &resources); + } + + if (hasShader) { + this->addShaderResources(paint, &resources); + } + } + + return resources; +} + +void SkSVGDevice::AutoElement::addShaderResources(const SkPaint& paint, Resources* resources) { + const SkShader* shader = paint.getShader(); + SkASSERT(SkToBool(shader)); + + SkShader::GradientInfo grInfo; + grInfo.fColorCount = 0; + if (SkShader::kLinear_GradientType != shader->asAGradient(&grInfo)) { + // TODO: non-linear gradient support + SkDebugf("unsupported shader type\n"); + return; + } + + SkAutoSTArray<16, SkColor> grColors(grInfo.fColorCount); + SkAutoSTArray<16, SkScalar> grOffsets(grInfo.fColorCount); + grInfo.fColors = grColors.get(); + grInfo.fColorOffsets = grOffsets.get(); + + // One more call to get the actual colors/offsets. + shader->asAGradient(&grInfo); + SkASSERT(grInfo.fColorCount <= grColors.count()); + SkASSERT(grInfo.fColorCount <= grOffsets.count()); + + resources->fPaintServer.printf("url(#%s)", addLinearGradientDef(grInfo, shader).c_str()); +} + +void SkSVGDevice::AutoElement::addClipResources(const SkDraw& draw, Resources* resources) { + SkASSERT(!draw.fClipStack->isWideOpen()); + + SkPath clipPath; + (void) draw.fClipStack->asPath(&clipPath); + + SkString clipID = fResourceBucket->addClip(); + const char* clipRule = clipPath.getFillType() == SkPath::kEvenOdd_FillType ? + "evenodd" : "nonzero"; + { + // clipPath is in device space, but since we're only pushing transform attributes + // to the leaf nodes, so are all our elements => SVG userSpaceOnUse == device space. + AutoElement clipPathElement("clipPath", fWriter); + clipPathElement.addAttribute("id", clipID); + + SkRect clipRect = SkRect::MakeEmpty(); + if (clipPath.isEmpty() || clipPath.isRect(&clipRect)) { + AutoElement rectElement("rect", fWriter); + rectElement.addRectAttributes(clipRect); + rectElement.addAttribute("clip-rule", clipRule); + } else { + AutoElement pathElement("path", fWriter); + pathElement.addPathAttributes(clipPath); + pathElement.addAttribute("clip-rule", clipRule); + } + } + + resources->fClip.printf("url(#%s)", clipID.c_str()); +} + +SkString SkSVGDevice::AutoElement::addLinearGradientDef(const SkShader::GradientInfo& info, + const SkShader* shader) { + SkASSERT(fResourceBucket); + SkString id = fResourceBucket->addLinearGradient(); + + { + AutoElement gradient("linearGradient", fWriter); + + gradient.addAttribute("id", id); + gradient.addAttribute("gradientUnits", "userSpaceOnUse"); + gradient.addAttribute("x1", info.fPoint[0].x()); + gradient.addAttribute("y1", info.fPoint[0].y()); + gradient.addAttribute("x2", info.fPoint[1].x()); + gradient.addAttribute("y2", info.fPoint[1].y()); + + if (!shader->getLocalMatrix().isIdentity()) { + this->addAttribute("gradientTransform", svg_transform(shader->getLocalMatrix())); + } + + SkASSERT(info.fColorCount >= 2); + for (int i = 0; i < info.fColorCount; ++i) { + SkColor color = info.fColors[i]; + SkString colorStr(svg_color(color)); + + { + AutoElement stop("stop", fWriter); + stop.addAttribute("offset", info.fColorOffsets[i]); + stop.addAttribute("stop-color", colorStr.c_str()); + + if (SK_AlphaOPAQUE != SkColorGetA(color)) { + stop.addAttribute("stop-opacity", svg_opacity(color)); + } + } + } + } + + return id; +} + +void SkSVGDevice::AutoElement::addRectAttributes(const SkRect& rect) { + // x, y default to 0 + if (rect.x() != 0) { + this->addAttribute("x", rect.x()); + } + if (rect.y() != 0) { + this->addAttribute("y", rect.y()); + } + + this->addAttribute("width", rect.width()); + this->addAttribute("height", rect.height()); +} + +void SkSVGDevice::AutoElement::addPathAttributes(const SkPath& path) { + SkString pathData; + SkParsePath::ToSVGString(path, &pathData); + this->addAttribute("d", pathData); +} + +void SkSVGDevice::AutoElement::addTextAttributes(const SkPaint& paint) { + this->addAttribute("font-size", paint.getTextSize()); + + SkTypeface::Style style = paint.getTypeface()->style(); + if (style & SkTypeface::kItalic) { + this->addAttribute("font-style", "italic"); + } + if (style & SkTypeface::kBold) { + this->addAttribute("font-weight", "bold"); + } + + SkAutoTUnref<const SkTypeface> tface(paint.getTypeface() ? + SkRef(paint.getTypeface()) : SkTypeface::RefDefault(style)); + SkString familyName; + tface->getFamilyName(&familyName); + if (!familyName.isEmpty()) { + this->addAttribute("font-family", familyName); + } + + if (const char* textAlign = svg_text_align(paint.getTextAlign())) { + this->addAttribute("text-anchor", textAlign); + } +} + +SkBaseDevice* SkSVGDevice::Create(const SkISize& size, SkXMLWriter* writer) { + if (!writer) { + return NULL; + } + + return SkNEW_ARGS(SkSVGDevice, (size, writer)); +} + +SkSVGDevice::SkSVGDevice(const SkISize& size, SkXMLWriter* writer) + : fWriter(writer) + , fResourceBucket(SkNEW(ResourceBucket)) { + SkASSERT(writer); + + fLegacyBitmap.setInfo(SkImageInfo::MakeUnknown(size.width(), size.height())); + + fWriter->writeHeader(); + + // The root <svg> tag gets closed by the destructor. + fRootElement.reset(SkNEW_ARGS(AutoElement, ("svg", fWriter))); + + fRootElement->addAttribute("xmlns", "http://www.w3.org/2000/svg"); + fRootElement->addAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink"); + fRootElement->addAttribute("width", size.width()); + fRootElement->addAttribute("height", size.height()); +} + +SkSVGDevice::~SkSVGDevice() { +} + +SkImageInfo SkSVGDevice::imageInfo() const { + return fLegacyBitmap.info(); +} + +const SkBitmap& SkSVGDevice::onAccessBitmap() { + return fLegacyBitmap; +} + +void SkSVGDevice::drawPaint(const SkDraw& draw, const SkPaint& paint) { + AutoElement rect("rect", fWriter, fResourceBucket, draw, paint); + rect.addRectAttributes(SkRect::MakeWH(SkIntToScalar(this->width()), + SkIntToScalar(this->height()))); +} + +void SkSVGDevice::drawPoints(const SkDraw&, SkCanvas::PointMode mode, size_t count, + const SkPoint[], const SkPaint& paint) { + // todo + SkDebugf("unsupported operation: drawPoints()\n"); +} + +void SkSVGDevice::drawRect(const SkDraw& draw, const SkRect& r, const SkPaint& paint) { + AutoElement rect("rect", fWriter, fResourceBucket, draw, paint); + rect.addRectAttributes(r); +} + +void SkSVGDevice::drawOval(const SkDraw& draw, const SkRect& oval, const SkPaint& paint) { + AutoElement ellipse("ellipse", fWriter, fResourceBucket, draw, paint); + ellipse.addAttribute("cx", oval.centerX()); + ellipse.addAttribute("cy", oval.centerY()); + ellipse.addAttribute("rx", oval.width() / 2); + ellipse.addAttribute("ry", oval.height() / 2); +} + +void SkSVGDevice::drawRRect(const SkDraw&, const SkRRect& rr, const SkPaint& paint) { + // todo + SkDebugf("unsupported operation: drawRRect()\n"); +} + +void SkSVGDevice::drawPath(const SkDraw& draw, const SkPath& path, const SkPaint& paint, + const SkMatrix* prePathMatrix, bool pathIsMutable) { + AutoElement elem("path", fWriter, fResourceBucket, draw, paint); + elem.addPathAttributes(path); +} + +void SkSVGDevice::drawBitmap(const SkDraw&, const SkBitmap& bitmap, + const SkMatrix& matrix, const SkPaint& paint) { + // todo + SkDebugf("unsupported operation: drawBitmap()\n"); +} + +void SkSVGDevice::drawSprite(const SkDraw&, const SkBitmap& bitmap, + int x, int y, const SkPaint& paint) { + // todo + SkDebugf("unsupported operation: drawSprite()\n"); +} + +void SkSVGDevice::drawBitmapRect(const SkDraw&, const SkBitmap&, const SkRect* srcOrNull, + const SkRect& dst, const SkPaint& paint, + SkCanvas::DrawBitmapRectFlags flags) { + // todo + SkDebugf("unsupported operation: drawBitmapRect()\n"); +} + +void SkSVGDevice::drawText(const SkDraw& draw, const void* text, size_t len, + SkScalar x, SkScalar y, const SkPaint& paint) { + AutoElement elem("text", fWriter, fResourceBucket, draw, paint); + elem.addTextAttributes(paint); + elem.addAttribute("x", x); + elem.addAttribute("y", y); + elem.addText(svg_text(text, len, paint)); +} + +void SkSVGDevice::drawPosText(const SkDraw& draw, const void* text, size_t len, + const SkScalar pos[], int scalarsPerPos, const SkPoint& offset, + const SkPaint& paint) { + SkASSERT(scalarsPerPos == 1 || scalarsPerPos == 2); + + AutoElement elem("text", fWriter, fResourceBucket, draw, paint); + elem.addTextAttributes(paint); + + SkString xStr; + SkString yStr; + for (int i = 0; i < paint.countText(text, len); ++i) { + xStr.appendf("%.8g, ", offset.x() + pos[i * scalarsPerPos]); + + if (scalarsPerPos == 2) { + yStr.appendf("%.8g, ", offset.y() + pos[i * scalarsPerPos + 1]); + } + } + + if (scalarsPerPos != 2) { + yStr.appendScalar(offset.y()); + } + + elem.addAttribute("x", xStr); + elem.addAttribute("y", yStr); + elem.addText(svg_text(text, len, paint)); +} + +void SkSVGDevice::drawTextOnPath(const SkDraw&, const void* text, size_t len, const SkPath& path, + const SkMatrix* matrix, const SkPaint& paint) { + SkString pathID = fResourceBucket->addPath(); + + { + AutoElement defs("defs", fWriter); + AutoElement pathElement("path", fWriter); + pathElement.addAttribute("id", pathID); + pathElement.addPathAttributes(path); + + } + + { + AutoElement textElement("text", fWriter); + textElement.addTextAttributes(paint); + + if (matrix && !matrix->isIdentity()) { + textElement.addAttribute("transform", svg_transform(*matrix)); + } + + { + AutoElement textPathElement("textPath", fWriter); + textPathElement.addAttribute("xlink:href", SkStringPrintf("#%s", pathID.c_str())); + + if (paint.getTextAlign() != SkPaint::kLeft_Align) { + SkASSERT(paint.getTextAlign() == SkPaint::kCenter_Align || + paint.getTextAlign() == SkPaint::kRight_Align); + textPathElement.addAttribute("startOffset", + paint.getTextAlign() == SkPaint::kCenter_Align ? "50%" : "100%"); + } + + textPathElement.addText(svg_text(text, len, paint)); + } + } +} + +void SkSVGDevice::drawVertices(const SkDraw&, SkCanvas::VertexMode, int vertexCount, + const SkPoint verts[], const SkPoint texs[], + const SkColor colors[], SkXfermode* xmode, + const uint16_t indices[], int indexCount, + const SkPaint& paint) { + // todo + SkDebugf("unsupported operation: drawVertices()\n"); +} + +void SkSVGDevice::drawDevice(const SkDraw&, SkBaseDevice*, int x, int y, + const SkPaint&) { + // todo + SkDebugf("unsupported operation: drawDevice()\n"); +} diff --git a/src/svg/SkSVGDevice.h b/src/svg/SkSVGDevice.h new file mode 100644 index 0000000000..fdfc8c3e03 --- /dev/null +++ b/src/svg/SkSVGDevice.h @@ -0,0 +1,73 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkSVGDevice_DEFINED +#define SkSVGDevice_DEFINED + +#include "SkDevice.h" + +class SkXMLWriter; + +class SkSVGDevice : public SkBaseDevice { +public: + static SkBaseDevice* Create(const SkISize& size, SkXMLWriter* writer); + + virtual SkImageInfo imageInfo() const SK_OVERRIDE; + +protected: + virtual void drawPaint(const SkDraw&, const SkPaint& paint) SK_OVERRIDE; + virtual void drawPoints(const SkDraw&, SkCanvas::PointMode mode, size_t count, + const SkPoint[], const SkPaint& paint) SK_OVERRIDE; + virtual void drawRect(const SkDraw&, const SkRect& r, const SkPaint& paint) SK_OVERRIDE; + virtual void drawOval(const SkDraw&, const SkRect& oval, const SkPaint& paint) SK_OVERRIDE; + virtual void drawRRect(const SkDraw&, const SkRRect& rr, const SkPaint& paint) SK_OVERRIDE; + virtual void drawPath(const SkDraw&, const SkPath& path, + const SkPaint& paint, + const SkMatrix* prePathMatrix = NULL, + bool pathIsMutable = false) SK_OVERRIDE; + + virtual void drawBitmap(const SkDraw&, const SkBitmap& bitmap, + const SkMatrix& matrix, const SkPaint& paint) SK_OVERRIDE; + virtual void drawSprite(const SkDraw&, const SkBitmap& bitmap, + int x, int y, const SkPaint& paint) SK_OVERRIDE; + virtual void drawBitmapRect(const SkDraw&, const SkBitmap&, + const SkRect* srcOrNull, const SkRect& dst, + const SkPaint& paint, + SkCanvas::DrawBitmapRectFlags flags) SK_OVERRIDE; + + virtual void drawText(const SkDraw&, const void* text, size_t len, + SkScalar x, SkScalar y, const SkPaint& paint) SK_OVERRIDE; + virtual void drawPosText(const SkDraw&, const void* text, size_t len, + const SkScalar pos[], int scalarsPerPos, + const SkPoint& offset, const SkPaint& paint) SK_OVERRIDE; + virtual void drawTextOnPath(const SkDraw&, const void* text, size_t len, + const SkPath& path, const SkMatrix* matrix, + const SkPaint& paint) SK_OVERRIDE; + virtual void drawVertices(const SkDraw&, SkCanvas::VertexMode, int vertexCount, + const SkPoint verts[], const SkPoint texs[], + const SkColor colors[], SkXfermode* xmode, + const uint16_t indices[], int indexCount, + const SkPaint& paint) SK_OVERRIDE; + + virtual void drawDevice(const SkDraw&, SkBaseDevice*, int x, int y, + const SkPaint&) SK_OVERRIDE; + virtual const SkBitmap& onAccessBitmap() SK_OVERRIDE; + +private: + SkSVGDevice(const SkISize& size, SkXMLWriter* writer); + virtual ~SkSVGDevice(); + + class AutoElement; + class ResourceBucket; + + SkXMLWriter* fWriter; + SkAutoTDelete<AutoElement> fRootElement; + SkAutoTDelete<ResourceBucket> fResourceBucket; + SkBitmap fLegacyBitmap; +}; + +#endif // SkSVGDevice_DEFINED diff --git a/src/svg/skp2svg.cpp b/src/svg/skp2svg.cpp new file mode 100644 index 0000000000..ae6e54c885 --- /dev/null +++ b/src/svg/skp2svg.cpp @@ -0,0 +1,70 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "LazyDecodeBitmap.h" +#include "SkCommandLineFlags.h" +#include "SkPicture.h" +#include "SkStream.h" +#include "SkSVGCanvas.h" +#include "SkXMLWriter.h" + +DEFINE_string2(input, i, "", "input skp file"); +DEFINE_string2(output, o, "", "output svg file (optional)"); + +// return codes: +static const int kSuccess = 0; +static const int kInvalidArgs = 1; +static const int kIOError = 2; +static const int kNotAnSKP = 3; + +int tool_main(int argc, char** argv); +int tool_main(int argc, char** argv) { + SkCommandLineFlags::SetUsage("Converts an SKP file to SVG."); + SkCommandLineFlags::Parse(argc, argv); + + if (FLAGS_input.count() != 1) { + SkDebugf("Missing input file\n"); + return kInvalidArgs; + } + + SkFILEStream stream(FLAGS_input[0]); + if (!stream.isValid()) { + SkDebugf("Couldn't open file: %s\n", FLAGS_input[0]); + return kIOError; + } + + SkAutoTUnref<SkPicture> pic(SkPicture::CreateFromStream(&stream, &sk_tools::LazyDecodeBitmap)); + if (!SkToBool(pic.get())) { + SkDebugf("Could not load SKP: %s\n", FLAGS_input[0]); + return kNotAnSKP; + } + + SkAutoTDelete<SkWStream> outStream; + if (FLAGS_output.count() > 0) { + SkFILEWStream* fileStream = SkNEW_ARGS(SkFILEWStream, (FLAGS_output[0])); + if (!fileStream->isValid()) { + SkDebugf("Couldn't open output file for writing: %s\n", FLAGS_output[0]); + return kIOError; + } + outStream.reset(fileStream); + } else { + outStream.reset(SkNEW(SkDebugWStream)); + } + + SkAutoTDelete<SkXMLWriter> xmlWriter(SkNEW_ARGS(SkXMLStreamWriter, (outStream.get()))); + SkAutoTUnref<SkCanvas> svgCanvas(SkSVGCanvas::Create(pic->cullRect(), xmlWriter.get())); + + pic->playback(svgCanvas); + + return kSuccess; +} + +#if !defined SK_BUILD_FOR_IOS +int main(int argc, char * const argv[]) { + return tool_main(argc, (char**) argv); +} +#endif |