Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
### Breaking changes

- macOS: OpenGL rendering backend disabled in favor of Metal
- `LottieReader::parseFile()`, `parseData()`, `parseStream()`, and `parseFromZip()` now return `ResultValue<AnimationComposition::Ptr>` and no longer take a trailing `String* outError` out-parameter; check `wasOk()`/`failed()` and read the message via `getErrorMessage()`.
- `AnimationFrameExporter` is now an instance-based class bound to a `GraphicsContext` (construct `AnimationFrameExporter exporter (ctx);` then call `exporter.renderFrame(anim, …)` / `exporter.renderAllFrames(…)` / `exporter.exportToGif(anim, …)`), so it can own and reuse the GPU matte-composite pipeline across frames instead of recompiling it per frame. The `exportToGif(frames, frameRate, …)` frame-sequence encoder remains a static helper.

### Graphics
Expand Down
12 changes: 6 additions & 6 deletions modules/yup_animation/animation/yup_Animation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,10 @@ Animation Animation::loadFromFile (const File& file, const LoadOptions& opts)
loaderOpts.resourceDirectory = opts.resourceDirectory;

auto comp = LottieReader::parseFile (file, loaderOpts);
if (comp == nullptr)
if (comp.failed())
return {};

return Animation (std::move (comp));
return Animation (comp.getReference());
}

Animation Animation::loadFromData (const String& jsonText, const LoadOptions& opts)
Expand All @@ -60,10 +60,10 @@ Animation Animation::loadFromData (const String& jsonText, const LoadOptions& op
loaderOpts.resourceDirectory = opts.resourceDirectory;

auto comp = LottieReader::parseData (jsonText, loaderOpts);
if (comp == nullptr)
if (comp.failed())
return {};

return Animation (std::move (comp));
return Animation (comp.getReference());
}

Animation Animation::loadFromStream (InputStream& stream, const LoadOptions& opts)
Expand All @@ -72,10 +72,10 @@ Animation Animation::loadFromStream (InputStream& stream, const LoadOptions& opt
loaderOpts.resourceDirectory = opts.resourceDirectory;

auto comp = LottieReader::parseStream (stream, loaderOpts);
if (comp == nullptr)
if (comp.failed())
return {};

return Animation (std::move (comp));
return Animation (comp.getReference());
}

Animation Animation::fromComposition (AnimationComposition::Ptr comp)
Expand Down
13 changes: 12 additions & 1 deletion modules/yup_animation/core/yup_AnimationTransform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,23 @@ AffineTransform AnimationTransform::toAffineTransform (float frameNo) const

const Point<float> a = anchor.getValueAt (frameNo);
const Size<float> s = scale.getValueAt (frameNo);
const float r = is3DData ? rotationZ.getValueAt (frameNo) : rotation.getValueAt (frameNo);
float r = is3DData ? rotationZ.getValueAt (frameNo) : rotation.getValueAt (frameNo);
const float sk = skew.getValueAt (frameNo);
const float sa = skewAxis.getValueAt (frameNo);

const Point<float> p = positionAt (frameNo);

if (autoOrient)
{
constexpr float sampleOffset = 0.01f;
const Point<float> before = positionAt (frameNo - sampleOffset);
const Point<float> after = positionAt (frameNo + sampleOffset);
const Point<float> direction = after - before;

if (direction.getX() * direction.getX() + direction.getY() * direction.getY() > 1.0e-8f)
r += radiansToDegrees (std::atan2 (direction.getY(), direction.getX()));
}

// Compose: translate(p) * rotate(r) * skew * scale(s/100) * translate(-a)
AffineTransform t;
t = t.translated (-a.getX(), -a.getY());
Expand Down
3 changes: 3 additions & 0 deletions modules/yup_animation/core/yup_AnimationTransform.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ class YUP_API AnimationTransform
FloatProperty skew { AnimationProperty<float>::staticValue (0.0f) };
FloatProperty skewAxis { AnimationProperty<float>::staticValue (0.0f) };

/** When true, aligns the local X axis with the instantaneous position motion path. */
bool autoOrient = false;

/** 3D rotation channels (Lottie "rx", "ry", "rz").
When is3DData is true, these replace the 2D rotation for 3D layers. */
bool is3DData = false;
Expand Down
42 changes: 35 additions & 7 deletions modules/yup_animation/io/yup_LottieExpressionEvaluator.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,15 @@ class LottieExpressionEvaluator
{
public:
//==============================================================================
/** Represents a layer in the composition context. */
struct LayerContext
{
String name;
int id = -1;
AnimationTransform* transform = nullptr;
};

/** Represents the composition context for evaluating expressions. */
struct CompositionContext
{
Size<float> size;
Expand All @@ -50,6 +52,7 @@ class LottieExpressionEvaluator
};

//==============================================================================
/** Represents a shape layer's top-level content group. */
struct EvalResult
{
enum class Kind
Expand All @@ -60,7 +63,7 @@ class LottieExpressionEvaluator
ShapeContentRef ///< content("G").content("P").path or content("G").transform.rotation
};

Kind kind = Kind::Unknown;
Kind kind = Kind::Unknown; ///< The kind of evaluation result

var value; ///< Computed frame-0 value (set for StaticValue and as a snapshot for ref kinds)

Expand All @@ -76,40 +79,65 @@ class LottieExpressionEvaluator
};

//==============================================================================
/** Constructs a new LottieExpressionEvaluator. */
LottieExpressionEvaluator();

/** Registers thisComp with the given composition data.
Must be called before evaluating layer transform expressions. */

@param ctx The composition context to use for evaluating expressions.

Must be called before evaluating layer transform expressions.
*/
void setupCompositionContext (const CompositionContext& ctx);

/** Registers content() pointing into a ShapeLayer's top-level groups.
Call before evaluating expressions inside parseShapeContents. */

@param layer The shape layer to use for evaluating expressions.

Call before evaluating expressions inside parseShapeContents.
*/
void setupShapeContext (const ShapeLayer& layer);

/** Registers content() pointing into an AnimationGroup's child groups.
Call before evaluating expressions inside parseGroupItems. */

@param group The animation group to use for evaluating expressions.

Call before evaluating expressions inside parseGroupItems.
*/
void setupGroupContext (const AnimationGroup& group);

/** Evaluates an AE expression string.
Returns Kind::Unknown for empty input or evaluation failures. */
/** Evaluates an AfterEffects expression string.

@param expression The expression string to evaluate.

@returns An EvalResult describing the result of the evaluation, or
Kind::Unknown if the expression is empty or evaluation failed.
*/
[[nodiscard]] EvalResult evaluate (const String& expression);

//==============================================================================
// Internal state setters used by proxy objects during evaluation.
/** @internal */
void setLastLayerName (const String& s) { lastLayerName_ = s; }

/** @internal */
void setLastLayerId (int id) { lastLayerId_ = id; }

/** @internal */
void setLastContentGroup (const String& s) { lastContentGroup_ = s; }

/** @internal */
void setLastContentItem (const String& s) { lastContentItem_ = s; }

/** @internal */
void setLastProperty (const String& s) { lastProperty_ = s; }

/** @internal */
String getLastContentGroup() const { return lastContentGroup_; }

/** @internal */
String getLastContentItem() const { return lastContentItem_; }

/** @internal */
String& getLastPropertyRef() { return lastProperty_; }

private:
Expand Down
Loading
Loading