| Index: experimental/svg/SkSVGDevice.cpp
|
| diff --git a/experimental/svg/SkSVGDevice.cpp b/experimental/svg/SkSVGDevice.cpp
|
| index 91ff31364dd610b6f07532ce9ba0bc4b63b61821..f3ac02d8535cabcd0de6866aee555b50e301a9a9 100644
|
| --- a/experimental/svg/SkSVGDevice.cpp
|
| +++ b/experimental/svg/SkSVGDevice.cpp
|
| @@ -57,6 +57,48 @@ static const char* svg_join(SkPaint::Join join) {
|
| 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 '&':
|
| @@ -136,7 +178,7 @@ struct Resources {
|
| // and deduplicate resources.
|
| class SkSVGDevice::ResourceBucket : ::SkNoncopyable {
|
| public:
|
| - ResourceBucket() : fGradientCount(0), fClipCount(0) {}
|
| + ResourceBucket() : fGradientCount(0), fClipCount(0), fPathCount(0) {}
|
|
|
| SkString addLinearGradient() {
|
| return SkStringPrintf("gradient_%d", fGradientCount++);
|
| @@ -146,9 +188,14 @@ public:
|
| 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 {
|
| @@ -169,7 +216,10 @@ public:
|
| fWriter->startElement(name);
|
|
|
| this->addPaint(paint, res);
|
| - this->addTransform(*draw.fMatrix);
|
| +
|
| + if (!draw.fMatrix->isIdentity()) {
|
| + this->addAttribute("transform", svg_transform(*draw.fMatrix));
|
| + }
|
| }
|
|
|
| ~AutoElement() {
|
| @@ -197,7 +247,8 @@ public:
|
| }
|
|
|
| void addRectAttributes(const SkRect&);
|
| - void addFontAttributes(const SkPaint&);
|
| + void addPathAttributes(const SkPath&);
|
| + void addTextAttributes(const SkPaint&);
|
|
|
| private:
|
| Resources addResources(const SkDraw& draw, const SkPaint& paint);
|
| @@ -205,7 +256,6 @@ private:
|
| void addShaderResources(const SkPaint& paint, Resources* resources);
|
|
|
| void addPaint(const SkPaint& paint, const Resources& resources);
|
| - void addTransform(const SkMatrix& transform, const char name[] = "transform");
|
|
|
| SkString addLinearGradientDef(const SkShader::GradientInfo& info, const SkShader* shader);
|
|
|
| @@ -262,37 +312,6 @@ void SkSVGDevice::AutoElement::addPaint(const SkPaint& paint, const Resources& r
|
| }
|
| }
|
|
|
| -void SkSVGDevice::AutoElement::addTransform(const SkMatrix& t, const char name[]) {
|
| - if (t.isIdentity()) {
|
| - return;
|
| - }
|
| -
|
| - 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)",
|
| - SkScalarToFloat(t.getTranslateX()),
|
| - SkScalarToFloat(t.getTranslateY()));
|
| - break;
|
| - case SkMatrix::kScale_Mask:
|
| - tstr.printf("scale(%g %g)",
|
| - SkScalarToFloat(t.getScaleX()),
|
| - SkScalarToFloat(t.getScaleY()));
|
| - break;
|
| - default:
|
| - tstr.printf("matrix(%g %g %g %g %g %g)",
|
| - SkScalarToFloat(t.getScaleX()), SkScalarToFloat(t.getSkewY()),
|
| - SkScalarToFloat(t.getSkewX()), SkScalarToFloat(t.getScaleY()),
|
| - SkScalarToFloat(t.getTranslateX()), SkScalarToFloat(t.getTranslateY()));
|
| - break;
|
| - }
|
| -
|
| - fWriter->addAttribute(name, tstr.c_str());
|
| -}
|
| -
|
| Resources SkSVGDevice::AutoElement::addResources(const SkDraw& draw, const SkPaint& paint) {
|
| Resources resources(paint);
|
|
|
| @@ -362,9 +381,7 @@ void SkSVGDevice::AutoElement::addClipResources(const SkDraw& draw, Resources* r
|
| rectElement.addAttribute("clip-rule", clipRule);
|
| } else {
|
| AutoElement pathElement("path", fWriter);
|
| - SkString pathStr;
|
| - SkParsePath::ToSVGString(clipPath, &pathStr);
|
| - pathElement.addAttribute("d", pathStr.c_str());
|
| + pathElement.addPathAttributes(clipPath);
|
| pathElement.addAttribute("clip-rule", clipRule);
|
| }
|
| }
|
| @@ -386,7 +403,10 @@ SkString SkSVGDevice::AutoElement::addLinearGradientDef(const SkShader::Gradient
|
| gradient.addAttribute("y1", info.fPoint[0].y());
|
| gradient.addAttribute("x2", info.fPoint[1].x());
|
| gradient.addAttribute("y2", info.fPoint[1].y());
|
| - gradient.addTransform(shader->getLocalMatrix(), "gradientTransform");
|
| +
|
| + if (!shader->getLocalMatrix().isIdentity()) {
|
| + this->addAttribute("gradientTransform", svg_transform(shader->getLocalMatrix()));
|
| + }
|
|
|
| SkASSERT(info.fColorCount >= 2);
|
| for (int i = 0; i < info.fColorCount; ++i) {
|
| @@ -421,7 +441,13 @@ void SkSVGDevice::AutoElement::addRectAttributes(const SkRect& rect) {
|
| this->addAttribute("height", rect.height());
|
| }
|
|
|
| -void SkSVGDevice::AutoElement::addFontAttributes(const SkPaint& paint) {
|
| +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();
|
| @@ -439,6 +465,10 @@ void SkSVGDevice::AutoElement::addFontAttributes(const SkPaint& paint) {
|
| 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, SkWStream* wstream) {
|
| @@ -510,10 +540,7 @@ void SkSVGDevice::drawRRect(const SkDraw&, const SkRRect& rr, const SkPaint& pai
|
| void SkSVGDevice::drawPath(const SkDraw& draw, const SkPath& path, const SkPaint& paint,
|
| const SkMatrix* prePathMatrix, bool pathIsMutable) {
|
| AutoElement elem("path", fWriter, fResourceBucket, draw, paint);
|
| -
|
| - SkString pathStr;
|
| - SkParsePath::ToSVGString(path, &pathStr);
|
| - elem.addAttribute("d", pathStr.c_str());
|
| + elem.addPathAttributes(path);
|
| }
|
|
|
| void SkSVGDevice::drawBitmap(const SkDraw&, const SkBitmap& bitmap,
|
| @@ -538,7 +565,7 @@ void SkSVGDevice::drawBitmapRect(const SkDraw&, const SkBitmap&, const SkRect* s
|
| 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.addFontAttributes(paint);
|
| + elem.addTextAttributes(paint);
|
| elem.addAttribute("x", x);
|
| elem.addAttribute("y", y);
|
| elem.addText(svg_text(text, len, paint));
|
| @@ -550,7 +577,7 @@ void SkSVGDevice::drawPosText(const SkDraw& draw, const void* text, size_t len,
|
| SkASSERT(scalarsPerPos == 1 || scalarsPerPos == 2);
|
|
|
| AutoElement elem("text", fWriter, fResourceBucket, draw, paint);
|
| - elem.addFontAttributes(paint);
|
| + elem.addTextAttributes(paint);
|
|
|
| SkString xStr;
|
| SkString yStr;
|
| @@ -573,8 +600,38 @@ void SkSVGDevice::drawPosText(const SkDraw& draw, const void* text, size_t len,
|
|
|
| void SkSVGDevice::drawTextOnPath(const SkDraw&, const void* text, size_t len, const SkPath& path,
|
| const SkMatrix* matrix, const SkPaint& paint) {
|
| - // todo
|
| - SkDebugf("unsupported operation: drawTextOnPath()\n");
|
| + 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,
|
|
|