[skottie] Cleanup: convert transform adapters to new pattern
Use discardable adapters for 2D-transforms, 3D-transforms, cameras and transform effects. Improvements: - ~4KB smaller object size - 3D rotation and orientation now compose correctly (instead of overriding each other) - streamlined no-op check discards no-effect transform nodes more aggressively (20% more nodes discarded) TBR= Change-Id: If75e53021871a15b7ea9e3ccb988c734a018f06d Reviewed-on: https://skia-review.googlesource.com/c/skia/+/266635 Reviewed-by: Florin Malita <fmalita@chromium.org> Commit-Queue: Florin Malita <fmalita@chromium.org>
This commit is contained in:
parent
2fa68dbcc7
commit
e7bd58f512
@ -18,7 +18,9 @@ class DiscardableAdapterBase : public AnimatablePropertyContainer {
|
||||
public:
|
||||
template <typename... Args>
|
||||
static sk_sp<AdapterT> Make(Args&&... args) {
|
||||
return sk_sp<AdapterT>(new AdapterT(std::forward<Args>(args)...));
|
||||
sk_sp<AdapterT> adapter(new AdapterT(std::forward<Args>(args)...));
|
||||
adapter->shrink_to_fit();
|
||||
return adapter;
|
||||
}
|
||||
|
||||
const sk_sp<T>& node() const { return fNode; }
|
||||
|
@ -25,6 +25,14 @@ void AnimatablePropertyContainer::onTick(float t) {
|
||||
this->onSync();
|
||||
}
|
||||
|
||||
void AnimatablePropertyContainer::addNestedContainer(sk_sp<AnimatablePropertyContainer> nested) {
|
||||
fAnimators.push_back(nested);
|
||||
}
|
||||
|
||||
void AnimatablePropertyContainer::shrink_to_fit() {
|
||||
fAnimators.shrink_to_fit();
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class KeyframeAnimatorBase : public sksg::Animator {
|
||||
|
@ -36,9 +36,13 @@ public:
|
||||
|
||||
bool isStatic() const { return fAnimators.empty(); }
|
||||
|
||||
void addNestedContainer(sk_sp<AnimatablePropertyContainer>);
|
||||
|
||||
protected:
|
||||
virtual void onSync() = 0;
|
||||
|
||||
void shrink_to_fit();
|
||||
|
||||
private:
|
||||
void onTick(float) final;
|
||||
|
||||
|
@ -8,71 +8,35 @@
|
||||
#include "modules/skottie/src/Camera.h"
|
||||
|
||||
#include "include/utils/Sk3D.h"
|
||||
#include "modules/skottie/src/SkottieJson.h"
|
||||
#include "modules/skottie/src/SkottiePriv.h"
|
||||
#include "modules/sksg/include/SkSGTransform.h"
|
||||
|
||||
namespace skottie {
|
||||
namespace internal {
|
||||
|
||||
CameraAdapter:: CameraAdapter(const SkSize& viewport_size, Type type)
|
||||
: fViewportSize(viewport_size)
|
||||
, fType(type)
|
||||
{}
|
||||
namespace {
|
||||
|
||||
CameraAdapter::~CameraAdapter() = default;
|
||||
|
||||
sk_sp<CameraAdapter> CameraAdapter::MakeDefault(const SkSize &viewport_size) {
|
||||
auto adapter = sk_make_sp<CameraAdapter>(viewport_size, Type::kOneNode);
|
||||
|
||||
static constexpr float kDefaultAEZoom = 879.13f;
|
||||
const auto center = SkVector::Make(viewport_size.width() * 0.5f,
|
||||
viewport_size.height() * 0.5f);
|
||||
adapter->setZoom(kDefaultAEZoom);
|
||||
adapter->setPosition (TransformAdapter3D::Vec3({center.fX, center.fY, -kDefaultAEZoom}));
|
||||
|
||||
return adapter;
|
||||
}
|
||||
|
||||
SkPoint3 CameraAdapter::poi() const {
|
||||
// AE supports two camera types:
|
||||
//
|
||||
// - one-node camera: does not auto-orient, and starts off perpendicular to the z = 0 plane,
|
||||
// facing "forward" (decreasing z).
|
||||
//
|
||||
// - two-node camera: has a point of interest (encoded as the anchor point), and auto-orients
|
||||
// to point in its direction.
|
||||
return fType == Type::kOneNode
|
||||
? SkPoint3{ this->getPosition().fX,
|
||||
this->getPosition().fY,
|
||||
-this->getPosition().fZ - 1 }
|
||||
: SkPoint3{ this->getAnchorPoint().fX,
|
||||
this->getAnchorPoint().fY,
|
||||
-this->getAnchorPoint().fZ};
|
||||
}
|
||||
|
||||
SkMatrix44 CameraAdapter::totalMatrix() const {
|
||||
// Camera parameters:
|
||||
//
|
||||
// * location -> position attribute
|
||||
// * point of interest -> anchor point attribute (two-node camera only)
|
||||
// * orientation -> rotation attribute
|
||||
//
|
||||
const auto pos = SkPoint3{ this->getPosition().fX,
|
||||
this->getPosition().fY,
|
||||
-this->getPosition().fZ },
|
||||
up = SkPoint3{ 0, 1, 0 };
|
||||
SkMatrix44 ComputeCameraMatrix(const SkPoint3& position,
|
||||
const SkPoint3& poi,
|
||||
const SkPoint3& rotation,
|
||||
const SkSize& viewport_size,
|
||||
float zoom) {
|
||||
const auto pos = SkPoint3{ position.fX, position.fY, -position.fZ },
|
||||
up = SkPoint3{ 0, 1, 0 };
|
||||
|
||||
// Initial camera vector.
|
||||
SkMatrix44 cam_t;
|
||||
Sk3LookAt(&cam_t, pos, this->poi(), up);
|
||||
Sk3LookAt(&cam_t, pos, poi, up);
|
||||
|
||||
// Rotation origin is camera position.
|
||||
{
|
||||
SkMatrix44 rot;
|
||||
rot.setRotateDegreesAbout(1, 0, 0, this->getRotation().fX);
|
||||
rot.setRotateDegreesAbout(1, 0, 0, rotation.fX);
|
||||
cam_t.postConcat(rot);
|
||||
rot.setRotateDegreesAbout(0, 1, 0, this->getRotation().fY);
|
||||
rot.setRotateDegreesAbout(0, 1, 0, rotation.fY);
|
||||
cam_t.postConcat(rot);
|
||||
rot.setRotateDegreesAbout(0, 0, 1, -this->getRotation().fZ);
|
||||
rot.setRotateDegreesAbout(0, 0, 1, -rotation.fZ);
|
||||
cam_t.postConcat(rot);
|
||||
}
|
||||
|
||||
@ -84,20 +48,101 @@ SkMatrix44 CameraAdapter::totalMatrix() const {
|
||||
// * size -> composition size (TODO: AE seems to base it on width only?)
|
||||
// * distance -> "zoom" camera attribute
|
||||
//
|
||||
const auto view_size = SkTMax(fViewportSize.width(), fViewportSize.height()),
|
||||
view_distance = this->getZoom(),
|
||||
const auto view_size = SkTMax(viewport_size.width(), viewport_size.height()),
|
||||
view_distance = zoom,
|
||||
view_angle = std::atan(sk_ieee_float_divide(view_size * 0.5f, view_distance));
|
||||
|
||||
SkMatrix44 persp_t;
|
||||
Sk3Perspective(&persp_t, 0, view_distance, 2 * view_angle);
|
||||
persp_t.postScale(view_size * 0.5f, view_size * 0.5f, 1);
|
||||
|
||||
SkMatrix44 t;
|
||||
t.setTranslate(fViewportSize.width() * 0.5f, fViewportSize.height() * 0.5f, 0);
|
||||
t.preConcat(persp_t);
|
||||
t.preConcat(cam_t);
|
||||
SkMatrix44 m;
|
||||
m.setTranslate(viewport_size.width() * 0.5f, viewport_size.height() * 0.5f, 0);
|
||||
m.preConcat(persp_t);
|
||||
m.preConcat(cam_t);
|
||||
|
||||
return t;
|
||||
return m;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
CameraAdaper::CameraAdaper(const skjson::ObjectValue& jlayer,
|
||||
const skjson::ObjectValue& jtransform,
|
||||
const AnimationBuilder& abuilder,
|
||||
const SkSize& viewport_size)
|
||||
: INHERITED(jtransform, abuilder)
|
||||
, fViewportSize(viewport_size)
|
||||
// The presence of an anchor point property ('a') differentiates
|
||||
// one-node vs. two-node cameras.
|
||||
, fType(jtransform["a"].is<skjson::NullValue>() ? CameraType::kOneNode
|
||||
: CameraType::kTwoNode) {
|
||||
// 'pe' (perspective?) corresponds to AE's "zoom" camera property.
|
||||
this->bind(abuilder, jlayer["pe"], fZoom);
|
||||
}
|
||||
|
||||
CameraAdaper::~CameraAdaper() = default;
|
||||
|
||||
SkMatrix44 CameraAdaper::totalMatrix() const {
|
||||
// Camera parameters:
|
||||
//
|
||||
// * location -> position attribute
|
||||
// * point of interest -> anchor point attribute (two-node camera only)
|
||||
// * orientation -> rotation attribute
|
||||
//
|
||||
const auto position = this->position();
|
||||
|
||||
return ComputeCameraMatrix(position,
|
||||
this->poi(position),
|
||||
this->rotation(),
|
||||
fViewportSize,
|
||||
fZoom);
|
||||
}
|
||||
|
||||
SkPoint3 CameraAdaper::poi(const SkPoint3& pos) const {
|
||||
// AE supports two camera types:
|
||||
//
|
||||
// - one-node camera: does not auto-orient, and starts off perpendicular
|
||||
// to the z = 0 plane, facing "forward" (decreasing z).
|
||||
//
|
||||
// - two-node camera: has a point of interest (encoded as the anchor point),
|
||||
// and auto-orients to point in its direction.
|
||||
|
||||
if (fType == CameraType::kOneNode) {
|
||||
return { pos.fX, pos.fY, -pos.fZ - 1};
|
||||
}
|
||||
|
||||
const auto ap = this->anchor_point();
|
||||
|
||||
return { ap.fX, ap.fY, -ap.fZ };
|
||||
}
|
||||
|
||||
sk_sp<sksg::Transform> CameraAdaper::DefaultCameraTransform(const SkSize& viewport_size) {
|
||||
const auto center = SkVector::Make(viewport_size.width() * 0.5f,
|
||||
viewport_size.height() * 0.5f);
|
||||
|
||||
static constexpr float kDefaultAEZoom = 879.13f;
|
||||
|
||||
const SkPoint3 pos = { center.fX, center.fY, -kDefaultAEZoom },
|
||||
poi = { pos.fX, pos.fY, -pos.fZ - 1 },
|
||||
rot = { 0, 0, 0 };
|
||||
|
||||
return sksg::Matrix<SkMatrix44>::Make(
|
||||
ComputeCameraMatrix(pos, poi, rot, viewport_size, kDefaultAEZoom));
|
||||
}
|
||||
|
||||
sk_sp<sksg::Transform> AnimationBuilder::attachCamera(const skjson::ObjectValue& jlayer,
|
||||
const skjson::ObjectValue& jtransform,
|
||||
sk_sp<sksg::Transform> parent,
|
||||
const SkSize& viewport_size) const {
|
||||
auto adapter = sk_make_sp<CameraAdaper>(jlayer, jtransform, *this, viewport_size);
|
||||
|
||||
if (adapter->isStatic()) {
|
||||
adapter->tick(0);
|
||||
} else {
|
||||
fCurrentAnimatorScope->push_back(adapter);
|
||||
}
|
||||
|
||||
return sksg::Transform::MakeConcat(adapter->node(), std::move(parent));
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
@ -10,32 +10,34 @@
|
||||
|
||||
#include "modules/skottie/src/Transform.h"
|
||||
|
||||
#include "include/core/SkMatrix44.h"
|
||||
|
||||
namespace skottie {
|
||||
namespace internal {
|
||||
|
||||
class CameraAdapter final : public TransformAdapter3D {
|
||||
class CameraAdaper final : public TransformAdapter3D {
|
||||
public:
|
||||
enum class Type {
|
||||
CameraAdaper(const skjson::ObjectValue& jlayer,
|
||||
const skjson::ObjectValue& jtransform,
|
||||
const AnimationBuilder& abuilder,
|
||||
const SkSize& viewport_size);
|
||||
~CameraAdaper() override;
|
||||
|
||||
// Used in the absence of an explicit camera layer.
|
||||
static sk_sp<sksg::Transform> DefaultCameraTransform(const SkSize& viewport_size);
|
||||
|
||||
SkMatrix44 totalMatrix() const override;
|
||||
|
||||
private:
|
||||
enum class CameraType {
|
||||
kOneNode, // implicitly facing forward (decreasing z), does not auto-orient
|
||||
kTwoNode, // explicitly facing a POI (the anchor point), auto-orients
|
||||
};
|
||||
|
||||
static sk_sp<CameraAdapter> MakeDefault(const SkSize& viewport_size);
|
||||
SkPoint3 poi(const SkPoint3& pos) const;
|
||||
|
||||
CameraAdapter(const SkSize& viewport_size, Type);
|
||||
~CameraAdapter() override;
|
||||
const SkSize fViewportSize;
|
||||
const CameraType fType;
|
||||
|
||||
ADAPTER_PROPERTY(Zoom, SkScalar, 0)
|
||||
|
||||
private:
|
||||
SkMatrix44 totalMatrix() const override;
|
||||
|
||||
SkPoint3 poi() const;
|
||||
|
||||
const SkSize fViewportSize;
|
||||
const Type fType;
|
||||
ScalarValue fZoom = 0;
|
||||
|
||||
using INHERITED = TransformAdapter3D;
|
||||
};
|
||||
|
@ -166,7 +166,7 @@ CompositionBuilder::CompositionBuilder(const AnimationBuilder& abuilder,
|
||||
fCameraTransform = fLayerBuilders[camera_builder_index].buildTransform(abuilder, this);
|
||||
} else if (ParseDefault<int>(jcomp["ddd"], 0)) {
|
||||
// Default/implicit camera when 3D layers are present.
|
||||
fCameraTransform = CameraAdapter::MakeDefault(fSize)->refTransform();
|
||||
fCameraTransform = CameraAdaper::DefaultCameraTransform(fSize);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -325,30 +325,15 @@ sk_sp<sksg::Transform> LayerBuilder::doAttachTransform(const AnimationBuilder& a
|
||||
auto parent_transform = this->getParentTransform(abuilder, cbuilder, ttype);
|
||||
|
||||
if (this->isCamera()) {
|
||||
// The presence of an anchor point property ('a') differentiates
|
||||
// one-node vs. two-node cameras.
|
||||
const auto camera_type = (*jtransform)["a"].is<skjson::NullValue>()
|
||||
? CameraAdapter::Type::kOneNode
|
||||
: CameraAdapter::Type::kTwoNode;
|
||||
auto camera_adapter = sk_make_sp<CameraAdapter>(cbuilder->fSize, camera_type);
|
||||
|
||||
abuilder.bindProperty<ScalarValue>(fJlayer["pe"],
|
||||
[camera_adapter] (const ScalarValue& pe) {
|
||||
// 'pe' (perspective?) corresponds to AE's "zoom" camera property.
|
||||
camera_adapter->setZoom(pe);
|
||||
});
|
||||
|
||||
// parent_transform applies to the camera itself => it pre-composes inverted to the
|
||||
// camera/view/adapter transform.
|
||||
//
|
||||
// T_camera' = T_camera x Inv(parent_transform)
|
||||
//
|
||||
parent_transform = sksg::Transform::MakeInverse(std::move(parent_transform));
|
||||
|
||||
return abuilder.attachMatrix3D(*jtransform,
|
||||
std::move(parent_transform),
|
||||
std::move(camera_adapter),
|
||||
true); // pre-compose parent
|
||||
return abuilder.attachCamera(fJlayer,
|
||||
*jtransform,
|
||||
sksg::Transform::MakeInverse(std::move(parent_transform)),
|
||||
cbuilder->fSize);
|
||||
}
|
||||
|
||||
return this->is3D()
|
||||
|
@ -68,114 +68,6 @@ void AnimationBuilder::log(Logger::Level lvl, const skjson::Value* json,
|
||||
fLogger->log(lvl, buff, jsonstr.c_str());
|
||||
}
|
||||
|
||||
sk_sp<sksg::Transform> AnimationBuilder::attachMatrix2D(const skjson::ObjectValue& t,
|
||||
sk_sp<sksg::Transform> parent) const {
|
||||
static const VectorValue g_default_vec_0 = { 0, 0},
|
||||
g_default_vec_100 = {100, 100};
|
||||
|
||||
auto matrix = sksg::Matrix<SkMatrix>::Make(SkMatrix::I());
|
||||
auto adapter = sk_make_sp<TransformAdapter2D>(matrix);
|
||||
|
||||
auto bound = this->bindProperty<VectorValue>(t["a"],
|
||||
[adapter](const VectorValue& a) {
|
||||
adapter->setAnchorPoint(ValueTraits<VectorValue>::As<SkPoint>(a));
|
||||
}, g_default_vec_0);
|
||||
bound |= this->bindProperty<VectorValue>(t["p"],
|
||||
[adapter](const VectorValue& p) {
|
||||
adapter->setPosition(ValueTraits<VectorValue>::As<SkPoint>(p));
|
||||
}, g_default_vec_0);
|
||||
bound |= this->bindProperty<VectorValue>(t["s"],
|
||||
[adapter](const VectorValue& s) {
|
||||
adapter->setScale(ValueTraits<VectorValue>::As<SkVector>(s));
|
||||
}, g_default_vec_100);
|
||||
|
||||
const auto* jrotation = &t["r"];
|
||||
if (jrotation->is<skjson::NullValue>()) {
|
||||
// 3d rotations have separate rx,ry,rz components. While we don't fully support them,
|
||||
// we can still make use of rz.
|
||||
jrotation = &t["rz"];
|
||||
}
|
||||
bound |= this->bindProperty<ScalarValue>(*jrotation,
|
||||
[adapter](const ScalarValue& r) {
|
||||
adapter->setRotation(r);
|
||||
}, 0.0f);
|
||||
bound |= this->bindProperty<ScalarValue>(t["sk"],
|
||||
[adapter](const ScalarValue& sk) {
|
||||
adapter->setSkew(sk);
|
||||
}, 0.0f);
|
||||
bound |= this->bindProperty<ScalarValue>(t["sa"],
|
||||
[adapter](const ScalarValue& sa) {
|
||||
adapter->setSkewAxis(sa);
|
||||
}, 0.0f);
|
||||
|
||||
const auto dispatched = this->dispatchTransformProperty(adapter);
|
||||
|
||||
return (bound || dispatched)
|
||||
? sksg::Transform::MakeConcat(std::move(parent), std::move(matrix))
|
||||
: parent;
|
||||
}
|
||||
|
||||
sk_sp<sksg::Transform> AnimationBuilder::attachMatrix3D(const skjson::ObjectValue& t,
|
||||
sk_sp<sksg::Transform> parent,
|
||||
sk_sp<TransformAdapter3D> adapter,
|
||||
bool precompose_parent) const {
|
||||
static const VectorValue g_default_vec_0 = { 0, 0, 0},
|
||||
g_default_vec_100 = {100, 100, 100};
|
||||
|
||||
if (!adapter) {
|
||||
// Default to TransformAdapter3D (we only use external adapters for cameras).
|
||||
adapter = sk_make_sp<TransformAdapter3D>();
|
||||
}
|
||||
|
||||
auto bound = this->bindProperty<VectorValue>(t["a"],
|
||||
[adapter](const VectorValue& a) {
|
||||
adapter->setAnchorPoint(TransformAdapter3D::Vec3(a));
|
||||
}, g_default_vec_0);
|
||||
bound |= this->bindProperty<VectorValue>(t["p"],
|
||||
[adapter](const VectorValue& p) {
|
||||
adapter->setPosition(TransformAdapter3D::Vec3(p));
|
||||
}, g_default_vec_0);
|
||||
bound |= this->bindProperty<VectorValue>(t["s"],
|
||||
[adapter](const VectorValue& s) {
|
||||
adapter->setScale(TransformAdapter3D::Vec3(s));
|
||||
}, g_default_vec_100);
|
||||
|
||||
// Orientation and rx/ry/rz are mapped to the same rotation property -- the difference is
|
||||
// in how they get interpolated (vector vs. scalar/decomposed interpolation).
|
||||
bound |= this->bindProperty<VectorValue>(t["or"],
|
||||
[adapter](const VectorValue& o) {
|
||||
adapter->setRotation(TransformAdapter3D::Vec3(o));
|
||||
}, g_default_vec_0);
|
||||
|
||||
bound |= this->bindProperty<ScalarValue>(t["rx"],
|
||||
[adapter](const ScalarValue& rx) {
|
||||
const auto& r = adapter->getRotation();
|
||||
adapter->setRotation(TransformAdapter3D::Vec3({rx, r.fY, r.fZ}));
|
||||
}, 0.0f);
|
||||
|
||||
bound |= this->bindProperty<ScalarValue>(t["ry"],
|
||||
[adapter](const ScalarValue& ry) {
|
||||
const auto& r = adapter->getRotation();
|
||||
adapter->setRotation(TransformAdapter3D::Vec3({r.fX, ry, r.fZ}));
|
||||
}, 0.0f);
|
||||
|
||||
bound |= this->bindProperty<ScalarValue>(t["rz"],
|
||||
[adapter](const ScalarValue& rz) {
|
||||
const auto& r = adapter->getRotation();
|
||||
adapter->setRotation(TransformAdapter3D::Vec3({r.fX, r.fY, rz}));
|
||||
}, 0.0f);
|
||||
|
||||
// TODO: dispatch 3D transform properties
|
||||
|
||||
if (!bound) {
|
||||
return parent;
|
||||
}
|
||||
|
||||
return precompose_parent
|
||||
? sksg::Transform::MakeConcat(adapter->refTransform(), std::move(parent))
|
||||
: sksg::Transform::MakeConcat(std::move(parent), adapter->refTransform());
|
||||
}
|
||||
|
||||
sk_sp<sksg::RenderNode> AnimationBuilder::attachOpacity(const skjson::ObjectValue& jtransform,
|
||||
sk_sp<sksg::RenderNode> childNode) const {
|
||||
if (!childNode)
|
||||
|
@ -80,9 +80,13 @@ public:
|
||||
|
||||
sk_sp<sksg::Color> attachColor(const skjson::ObjectValue&, const char prop_name[]) const;
|
||||
sk_sp<sksg::Transform> attachMatrix2D(const skjson::ObjectValue&, sk_sp<sksg::Transform>) const;
|
||||
sk_sp<sksg::Transform> attachMatrix3D(const skjson::ObjectValue&, sk_sp<sksg::Transform>,
|
||||
sk_sp<TransformAdapter3D> = nullptr,
|
||||
bool precompose_parent = false) const;
|
||||
sk_sp<sksg::Transform> attachMatrix3D(const skjson::ObjectValue&, sk_sp<sksg::Transform>) const;
|
||||
|
||||
sk_sp<sksg::Transform> attachCamera(const skjson::ObjectValue& jlayer,
|
||||
const skjson::ObjectValue& jtransform,
|
||||
sk_sp<sksg::Transform>,
|
||||
const SkSize&) const;
|
||||
|
||||
sk_sp<sksg::RenderNode> attachOpacity(const skjson::ObjectValue&,
|
||||
sk_sp<sksg::RenderNode>) const;
|
||||
sk_sp<sksg::Path> attachPath(const skjson::Value&) const;
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
#include "include/core/SkColor.h"
|
||||
#include "include/core/SkPoint.h"
|
||||
#include "include/core/SkPoint3.h"
|
||||
#include "include/core/SkSize.h"
|
||||
#include "include/private/SkNx.h"
|
||||
#include "modules/skottie/src/SkottieJson.h"
|
||||
@ -93,10 +94,22 @@ SkColor4f ValueTraits<VectorValue>::As<SkColor4f>(const VectorValue& v) {
|
||||
template <>
|
||||
template <>
|
||||
SkPoint ValueTraits<VectorValue>::As<SkPoint>(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);
|
||||
// best effort to turn this into a 2D point
|
||||
return SkPoint {
|
||||
vec.size() > 0 ? vec[0] : 0,
|
||||
vec.size() > 1 ? vec[1] : 0,
|
||||
};
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
SkPoint3 ValueTraits<VectorValue>::As<SkPoint3>(const VectorValue& vec) {
|
||||
// best effort to turn this into a 3D point
|
||||
return SkPoint3 {
|
||||
vec.size() > 0 ? vec[0] : 0,
|
||||
vec.size() > 1 ? vec[1] : 0,
|
||||
vec.size() > 2 ? vec[2] : 0,
|
||||
};
|
||||
}
|
||||
|
||||
template <>
|
||||
|
@ -7,67 +7,199 @@
|
||||
|
||||
#include "modules/skottie/src/Transform.h"
|
||||
|
||||
#include "modules/skottie/src/SkottieJson.h"
|
||||
#include "modules/skottie/src/SkottiePriv.h"
|
||||
#include "modules/sksg/include/SkSGTransform.h"
|
||||
|
||||
namespace skottie {
|
||||
namespace internal {
|
||||
|
||||
TransformAdapter2D::TransformAdapter2D(sk_sp<sksg::Matrix<SkMatrix>> matrix)
|
||||
: fMatrixNode(std::move(matrix)) {}
|
||||
TransformAdapter2D::TransformAdapter2D(const AnimationBuilder& abuilder,
|
||||
const skjson::ObjectValue* janchor_point,
|
||||
const skjson::ObjectValue* jposition,
|
||||
const skjson::ObjectValue* jscale,
|
||||
const skjson::ObjectValue* jrotation,
|
||||
const skjson::ObjectValue* jskew,
|
||||
const skjson::ObjectValue* jskew_axis)
|
||||
: INHERITED(sksg::Matrix<SkMatrix>::Make(SkMatrix::I())) {
|
||||
|
||||
TransformAdapter2D::~TransformAdapter2D() = default;
|
||||
this->bind(abuilder, janchor_point, fAnchorPoint);
|
||||
this->bind(abuilder, jposition , fPosition);
|
||||
this->bind(abuilder, jscale , fScale);
|
||||
this->bind(abuilder, jrotation , fRotation);
|
||||
this->bind(abuilder, jskew , fSkew);
|
||||
this->bind(abuilder, jskew_axis , fSkewAxis);
|
||||
}
|
||||
|
||||
TransformAdapter2D::~TransformAdapter2D() {}
|
||||
|
||||
void TransformAdapter2D::onSync() {
|
||||
this->node()->setMatrix(this->totalMatrix());
|
||||
}
|
||||
|
||||
SkMatrix TransformAdapter2D::totalMatrix() const {
|
||||
SkMatrix t = SkMatrix::MakeTrans(-fAnchorPoint.x(), -fAnchorPoint.y());
|
||||
const auto anchor_point = ValueTraits<VectorValue>::As<SkPoint>(fAnchorPoint),
|
||||
position = ValueTraits<VectorValue>::As<SkPoint>(fPosition),
|
||||
scale = ValueTraits<VectorValue>::As<SkPoint>(fScale);
|
||||
|
||||
t.postScale(fScale.x() / 100, fScale.y() / 100); // 100% based
|
||||
SkMatrix t = SkMatrix::MakeTrans(-anchor_point.x(), -anchor_point.y());
|
||||
|
||||
t.postScale(scale.x() / 100, scale.y() / 100); // 100% based
|
||||
t.postRotate(fRotation);
|
||||
t.postTranslate(fPosition.x(), fPosition.y());
|
||||
t.postTranslate(position.x(), position.y());
|
||||
// TODO: skew
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
void TransformAdapter2D::apply() {
|
||||
fMatrixNode->setMatrix(this->totalMatrix());
|
||||
SkPoint TransformAdapter2D::getAnchorPoint() const {
|
||||
return ValueTraits<VectorValue>::As<SkPoint>(fAnchorPoint);
|
||||
}
|
||||
|
||||
TransformAdapter3D::Vec3::Vec3(const VectorValue& v) {
|
||||
fX = v.size() > 0 ? v[0] : 0;
|
||||
fY = v.size() > 1 ? v[1] : 0;
|
||||
fZ = v.size() > 2 ? v[2] : 0;
|
||||
void TransformAdapter2D::setAnchorPoint(const SkPoint& ap) {
|
||||
fAnchorPoint = { ap.x(), ap.y() };
|
||||
this->onSync();
|
||||
}
|
||||
|
||||
TransformAdapter3D::TransformAdapter3D()
|
||||
: fMatrixNode(sksg::Matrix<SkMatrix44>::Make(SkMatrix::I())) {}
|
||||
SkPoint TransformAdapter2D::getPosition() const {
|
||||
return ValueTraits<VectorValue>::As<SkPoint>(fPosition);
|
||||
}
|
||||
|
||||
void TransformAdapter2D::setPosition(const SkPoint& p) {
|
||||
fPosition = { p.x(), p.y() };
|
||||
this->onSync();
|
||||
}
|
||||
|
||||
SkVector TransformAdapter2D::getScale() const {
|
||||
return ValueTraits<VectorValue>::As<SkVector>(fScale);
|
||||
}
|
||||
|
||||
void TransformAdapter2D::setScale(const SkVector& s) {
|
||||
fScale = { s.x(), s.y() };
|
||||
this->onSync();
|
||||
}
|
||||
|
||||
void TransformAdapter2D::setRotation(float r) {
|
||||
fRotation = r;
|
||||
this->onSync();
|
||||
}
|
||||
|
||||
void TransformAdapter2D::setSkew(float sk) {
|
||||
fSkew = sk;
|
||||
this->onSync();
|
||||
}
|
||||
|
||||
void TransformAdapter2D::setSkewAxis(float sa) {
|
||||
fSkewAxis = sa;
|
||||
this->onSync();
|
||||
}
|
||||
|
||||
sk_sp<sksg::Transform> AnimationBuilder::attachMatrix2D(const skjson::ObjectValue& jtransform,
|
||||
sk_sp<sksg::Transform> parent) const {
|
||||
const auto* jrotation = &jtransform["r"];
|
||||
if (jrotation->is<skjson::NullValue>()) {
|
||||
// Some 2D rotations are disguised as 3D...
|
||||
jrotation = &jtransform["rz"];
|
||||
}
|
||||
|
||||
auto adapter = TransformAdapter2D::Make(*this,
|
||||
jtransform["a"],
|
||||
jtransform["p"],
|
||||
jtransform["s"],
|
||||
*jrotation,
|
||||
jtransform["sk"],
|
||||
jtransform["sa"]);
|
||||
SkASSERT(adapter);
|
||||
|
||||
const auto dispatched = this->dispatchTransformProperty(adapter);
|
||||
|
||||
if (adapter->isStatic()) {
|
||||
if (!dispatched && adapter->totalMatrix().isIdentity()) {
|
||||
// The transform has no observable effects - we can discard.
|
||||
return parent;
|
||||
}
|
||||
adapter->tick(0);
|
||||
} else {
|
||||
fCurrentAnimatorScope->push_back(adapter);
|
||||
}
|
||||
|
||||
return sksg::Transform::MakeConcat(std::move(parent), adapter->node());
|
||||
}
|
||||
|
||||
TransformAdapter3D::TransformAdapter3D(const skjson::ObjectValue& jtransform,
|
||||
const AnimationBuilder& abuilder)
|
||||
: INHERITED(sksg::Matrix<SkMatrix44>::Make(SkMatrix44::I())) {
|
||||
|
||||
this->bind(abuilder, jtransform["a"], fAnchorPoint);
|
||||
this->bind(abuilder, jtransform["p"], fPosition);
|
||||
this->bind(abuilder, jtransform["s"], fScale);
|
||||
|
||||
// Axis-wise rotation and orientation are mapped to the same rotation property (3D rotation).
|
||||
// The difference is in how they get interpolated (scalar/decomposed vs. vector).
|
||||
this->bind(abuilder, jtransform["rx"], fRx);
|
||||
this->bind(abuilder, jtransform["ry"], fRy);
|
||||
this->bind(abuilder, jtransform["rz"], fRz);
|
||||
this->bind(abuilder, jtransform["or"], fOrientation);
|
||||
}
|
||||
|
||||
TransformAdapter3D::~TransformAdapter3D() = default;
|
||||
|
||||
sk_sp<sksg::Transform> TransformAdapter3D::refTransform() const {
|
||||
return fMatrixNode;
|
||||
void TransformAdapter3D::onSync() {
|
||||
this->node()->setMatrix(this->totalMatrix());
|
||||
}
|
||||
|
||||
SkPoint3 TransformAdapter3D::anchor_point() const {
|
||||
return ValueTraits<VectorValue>::As<SkPoint3>(fAnchorPoint);
|
||||
}
|
||||
|
||||
SkPoint3 TransformAdapter3D::position() const {
|
||||
return ValueTraits<VectorValue>::As<SkPoint3>(fPosition);
|
||||
}
|
||||
|
||||
SkVector3 TransformAdapter3D::rotation() const {
|
||||
// orientation and axis-wise rotation map onto the same property.
|
||||
return ValueTraits<VectorValue>::As<SkPoint3>(fOrientation) + SkPoint3{ fRx, fRy, fRz };
|
||||
}
|
||||
|
||||
SkMatrix44 TransformAdapter3D::totalMatrix() const {
|
||||
SkMatrix44 t;
|
||||
const auto anchor_point = this->anchor_point(),
|
||||
positon = this->position(),
|
||||
scale = ValueTraits<VectorValue>::As<SkPoint3>(fScale),
|
||||
rotation = this->rotation();
|
||||
|
||||
t.setTranslate(-fAnchorPoint.fX, -fAnchorPoint.fY, -fAnchorPoint.fZ);
|
||||
t.postScale(fScale.fX / 100, fScale.fY / 100, fScale.fZ / 100);
|
||||
SkMatrix44 m;
|
||||
m.setTranslate(-anchor_point.fX, -anchor_point.fY, -anchor_point.fZ);
|
||||
m.postScale(scale.fX / 100, scale.fY / 100, scale.fZ / 100);
|
||||
|
||||
SkMatrix44 r;
|
||||
r.setRotateDegreesAbout(0, 0, 1, fRotation.fZ);
|
||||
t.postConcat(r);
|
||||
r.setRotateDegreesAbout(0, 1, 0, fRotation.fY);
|
||||
t.postConcat(r);
|
||||
r.setRotateDegreesAbout(1, 0, 0, fRotation.fX);
|
||||
t.postConcat(r);
|
||||
r.setRotateDegreesAbout(0, 0, 1, rotation.fZ);
|
||||
m.postConcat(r);
|
||||
r.setRotateDegreesAbout(0, 1, 0, rotation.fY);
|
||||
m.postConcat(r);
|
||||
r.setRotateDegreesAbout(1, 0, 0, rotation.fX);
|
||||
m.postConcat(r);
|
||||
|
||||
t.postTranslate(fPosition.fX, fPosition.fY, fPosition.fZ);
|
||||
m.postTranslate(positon.fX, positon.fY, positon.fZ);
|
||||
|
||||
return t;
|
||||
return m;
|
||||
}
|
||||
|
||||
void TransformAdapter3D::apply() {
|
||||
fMatrixNode->setMatrix(this->totalMatrix());
|
||||
sk_sp<sksg::Transform> AnimationBuilder::attachMatrix3D(const skjson::ObjectValue& jtransform,
|
||||
sk_sp<sksg::Transform> parent) const {
|
||||
auto adapter = TransformAdapter3D::Make(jtransform, *this);
|
||||
SkASSERT(adapter);
|
||||
|
||||
if (adapter->isStatic()) {
|
||||
if (adapter->totalMatrix().isIdentity()) {
|
||||
// The transform has no observable effects - we can discard.
|
||||
return parent;
|
||||
}
|
||||
adapter->tick(0);
|
||||
} else {
|
||||
fCurrentAnimatorScope->push_back(adapter);
|
||||
}
|
||||
|
||||
return sksg::Transform::MakeConcat(std::move(parent), adapter->node());
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
@ -12,62 +12,91 @@
|
||||
|
||||
#include "include/core/SkMatrix.h"
|
||||
#include "include/core/SkMatrix44.h"
|
||||
#include "include/core/SkPoint.h"
|
||||
#include "include/core/SkPoint3.h"
|
||||
#include "modules/skottie/src/Adapter.h"
|
||||
|
||||
namespace skjson {
|
||||
|
||||
class ObjectValue;
|
||||
|
||||
} // namespace skjson
|
||||
|
||||
namespace skottie {
|
||||
namespace internal {
|
||||
|
||||
class TransformAdapter2D final : public SkNVRefCnt<TransformAdapter2D> {
|
||||
class TransformAdapter2D final : public DiscardableAdapterBase<TransformAdapter2D,
|
||||
sksg::Matrix<SkMatrix>> {
|
||||
public:
|
||||
explicit TransformAdapter2D(sk_sp<sksg::Matrix<SkMatrix>>);
|
||||
~TransformAdapter2D();
|
||||
TransformAdapter2D(const AnimationBuilder&,
|
||||
const skjson::ObjectValue* janchor_point,
|
||||
const skjson::ObjectValue* jposition,
|
||||
const skjson::ObjectValue* jscale,
|
||||
const skjson::ObjectValue* jrotation,
|
||||
const skjson::ObjectValue* jskew,
|
||||
const skjson::ObjectValue* jskew_axis);
|
||||
~TransformAdapter2D() override;
|
||||
|
||||
ADAPTER_PROPERTY(AnchorPoint, SkPoint , SkPoint::Make(0, 0))
|
||||
ADAPTER_PROPERTY(Position , SkPoint , SkPoint::Make(0, 0))
|
||||
ADAPTER_PROPERTY(Scale , SkVector, SkPoint::Make(100, 100))
|
||||
ADAPTER_PROPERTY(Rotation , SkScalar, 0)
|
||||
ADAPTER_PROPERTY(Skew , SkScalar, 0)
|
||||
ADAPTER_PROPERTY(SkewAxis , SkScalar, 0)
|
||||
// Accessors needed for public property APIs.
|
||||
// TODO: introduce a separate public type.
|
||||
SkPoint getAnchorPoint() const;
|
||||
void setAnchorPoint(const SkPoint&);
|
||||
|
||||
SkPoint getPosition() const;
|
||||
void setPosition(const SkPoint&);
|
||||
|
||||
SkVector getScale() const;
|
||||
void setScale(const SkVector&);
|
||||
|
||||
float getRotation() const { return fRotation; }
|
||||
void setRotation(float r);
|
||||
|
||||
float getSkew() const { return fSkew; }
|
||||
void setSkew(float sk);
|
||||
|
||||
float getSkewAxis() const { return fSkewAxis; }
|
||||
void setSkewAxis(float sa );
|
||||
|
||||
SkMatrix totalMatrix() const;
|
||||
|
||||
private:
|
||||
void apply();
|
||||
void onSync() override;
|
||||
|
||||
sk_sp<sksg::Matrix<SkMatrix>> fMatrixNode;
|
||||
VectorValue fAnchorPoint,
|
||||
fPosition,
|
||||
fScale = { 100, 100 };
|
||||
ScalarValue fRotation = 0,
|
||||
fSkew = 0,
|
||||
fSkewAxis = 0;
|
||||
|
||||
using INHERITED = DiscardableAdapterBase<TransformAdapter2D, sksg::Matrix<SkMatrix>>;
|
||||
};
|
||||
|
||||
class TransformAdapter3D : public SkRefCnt {
|
||||
class TransformAdapter3D : public DiscardableAdapterBase<TransformAdapter3D,
|
||||
sksg::Matrix<SkMatrix44>> {
|
||||
public:
|
||||
TransformAdapter3D();
|
||||
TransformAdapter3D(const skjson::ObjectValue&, const AnimationBuilder&);
|
||||
~TransformAdapter3D() override;
|
||||
|
||||
struct Vec3 {
|
||||
float fX, fY, fZ;
|
||||
|
||||
explicit Vec3(const VectorValue&);
|
||||
|
||||
bool operator==(const Vec3& other) const {
|
||||
return fX == other.fX && fY == other.fY && fZ == other.fZ;
|
||||
}
|
||||
bool operator!=(const Vec3& other) const { return !(*this == other); }
|
||||
};
|
||||
|
||||
ADAPTER_PROPERTY(AnchorPoint, Vec3, Vec3({ 0, 0, 0}))
|
||||
ADAPTER_PROPERTY(Position , Vec3, Vec3({ 0, 0, 0}))
|
||||
ADAPTER_PROPERTY(Rotation , Vec3, Vec3({ 0, 0, 0}))
|
||||
ADAPTER_PROPERTY(Scale , Vec3, Vec3({100, 100, 100}))
|
||||
|
||||
sk_sp<sksg::Transform> refTransform() const;
|
||||
|
||||
protected:
|
||||
void apply();
|
||||
|
||||
private:
|
||||
virtual SkMatrix44 totalMatrix() const;
|
||||
|
||||
sk_sp<sksg::Matrix<SkMatrix44>> fMatrixNode;
|
||||
protected:
|
||||
SkPoint3 anchor_point() const;
|
||||
SkPoint3 position() const;
|
||||
SkVector3 rotation() const;
|
||||
|
||||
using INHERITED = SkRefCnt;
|
||||
private:
|
||||
void onSync() final;
|
||||
|
||||
VectorValue fAnchorPoint,
|
||||
fPosition,
|
||||
fOrientation,
|
||||
fScale = { 100, 100, 100 };
|
||||
ScalarValue fRx = 0,
|
||||
fRy = 0,
|
||||
fRz = 0;
|
||||
|
||||
using INHERITED = DiscardableAdapterBase<TransformAdapter3D, sksg::Matrix<SkMatrix44>>;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
@ -17,26 +17,50 @@ namespace internal {
|
||||
|
||||
namespace {
|
||||
|
||||
// Transform effects can operate in either uniform or anisotropic mode, with each
|
||||
// component (including mode) animated separately.
|
||||
class ScaleAdapter final : public SkNVRefCnt<ScaleAdapter> {
|
||||
class TransformEffectAdapter final : public DiscardableAdapterBase<TransformEffectAdapter,
|
||||
sksg::OpacityEffect> {
|
||||
public:
|
||||
explicit ScaleAdapter(sk_sp<TransformAdapter2D> tadapter)
|
||||
: fTransformAdapter(std::move(tadapter)) {}
|
||||
TransformEffectAdapter(const AnimationBuilder& abuilder,
|
||||
const skjson::ObjectValue* jopacity,
|
||||
const skjson::ObjectValue* jscale_uniform,
|
||||
const skjson::ObjectValue* jscale_width,
|
||||
const skjson::ObjectValue* jscale_height,
|
||||
sk_sp<TransformAdapter2D> tadapter,
|
||||
sk_sp<sksg::RenderNode> child)
|
||||
: INHERITED(sksg::OpacityEffect::Make(std::move(child)))
|
||||
, fTransformAdapter(std::move(tadapter)) {
|
||||
this->bind(abuilder, jopacity , fOpacity );
|
||||
this->bind(abuilder, jscale_uniform, fUniformScale);
|
||||
this->bind(abuilder, jscale_width , fScaleWidth );
|
||||
this->bind(abuilder, jscale_height , fScaleHeight );
|
||||
|
||||
ADAPTER_PROPERTY(IsUniform , bool , false)
|
||||
ADAPTER_PROPERTY(ScaleWidth , SkScalar, 100)
|
||||
ADAPTER_PROPERTY(ScaleHeight, SkScalar, 100)
|
||||
if (!fTransformAdapter->isStatic()) {
|
||||
// Connected animated transform addapters to the animator tree.
|
||||
this->addNestedContainer(fTransformAdapter);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void apply() {
|
||||
void onSync() override {
|
||||
this->node()->setOpacity(fOpacity * 0.01f);
|
||||
|
||||
// In uniform mode, the scale is based solely in ScaleHeight.
|
||||
const auto scale = SkVector::Make(fIsUniform ? fScaleHeight : fScaleWidth,
|
||||
const auto scale = SkVector::Make(SkScalarRoundToInt(fUniformScale) ? fScaleHeight
|
||||
: fScaleWidth,
|
||||
fScaleHeight);
|
||||
|
||||
// NB: this triggers an transform adapter -> SG sync.
|
||||
fTransformAdapter->setScale(scale);
|
||||
}
|
||||
|
||||
const sk_sp<TransformAdapter2D> fTransformAdapter;
|
||||
|
||||
ScalarValue fOpacity = 100,
|
||||
fUniformScale = 0, // bool
|
||||
fScaleWidth = 100,
|
||||
fScaleHeight = 100;
|
||||
|
||||
using INHERITED = DiscardableAdapterBase<TransformEffectAdapter, sksg::OpacityEffect>;
|
||||
};
|
||||
|
||||
} // anonymous ns
|
||||
@ -58,53 +82,28 @@ sk_sp<sksg::RenderNode> EffectBuilder::attachTransformEffect(const skjson::Array
|
||||
// kSampling_Index = 11,
|
||||
};
|
||||
|
||||
auto matrix = sksg::Matrix<SkMatrix>::Make(SkMatrix::I());
|
||||
auto t_adapter = sk_make_sp<TransformAdapter2D>(matrix);
|
||||
auto s_adapter = sk_make_sp<ScaleAdapter>(t_adapter);
|
||||
auto transform_adapter = TransformAdapter2D::Make(*fBuilder,
|
||||
GetPropValue(jprops, kAnchorPoint_Index),
|
||||
GetPropValue(jprops, kPosition_Index),
|
||||
nullptr, // scale is handled externally
|
||||
GetPropValue(jprops, kRotation_Index),
|
||||
GetPropValue(jprops, kSkew_Index),
|
||||
GetPropValue(jprops, kSkewAxis_Index));
|
||||
if (!transform_adapter) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
fBuilder->bindProperty<VectorValue>(GetPropValue(jprops, kAnchorPoint_Index),
|
||||
[t_adapter](const VectorValue& ap) {
|
||||
t_adapter->setAnchorPoint(ValueTraits<VectorValue>::As<SkPoint>(ap));
|
||||
});
|
||||
fBuilder->bindProperty<VectorValue>(GetPropValue(jprops, kPosition_Index),
|
||||
[t_adapter](const VectorValue& p) {
|
||||
t_adapter->setPosition(ValueTraits<VectorValue>::As<SkPoint>(p));
|
||||
});
|
||||
fBuilder->bindProperty<ScalarValue>(GetPropValue(jprops, kRotation_Index),
|
||||
[t_adapter](const ScalarValue& r) {
|
||||
t_adapter->setRotation(r);
|
||||
});
|
||||
fBuilder->bindProperty<ScalarValue>(GetPropValue(jprops, kSkew_Index),
|
||||
[t_adapter](const ScalarValue& s) {
|
||||
t_adapter->setSkew(s);
|
||||
});
|
||||
fBuilder->bindProperty<ScalarValue>(GetPropValue(jprops, kSkewAxis_Index),
|
||||
[t_adapter](const ScalarValue& sa) {
|
||||
t_adapter->setSkewAxis(sa);
|
||||
});
|
||||
|
||||
fBuilder->bindProperty<ScalarValue>(GetPropValue(jprops, kUniformScale_Index),
|
||||
[s_adapter](const ScalarValue& u) {
|
||||
s_adapter->setIsUniform(SkScalarRoundToInt(u));
|
||||
});
|
||||
fBuilder->bindProperty<ScalarValue>(GetPropValue(jprops, kScaleHeight_Index),
|
||||
[s_adapter](const ScalarValue& sh) {
|
||||
s_adapter->setScaleHeight(sh);
|
||||
});
|
||||
fBuilder->bindProperty<ScalarValue>(GetPropValue(jprops, kScaleWidth_Index),
|
||||
[s_adapter](const ScalarValue& sw) {
|
||||
s_adapter->setScaleWidth(sw);
|
||||
});
|
||||
|
||||
auto opacity_node = sksg::OpacityEffect::Make(sksg::TransformEffect::Make(std::move(layer),
|
||||
std::move(matrix)));
|
||||
|
||||
fBuilder->bindProperty<ScalarValue>(GetPropValue(jprops, kOpacity_Index),
|
||||
[opacity_node](const ScalarValue& o) {
|
||||
opacity_node->setOpacity(o * 0.01f);
|
||||
});
|
||||
|
||||
return opacity_node;
|
||||
auto transform_effect_node = sksg::TransformEffect::Make(std::move(layer),
|
||||
transform_adapter->node());
|
||||
return fBuilder->attachDiscardableAdapter<TransformEffectAdapter>
|
||||
(*fBuilder,
|
||||
GetPropValue(jprops, kOpacity_Index),
|
||||
GetPropValue(jprops, kUniformScale_Index),
|
||||
GetPropValue(jprops, kScaleWidth_Index),
|
||||
GetPropValue(jprops, kScaleHeight_Index),
|
||||
std::move(transform_adapter),
|
||||
std::move(transform_effect_node)
|
||||
);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
Loading…
Reference in New Issue
Block a user