• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "experimental/svg/model/SkSVGGradient.h"
9 #include "experimental/svg/model/SkSVGRenderContext.h"
10 #include "experimental/svg/model/SkSVGStop.h"
11 #include "experimental/svg/model/SkSVGValue.h"
12 
setHref(const SkSVGStringType & href)13 void SkSVGGradient::setHref(const SkSVGStringType& href) {
14     fHref = std::move(href);
15 }
16 
setGradientTransform(const SkSVGTransformType & t)17 void SkSVGGradient::setGradientTransform(const SkSVGTransformType& t) {
18     fGradientTransform = t;
19 }
20 
setSpreadMethod(const SkSVGSpreadMethod & spread)21 void SkSVGGradient::setSpreadMethod(const SkSVGSpreadMethod& spread) {
22     fSpreadMethod = spread;
23 }
24 
onSetAttribute(SkSVGAttribute attr,const SkSVGValue & v)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
collectColorStops(const SkSVGRenderContext & ctx,StopPositionArray * pos,StopColorArray * colors) const48 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 
onAsPaint(const SkSVGRenderContext & ctx,SkPaint * paint) const77 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<SkTileMode>(SkSVGSpreadMethod::Type::kPad) ==
90                   SkTileMode::kClamp, "SkSVGSpreadMethod::Type is out of sync");
91     static_assert(static_cast<SkTileMode>(SkSVGSpreadMethod::Type::kRepeat) ==
92                   SkTileMode::kRepeat, "SkSVGSpreadMethod::Type is out of sync");
93     static_assert(static_cast<SkTileMode>(SkSVGSpreadMethod::Type::kReflect) ==
94                   SkTileMode::kMirror, "SkSVGSpreadMethod::Type is out of sync");
95     const auto tileMode = static_cast<SkTileMode>(fSpreadMethod.type());
96 
97     paint->setShader(this->onMakeShader(ctx, colors.begin(), pos.begin(), colors.count(), tileMode,
98                                         fGradientTransform.value()));
99     return true;
100 }
101