blob: 5718aac638b87c795437efef6bb505adaa49ec2a [file] [log] [blame]
Florin Malitadf007e12017-10-09 15:14:13 -04001/*
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
13void SkSVGGradient::setHref(const SkSVGStringType& href) {
14 fHref = std::move(href);
15}
16
17void SkSVGGradient::setGradientTransform(const SkSVGTransformType& t) {
18 fGradientTransform = t;
19}
20
21void SkSVGGradient::setSpreadMethod(const SkSVGSpreadMethod& spread) {
22 fSpreadMethod = spread;
23}
24
25void 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
48void 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);
Florin Malita532a0912017-10-09 22:11:50 -040070 if (ref && (ref->tag() == SkSVGTag::kLinearGradient ||
71 ref->tag() == SkSVGTag::kRadialGradient)) {
Florin Malitadf007e12017-10-09 15:14:13 -040072 static_cast<const SkSVGGradient*>(ref)->collectColorStops(ctx, pos, colors);
73 }
74 }
75}
76
77bool 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}