1 /* 2 * Copyright 2017 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #include "SkSVGGradient.h" 9 #include "SkSVGRenderContext.h" 10 #include "SkSVGStop.h" 11 #include "SkSVGValue.h" 12 13 void SkSVGGradient::setHref(const SkSVGStringType& href) { 14 fHref = std::move(href); 15 } 16 17 void SkSVGGradient::setGradientTransform(const SkSVGTransformType& t) { 18 fGradientTransform = t; 19 } 20 21 void SkSVGGradient::setSpreadMethod(const SkSVGSpreadMethod& spread) { 22 fSpreadMethod = spread; 23 } 24 25 void SkSVGGradient::onSetAttribute(SkSVGAttribute attr, const SkSVGValue& v) { 26 switch (attr) { 27 case SkSVGAttribute::kGradientTransform: 28 if (const auto* t = v.as<SkSVGTransformValue>()) { 29 this->setGradientTransform(*t); 30 } 31 break; 32 case SkSVGAttribute::kHref: 33 if (const auto* href = v.as<SkSVGStringValue>()) { 34 this->setHref(*href); 35 } 36 break; 37 case SkSVGAttribute::kSpreadMethod: 38 if (const auto* spread = v.as<SkSVGSpreadMethodValue>()) { 39 this->setSpreadMethod(*spread); 40 } 41 break; 42 default: 43 this->INHERITED::onSetAttribute(attr, v); 44 } 45 } 46 47 // https://www.w3.org/TR/SVG/pservers.html#LinearGradientElementHrefAttribute 48 void SkSVGGradient::collectColorStops(const SkSVGRenderContext& ctx, 49 StopPositionArray* pos, 50 StopColorArray* colors) const { 51 // Used to resolve percentage offsets. 52 const SkSVGLengthContext ltx(SkSize::Make(1, 1)); 53 54 for (const auto& child : fChildren) { 55 if (child->tag() != SkSVGTag::kStop) { 56 continue; 57 } 58 59 const auto& stop = static_cast<const SkSVGStop&>(*child); 60 colors->push_back(SkColorSetA(stop.stopColor(), 61 SkScalarRoundToInt(stop.stopOpacity() * 255))); 62 pos->push_back(SkTPin(ltx.resolve(stop.offset(), SkSVGLengthContext::LengthType::kOther), 63 0.f, 1.f)); 64 } 65 66 SkASSERT(colors->count() == pos->count()); 67 68 if (pos->empty() && !fHref.value().isEmpty()) { 69 const auto* ref = ctx.findNodeById(fHref); 70 if (ref && (ref->tag() == SkSVGTag::kLinearGradient || 71 ref->tag() == SkSVGTag::kRadialGradient)) { 72 static_cast<const SkSVGGradient*>(ref)->collectColorStops(ctx, pos, colors); 73 } 74 } 75 } 76 77 bool SkSVGGradient::onAsPaint(const SkSVGRenderContext& ctx, SkPaint* paint) const { 78 StopColorArray colors; 79 StopPositionArray pos; 80 81 this->collectColorStops(ctx, &pos, &colors); 82 83 // TODO: 84 // * stop (lazy?) sorting 85 // * href loop detection 86 // * href attribute inheritance (not just color stops) 87 // * objectBoundingBox units support 88 89 static_assert(static_cast<SkShader::TileMode>(SkSVGSpreadMethod::Type::kPad) == 90 SkShader::kClamp_TileMode, "SkSVGSpreadMethod::Type is out of sync"); 91 static_assert(static_cast<SkShader::TileMode>(SkSVGSpreadMethod::Type::kRepeat) == 92 SkShader::kRepeat_TileMode, "SkSVGSpreadMethod::Type is out of sync"); 93 static_assert(static_cast<SkShader::TileMode>(SkSVGSpreadMethod::Type::kReflect) == 94 SkShader::kMirror_TileMode, "SkSVGSpreadMethod::Type is out of sync"); 95 const auto tileMode = static_cast<SkShader::TileMode>(fSpreadMethod.type()); 96 97 paint->setShader(this->onMakeShader(ctx, colors.begin(), pos.begin(), colors.count(), tileMode, 98 fGradientTransform.value())); 99 return true; 100 } 101