diff options
author | fmalita <fmalita@chromium.org> | 2015-02-04 07:39:34 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-02-04 07:39:34 -0800 |
commit | 1a481fe4bf632ed4f76cb337691236fabfd4ab03 (patch) | |
tree | 1dbb47d73d8565084c6edb664b75e986bcf8f4a2 /experimental/svg | |
parent | b9d4d279cd381392e69c6638457055264696f7eb (diff) |
[SkSVGDevice] Initial clipping support
Implement SVG clips based on clip stack flattening -
which is now exposed in SkClipStack::asPath() and shared
with SkCanvas's simplify-clip code.
R=reed@google.com,mtklein@google.com
Review URL: https://codereview.chromium.org/876923003
Diffstat (limited to 'experimental/svg')
-rw-r--r-- | experimental/svg/SkSVGDevice.cpp | 136 |
1 files changed, 97 insertions, 39 deletions
diff --git a/experimental/svg/SkSVGDevice.cpp b/experimental/svg/SkSVGDevice.cpp index bd503b7d8f..9f4a54a8a8 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,18 +242,31 @@ 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); + + // 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::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; - } + SkASSERT(SkToBool(shader)); SkShader::GradientInfo grInfo; grInfo.fColorCount = 0; @@ -254,21 +276,49 @@ 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(); - 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()); - // 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()); +} - 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); + 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,8 @@ 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(SkIntToScalar(this->width()), + SkIntToScalar(this->height()))); } void SkSVGDevice::drawPoints(const SkDraw&, SkCanvas::PointMode mode, size_t count, @@ -379,10 +440,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) { |