[skottie] Motion tile effect
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 <reed@google.com> Commit-Queue: Florin Malita <fmalita@chromium.org>
This commit is contained in:
parent
e66780dd1c
commit
b97824d4d1
@ -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",
|
||||
|
@ -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"];
|
||||
@ -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) {
|
||||
@ -398,7 +397,7 @@ 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));
|
||||
}
|
||||
|
||||
@ -406,6 +405,9 @@ sk_sp<sksg::RenderNode> AnimationBuilder::attachImageAsset(const skjson::ObjectV
|
||||
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);
|
||||
@ -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) {
|
||||
@ -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;
|
||||
@ -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,
|
||||
@ -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
|
||||
@ -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;
|
||||
@ -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.
|
||||
|
@ -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.
|
||||
@ -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,
|
||||
|
@ -110,22 +110,22 @@ private:
|
||||
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;
|
||||
@ -184,10 +184,10 @@ private:
|
||||
mutable const char* fPropertyObserverContext;
|
||||
mutable bool fHasNontrivialBlending : 1;
|
||||
|
||||
|
||||
struct LayerInfo {
|
||||
float fInPoint,
|
||||
fOutPoint;
|
||||
SkSize fSize;
|
||||
const float fInPoint,
|
||||
fOutPoint;
|
||||
};
|
||||
|
||||
struct AssetInfo {
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
@ -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"]) {
|
||||
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
@ -24,28 +24,31 @@ private:
|
||||
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;
|
||||
};
|
||||
|
||||
|
174
modules/skottie/src/effects/MotionTileEffect.cpp
Normal file
174
modules/skottie/src/effects/MotionTileEffect.cpp
Normal file
@ -0,0 +1,174 @@
|
||||
/*
|
||||
* Copyright 2019 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "modules/skottie/src/effects/Effects.h"
|
||||
|
||||
#include "include/core/SkCanvas.h"
|
||||
#include "include/core/SkPictureRecorder.h"
|
||||
#include "modules/skottie/src/SkottieValue.h"
|
||||
#include "modules/sksg/include/SkSGRenderNode.h"
|
||||
#include "src/utils/SkJSON.h"
|
||||
|
||||
namespace skottie {
|
||||
namespace internal {
|
||||
|
||||
namespace {
|
||||
|
||||
// AE motion tile effect semantics
|
||||
// (https://helpx.adobe.com/after-effects/using/stylize-effects.html#motion_tile_effect):
|
||||
//
|
||||
// - the full content of the layer is mapped to a tile: tile_center, tile_width, tile_height
|
||||
//
|
||||
// - tiles are repeated in both dimensions to fill the output area: output_width, output_height
|
||||
//
|
||||
// - tiling mode is either kRepeat (default) or kMirror (when mirror_edges == true)
|
||||
//
|
||||
// - for a non-zero phase, alternating vertical columns (every other column) are offset by
|
||||
// the specified amount
|
||||
//
|
||||
// - when horizontal_phase is true, the phase is applied to horizontal rows instead of columns
|
||||
//
|
||||
class TileRenderNode final : public sksg::CustomRenderNode {
|
||||
public:
|
||||
TileRenderNode(const SkSize& size, sk_sp<sksg::RenderNode> layer)
|
||||
: INHERITED({std::move(layer)})
|
||||
, fLayerSize(size) {}
|
||||
|
||||
SG_ATTRIBUTE(TileCenter , SkPoint , fTileCenter )
|
||||
SG_ATTRIBUTE(TileWidth , SkScalar, fTileW )
|
||||
SG_ATTRIBUTE(TileHeight , SkScalar, fTileH )
|
||||
SG_ATTRIBUTE(OutputWidth , SkScalar, fOutputW )
|
||||
SG_ATTRIBUTE(OutputHeight , SkScalar, fOutputH )
|
||||
SG_ATTRIBUTE(Phase , SkScalar, fPhase )
|
||||
SG_ATTRIBUTE(MirrorEdges , bool , fMirrorEdges )
|
||||
SG_ATTRIBUTE(HorizontalPhase, bool , fHorizontalPhase)
|
||||
|
||||
protected:
|
||||
const RenderNode* onNodeAt(const SkPoint&) const override { return nullptr; } // no hit-testing
|
||||
|
||||
SkRect onRevalidate(sksg::InvalidationController* ic, const SkMatrix& ctm) override {
|
||||
SkASSERT(this->children().size() == 1ul);
|
||||
this->children()[0]->revalidate(ic, ctm);
|
||||
|
||||
// outputW and outputH are layer size percentage units.
|
||||
const auto outputW = fOutputW * 0.01f * fLayerSize.width(),
|
||||
outputH = fOutputH * 0.01f * fLayerSize.height();
|
||||
|
||||
return SkRect::MakeXYWH((fLayerSize.width() - outputW) * 0.5f,
|
||||
(fLayerSize.height() - outputH) * 0.5f,
|
||||
outputW, outputH);
|
||||
}
|
||||
|
||||
void onRender(SkCanvas* canvas, const RenderContext* ctx) const override {
|
||||
// tileW and tileH are also layer size percentage units
|
||||
const auto tileW = SkTPin(fTileW, 0.0f, 100.0f) * 0.01f * fLayerSize.width(),
|
||||
tileH = SkTPin(fTileH, 0.0f, 100.0f) * 0.01f * fLayerSize.height();
|
||||
|
||||
// AE allow one of the tile dimensions to collapse, but not both.
|
||||
if (this->bounds().isEmpty() || (!tileW && !tileH)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto tile_size = SkSize::Make(std::max(tileW, 1.0f),
|
||||
std::max(tileH, 1.0f));
|
||||
const auto tile = SkRect::MakeXYWH(fTileCenter.fX - 0.5f * tile_size.width(),
|
||||
fTileCenter.fY - 0.5f * tile_size.height(),
|
||||
tile_size.width(),
|
||||
tile_size.height());
|
||||
|
||||
SkASSERT(this->children().size() == 1ul);
|
||||
const auto& layer = this->children()[0];
|
||||
const auto layer_bounds = SkRect::MakeWH(fLayerSize.width(), fLayerSize.height());
|
||||
|
||||
// TODO: phase
|
||||
|
||||
SkPictureRecorder recorder;
|
||||
layer->render(recorder.beginRecording(layer_bounds));
|
||||
const auto layer_pic = recorder.finishRecordingAsPicture();
|
||||
|
||||
const auto shader_matrix = SkMatrix::MakeRectToRect(layer_bounds, tile,
|
||||
SkMatrix::kFill_ScaleToFit);
|
||||
|
||||
const auto tm = fMirrorEdges ? SkTileMode::kMirror : SkTileMode::kRepeat;
|
||||
|
||||
SkPaint paint;
|
||||
paint.setAntiAlias(true);
|
||||
paint.setShader(layer_pic->makeShader(tm, tm, &shader_matrix));
|
||||
|
||||
canvas->drawRect(this->bounds(), paint);
|
||||
}
|
||||
|
||||
private:
|
||||
const SkSize fLayerSize;
|
||||
|
||||
SkPoint fTileCenter = { 0, 0 };
|
||||
SkScalar fTileW = 1,
|
||||
fTileH = 1,
|
||||
fOutputW = 1,
|
||||
fOutputH = 1,
|
||||
fPhase = 0;
|
||||
bool fMirrorEdges = false;
|
||||
bool fHorizontalPhase = false;
|
||||
|
||||
using INHERITED = sksg::CustomRenderNode;
|
||||
};
|
||||
|
||||
} // anonymous ns
|
||||
|
||||
sk_sp<sksg::RenderNode> EffectBuilder::attachMotionTileEffect(const skjson::ArrayValue& jprops,
|
||||
sk_sp<sksg::RenderNode> layer) const {
|
||||
enum : size_t {
|
||||
kTileCenter_Index = 0,
|
||||
kTileWidth_Index = 1,
|
||||
kTileHeight_Index = 2,
|
||||
kOutputWidth_Index = 3,
|
||||
kOutputHeight_Index = 4,
|
||||
kMirrorEdges_Index = 5,
|
||||
kPhase_Index = 6,
|
||||
kHorizontalPhaseShift_Index = 7,
|
||||
};
|
||||
|
||||
auto tiler = sk_make_sp<TileRenderNode>(fLayerSize, std::move(layer));
|
||||
|
||||
fBuilder->bindProperty<VectorValue>(GetPropValue(jprops, kTileCenter_Index), fScope,
|
||||
[tiler](const VectorValue& tc) {
|
||||
tiler->setTileCenter(ValueTraits<VectorValue>::As<SkPoint>(tc));
|
||||
});
|
||||
fBuilder->bindProperty<ScalarValue>(GetPropValue(jprops, kTileWidth_Index), fScope,
|
||||
[tiler](const ScalarValue& tw) {
|
||||
tiler->setTileWidth(tw);
|
||||
});
|
||||
fBuilder->bindProperty<ScalarValue>(GetPropValue(jprops, kTileHeight_Index), fScope,
|
||||
[tiler](const ScalarValue& th) {
|
||||
tiler->setTileHeight(th);
|
||||
});
|
||||
fBuilder->bindProperty<ScalarValue>(GetPropValue(jprops, kOutputWidth_Index), fScope,
|
||||
[tiler](const ScalarValue& ow) {
|
||||
tiler->setOutputWidth(ow);
|
||||
});
|
||||
fBuilder->bindProperty<ScalarValue>(GetPropValue(jprops, kOutputHeight_Index), fScope,
|
||||
[tiler](const ScalarValue& oh) {
|
||||
tiler->setOutputHeight(oh);
|
||||
});
|
||||
fBuilder->bindProperty<ScalarValue>(GetPropValue(jprops, kMirrorEdges_Index), fScope,
|
||||
[tiler](const ScalarValue& me) {
|
||||
tiler->setMirrorEdges(SkScalarRoundToInt(me));
|
||||
});
|
||||
fBuilder->bindProperty<ScalarValue>(GetPropValue(jprops, kPhase_Index), fScope,
|
||||
[tiler](const ScalarValue& ph) {
|
||||
tiler->setPhase(ph);
|
||||
});
|
||||
fBuilder->bindProperty<ScalarValue>(GetPropValue(jprops, kHorizontalPhaseShift_Index), fScope,
|
||||
[tiler](const ScalarValue& hp) {
|
||||
tiler->setHorizontalPhase(SkScalarRoundToInt(hp));
|
||||
});
|
||||
|
||||
return std::move(tiler);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace skottie
|
@ -254,7 +254,7 @@ sk_sp<SkTypeface> AnimationBuilder::findFont(const SkString& font_name) const {
|
||||
}
|
||||
|
||||
sk_sp<sksg::RenderNode> AnimationBuilder::attachTextLayer(const skjson::ObjectValue& layer,
|
||||
const LayerInfo&,
|
||||
LayerInfo*,
|
||||
AnimatorScope* ascope) const {
|
||||
// General text node format:
|
||||
// "t": {
|
||||
|
@ -113,6 +113,24 @@ private:
|
||||
typedef Node INHERITED;
|
||||
};
|
||||
|
||||
/**
|
||||
* Clients outside SkSG looking to implement custom render nodes,
|
||||
* should derive from this class instead of RenderNode. It handles
|
||||
* various book-keeping, and provides a controlled extension point.
|
||||
*/
|
||||
class CustomRenderNode : public RenderNode {
|
||||
protected:
|
||||
explicit CustomRenderNode(std::vector<sk_sp<RenderNode>>&& children);
|
||||
~CustomRenderNode() override;
|
||||
|
||||
const std::vector<sk_sp<RenderNode>>& children() const { return fChildren; }
|
||||
|
||||
private:
|
||||
std::vector<sk_sp<RenderNode>> fChildren;
|
||||
|
||||
using INHERITED = RenderNode;
|
||||
};
|
||||
|
||||
} // namespace sksg
|
||||
|
||||
#endif // SkSGRenderNode_DEFINED
|
||||
|
@ -139,4 +139,18 @@ RenderNode::ScopedRenderContext::setFilterIsolation(const SkRect& bounds, const
|
||||
return std::move(*this);
|
||||
}
|
||||
|
||||
CustomRenderNode::CustomRenderNode(std::vector<sk_sp<RenderNode>>&& children)
|
||||
: INHERITED(kOverrideDamage_Trait) // We cannot make any assumptions - override conservatively.
|
||||
, fChildren(std::move(children)) {
|
||||
for (const auto& child : fChildren) {
|
||||
this->observeInval(child);
|
||||
}
|
||||
}
|
||||
|
||||
CustomRenderNode::~CustomRenderNode() {
|
||||
for (const auto& child : fChildren) {
|
||||
this->unobserveInval(child);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace sksg
|
||||
|
1
resources/skottie/skottie-motiontile-effect.json
Normal file
1
resources/skottie/skottie-motiontile-effect.json
Normal file
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user