Skip to content

Commit

Permalink
[skottie] Motion tile effect
Browse files Browse the repository at this point in the history
Implement support for AE's Motion Tile effect [1].

This is the first effect which needs layer size information, so the CL includes
related plumbing.

Limitations: no phase support at this point.

[1] https://helpx.adobe.com/after-effects/using/stylize-effects.html#motion_tile_effect

Change-Id: I023bf8a9d3e3d2a48458fa94218f143e6aac4c9f
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/221244
Reviewed-by: Mike Reed <[email protected]>
Commit-Queue: Florin Malita <[email protected]>
  • Loading branch information
fmalita authored and Skia Commit-Bot committed Jun 17, 2019
1 parent e66780d commit b97824d
Show file tree
Hide file tree
Showing 12 changed files with 256 additions and 32 deletions.
1 change: 1 addition & 0 deletions modules/skottie/skottie.gni
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ skia_skottie_sources = [
"$_src/effects/GaussianBlurEffect.cpp",
"$_src/effects/GradientEffect.cpp",
"$_src/effects/LevelsEffect.cpp",
"$_src/effects/MotionTileEffect.cpp",
"$_src/effects/TintEffect.cpp",
"$_src/effects/TransformEffect.cpp",
"$_src/effects/TritoneEffect.cpp",
Expand Down
27 changes: 15 additions & 12 deletions modules/skottie/src/SkottieLayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -307,8 +307,7 @@ sk_sp<sksg::RenderNode> AnimationBuilder::attachAssetRef(
}

sk_sp<sksg::RenderNode> AnimationBuilder::attachSolidLayer(const skjson::ObjectValue& jlayer,
const LayerInfo&,
AnimatorScope*) const {
LayerInfo*, AnimatorScope*) const {
const auto size = SkSize::Make(ParseDefault<float>(jlayer["sw"], 0.0f),
ParseDefault<float>(jlayer["sh"], 0.0f));
const skjson::StringValue* hex_str = jlayer["sc"];
Expand Down Expand Up @@ -358,7 +357,7 @@ AnimationBuilder::loadImageAsset(const skjson::ObjectValue& jimage) const {
}

sk_sp<sksg::RenderNode> AnimationBuilder::attachImageAsset(const skjson::ObjectValue& jimage,
const LayerInfo& layer_info,
LayerInfo* layer_info,
AnimatorScope* ascope) const {
const auto* asset_info = this->loadImageAsset(jimage);
if (!asset_info) {
Expand Down Expand Up @@ -398,14 +397,17 @@ sk_sp<sksg::RenderNode> AnimationBuilder::attachImageAsset(const skjson::ObjectV

ascope->push_back(skstd::make_unique<MultiFrameAnimator>(asset_info->fAsset,
image_node,
-layer_info.fInPoint,
-layer_info->fInPoint,
1 / fFrameRate));
}

const auto asset_size = SkISize::Make(
asset_info->fSize.width() > 0 ? asset_info->fSize.width() : image->width(),
asset_info->fSize.height() > 0 ? asset_info->fSize.height() : image->height());

// Image layers are sized explicitly.
layer_info->fSize = asset_size;

if (asset_size == image->bounds().size()) {
// No resize needed.
return std::move(image_node);
Expand All @@ -418,7 +420,7 @@ sk_sp<sksg::RenderNode> AnimationBuilder::attachImageAsset(const skjson::ObjectV
}

sk_sp<sksg::RenderNode> AnimationBuilder::attachImageLayer(const skjson::ObjectValue& jlayer,
const LayerInfo& layer_info,
LayerInfo* layer_info,
AnimatorScope* ascope) const {
return this->attachAssetRef(jlayer, ascope,
[this, &layer_info] (const skjson::ObjectValue& jimage, AnimatorScope* ascope) {
Expand All @@ -427,8 +429,7 @@ sk_sp<sksg::RenderNode> AnimationBuilder::attachImageLayer(const skjson::ObjectV
}

sk_sp<sksg::RenderNode> AnimationBuilder::attachNullLayer(const skjson::ObjectValue& layer,
const LayerInfo&,
AnimatorScope*) const {
LayerInfo*, AnimatorScope*) const {
// Null layers are used solely to drive dependent transforms,
// but we use free-floating sksg::Matrices for that purpose.
return nullptr;
Expand Down Expand Up @@ -544,9 +545,10 @@ sk_sp<sksg::RenderNode> AnimationBuilder::attachLayer(const skjson::ObjectValue*
return nullptr;
}

const LayerInfo layer_info = {
LayerInfo layer_info = {
fSize,
ParseDefault<float>((*jlayer)["ip"], 0.0f),
ParseDefault<float>((*jlayer)["op"], 0.0f)
ParseDefault<float>((*jlayer)["op"], 0.0f),
};
if (layer_info.fInPoint >= layer_info.fOutPoint) {
this->log(Logger::Level::kError, nullptr,
Expand All @@ -557,7 +559,7 @@ sk_sp<sksg::RenderNode> AnimationBuilder::attachLayer(const skjson::ObjectValue*
const AutoPropertyTracker apt(this, *jlayer);

using LayerBuilder = sk_sp<sksg::RenderNode> (AnimationBuilder::*)(const skjson::ObjectValue&,
const LayerInfo&,
LayerInfo*,
AnimatorScope*) const;

// AE is annoyingly inconsistent in how effects interact with layer transforms: depending on
Expand Down Expand Up @@ -606,7 +608,7 @@ sk_sp<sksg::RenderNode> AnimationBuilder::attachLayer(const skjson::ObjectValue*
AnimatorScope layer_animators;

// Build the layer content fragment.
auto layer = (this->*(build_info.fBuilder))(*jlayer, layer_info, &layer_animators);
auto layer = (this->*(build_info.fBuilder))(*jlayer, &layer_info, &layer_animators);

// Clip layers with explicit dimensions.
float w = 0, h = 0;
Expand All @@ -633,7 +635,8 @@ sk_sp<sksg::RenderNode> AnimationBuilder::attachLayer(const skjson::ObjectValue*

// Optional layer effects.
if (const skjson::ArrayValue* jeffects = (*jlayer)["ef"]) {
layer = EffectBuilder(this, &layer_animators).attachEffects(*jeffects, std::move(layer));
layer = EffectBuilder(this, layer_info.fSize, &layer_animators)
.attachEffects(*jeffects, std::move(layer));
}

// Attach the transform after effects, when needed.
Expand Down
6 changes: 5 additions & 1 deletion modules/skottie/src/SkottiePrecompLayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ namespace skottie {
namespace internal {

sk_sp<sksg::RenderNode> AnimationBuilder::attachPrecompLayer(const skjson::ObjectValue& jlayer,
const LayerInfo&,
LayerInfo* layer_info,
AnimatorScope* ascope) const {
const skjson::ObjectValue* time_remap = jlayer["tm"];
// Empirically, a time mapper supersedes start/stretch.
Expand All @@ -29,6 +29,10 @@ sk_sp<sksg::RenderNode> AnimationBuilder::attachPrecompLayer(const skjson::Objec
!SkScalarNearlyEqual(stretch_time, 1) ||
time_remap;

// Precomp layers are sized explicitly.
layer_info->fSize = SkSize::Make(ParseDefault<float>(jlayer["w"], 0.0f),
ParseDefault<float>(jlayer["h"], 0.0f));

AnimatorScope local_animators;
auto precomp_layer = this->attachAssetRef(jlayer,
requires_time_mapping ? &local_animators : ascope,
Expand Down
20 changes: 10 additions & 10 deletions modules/skottie/src/SkottiePriv.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,22 +110,22 @@ class AnimationBuilder final : public SkNoncopyable {
const std::function<sk_sp<sksg::RenderNode>(const skjson::ObjectValue&,
AnimatorScope* ctx)>&) const;
const ImageAssetInfo* loadImageAsset(const skjson::ObjectValue&) const;
sk_sp<sksg::RenderNode> attachImageAsset(const skjson::ObjectValue&, const LayerInfo&,
sk_sp<sksg::RenderNode> attachImageAsset(const skjson::ObjectValue&, LayerInfo*,
AnimatorScope*) const;

sk_sp<sksg::RenderNode> attachNestedAnimation(const char* name, AnimatorScope* ascope) const;

sk_sp<sksg::RenderNode> attachImageLayer (const skjson::ObjectValue&, const LayerInfo&,
sk_sp<sksg::RenderNode> attachImageLayer (const skjson::ObjectValue&, LayerInfo*,
AnimatorScope*) const;
sk_sp<sksg::RenderNode> attachNullLayer (const skjson::ObjectValue&, const LayerInfo&,
sk_sp<sksg::RenderNode> attachNullLayer (const skjson::ObjectValue&, LayerInfo*,
AnimatorScope*) const;
sk_sp<sksg::RenderNode> attachPrecompLayer(const skjson::ObjectValue&, const LayerInfo&,
sk_sp<sksg::RenderNode> attachPrecompLayer(const skjson::ObjectValue&, LayerInfo*,
AnimatorScope*) const;
sk_sp<sksg::RenderNode> attachShapeLayer (const skjson::ObjectValue&, const LayerInfo&,
sk_sp<sksg::RenderNode> attachShapeLayer (const skjson::ObjectValue&, LayerInfo*,
AnimatorScope*) const;
sk_sp<sksg::RenderNode> attachSolidLayer (const skjson::ObjectValue&, const LayerInfo&,
sk_sp<sksg::RenderNode> attachSolidLayer (const skjson::ObjectValue&, LayerInfo*,
AnimatorScope*) const;
sk_sp<sksg::RenderNode> attachTextLayer (const skjson::ObjectValue&, const LayerInfo&,
sk_sp<sksg::RenderNode> attachTextLayer (const skjson::ObjectValue&, LayerInfo*,
AnimatorScope*) const;

bool dispatchColorProperty(const sk_sp<sksg::Color>&) const;
Expand Down Expand Up @@ -184,10 +184,10 @@ class AnimationBuilder final : public SkNoncopyable {
mutable const char* fPropertyObserverContext;
mutable bool fHasNontrivialBlending : 1;


struct LayerInfo {
float fInPoint,
fOutPoint;
SkSize fSize;
const float fInPoint,
fOutPoint;
};

struct AssetInfo {
Expand Down
2 changes: 1 addition & 1 deletion modules/skottie/src/SkottieShapeLayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -724,7 +724,7 @@ sk_sp<sksg::RenderNode> AnimationBuilder::attachShape(const skjson::ArrayValue*
}

sk_sp<sksg::RenderNode> AnimationBuilder::attachShapeLayer(const skjson::ObjectValue& layer,
const LayerInfo&,
LayerInfo*,
AnimatorScope* ascope) const {
std::vector<sk_sp<sksg::GeometryNode>> geometryStack;
std::vector<GeometryEffectRec> geometryEffectStack;
Expand Down
8 changes: 7 additions & 1 deletion modules/skottie/src/effects/Effects.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@
namespace skottie {
namespace internal {

EffectBuilder::EffectBuilder(const AnimationBuilder* abuilder, AnimatorScope* ascope)
EffectBuilder::EffectBuilder(const AnimationBuilder* abuilder, const SkSize& layer_size,
AnimatorScope* ascope)
: fBuilder(abuilder)
, fLayerSize(layer_size)
, fScope(ascope) {}

EffectBuilder::EffectBuilderT EffectBuilder::findBuilder(const skjson::ObjectValue& jeffect) const {
Expand Down Expand Up @@ -50,6 +52,7 @@ EffectBuilder::EffectBuilderT EffectBuilder::findBuilder(const skjson::ObjectVal

static constexpr char kGradientEffectMN[] = "ADBE Ramp",
kLevelsEffectMN[] = "ADBE Easy Levels2",
kMotionTileEffectMN[] = "ADBE Tile",
kTransformEffectMN[] = "ADBE Geometry2";

if (const skjson::StringValue* mn = jeffect["mn"]) {
Expand All @@ -59,6 +62,9 @@ EffectBuilder::EffectBuilderT EffectBuilder::findBuilder(const skjson::ObjectVal
if (!strcmp(mn->begin(), kLevelsEffectMN)) {
return &EffectBuilder::attachLevelsEffect;
}
if (!strcmp(mn->begin(), kMotionTileEffectMN)) {
return &EffectBuilder::attachMotionTileEffect;
}
if (!strcmp(mn->begin(), kTransformEffectMN)) {
return &EffectBuilder::attachTransformEffect;
}
Expand Down
15 changes: 9 additions & 6 deletions modules/skottie/src/effects/Effects.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ namespace internal {

class EffectBuilder final : public SkNoncopyable {
public:
EffectBuilder(const AnimationBuilder*, AnimatorScope*);
EffectBuilder(const AnimationBuilder*, const SkSize&, AnimatorScope*);

sk_sp<sksg::RenderNode> attachEffects(const skjson::ArrayValue&,
sk_sp<sksg::RenderNode>) const;
Expand All @@ -24,28 +24,31 @@ class EffectBuilder final : public SkNoncopyable {
using EffectBuilderT = sk_sp<sksg::RenderNode>(EffectBuilder::*)(const skjson::ArrayValue&,
sk_sp<sksg::RenderNode>) const;

sk_sp<sksg::RenderNode> attachTintEffect (const skjson::ArrayValue&,
sk_sp<sksg::RenderNode> attachDropShadowEffect (const skjson::ArrayValue&,
sk_sp<sksg::RenderNode>) const;
sk_sp<sksg::RenderNode> attachFillEffect (const skjson::ArrayValue&,
sk_sp<sksg::RenderNode>) const;
sk_sp<sksg::RenderNode> attachTritoneEffect (const skjson::ArrayValue&,
sk_sp<sksg::RenderNode>) const;
sk_sp<sksg::RenderNode> attachDropShadowEffect (const skjson::ArrayValue&,
sk_sp<sksg::RenderNode>) const;
sk_sp<sksg::RenderNode> attachGaussianBlurEffect(const skjson::ArrayValue&,
sk_sp<sksg::RenderNode>) const;
sk_sp<sksg::RenderNode> attachGradientEffect (const skjson::ArrayValue&,
sk_sp<sksg::RenderNode>) const;
sk_sp<sksg::RenderNode> attachLevelsEffect (const skjson::ArrayValue&,
sk_sp<sksg::RenderNode>) const;
sk_sp<sksg::RenderNode> attachMotionTileEffect (const skjson::ArrayValue&,
sk_sp<sksg::RenderNode>) const;
sk_sp<sksg::RenderNode> attachTintEffect (const skjson::ArrayValue&,
sk_sp<sksg::RenderNode>) const;
sk_sp<sksg::RenderNode> attachTransformEffect (const skjson::ArrayValue&,
sk_sp<sksg::RenderNode>) const;
sk_sp<sksg::RenderNode> attachTritoneEffect (const skjson::ArrayValue&,
sk_sp<sksg::RenderNode>) const;

EffectBuilderT findBuilder(const skjson::ObjectValue&) const;

static const skjson::Value& GetPropValue(const skjson::ArrayValue& jprops, size_t prop_index);

const AnimationBuilder* fBuilder;
const SkSize fLayerSize;
AnimatorScope* fScope;
};

Expand Down
Loading

0 comments on commit b97824d

Please sign in to comment.