diff options
author | fmalita <fmalita@chromium.org> | 2015-02-03 05:44:40 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-02-03 05:44:40 -0800 |
commit | 532faa9021cde714bc1fcc5650257fd7310f9712 (patch) | |
tree | 0abbcca9eae5f5794e4b3fbf14bc898bfb56ed34 /experimental/svg | |
parent | 1ca3e01b3e766c9cf086cb685e31709fb9c189b4 (diff) |
[SkSVGDevice] Initial shader/gradient support
* linear gradient support (based on shawcroft@google.com's CL)
* generic paint resources reorg
* opacity support
R=reed@google.com,mtklein@google.com,halcanary@google.com
Review URL: https://codereview.chromium.org/892973002
Diffstat (limited to 'experimental/svg')
-rw-r--r-- | experimental/svg/SkSVGDevice.cpp | 306 | ||||
-rw-r--r-- | experimental/svg/SkSVGDevice.h | 10 |
2 files changed, 249 insertions, 67 deletions
diff --git a/experimental/svg/SkSVGDevice.cpp b/experimental/svg/SkSVGDevice.cpp index 4a662e7b94..c0ca612cde 100644 --- a/experimental/svg/SkSVGDevice.cpp +++ b/experimental/svg/SkSVGDevice.cpp @@ -11,26 +11,227 @@ #include "SkDraw.h" #include "SkPaint.h" #include "SkParsePath.h" +#include "SkShader.h" #include "SkStream.h" #include "SkXMLWriter.h" namespace { -class AutoElement { +static SkString svg_color(SkColor color) { + SkString colorStr; + colorStr.printf("rgb(%u,%u,%u)", + SkColorGetR(color), + SkColorGetG(color), + SkColorGetB(color)); + return colorStr; +} + +static SkScalar svg_opacity(SkColor color) { + return SkIntToScalar(SkColorGetA(color)) / SK_AlphaOPAQUE; +} + +struct Resources { + Resources(const SkPaint& paint) + : fPaintServer(svg_color(paint.getColor())) {} + + SkString fPaintServer; +}; + +} + +// 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) {} + + SkString addLinearGradient() { + SkString id; + id.printf("gradient_%d", fGradientCount++); + return id; + } + +private: + uint32_t fGradientCount; +}; + +class SkSVGDevice::AutoElement : ::SkNoncopyable { public: AutoElement(const char name[], SkXMLWriter* writer) - : fWriter(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(paint); + fWriter->startElement(name); + + this->addPaint(paint, res); + this->addTransform(*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); + } + private: - SkXMLWriter* fWriter; + Resources addResources(const SkPaint& paint); + void addResourceDefs(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); + + 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); + } else { + this->addAttribute("fill", "none"); + } + + if (style == SkPaint::kStroke_Style || style == SkPaint::kStrokeAndFill_Style) { + this->addAttribute("stroke", resources.fPaintServer); + this->addAttribute("stroke-width", paint.getStrokeWidth()); + } else { + this->addAttribute("stroke", "none"); + } + + if (SK_AlphaOPAQUE != SkColorGetA(paint.getColor())) { + this->addAttribute("opacity", svg_opacity(paint.getColor())); + } +} + +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 SkPaint& paint) { + Resources resources(paint); + this->addResourceDefs(paint, &resources); + return resources; +} + +void SkSVGDevice::AutoElement::addResourceDefs(const SkPaint& paint, Resources* resources) { + const SkShader* shader = paint.getShader(); + if (!SkToBool(shader)) { + // TODO: clip support + return; + } + + SkShader::GradientInfo grInfo; + grInfo.fColorCount = 0; + if (SkShader::kLinear_GradientType != shader->asAGradient(&grInfo)) { + // TODO: non-linear gradient support + SkDebugf("unsupported shader type\n"); + 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()); + + resources->fPaintServer.printf("url(#%s)", addLinearGradientDef(grInfo, shader).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()); + gradient.addTransform(shader->getLocalMatrix(), "gradientTransform"); + + 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; } SkBaseDevice* SkSVGDevice::Create(const SkISize& size, SkWStream* wstream) { @@ -42,22 +243,23 @@ SkBaseDevice* SkSVGDevice::Create(const SkISize& size, SkWStream* wstream) { } SkSVGDevice::SkSVGDevice(const SkISize& size, SkWStream* wstream) - : fWriter(SkNEW_ARGS(SkXMLStreamWriter, (wstream))) { + : fWriter(SkNEW_ARGS(SkXMLStreamWriter, (wstream))) + , fResourceBucket(SkNEW(ResourceBucket)) { fLegacyBitmap.setInfo(SkImageInfo::MakeUnknown(size.width(), size.height())); fWriter->writeHeader(); - fWriter->startElement("svg"); - fWriter->addAttribute("xmlns", "http://www.w3.org/2000/svg"); - fWriter->addAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink"); - fWriter->addS32Attribute("width", size.width()); - fWriter->addS32Attribute("height", size.height()); + + // 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() { - fWriter->endElement(); - fWriter->flush(); - SkDELETE(fWriter); } SkImageInfo SkSVGDevice::imageInfo() const { @@ -68,109 +270,85 @@ const SkBitmap& SkSVGDevice::onAccessBitmap() { return fLegacyBitmap; } -void SkSVGDevice::addPaint(const SkPaint& paint) { - SkColor color = paint.getColor(); - SkString colorStr; - colorStr.appendf("rgb(%u,%u,%u)", SkColorGetR(color), SkColorGetG(color), SkColorGetB(color)); - - SkPaint::Style style = paint.getStyle(); - if (style == SkPaint::kFill_Style || style == SkPaint::kStrokeAndFill_Style) { - fWriter->addAttribute("fill", colorStr.c_str()); - } else { - fWriter->addAttribute("fill", "none"); - } - - if (style == SkPaint::kStroke_Style || style == SkPaint::kStrokeAndFill_Style) { - fWriter->addAttribute("stroke", colorStr.c_str()); - fWriter->addScalarAttribute("stroke-width", paint.getStrokeWidth()); - } else { - fWriter->addAttribute("stroke", "none"); - } -} - -void SkSVGDevice::addTransform(const SkMatrix &t) { - if (t.isIdentity()) { - return; - } - - SkString tstr; - tstr.appendf("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())); - fWriter->addAttribute("transform", tstr.c_str()); -} - -void SkSVGDevice::drawPaint(const SkDraw&, const SkPaint& paint) { - // todo +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()); } void SkSVGDevice::drawPoints(const SkDraw&, SkCanvas::PointMode mode, size_t count, const SkPoint[], const SkPaint& paint) { // todo + SkDebugf("unsupported operation: drawPoints()"); } void SkSVGDevice::drawRect(const SkDraw& draw, const SkRect& r, const SkPaint& paint) { - AutoElement elem("rect", fWriter); - - fWriter->addScalarAttribute("x", r.fLeft); - fWriter->addScalarAttribute("y", r.fTop); - fWriter->addScalarAttribute("width", r.width()); - fWriter->addScalarAttribute("height", r.height()); - - this->addPaint(paint); - this->addTransform(*draw.fMatrix); + 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()); } -void SkSVGDevice::drawOval(const SkDraw&, const SkRect& oval, const SkPaint& paint) { - // todo +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()"); } void SkSVGDevice::drawPath(const SkDraw& draw, const SkPath& path, const SkPaint& paint, const SkMatrix* prePathMatrix, bool pathIsMutable) { - AutoElement elem("path", fWriter); + AutoElement elem("path", fWriter, fResourceBucket, draw, paint); SkString pathStr; SkParsePath::ToSVGString(path, &pathStr); - fWriter->addAttribute("d", pathStr.c_str()); - - this->addPaint(paint); - this->addTransform(*draw.fMatrix); + elem.addAttribute("d", pathStr.c_str()); } void SkSVGDevice::drawBitmap(const SkDraw&, const SkBitmap& bitmap, const SkMatrix& matrix, const SkPaint& paint) { // todo + SkDebugf("unsupported operation: drawBitmap()"); } void SkSVGDevice::drawSprite(const SkDraw&, const SkBitmap& bitmap, int x, int y, const SkPaint& paint) { // todo + SkDebugf("unsupported operation: drawSprite()"); } void SkSVGDevice::drawBitmapRect(const SkDraw&, const SkBitmap&, const SkRect* srcOrNull, const SkRect& dst, const SkPaint& paint, SkCanvas::DrawBitmapRectFlags flags) { // todo + SkDebugf("unsupported operation: drawBitmapRect()"); } void SkSVGDevice::drawText(const SkDraw&, const void* text, size_t len, SkScalar x, SkScalar y, const SkPaint& paint) { // todo + SkDebugf("unsupported operation: drawText()"); } void SkSVGDevice::drawPosText(const SkDraw&, const void* text, size_t len,const SkScalar pos[], int scalarsPerPos, const SkPoint& offset, const SkPaint& paint) { // todo + SkDebugf("unsupported operation: drawPosText()"); } 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()"); } void SkSVGDevice::drawVertices(const SkDraw&, SkCanvas::VertexMode, int vertexCount, @@ -179,9 +357,11 @@ void SkSVGDevice::drawVertices(const SkDraw&, SkCanvas::VertexMode, int vertexCo const uint16_t indices[], int indexCount, const SkPaint& paint) { // todo + SkDebugf("unsupported operation: drawVertices()"); } void SkSVGDevice::drawDevice(const SkDraw&, SkBaseDevice*, int x, int y, const SkPaint&) { // todo + SkDebugf("unsupported operation: drawDevice()"); } diff --git a/experimental/svg/SkSVGDevice.h b/experimental/svg/SkSVGDevice.h index d6c48e2c0f..0ea9e19bfc 100644 --- a/experimental/svg/SkSVGDevice.h +++ b/experimental/svg/SkSVGDevice.h @@ -62,11 +62,13 @@ private: SkSVGDevice(const SkISize& size, SkWStream* wstream); virtual ~SkSVGDevice(); - void addPaint(const SkPaint& paint); - void addTransform(const SkMatrix& t); + class AutoElement; + class ResourceBucket; - SkXMLWriter* fWriter; - SkBitmap fLegacyBitmap; + SkAutoTDelete<SkXMLWriter> fWriter; + SkAutoTDelete<AutoElement> fRootElement; + SkAutoTDelete<ResourceBucket> fResourceBucket; + SkBitmap fLegacyBitmap; }; #endif // SkSVGDevice_DEFINED |