[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;
@ -239,36 +251,37 @@ struct AttrParseInfo {
};
SortedDictionaryEntry<AttrParseInfo> gAttributeParseInfo[] = {
{ "cx" , { SkSVGAttribute::kCx , SetLengthAttribute }},
{ "cy" , { SkSVGAttribute::kCy , SetLengthAttribute }},
{ "d" , { SkSVGAttribute::kD , SetPathDataAttribute }},
{ "fill" , { SkSVGAttribute::kFill , SetPaintAttribute }},
{ "fill-opacity" , { SkSVGAttribute::kFillOpacity , SetNumberAttribute }},
{ "height" , { SkSVGAttribute::kHeight , SetLengthAttribute }},
{ "offset" , { SkSVGAttribute::kOffset , SetLengthAttribute }},
{ "opacity" , { SkSVGAttribute::kOpacity , SetNumberAttribute }},
{ "points" , { SkSVGAttribute::kPoints , SetPointsAttribute }},
{ "r" , { SkSVGAttribute::kR , SetLengthAttribute }},
{ "rx" , { SkSVGAttribute::kRx , SetLengthAttribute }},
{ "ry" , { SkSVGAttribute::kRy , SetLengthAttribute }},
{ "stop-color" , { SkSVGAttribute::kStopColor , SetColorAttribute }},
{ "stop-opacity" , { SkSVGAttribute::kStopOpacity , SetNumberAttribute }},
{ "stroke" , { SkSVGAttribute::kStroke , SetPaintAttribute }},
{ "stroke-linecap" , { SkSVGAttribute::kStrokeLineCap , SetLineCapAttribute }},
{ "stroke-linejoin", { SkSVGAttribute::kStrokeLineJoin, SetLineJoinAttribute }},
{ "stroke-opacity" , { SkSVGAttribute::kStrokeOpacity , SetNumberAttribute }},
{ "stroke-width" , { SkSVGAttribute::kStrokeWidth , SetLengthAttribute }},
{ "style" , { SkSVGAttribute::kUnknown , SetStyleAttributes }},
{ "transform" , { SkSVGAttribute::kTransform , SetTransformAttribute }},
{ "viewBox" , { SkSVGAttribute::kViewBox , SetViewBoxAttribute }},
{ "width" , { SkSVGAttribute::kWidth , SetLengthAttribute }},
{ "x" , { SkSVGAttribute::kX , SetLengthAttribute }},
{ "x1" , { SkSVGAttribute::kX1 , SetLengthAttribute }},
{ "x2" , { SkSVGAttribute::kX2 , SetLengthAttribute }},
{ "xlink:href" , { SkSVGAttribute::kHref , SetIRIAttribute }},
{ "y" , { SkSVGAttribute::kY , SetLengthAttribute }},
{ "y1" , { SkSVGAttribute::kY1 , SetLengthAttribute }},
{ "y2" , { SkSVGAttribute::kY2 , SetLengthAttribute }},
{ "cx" , { SkSVGAttribute::kCx , SetLengthAttribute }},
{ "cy" , { SkSVGAttribute::kCy , SetLengthAttribute }},
{ "d" , { SkSVGAttribute::kD , SetPathDataAttribute }},
{ "fill" , { SkSVGAttribute::kFill , SetPaintAttribute }},
{ "fill-opacity" , { SkSVGAttribute::kFillOpacity , SetNumberAttribute }},
{ "height" , { SkSVGAttribute::kHeight , SetLengthAttribute }},
{ "offset" , { SkSVGAttribute::kOffset , SetLengthAttribute }},
{ "opacity" , { SkSVGAttribute::kOpacity , SetNumberAttribute }},
{ "points" , { SkSVGAttribute::kPoints , SetPointsAttribute }},
{ "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 }},
{ "stroke-linecap" , { SkSVGAttribute::kStrokeLineCap , SetLineCapAttribute }},
{ "stroke-linejoin", { SkSVGAttribute::kStrokeLineJoin, SetLineJoinAttribute }},
{ "stroke-opacity" , { SkSVGAttribute::kStrokeOpacity , SetNumberAttribute }},
{ "stroke-width" , { SkSVGAttribute::kStrokeWidth , SetLengthAttribute }},
{ "style" , { SkSVGAttribute::kUnknown , SetStyleAttributes }},
{ "transform" , { SkSVGAttribute::kTransform , SetTransformAttribute }},
{ "viewBox" , { SkSVGAttribute::kViewBox , SetViewBoxAttribute }},
{ "width" , { SkSVGAttribute::kWidth , SetLengthAttribute }},
{ "x" , { SkSVGAttribute::kX , SetLengthAttribute }},
{ "x1" , { SkSVGAttribute::kX1 , SetLengthAttribute }},
{ "x2" , { SkSVGAttribute::kX2 , SetLengthAttribute }},
{ "xlink:href" , { SkSVGAttribute::kHref , SetIRIAttribute }},
{ "y" , { SkSVGAttribute::kY , SetLengthAttribute }},
{ "y1" , { SkSVGAttribute::kY1 , SetLengthAttribute }},
{ "y2" , { SkSVGAttribute::kY2 , SetLengthAttribute }},
};
SortedDictionaryEntry<sk_sp<SkSVGNode>(*)()> gTagFactories[] = {

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&);
@ -41,7 +42,8 @@ private:
SkSVGLength fX2 = SkSVGLength(100, SkSVGLength::Unit::kPercentage);
SkSVGLength fY2 = SkSVGLength(0 , SkSVGLength::Unit::kPercentage);
SkSVGStringType fHref;
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,
@ -68,16 +69,18 @@ private:
typedef SkSVGValue INHERITED;
};
using SkSVGColorValue = SkSVGWrapperValue<SkSVGColorType , SkSVGValue::Type::kColor >;
using SkSVGLengthValue = SkSVGWrapperValue<SkSVGLength , SkSVGValue::Type::kLength >;
using SkSVGPathValue = SkSVGWrapperValue<SkPath , SkSVGValue::Type::kPath >;
using SkSVGTransformValue = SkSVGWrapperValue<SkSVGTransformType, SkSVGValue::Type::kTransform>;
using SkSVGViewBoxValue = SkSVGWrapperValue<SkSVGViewBoxType , SkSVGValue::Type::kViewBox >;
using SkSVGPaintValue = SkSVGWrapperValue<SkSVGPaint , SkSVGValue::Type::kPaint >;
using SkSVGLineCapValue = SkSVGWrapperValue<SkSVGLineCap , SkSVGValue::Type::kLineCap >;
using SkSVGLineJoinValue = SkSVGWrapperValue<SkSVGLineJoin , SkSVGValue::Type::kLineJoin >;
using SkSVGNumberValue = SkSVGWrapperValue<SkSVGNumberType , SkSVGValue::Type::kNumber >;
using SkSVGPointsValue = SkSVGWrapperValue<SkSVGPointsType , SkSVGValue::Type::kPoints >;
using SkSVGStringValue = SkSVGWrapperValue<SkSVGStringType , SkSVGValue::Type::kString >;
using SkSVGColorValue = SkSVGWrapperValue<SkSVGColorType , SkSVGValue::Type::kColor >;
using SkSVGLengthValue = SkSVGWrapperValue<SkSVGLength , SkSVGValue::Type::kLength >;
using SkSVGPathValue = SkSVGWrapperValue<SkPath , SkSVGValue::Type::kPath >;
using SkSVGTransformValue = SkSVGWrapperValue<SkSVGTransformType, SkSVGValue::Type::kTransform>;
using SkSVGViewBoxValue = SkSVGWrapperValue<SkSVGViewBoxType , SkSVGValue::Type::kViewBox >;
using SkSVGPaintValue = SkSVGWrapperValue<SkSVGPaint , SkSVGValue::Type::kPaint >;
using SkSVGLineCapValue = SkSVGWrapperValue<SkSVGLineCap , SkSVGValue::Type::kLineCap >;
using SkSVGLineJoinValue = SkSVGWrapperValue<SkSVGLineJoin , SkSVGValue::Type::kLineJoin >;
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