Index: experimental/svg/SkSVGDevice.cpp |
diff --git a/experimental/svg/SkSVGDevice.cpp b/experimental/svg/SkSVGDevice.cpp |
index bd503b7d8fb29d074d7a625d84590beff036af21..9f4a54a8a8b5f4821be7467548d86de008654f27 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) { |