1 /* 2 * Copyright 2016 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 "SkGradientShader.h" 9 #include "SkSVGLinearGradient.h" 10 #include "SkSVGRenderContext.h" 11 #include "SkSVGStop.h" 12 #include "SkSVGValue.h" 13 14 SkSVGLinearGradient::SkSVGLinearGradient() : INHERITED(SkSVGTag::kLinearGradient) {} 15 16 void SkSVGLinearGradient::setHref(const SkSVGStringType& href) { 17 fHref = std::move(href); 18 } 19 20 void SkSVGLinearGradient::setGradientTransform(const SkSVGTransformType& t) { 21 fGradientTransform = t; 22 } 23 24 void SkSVGLinearGradient::setSpreadMethod(const SkSVGSpreadMethod& spread) { 25 fSpreadMethod = spread; 26 } 27 28 void SkSVGLinearGradient::setX1(const SkSVGLength& x1) { 29 fX1 = x1; 30 } 31 32 void SkSVGLinearGradient::setY1(const SkSVGLength& y1) { 33 fY1 = y1; 34 } 35 36 void SkSVGLinearGradient::setX2(const SkSVGLength& x2) { 37 fX2 = x2; 38 } 39 40 void SkSVGLinearGradient::setY2(const SkSVGLength& y2) { 41 fY2 = y2; 42 } 43 44 void SkSVGLinearGradient::onSetAttribute(SkSVGAttribute attr, const SkSVGValue& v) { 45 switch (attr) { 46 case SkSVGAttribute::kGradientTransform: 47 if (const auto* t = v.as<SkSVGTransformValue>()) { 48 this->setGradientTransform(*t); 49 } 50 break; 51 case SkSVGAttribute::kHref: 52 if (const auto* href = v.as<SkSVGStringValue>()) { 53 this->setHref(*href); 54 } 55 break; 56 case SkSVGAttribute::kSpreadMethod: 57 if (const auto* spread = v.as<SkSVGSpreadMethodValue>()) { 58 this->setSpreadMethod(*spread); 59 } 60 break; 61 case SkSVGAttribute::kX1: 62 if (const auto* x1 = v.as<SkSVGLengthValue>()) { 63 this->setX1(*x1); 64 } 65 break; 66 case SkSVGAttribute::kY1: 67 if (const auto* y1 = v.as<SkSVGLengthValue>()) { 68 this->setY1(*y1); 69 } 70 break; 71 case SkSVGAttribute::kX2: 72 if (const auto* x2 = v.as<SkSVGLengthValue>()) { 73 this->setX2(*x2); 74 } 75 break; 76 case SkSVGAttribute::kY2: 77 if (const auto* y2 = v.as<SkSVGLengthValue>()) { 78 this->setY2(*y2); 79 } 80 break; 81 default: 82 this->INHERITED::onSetAttribute(attr, v); 83 } 84 } 85 86 // https://www.w3.org/TR/SVG/pservers.html#LinearGradientElementHrefAttribute 87 void SkSVGLinearGradient::collectColorStops(const SkSVGRenderContext& ctx, 88 SkSTArray<2, SkScalar, true>* pos, 89 SkSTArray<2, SkColor, true>* colors) const { 90 // Used to resolve percentage offsets. 91 const SkSVGLengthContext ltx(SkSize::Make(1, 1)); 92 93 for (const auto& child : fChildren) { 94 if (child->tag() != SkSVGTag::kStop) { 95 continue; 96 } 97 98 const auto& stop = static_cast<const SkSVGStop&>(*child); 99 colors->push_back(SkColorSetA(stop.stopColor(), 100 SkScalarRoundToInt(stop.stopOpacity() * 255))); 101 pos->push_back(SkTPin(ltx.resolve(stop.offset(), SkSVGLengthContext::LengthType::kOther), 102 0.f, 1.f)); 103 } 104 105 SkASSERT(colors->count() == pos->count()); 106 107 if (pos->empty() && !fHref.value().isEmpty()) { 108 const auto* ref = ctx.findNodeById(fHref); 109 if (ref && ref->tag() == SkSVGTag::kLinearGradient) { 110 static_cast<const SkSVGLinearGradient*>(ref)->collectColorStops(ctx, pos, colors); 111 } 112 } 113 } 114 115 bool SkSVGLinearGradient::onAsPaint(const SkSVGRenderContext& ctx, SkPaint* paint) const { 116 const auto& lctx = ctx.lengthContext(); 117 const auto x1 = lctx.resolve(fX1, SkSVGLengthContext::LengthType::kHorizontal); 118 const auto y1 = lctx.resolve(fY1, SkSVGLengthContext::LengthType::kVertical); 119 const auto x2 = lctx.resolve(fX2, SkSVGLengthContext::LengthType::kHorizontal); 120 const auto y2 = lctx.resolve(fY2, SkSVGLengthContext::LengthType::kVertical); 121 122 const SkPoint pts[2] = { {x1, y1}, {x2, y2}}; 123 SkSTArray<2, SkColor , true> colors; 124 SkSTArray<2, SkScalar, true> pos; 125 126 this->collectColorStops(ctx, &pos, &colors); 127 // TODO: 128 // * stop (lazy?) sorting 129 // * href loop detection 130 // * href attribute inheritance (not just color stops) 131 // * objectBoundingBox units support 132 133 static_assert(static_cast<SkShader::TileMode>(SkSVGSpreadMethod::Type::kPad) == 134 SkShader::kClamp_TileMode, "SkSVGSpreadMethod::Type is out of sync"); 135 static_assert(static_cast<SkShader::TileMode>(SkSVGSpreadMethod::Type::kRepeat) == 136 SkShader::kRepeat_TileMode, "SkSVGSpreadMethod::Type is out of sync"); 137 static_assert(static_cast<SkShader::TileMode>(SkSVGSpreadMethod::Type::kReflect) == 138 SkShader::kMirror_TileMode, "SkSVGSpreadMethod::Type is out of sync"); 139 const auto tileMode = static_cast<SkShader::TileMode>(fSpreadMethod.type()); 140 141 paint->setShader(SkGradientShader::MakeLinear(pts, colors.begin(), pos.begin(), colors.count(), 142 tileMode, 0, &fGradientTransform.value())); 143 return true; 144 } 145