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 "SkottieProperties.h"
9
10#include "SkColor.h"
11#include "SkJSONCPP.h"
12#include "SkPath.h"
13#include "SkSGColor.h"
14#include "SkSGGradient.h"
15#include "SkSGPath.h"
16#include "SkSGRect.h"
17#include "SkSGTransform.h"
18
19#include <cmath>
20
21namespace  skottie {
22
23namespace {
24
25SkColor VecToColor(const float* v, size_t size) {
26    // best effort to turn this into a color
27    const auto r = size > 0 ? v[0] : 0,
28               g = size > 1 ? v[1] : 0,
29               b = size > 2 ? v[2] : 0,
30               a = size > 3 ? v[3] : 1;
31
32    return SkColorSetARGB(SkTPin<SkScalar>(a, 0, 1) * 255,
33                          SkTPin<SkScalar>(r, 0, 1) * 255,
34                          SkTPin<SkScalar>(g, 0, 1) * 255,
35                          SkTPin<SkScalar>(b, 0, 1) * 255);
36}
37
38} // namespace
39
40template <>
41size_t ValueTraits<ScalarValue>::Cardinality(const ScalarValue&) {
42    return 1;
43}
44
45template <>
46template <>
47SkScalar ValueTraits<ScalarValue>::As<SkScalar>(const ScalarValue& v) {
48    return v;
49}
50
51template <>
52size_t ValueTraits<VectorValue>::Cardinality(const VectorValue& vec) {
53    return vec.size();
54}
55
56template <>
57template <>
58SkColor ValueTraits<VectorValue>::As<SkColor>(const VectorValue& vec) {
59    return VecToColor(vec.data(), vec.size());
60}
61
62template <>
63template <>
64SkPoint ValueTraits<VectorValue>::As<SkPoint>(const VectorValue& vec) {
65    // best effort to turn this into a point
66    const auto x = vec.size() > 0 ? vec[0] : 0,
67               y = vec.size() > 1 ? vec[1] : 0;
68    return SkPoint::Make(x, y);
69}
70
71template <>
72template <>
73SkSize ValueTraits<VectorValue>::As<SkSize>(const VectorValue& vec) {
74    const auto pt = ValueTraits::As<SkPoint>(vec);
75    return SkSize::Make(pt.x(), pt.y());
76}
77
78template <>
79size_t ValueTraits<ShapeValue>::Cardinality(const ShapeValue& path) {
80    return SkTo<size_t>(path.countVerbs());
81}
82
83template <>
84template <>
85SkPath ValueTraits<ShapeValue>::As<SkPath>(const ShapeValue& path) {
86    return path;
87}
88
89CompositeRRect::CompositeRRect(sk_sp<sksg::RRect> wrapped_node)
90    : fRRectNode(std::move(wrapped_node)) {}
91
92void CompositeRRect::apply() {
93    // BM "position" == "center position"
94    auto rr = SkRRect::MakeRectXY(SkRect::MakeXYWH(fPosition.x() - fSize.width() / 2,
95                                                   fPosition.y() - fSize.height() / 2,
96                                                   fSize.width(), fSize.height()),
97                                  fRadius.width(),
98                                  fRadius.height());
99   fRRectNode->setRRect(rr);
100}
101
102CompositeTransform::CompositeTransform(sk_sp<sksg::Matrix> matrix)
103    : fMatrixNode(std::move(matrix)) {}
104
105void CompositeTransform::apply() {
106    SkMatrix t = SkMatrix::MakeTrans(-fAnchorPoint.x(), -fAnchorPoint.y());
107
108    t.postScale(fScale.x() / 100, fScale.y() / 100); // 100% based
109    t.postRotate(fRotation);
110    t.postTranslate(fPosition.x(), fPosition.y());
111    // TODO: skew
112
113    fMatrixNode->setMatrix(t);
114}
115
116CompositePolyStar::CompositePolyStar(sk_sp<sksg::Path> wrapped_node, Type t)
117    : fPathNode(std::move(wrapped_node))
118    , fType(t) {}
119
120void CompositePolyStar::apply() {
121    const auto count = SkScalarTruncToInt(fPointCount);
122    const auto arc   = SK_ScalarPI * 2 / count;
123
124    const auto pt_on_circle = [](const SkPoint& c, SkScalar r, SkScalar a) {
125        return SkPoint::Make(c.x() + r * std::cos(a),
126                             c.y() + r * std::sin(a));
127    };
128
129    // TODO: inner/outer "roundness"?
130
131    SkPath poly;
132
133    auto angle = SkDegreesToRadians(fRotation);
134    poly.moveTo(pt_on_circle(fPosition, fOuterRadius, angle));
135
136    for (int i = 0; i < count; ++i) {
137        if (fType == Type::kStar) {
138            poly.lineTo(pt_on_circle(fPosition, fInnerRadius, angle + arc * 0.5f));
139        }
140        angle += arc;
141        poly.lineTo(pt_on_circle(fPosition, fOuterRadius, angle));
142    }
143
144    poly.close();
145    fPathNode->setPath(poly);
146}
147
148CompositeGradient::CompositeGradient(sk_sp<sksg::Gradient> grad, size_t stopCount)
149    : fGradient(std::move(grad))
150    , fStopCount(stopCount) {}
151
152void CompositeGradient::apply() {
153    this->onApply();
154
155    // |fColorStops| holds |fStopCount| x [ pos, r, g, g ] + ? x [ pos, alpha ]
156
157    if (fColorStops.size() < fStopCount * 4 || ((fColorStops.size() - fStopCount * 4) % 2)) {
158        SkDebugf("!! Invalid gradient stop array size: %zu", fColorStops.size());
159        return;
160    }
161
162    std::vector<sksg::Gradient::ColorStop> stops;
163
164    // TODO: merge/lerp opacity stops
165    const auto csEnd = fColorStops.cbegin() + fStopCount * 4;
166    for (auto cs = fColorStops.cbegin(); cs != csEnd; cs += 4) {
167        stops.push_back({ *cs, VecToColor(&*(cs + 1), 3) });
168    }
169
170    fGradient->setColorStops(std::move(stops));
171}
172
173CompositeLinearGradient::CompositeLinearGradient(sk_sp<sksg::LinearGradient> grad, size_t stopCount)
174    : INHERITED(std::move(grad), stopCount) {}
175
176void CompositeLinearGradient::onApply() {
177    auto* grad = static_cast<sksg::LinearGradient*>(fGradient.get());
178    grad->setStartPoint(this->startPoint());
179    grad->setEndPoint(this->endPoint());
180}
181
182CompositeRadialGradient::CompositeRadialGradient(sk_sp<sksg::RadialGradient> grad, size_t stopCount)
183    : INHERITED(std::move(grad), stopCount) {}
184
185void CompositeRadialGradient::onApply() {
186    auto* grad = static_cast<sksg::RadialGradient*>(fGradient.get());
187    grad->setStartCenter(this->startPoint());
188    grad->setEndCenter(this->startPoint());
189    grad->setStartRadius(0);
190    grad->setEndRadius(SkPoint::Distance(this->startPoint(), this->endPoint()));
191}
192
193} // namespace skottie
194