[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:
parent
649985a530
commit
cecd617a42
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -24,6 +24,7 @@ public:
|
||||
bool parseLineJoin(SkSVGLineJoin*);
|
||||
bool parsePoints(SkSVGPointsType*);
|
||||
bool parseIRI(SkSVGStringType*);
|
||||
bool parseSpreadMethod(SkSVGSpreadMethod*);
|
||||
|
||||
private:
|
||||
// Stack-only
|
||||
|
@ -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[] = {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user