Chromium Code Reviews| Index: experimental/svg/SkSVGDevice.cpp |
| diff --git a/experimental/svg/SkSVGDevice.cpp b/experimental/svg/SkSVGDevice.cpp |
| index bd503b7d8fb29d074d7a625d84590beff036af21..db917fa69d284cc0f71a65156b974431dc69a9a2 100644 |
| --- a/experimental/svg/SkSVGDevice.cpp |
| +++ b/experimental/svg/SkSVGDevice.cpp |
| @@ -11,6 +11,7 @@ |
| #include "SkDraw.h" |
| #include "SkPaint.h" |
| #include "SkParsePath.h" |
| +#include "SkPathOps.h" |
| #include "SkShader.h" |
| #include "SkStream.h" |
| #include "SkTypeface.h" |
| @@ -20,12 +21,10 @@ |
| namespace { |
| static SkString svg_color(SkColor color) { |
| - SkString colorStr; |
| - colorStr.printf("rgb(%u,%u,%u)", |
| - SkColorGetR(color), |
| - SkColorGetG(color), |
| - SkColorGetB(color)); |
| - return colorStr; |
| + return SkStringPrintf("rgb(%u,%u,%u)", |
| + SkColorGetR(color), |
| + SkColorGetG(color), |
| + SkColorGetB(color)); |
| } |
| static SkScalar svg_opacity(SkColor color) { |
| @@ -102,6 +101,7 @@ struct Resources { |
| : fPaintServer(svg_color(paint.getColor())) {} |
| SkString fPaintServer; |
| + SkString fClip; |
| }; |
| } |
| @@ -110,16 +110,19 @@ struct Resources { |
| // and deduplicate resources. |
| class SkSVGDevice::ResourceBucket : ::SkNoncopyable { |
| public: |
| - ResourceBucket() : fGradientCount(0) {} |
| + ResourceBucket() : fGradientCount(0), fClipCount(0) {} |
| SkString addLinearGradient() { |
| - SkString id; |
| - id.printf("gradient_%d", fGradientCount++); |
| - return id; |
| + return SkStringPrintf("gradient_%d", fGradientCount++); |
| + } |
| + |
| + SkString addClip() { |
| + return SkStringPrintf("clip_%d", fClipCount++); |
| } |
| private: |
| uint32_t fGradientCount; |
| + uint32_t fClipCount; |
| }; |
| class SkSVGDevice::AutoElement : ::SkNoncopyable { |
| @@ -135,7 +138,7 @@ public: |
| : fWriter(writer) |
| , fResourceBucket(bucket) { |
| - Resources res = this->addResources(paint); |
| + Resources res = this->addResources(draw, paint); |
| fWriter->startElement(name); |
| @@ -167,11 +170,13 @@ public: |
| fWriter->addText(text.c_str()); |
| } |
| + void addRectAttributes(const SkRect&); |
| void addFontAttributes(const SkPaint&); |
| private: |
| - Resources addResources(const SkPaint& paint); |
| - void addResourceDefs(const SkPaint& paint, Resources* resources); |
| + 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); |
| void addTransform(const SkMatrix& transform, const char name[] = "transform"); |
| @@ -200,6 +205,10 @@ void SkSVGDevice::AutoElement::addPaint(const SkPaint& paint, const Resources& r |
| if (SK_AlphaOPAQUE != SkColorGetA(paint.getColor())) { |
| this->addAttribute("opacity", svg_opacity(paint.getColor())); |
| } |
| + |
| + if (!resources.fClip.isEmpty()) { |
| + this->addAttribute("clip-path", resources.fClip); |
| + } |
| } |
| void SkSVGDevice::AutoElement::addTransform(const SkMatrix& t, const char name[]) { |
| @@ -233,16 +242,25 @@ void SkSVGDevice::AutoElement::addTransform(const SkMatrix& t, const char name[] |
| fWriter->addAttribute(name, tstr.c_str()); |
| } |
| -Resources SkSVGDevice::AutoElement::addResources(const SkPaint& paint) { |
| +Resources SkSVGDevice::AutoElement::addResources(const SkDraw& draw, const SkPaint& paint) { |
| Resources resources(paint); |
| - this->addResourceDefs(paint, &resources); |
| + |
| + bool hasClip = !draw.fClipStack->isWideOpen(); |
| + bool hasShader = SkToBool(paint.getShader()); |
| + |
| + if (hasClip || hasShader) { |
| + AutoElement defs("defs", fWriter); |
| + |
| + this->addClipResources(draw, &resources); |
| + this->addShaderResources(paint, &resources); |
| + } |
| + |
| return resources; |
| } |
| -void SkSVGDevice::AutoElement::addResourceDefs(const SkPaint& paint, Resources* resources) { |
| +void SkSVGDevice::AutoElement::addShaderResources(const SkPaint& paint, Resources* resources) { |
| const SkShader* shader = paint.getShader(); |
| if (!SkToBool(shader)) { |
| - // TODO: clip support |
| return; |
| } |
| @@ -254,21 +272,53 @@ void SkSVGDevice::AutoElement::addResourceDefs(const SkPaint& paint, Resources* |
| return; |
| } |
| - { |
| - AutoElement defs("defs", fWriter); |
| + 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()); |
| - SkAutoSTArray<16, SkColor> grColors(grInfo.fColorCount); |
| - SkAutoSTArray<16, SkScalar> grOffsets(grInfo.fColorCount); |
| - grInfo.fColors = grColors.get(); |
| - grInfo.fColorOffsets = grOffsets.get(); |
| + resources->fPaintServer.printf("url(#%s)", addLinearGradientDef(grInfo, shader).c_str()); |
| +} |
| + |
| +void SkSVGDevice::AutoElement::addClipResources(const SkDraw& draw, Resources* resources) { |
| + // FIXME: this is a weak heuristic and we end up with LOTS of redundant clips. |
| + if (draw.fClipStack->isWideOpen()) { |
|
mtklein
2015/02/04 14:40:36
Might want to put this logic in one place. Since
f(malita)
2015/02/04 15:20:35
Done.
|
| + return; |
| + } |
| - // One more call to get the actual colors/offsets. |
| - shader->asAGradient(&grInfo); |
| - SkASSERT(grInfo.fColorCount <= grColors.count()); |
| - SkASSERT(grInfo.fColorCount <= grOffsets.count()); |
| + SkPath clipPath; |
| + draw.fClipStack->asPath(&clipPath); |
|
mtklein
2015/02/04 14:40:36
Can't hurt to add a (void) prefix?
f(malita)
2015/02/04 15:20:35
Done.
|
| - resources->fPaintServer.printf("url(#%s)", addLinearGradientDef(grInfo, shader).c_str()); |
| + 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); |
| + SkString pathStr; |
| + SkParsePath::ToSVGString(clipPath, &pathStr); |
| + pathElement.addAttribute("d", pathStr.c_str()); |
| + pathElement.addAttribute("clip-rule", clipRule); |
| + } |
| } |
| + |
| + resources->fClip.printf("url(#%s)", clipID.c_str()); |
| } |
| SkString SkSVGDevice::AutoElement::addLinearGradientDef(const SkShader::GradientInfo& info, |
| @@ -307,6 +357,19 @@ SkString SkSVGDevice::AutoElement::addLinearGradientDef(const SkShader::Gradient |
| 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::addFontAttributes(const SkPaint& paint) { |
| this->addAttribute("font-size", paint.getTextSize()); |
| @@ -365,10 +428,7 @@ const SkBitmap& SkSVGDevice::onAccessBitmap() { |
| void SkSVGDevice::drawPaint(const SkDraw& draw, const SkPaint& paint) { |
| AutoElement rect("rect", fWriter, fResourceBucket, draw, paint); |
| - rect.addAttribute("x", 0); |
| - rect.addAttribute("y", 0); |
| - rect.addAttribute("width", this->width()); |
| - rect.addAttribute("height", this->height()); |
| + rect.addRectAttributes(SkRect::MakeWH(this->width(), this->height())); |
| } |
| void SkSVGDevice::drawPoints(const SkDraw&, SkCanvas::PointMode mode, size_t count, |
| @@ -379,10 +439,7 @@ void SkSVGDevice::drawPoints(const SkDraw&, SkCanvas::PointMode mode, size_t cou |
| void SkSVGDevice::drawRect(const SkDraw& draw, const SkRect& r, const SkPaint& paint) { |
| AutoElement rect("rect", fWriter, fResourceBucket, draw, paint); |
| - rect.addAttribute("x", r.fLeft); |
| - rect.addAttribute("y", r.fTop); |
| - rect.addAttribute("width", r.width()); |
| - rect.addAttribute("height", r.height()); |
| + rect.addRectAttributes(r); |
| } |
| void SkSVGDevice::drawOval(const SkDraw& draw, const SkRect& oval, const SkPaint& paint) { |