/* * Copyright 2017 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "SkottieProperties.h" #include "SkColor.h" #include "SkJSONCPP.h" #include "SkPath.h" #include "SkSGColor.h" #include "SkSGGradient.h" #include "SkSGPath.h" #include "SkSGRect.h" #include "SkSGTransform.h" #include "SkSGTrimEffect.h" #include namespace skottie { namespace { SkColor VecToColor(const float* v, size_t size) { // best effort to turn this into a color const auto r = size > 0 ? v[0] : 0, g = size > 1 ? v[1] : 0, b = size > 2 ? v[2] : 0, a = size > 3 ? v[3] : 1; return SkColorSetARGB(SkTPin(a, 0, 1) * 255, SkTPin(r, 0, 1) * 255, SkTPin(g, 0, 1) * 255, SkTPin(b, 0, 1) * 255); } } // namespace template <> size_t ValueTraits::Cardinality(const ScalarValue&) { return 1; } template <> template <> SkScalar ValueTraits::As(const ScalarValue& v) { return v; } template <> size_t ValueTraits::Cardinality(const VectorValue& vec) { return vec.size(); } template <> template <> SkColor ValueTraits::As(const VectorValue& vec) { return VecToColor(vec.data(), vec.size()); } template <> template <> SkPoint ValueTraits::As(const VectorValue& vec) { // best effort to turn this into a point const auto x = vec.size() > 0 ? vec[0] : 0, y = vec.size() > 1 ? vec[1] : 0; return SkPoint::Make(x, y); } template <> template <> SkSize ValueTraits::As(const VectorValue& vec) { const auto pt = ValueTraits::As(vec); return SkSize::Make(pt.x(), pt.y()); } template <> size_t ValueTraits::Cardinality(const ShapeValue& path) { return SkTo(path.countVerbs()); } template <> template <> SkPath ValueTraits::As(const ShapeValue& path) { return path; } CompositeRRect::CompositeRRect(sk_sp wrapped_node) : fRRectNode(std::move(wrapped_node)) {} void CompositeRRect::apply() { // BM "position" == "center position" auto rr = SkRRect::MakeRectXY(SkRect::MakeXYWH(fPosition.x() - fSize.width() / 2, fPosition.y() - fSize.height() / 2, fSize.width(), fSize.height()), fRadius.width(), fRadius.height()); fRRectNode->setRRect(rr); } CompositeTransform::CompositeTransform(sk_sp matrix) : fMatrixNode(std::move(matrix)) {} void CompositeTransform::apply() { SkMatrix t = SkMatrix::MakeTrans(-fAnchorPoint.x(), -fAnchorPoint.y()); t.postScale(fScale.x() / 100, fScale.y() / 100); // 100% based t.postRotate(fRotation); t.postTranslate(fPosition.x(), fPosition.y()); // TODO: skew fMatrixNode->setMatrix(t); } CompositePolyStar::CompositePolyStar(sk_sp wrapped_node, Type t) : fPathNode(std::move(wrapped_node)) , fType(t) {} void CompositePolyStar::apply() { const auto count = SkScalarTruncToInt(fPointCount); const auto arc = SK_ScalarPI * 2 / count; const auto pt_on_circle = [](const SkPoint& c, SkScalar r, SkScalar a) { return SkPoint::Make(c.x() + r * std::cos(a), c.y() + r * std::sin(a)); }; // TODO: inner/outer "roundness"? SkPath poly; auto angle = SkDegreesToRadians(fRotation); poly.moveTo(pt_on_circle(fPosition, fOuterRadius, angle)); for (int i = 0; i < count; ++i) { if (fType == Type::kStar) { poly.lineTo(pt_on_circle(fPosition, fInnerRadius, angle + arc * 0.5f)); } angle += arc; poly.lineTo(pt_on_circle(fPosition, fOuterRadius, angle)); } poly.close(); fPathNode->setPath(poly); } CompositeGradient::CompositeGradient(sk_sp grad, size_t stopCount) : fGradient(std::move(grad)) , fStopCount(stopCount) {} void CompositeGradient::apply() { this->onApply(); // |fColorStops| holds |fStopCount| x [ pos, r, g, g ] + ? x [ pos, alpha ] if (fColorStops.size() < fStopCount * 4 || ((fColorStops.size() - fStopCount * 4) % 2)) { SkDebugf("!! Invalid gradient stop array size: %zu", fColorStops.size()); return; } std::vector stops; // TODO: merge/lerp opacity stops const auto csEnd = fColorStops.cbegin() + fStopCount * 4; for (auto cs = fColorStops.cbegin(); cs != csEnd; cs += 4) { stops.push_back({ *cs, VecToColor(&*(cs + 1), 3) }); } fGradient->setColorStops(std::move(stops)); } CompositeLinearGradient::CompositeLinearGradient(sk_sp grad, size_t stopCount) : INHERITED(std::move(grad), stopCount) {} void CompositeLinearGradient::onApply() { auto* grad = static_cast(fGradient.get()); grad->setStartPoint(this->startPoint()); grad->setEndPoint(this->endPoint()); } CompositeRadialGradient::CompositeRadialGradient(sk_sp grad, size_t stopCount) : INHERITED(std::move(grad), stopCount) {} void CompositeRadialGradient::onApply() { auto* grad = static_cast(fGradient.get()); grad->setStartCenter(this->startPoint()); grad->setEndCenter(this->startPoint()); grad->setStartRadius(0); grad->setEndRadius(SkPoint::Distance(this->startPoint(), this->endPoint())); } CompositeTrimEffect::CompositeTrimEffect(sk_sp trimEffect) : fTrimEffect(std::move(trimEffect)) { SkASSERT(fTrimEffect); } void CompositeTrimEffect::apply() { // BM semantics: start/end are percentages, offset is "degrees" (?!). const auto start = fStart / 100, end = fEnd / 100, offset = fOffset / 360; auto startT = SkTMin(start, end) + offset, stopT = SkTMax(start, end) + offset; auto mode = SkTrimPathEffect::Mode::kNormal; if (stopT - startT < 1) { startT -= SkScalarFloorToScalar(startT); stopT -= SkScalarFloorToScalar(stopT); if (startT > stopT) { SkTSwap(startT, stopT); mode = SkTrimPathEffect::Mode::kInverted; } } else { startT = 0; stopT = 1; } fTrimEffect->setStart(startT); fTrimEffect->setStop(stopT); fTrimEffect->setMode(mode); } } // namespace skottie