[SVGDom] Linear gradient 'spreadMethod' support

R=stephana@google.com,robertphillips@google.com
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2337203002

Review-Url: https://codereview.chromium.org/2337203002
This commit is contained in:
fmalita 2016-09-13 12:56:11 -07:00 committed by Commit bot
parent 649985a530
commit cecd617a42
8 changed files with 127 additions and 44 deletions

View File

@ -27,6 +27,7 @@ enum class SkSVGAttribute {
kR, // <circle>: radius
kRx, // <ellipse>,<rect>: horizontal (corner) radius
kRy, // <ellipse>,<rect>: vertical (corner) radius
kSpreadMethod,
kStopColor,
kStopOpacity,
kStroke,

View File

@ -491,6 +491,29 @@ bool SkSVGAttributeParser::parseLineJoin(SkSVGLineJoin* join) {
return parsedValue && this->parseEOSToken();
}
// https://www.w3.org/TR/SVG/pservers.html#LinearGradientElementSpreadMethodAttribute
bool SkSVGAttributeParser::parseSpreadMethod(SkSVGSpreadMethod* spread) {
static const struct {
SkSVGSpreadMethod::Type fType;
const char* fName;
} gSpreadInfo[] = {
{ SkSVGSpreadMethod::Type::kPad , "pad" },
{ SkSVGSpreadMethod::Type::kReflect, "reflect" },
{ SkSVGSpreadMethod::Type::kRepeat , "repeat" },
};
bool parsedValue = false;
for (size_t i = 0; i < SK_ARRAY_COUNT(gSpreadInfo); ++i) {
if (this->parseExpectedStringToken(gSpreadInfo[i].fName)) {
*spread = SkSVGSpreadMethod(gSpreadInfo[i].fType);
parsedValue = true;
break;
}
}
return parsedValue && this->parseEOSToken();
}
// https://www.w3.org/TR/SVG/shapes.html#PolygonElementPointsAttribute
bool SkSVGAttributeParser::parsePoints(SkSVGPointsType* points) {
SkTDArray<SkPoint> pts;

View File

@ -24,6 +24,7 @@ public:
bool parseLineJoin(SkSVGLineJoin*);
bool parsePoints(SkSVGPointsType*);
bool parseIRI(SkSVGStringType*);
bool parseSpreadMethod(SkSVGSpreadMethod*);
private:
// Stack-only

View File

@ -149,6 +149,18 @@ bool SetLineJoinAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
return true;
}
bool SetSpreadMethodAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
const char* stringValue) {
SkSVGSpreadMethod spread;
SkSVGAttributeParser parser(stringValue);
if (!parser.parseSpreadMethod(&spread)) {
return false;
}
node->setAttribute(attr, SkSVGSpreadMethodValue(spread));
return true;
}
bool SetPointsAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
const char* stringValue) {
SkSVGPointsType points;
@ -251,6 +263,7 @@ SortedDictionaryEntry<AttrParseInfo> gAttributeParseInfo[] = {
{ "r" , { SkSVGAttribute::kR , SetLengthAttribute }},
{ "rx" , { SkSVGAttribute::kRx , SetLengthAttribute }},
{ "ry" , { SkSVGAttribute::kRy , SetLengthAttribute }},
{ "spreadMethod" , { SkSVGAttribute::kSpreadMethod , SetSpreadMethodAttribute }},
{ "stop-color" , { SkSVGAttribute::kStopColor , SetColorAttribute }},
{ "stop-opacity" , { SkSVGAttribute::kStopOpacity , SetNumberAttribute }},
{ "stroke" , { SkSVGAttribute::kStroke , SetPaintAttribute }},

View File

@ -17,6 +17,10 @@ void SkSVGLinearGradient::setHref(const SkSVGStringType& href) {
fHref = std::move(href);
}
void SkSVGLinearGradient::setSpreadMethod(const SkSVGSpreadMethod& spread) {
fSpreadMethod = spread;
}
void SkSVGLinearGradient::setX1(const SkSVGLength& x1) {
fX1 = x1;
}
@ -40,6 +44,11 @@ void SkSVGLinearGradient::onSetAttribute(SkSVGAttribute attr, const SkSVGValue&
this->setHref(*href);
}
break;
case SkSVGAttribute::kSpreadMethod:
if (const auto* spread = v.as<SkSVGSpreadMethodValue>()) {
this->setSpreadMethod(*spread);
}
break;
case SkSVGAttribute::kX1:
if (const auto* x1 = v.as<SkSVGLengthValue>()) {
this->setX1(*x1);
@ -110,10 +119,17 @@ bool SkSVGLinearGradient::onAsPaint(const SkSVGRenderContext& ctx, SkPaint* pain
// * stop (lazy?) sorting
// * href loop detection
// * href attribute inheritance (not just color stops)
// * spreadMethods support
// * objectBoundingBox units support
static_assert(static_cast<SkShader::TileMode>(SkSVGSpreadMethod::Type::kPad) ==
SkShader::kClamp_TileMode, "SkSVGSpreadMethod::Type is out of sync");
static_assert(static_cast<SkShader::TileMode>(SkSVGSpreadMethod::Type::kRepeat) ==
SkShader::kRepeat_TileMode, "SkSVGSpreadMethod::Type is out of sync");
static_assert(static_cast<SkShader::TileMode>(SkSVGSpreadMethod::Type::kReflect) ==
SkShader::kMirror_TileMode, "SkSVGSpreadMethod::Type is out of sync");
const auto tileMode = static_cast<SkShader::TileMode>(fSpreadMethod.type());
paint->setShader(SkGradientShader::MakeLinear(pts, colors.begin(), pos.begin(), colors.count(),
SkShader::kClamp_TileMode));
tileMode));
return true;
}

View File

@ -19,6 +19,7 @@ public:
}
void setHref(const SkSVGStringType&);
void setSpreadMethod(const SkSVGSpreadMethod&);
void setX1(const SkSVGLength&);
void setY1(const SkSVGLength&);
void setX2(const SkSVGLength&);
@ -42,6 +43,7 @@ private:
SkSVGLength fY2 = SkSVGLength(0 , SkSVGLength::Unit::kPercentage);
SkSVGStringType fHref;
SkSVGSpreadMethod fSpreadMethod = SkSVGSpreadMethod(SkSVGSpreadMethod::Type::kPad);
typedef SkSVGHiddenContainer INHERITED;
};

