/* * Copyright 2018 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "SkottieAdapter.h" #include "SkMatrix.h" #include "SkPath.h" #include "SkRRect.h" #include "SkSGGradient.h" #include "SkSGPath.h" #include "SkSGRect.h" #include "SkSGTransform.h" #include "SkSGTrimEffect.h" #include "SkTo.h" #include "SkottieValue.h" #include #include namespace skottie { RRectAdapter::RRectAdapter(sk_sp wrapped_node) : fRRectNode(std::move(wrapped_node)) {} void RRectAdapter::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); } TransformAdapter::TransformAdapter(sk_sp matrix) : fMatrixNode(std::move(matrix)) {} void TransformAdapter::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); } PolyStarAdapter::PolyStarAdapter(sk_sp wrapped_node, Type t) : fPathNode(std::move(wrapped_node)) , fType(t) {} void PolyStarAdapter::apply() { static constexpr int kMaxPointCount = 100000; const auto count = SkToUInt(SkTPin(SkScalarRoundToInt(fPointCount), 0, kMaxPointCount)); const auto arc = sk_ieee_float_divide(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)); poly.incReserve(fType == Type::kStar ? count * 2 : count); for (unsigned 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); } GradientAdapter::GradientAdapter(sk_sp grad, size_t stopCount) : fGradient(std::move(grad)) , fStopCount(stopCount) {} void GradientAdapter::apply() { this->onApply(); // |fColorStops| holds |fStopCount| x [ pos, r, g, g ] + ? x [ pos, alpha ] if (fColorStops.size() < fStopCount * 4 || ((fColorStops.size() - fStopCount * 4) % 2)) { // apply() may get called before the stops are set, so only log when we have some stops. if (!fColorStops.empty()) { SkDebugf("!! Invalid gradient stop array size: %zu\n", 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) { const auto pos = cs[0]; const VectorValue rgb({ cs[1], cs[2], cs[3] }); stops.push_back({ pos, ValueTraits::As(rgb) }); } fGradient->setColorStops(std::move(stops)); } LinearGradientAdapter::LinearGradientAdapter(sk_sp grad, size_t stopCount) : INHERITED(std::move(grad), stopCount) {} void LinearGradientAdapter::onApply() { auto* grad = static_cast(fGradient.get()); grad->setStartPoint(this->startPoint()); grad->setEndPoint(this->endPoint()); } RadialGradientAdapter::RadialGradientAdapter(sk_sp grad, size_t stopCount) : INHERITED(std::move(grad), stopCount) {} void RadialGradientAdapter::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())); } TrimEffectAdapter::TrimEffectAdapter(sk_sp trimEffect) : fTrimEffect(std::move(trimEffect)) { SkASSERT(fTrimEffect); } void TrimEffectAdapter::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) { using std::swap; swap(startT, stopT); mode = SkTrimPathEffect::Mode::kInverted; } } else { startT = 0; stopT = 1; } fTrimEffect->setStart(startT); fTrimEffect->setStop(stopT); fTrimEffect->setMode(mode); } } // namespace skottie