loader/lottie: fix a missing layer timeremap.

The frame count should have been multiplied with the timeStretch property.
also newly implemented the TimeRemap(tm) property
This commit is contained in:
Hermet Park 2023-08-09 12:47:41 +09:00 committed by Hermet Park
parent 6f8504d3b9
commit d7c70c5371
6 changed files with 130 additions and 75 deletions

File diff suppressed because one or more lines are too long

View file

@ -9,6 +9,7 @@ source_file = [
'tvgLottieBuilder.cpp',
'tvgLottieInterpolator.cpp',
'tvgLottieLoader.cpp',
'tvgLottieModel.cpp',
'tvgLottieParserHandler.cpp',
'tvgLottieParser.cpp'
]

View file

@ -0,0 +1,101 @@
/*
* Copyright (c) 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "tvgLottieModel.h"
/************************************************************************/
/* Internal Class Implementation */
/************************************************************************/
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
Fill* LottieGradient::fill(int32_t frameNo)
{
Fill* fill = nullptr;
//Linear Graident
if (id == 1) {
fill = LinearGradient::gen().release();
static_cast<LinearGradient*>(fill)->linear(start(frameNo).x, start(frameNo).y, end(frameNo).x, end(frameNo).y);
}
//Radial Gradient
if (id == 2) {
fill = RadialGradient::gen().release();
TVGLOG("LOTTIE", "TODO: Missing Radial Gradient!");
}
if (!fill) return nullptr;
colorStops(frameNo, fill);
return fill;
}
void LottieGroup::prepare(LottieObject::Type type)
{
LottieObject::type = type;
if (transform) statical &= transform->statical;
for (auto child = children.data; child < children.end(); ++child) {
statical &= (*child)->statical;
if (!statical) break;
}
}
int32_t LottieLayer::remap(int32_t frameNo)
{
if (timeRemap.frames) {
frameNo = comp->frameAtTime(timeRemap(frameNo));
}
if (timeStretch == 1.0f) return frameNo;
return (int32_t)(frameNo / timeStretch);
}
LottieComposition::~LottieComposition()
{
delete(root);
free(version);
free(name);
//delete interpolators
for (auto i = interpolators.data; i < interpolators.end(); ++i) {
free((*i)->key);
free(*i);
}
//delete assets
for (auto a = assets.data; a < assets.end(); ++a) {
delete(*a);
}
}

View file

@ -28,6 +28,8 @@
#include "tvgLottieProperty.h"
struct LottieComposition;
struct LottieStroke
{
bool dynamic()
@ -52,27 +54,7 @@ struct LottieGradient
return false;
}
Fill* fill(int32_t frameNo)
{
Fill* fill = nullptr;
//Linear Graident
if (id == 1) {
fill = LinearGradient::gen().release();
static_cast<LinearGradient*>(fill)->linear(start(frameNo).x, start(frameNo).y, end(frameNo).x, end(frameNo).y);
}
//Radial Gradient
if (id == 2) {
fill = RadialGradient::gen().release();
TVGLOG("LOTTIE", "TODO: Missing Radial Gradient!");
}
if (!fill) return nullptr;
colorStops(frameNo, fill);
return fill;
}
Fill* fill(int32_t frameNo);
LottiePoint start = Point{0.0f, 0.0f};
LottiePoint end = Point{0.0f, 0.0f};
@ -322,15 +304,7 @@ struct LottieGroup : LottieObject
delete(transform);
}
void prepare(LottieObject::Type type = LottieObject::Group)
{
LottieObject::type = type;
if (transform) statical &= transform->statical;
for (auto child = children.data; child < children.end(); ++child) {
statical &= (*child)->statical;
if (!statical) break;
}
}
void prepare(LottieObject::Type type = LottieObject::Group);
virtual uint8_t opacity(int32_t frameNo)
{
@ -379,22 +353,16 @@ struct LottieLayer : LottieGroup
return LottieGroup::opacity(frameNo);
}
/* frameRemap has the value in time domain(in sec)
To get the proper mapping first we get the mapped time at the current frame
Number then we need to convert mapped time to frame number using the
composition time line Ex: at frame 10 the mappend time is 0.5(500 ms) which
will be convert to frame number 30 if the frame rate is 60. or will result to
frame number 15 if the frame rate is 30. */
int32_t remap(int32_t frameNo)
{
return frameNo;
//return (int32_t)((frameNo - startFrame) / timeStretch);
}
int32_t remap(int32_t frameNo);
RGB24 color = {255, 255, 255}; //Optimize: used for solidcolor
//Optimize: compact data??
RGB24 color = {255, 255, 255};
CompositeMethod matteType = CompositeMethod::None;
BlendMethod blendMethod = BlendMethod::Normal;
LottieLayer* parent = nullptr;
LottieFloat timeRemap = 0.0f;
LottieComposition* comp = nullptr;
float timeStretch = 1.0f;
uint32_t w, h;
int32_t inFrame = 0;
@ -419,47 +387,27 @@ struct LottieLayer : LottieGroup
struct LottieComposition
{
~LottieComposition()
{
delete(root);
free(version);
free(name);
//delete interpolators
for (auto i = interpolators.data; i < interpolators.end(); ++i) {
free((*i)->key);
free(*i);
}
//delete assets
for (auto a = assets.data; a < assets.end(); ++a) {
delete(*a);
}
}
~LottieComposition();
float duration() const
{
return frameDuration() / frameRate; // in second
}
uint32_t frameAtPos(float pos) const
int32_t frameAtTime(float timeInSec) const
{
if (pos < 0) pos = 0;
if (pos > 1) pos = 1;
return (uint32_t)lroundf(pos * frameDuration());
}
long frameAtTime(double timeInSec) const
{
return long(frameAtPos(timeInSec / duration()));
auto p = timeInSec / duration();
if (p < 0.0f) p = 0.0f;
else if (p > 1.0f) p = 1.0f;
return (int32_t)lroundf(p * frameDuration());
}
uint32_t frameCnt() const
{
return endFrame - startFrame + 1;
return frameDuration() + 1;
}
long frameDuration() const
uint32_t frameDuration() const
{
return endFrame - startFrame;
}

View file

@ -918,6 +918,13 @@ LottieObject* LottieParser::parseGroup()
}
void LottieParser::parseTimeRemap(LottieLayer* layer)
{
layer->comp = comp;
parseProperty(layer->timeRemap);
}
void LottieParser::parseShapes(LottieLayer* layer)
{
enterArray();
@ -956,11 +963,7 @@ LottieLayer* LottieParser::parseLayer()
else if (!strcmp(key, "st")) layer->startFrame = lroundf(getFloat());
else if (!strcmp(key, "bm")) layer->blendMethod = getBlendMethod();
else if (!strcmp(key, "parent")) layer->pid = getInt();
else if (!strcmp(key, "tm"))
{
TVGLOG("LOTTIE", "Time Remap(tm) is not supported");
skip(key);
}
else if (!strcmp(key, "tm")) parseTimeRemap(layer);
else if (!strcmp(key, "w")) layer->w = getInt();
else if (!strcmp(key, "h")) layer->h = getInt();
else if (!strcmp(key, "sw")) layer->w = getInt();

View file

@ -83,6 +83,7 @@ private:
void parseObject(LottieGroup* parent);
void parseShapes(LottieLayer* layer);
void parseTimeRemap(LottieLayer* layer);
void parseStrokeDash(LottieStroke* stroke);
void parseGradient(LottieGradient* gradient, const char* key);
void parseAssets();