View File

@ -167,4 +167,28 @@ private:
Type fType;
};
class SkSVGSpreadMethod {
public:
// These values must match Skia's SkShader::TileMode enum.
enum class Type {
kPad, // kClamp_TileMode
kRepeat, // kRepeat_TileMode
kReflect, // kMirror_TileMode
};
constexpr SkSVGSpreadMethod() : fType(Type::kPad) {}
constexpr explicit SkSVGSpreadMethod(Type t) : fType(t) {}
SkSVGSpreadMethod(const SkSVGSpreadMethod&) = default;
SkSVGSpreadMethod& operator=(const SkSVGSpreadMethod&) = default;
bool operator==(const SkSVGSpreadMethod& other) const { return fType == other.fType; }
bool operator!=(const SkSVGSpreadMethod& other) const { return !(*this == other); }
Type type() const { return fType; }
private:
Type fType;
};
#endif // SkSVGTypes_DEFINED

View File

@ -25,6 +25,7 @@ public:
kPaint,
kPath,
kPoints,
kSpreadMethod,
kString,
kTransform,
kViewBox,
@ -79,5 +80,7 @@ using SkSVGLineJoinValue = SkSVGWrapperValue<SkSVGLineJoin , SkSVGValue::Ty
using SkSVGNumberValue = SkSVGWrapperValue<SkSVGNumberType , SkSVGValue::Type::kNumber >;
using SkSVGPointsValue = SkSVGWrapperValue<SkSVGPointsType , SkSVGValue::Type::kPoints >;
using SkSVGStringValue = SkSVGWrapperValue<SkSVGStringType , SkSVGValue::Type::kString >;
using SkSVGSpreadMethodValue = SkSVGWrapperValue<SkSVGSpreadMethod ,
SkSVGValue::Type::kSpreadMethod>;
#endif // SkSVGValue_DEFINED