loader lottie: adds the lottie property, model and parser.

This commit is contained in:
Hermet Park 2023-07-18 18:34:12 +09:00 committed by Hermet Park
parent b060959e0d
commit 03f878bb2e
10 changed files with 2501 additions and 2 deletions

View file

@ -64,6 +64,8 @@ using namespace tvg;
enum class FileType { Tvg = 0, Svg, Lottie, Raw, Png, Jpg, Webp, Unknown };
using Size = Point;
#ifdef THORVG_LOG_ENABLED
constexpr auto ErrorColor = "\033[31m"; //red
constexpr auto ErrorBgColor = "\033[41m";//bg red

View file

@ -165,4 +165,29 @@ static inline Matrix mathMultiply(const Matrix* lhs, const Matrix* rhs)
}
static inline Point operator-(const Point& lhs, const Point& rhs)
{
return {lhs.x - rhs.x, lhs.y - rhs.y};
}
static inline Point operator+(const Point& lhs, const Point& rhs)
{
return {lhs.x + rhs.x, lhs.y + rhs.y};
}
static inline Point operator*(const Point& lhs, float rhs)
{
return {lhs.x * rhs, lhs.y * rhs};
}
template <typename T>
static inline T mathLerp(const T &start, const T &end, float t)
{
return static_cast<T>(start + (end - start) * t);
}
#endif //_TVG_MATH_H_

View file

@ -1,8 +1,14 @@
source_file = [
'tvgLottieInterpolator.h',
'tvgLottieLoader.h',
'tvgLottieModel.h',
'tvgLottieParser.h',
'tvgLottieParserHandler.h',
'tvgLottieProperty.h',
'tvgLottieInterpolator.cpp',
'tvgLottieLoader.cpp',
'tvgLottieParserHandler.cpp',
'tvgLottieParser.cpp'
]
subloader_dep += [declare_dependency(

View file

@ -23,6 +23,8 @@
#include <math.h>
#include "tvgLoader.h"
#include "tvgLottieLoader.h"
#include "tvgLottieModel.h"
#include "tvgLottieParser.h"
/************************************************************************/
/* Internal Class Implementation */
@ -87,8 +89,8 @@ LottieLoader::~LottieLoader()
void LottieLoader::run(unsigned tid)
{
/* TODO: Compose current frame of Lottie Scene tree
The result should be assigned to "this->root" */
LottieParser parser(content);
parser.parse();
}

View file

@ -0,0 +1,475 @@
/*
* 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.
*/
#ifndef _TVG_LOTTIE_MODEL_H_
#define _TVG_LOTTIE_MODEL_H_
#include "tvgCommon.h"
#include "tvgRender.h"
#include "tvgLottieProperty.h"
struct LottieStroke
{
bool dynamic()
{
if (dash.frames || width.frames) return true;
return false;
}
LottieFloat dash = 0.0f;
LottieFloat width = 0.0f;
StrokeCap cap = StrokeCap::Butt;
StrokeJoin join = StrokeJoin::Miter;
float miterLimit = 0;
};
struct LottieGradient
{
bool dynamic()
{
if (start.frames || end.frames || opacity.frames || height.frames || angle.frames || colorStops.frames) return true;
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;
}
LottiePoint start = Point{0.0f, 0.0f};
LottiePoint end = Point{0.0f, 0.0f};
LottieOpacity opacity = 255;
LottieFloat height = 0.0f; //TODO:
LottieFloat angle = 0.0f;
LottieColorStop colorStops;
uint8_t id = 0; //1: linear, 2: radial
};
struct LottieObject
{
enum Type : uint8_t
{
Composition = 0,
Layer,
Group,
Transform,
SolidFill,
SolidStroke,
GradientFill,
GradientStroke,
Rect,
Ellipse,
Path,
Polystar,
Trim,
Repeater,
RoundedCorner,
Image
};
virtual ~LottieObject()
{
free(name);
}
char* name = nullptr;
Type type;
bool statical = true; //no keyframes
bool hidden = false;
};
struct LottieShape : LottieObject
{
virtual ~LottieShape() {}
bool direction; //path direction (clock wise vs coutner clock wise)
};
struct LottieRoundedCorner : LottieObject
{
void prepare()
{
LottieObject::type = LottieObject::RoundedCorner;
if (radius.frames) statical = false;
}
LottieFloat radius = 0.0f;
};
struct LottiePath : LottieShape
{
void prepare()
{
LottieObject::type = LottieObject::Path;
if (pathset.frames) statical = false;
}
LottiePathSet pathset = PathSet{nullptr, nullptr, 0, 0};
};
struct LottieRect : LottieShape
{
void prepare()
{
LottieObject::type = LottieObject::Rect;
if (position.frames || size.frames || round.frames) statical = false;
}
float roundness(int32_t frameNo)
{
return roundedCorner ? roundedCorner->radius(frameNo) : round(frameNo);
}
bool roundnessChanged(int prevFrame, int curFrame)
{
//return roundedCorner ? roundedCorner->radius.changed(prevFrame, curFrame) : round.changed(prevFrame, curFrame);
TVGERR("LOTTIE", "TODO: LottieRect::roundnessChanged()");
return 0;
}
LottieRoundedCorner* roundedCorner = nullptr;
LottiePosition position = Point{0.0f, 0.0f};
LottiePoint size = Point{0.0f, 0.0f};
LottieFloat round = 0.0f;
};
struct LottiePolyStar : LottieShape
{
enum Type : uint8_t {Star = 1, Polygon};
void prepare()
{
LottieObject::type = LottieObject::Polystar;
if (position.frames || innerRadius.frames || outerRadius.frames || innerRoundness.frames || outerRoundness.frames || rotation.frames || ptsCnt.frames) statical = false;
}
LottiePosition position = Point{0.0f, 0.0f};
LottieFloat innerRadius = 0.0f;
LottieFloat outerRadius = 0.0f;
LottieFloat innerRoundness = 0.0f;
LottieFloat outerRoundness = 0.0f;
LottieFloat rotation = 0.0f;
LottieFloat ptsCnt = 0.0f;
Type type = Polygon;
};
struct LottieEllipse : LottieShape
{
void prepare()
{
LottieObject::type = LottieObject::Ellipse;
if (position.frames || size.frames) statical = false;
}
LottiePosition position = Point{0.0f, 0.0f};
LottiePoint size = Point{0.0f, 0.0f};
};
struct LottieTransform : LottieObject
{
struct SeparateCoord
{
LottieFloat x = 0.0f;
LottieFloat y = 0.0f;
};
~LottieTransform()
{
delete(coords);
}
void prepare()
{
LottieObject::type = LottieObject::Transform;
if (position.frames || rotation.frames || scale.frames || anchor.frames || opacity.frames) statical = false;
else if (coords && (coords->x.frames || coords->y.frames)) statical = false;
}
LottiePosition position = Point{0.0f, 0.0f};
LottieFloat rotation = 0.0f;
LottiePoint scale = Point{100.0f, 100.0f};
LottiePoint anchor = Point{0.0f, 0.0f};
LottieOpacity opacity = 255;
//either a position or separate coordinates
SeparateCoord* coords = nullptr;
};
struct LottieSolidStroke : LottieObject, LottieStroke
{
void prepare()
{
LottieObject::type = LottieObject::SolidStroke;
if (color.frames || opacity.frames || LottieStroke::dynamic()) statical = false;
}
LottieColor color = RGB24{255, 255, 255};
LottieOpacity opacity = 255;
bool disabled = false; //TODO: can't replace with hidden?
};
struct LottieSolidFill : LottieObject
{
void prepare()
{
LottieObject::type = LottieObject::SolidFill;
if (color.frames || opacity.frames) statical = false;
}
LottieColor color = RGB24{255, 255, 255};
LottieOpacity opacity = 255;
FillRule rule = FillRule::Winding;
bool disabled = false; //TODO: can't replace with hidden?
};
struct LottieGradientFill : LottieObject, LottieGradient
{
void prepare()
{
LottieObject::type = LottieObject::GradientFill;
if (LottieGradient::dynamic()) statical = false;
}
FillRule rule = FillRule::Winding;
};
struct LottieGradientStroke : LottieObject, LottieStroke, LottieGradient
{
void prepare()
{
LottieObject::type = LottieObject::GradientStroke;
if (LottieStroke::dynamic() || LottieGradient::dynamic()) statical = false;
}
};
struct LottieImage : LottieObject
{
Surface surface;
void prepare()
{
LottieObject::type = LottieObject::Image;
}
};
struct LottieGroup : LottieObject
{
virtual ~LottieGroup()
{
for (auto p = children.data; p < children.end(); ++p) delete(*p);
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;
}
}
virtual uint8_t opacity(int32_t frameNo)
{
if (children.empty()) return 0;
return (transform ? transform->opacity(frameNo) : 255);
}
Scene* scene = nullptr; //tvg render data
Array<LottieObject*> children;
LottieTransform* transform = nullptr;
};
struct LottieLayer : LottieGroup
{
enum Type : uint8_t {Precomp = 0, Solid, Image, Null, Shape, Text};
LottieLayer()
{
autoOrient = false;
mask = false;
}
~LottieLayer()
{
if (refId) {
//No need to free assets children because the Composition owns them.
children.clear();
free(refId);
}
}
void prepare()
{
LottieGroup::prepare(LottieObject::Layer);
/* if layer is hidden, only useulf data is its transform matrix.
so force it to be a Null Layer and release all resource. */
if (hidden) {
type = LottieLayer::Null;
children.reset();
return;
}
}
uint8_t opacity(int32_t frameNo) override
{
//return zero if the visibility is false.
if (frameNo < inFrame || frameNo > outFrame) return 0;
if (type == Null) return 255;
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);
}
RGB24 color = {255, 255, 255}; //Optimize: used for solidcolor
CompositeMethod matteType = CompositeMethod::None;
BlendMethod blendMethod = BlendMethod::Normal;
LottieLayer* parent = nullptr;
float timeStretch = 1.0f;
uint32_t w, h;
int32_t inFrame = 0;
int32_t outFrame = 0;
uint32_t startFrame = 0;
char* refId = nullptr; //pre-composition reference.
int16_t pid = -1; //id of the parent layer.
int16_t id = -1; //id of the current layer.
//cached data
struct {
Matrix matrix;
int32_t frameNo;
uint8_t opacity;
} cache;
Type type = Null;
bool autoOrient : 1;
bool mask : 1;
};
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);
}
}
float duration() const
{
return frameDuration() / frameRate; // in second
}
uint32_t frameAtPos(float pos) 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()));
}
uint32_t frameCnt() const
{
return endFrame - startFrame + 1;
}
long frameDuration() const
{
return endFrame - startFrame;
}
Scene* scene = nullptr; //tvg render data
LottieLayer* root = nullptr;
char* version = nullptr;
char* name = nullptr;
uint32_t w, h;
long startFrame, endFrame;
float frameRate;
Array<LottieObject*> assets;
Array<LottieInterpolator*> interpolators;
};
#endif //_TVG_LOTTIE_MODEL_H_

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,93 @@
/*
* 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.
*/
#ifndef _TVG_LOTTIE_PARSER_H_
#define _TVG_LOTTIE_PARSER_H_
#include "tvgCommon.h"
#include "tvgLottieParserHandler.h"
struct LottieParser : LookaheadParserHandler
{
public:
LottieParser(const char *str) : LookaheadParserHandler(str) {}
bool parse();
LottieComposition* comp = nullptr;
private:
BlendMethod getBlendMethod();
RGB24 getColor(const char *str);
CompositeMethod getMatteType();
FillRule getFillRule();
StrokeCap getStrokeCap();
StrokeJoin getStrokeJoin();
LottieInterpolator* getInterpolator(const char* key, Point& in, Point& out);
void getInperpolatorPoint(Point& pt);
void getPathSet(LottiePathSet& path);
void getValue(PathSet& path);
void getValue(Array<Point>& pts);
void getValue(ColorStop& color);
void getValue(float& val);
void getValue(uint8_t& val);
void getValue(Point& pt);
void getValue(RGB24& color);
template<typename T> bool parseTangent(const char *key, LottieVectorFrame<T>& value);
template<typename T> bool parseTangent(const char *key, LottieScalarFrame<T>& value);
template<typename T> void parseKeyFrame(T& prop);
template<typename T> void parsePropertyInternal(T& prop);
template<typename T> void parseProperty(T& prop);
LottieObject* parseObject();
LottieObject* parseAsset();
LottieImage* parseImage(const char* key);
LottieLayer* parseLayer();
LottieObject* parseGroup();
LottieRect* parseRect();
LottieEllipse* parseEllipse();
LottieSolidFill* parseSolidFill();
LottieTransform* parseTransform(bool ddd = false);
LottieSolidStroke* parseSolidStroke();
LottieGradientStroke* parseGradientStroke();
LottiePath* parsePath();
LottiePolyStar* parsePolyStar();
LottieRoundedCorner* parseRoundedCorner();
LottieGradientFill* parseGradientFill();
LottieLayer* parseLayers();
void parseObject(LottieGroup* parent);
void parseShapes(LottieLayer* layer);
void parseStrokeDash(LottieStroke* stroke);
void parseGradient(LottieGradient* gradient, const char* key);
void parseAssets();
//Current parsing context
struct Context {
LottieLayer* layer = nullptr;
LottieGradient* gradient = nullptr;
} *context;
};
#endif //_TVG_LOTTIE_PARSER_H_

View file

@ -0,0 +1,235 @@
/*
* 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.
*/
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd. 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.
*/
#include "tvgLottieParserHandler.h"
/************************************************************************/
/* Internal Class Implementation */
/************************************************************************/
static const int PARSE_FLAGS = kParseDefaultFlags | kParseInsituFlag;
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
bool LookaheadParserHandler::enterArray()
{
if (state != kEnteringArray) {
Error();
return false;
}
parseNext();
return true;
}
bool LookaheadParserHandler::nextArrayValue()
{
if (state == kExitingArray) {
parseNext();
return false;
}
//SPECIAL CASE: same as nextObjectKey()
if (state == kExitingObject) return false;
if (state == kError || state == kHasKey) {
Error();
return false;
}
return true;
}
int LookaheadParserHandler::getInt()
{
if (state != kHasNumber || !val.IsInt()) {
Error();
return 0;
}
auto result = val.GetInt();
parseNext();
return result;
}
float LookaheadParserHandler::getFloat()
{
if (state != kHasNumber) {
Error();
return 0;
}
auto result = val.GetFloat();
parseNext();
return result;
}
const char* LookaheadParserHandler::getString()
{
if (state != kHasString) {
Error();
return nullptr;
}
auto result = val.GetString();
parseNext();
return result;
}
char* LookaheadParserHandler::getStringCopy()
{
auto str = getString();
if (str) return strdup(str);
return nullptr;
}
bool LookaheadParserHandler::getBool()
{
if (state != kHasBool) {
Error();
return false;
}
auto result = val.GetBool();
parseNext();
return result;
}
void LookaheadParserHandler::getNull()
{
if (state != kHasNull) {
Error();
return;
}
parseNext();
}
bool LookaheadParserHandler::parseNext()
{
if (reader.HasParseError()) {
Error();
return false;
}
if (!reader.IterativeParseNext<PARSE_FLAGS>(iss, *this)) {
Error();
return false;
}
return true;
}
bool LookaheadParserHandler::enterObject()
{
if (state != kEnteringObject) {
Error();
return false;
}
parseNext();
return true;
}
int LookaheadParserHandler::peekType()
{
if (state >= kHasNull && state <= kHasKey) return val.GetType();
if (state == kEnteringArray) return kArrayType;
if (state == kEnteringObject) return kObjectType;
return -1;
}
void LookaheadParserHandler::skipOut(int depth)
{
do {
if (state == kEnteringArray || state == kEnteringObject) ++depth;
else if (state == kExitingArray || state == kExitingObject) --depth;
else if (state == kError) return;
parseNext();
} while (depth > 0);
}
const char* LookaheadParserHandler::nextObjectKey()
{
if (state == kHasKey) {
auto result = val.GetString();
parseNext();
return result;
}
/* SPECIAL CASE: The parser works with a prdefined rule that it will be only
while (nextObjectKey()) for each object but in case of our nested group
object we can call multiple time nextObjectKey() while exiting the object
so ignore those and don't put parser in the error state. */
if (state == kExitingArray || state == kEnteringObject) return nullptr;
if (state != kExitingObject) {
Error();
return nullptr;
}
parseNext();
return nullptr;
}
void LookaheadParserHandler::skip(const char* key)
{
if (key) TVGLOG("LOTTIE", "Skipped parsing value = %s", key);
if (peekType() == kArrayType) {
enterArray();
skipOut(1);
} else if (peekType() == kObjectType) {
enterObject();
skipOut(1);
} else {
skipOut(0);
}
}

View file

@ -0,0 +1,202 @@
/*
* 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.
*/
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd. 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.
*/
#ifndef _TVG_LOTTIE_PARSER_HANDLER_H_
#define _TVG_LOTTIE_PARSER_HANDLER_H_
#include "rapidjson/document.h"
#include "tvgCommon.h"
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(effc++)
using namespace rapidjson;
struct LookaheadParserHandler
{
enum LookaheadParsingState {
kInit = 0,
kError,
kHasNull,
kHasBool,
kHasNumber,
kHasString,
kHasKey,
kEnteringObject,
kExitingObject,
kEnteringArray,
kExitingArray
};
Value val;
LookaheadParsingState state = kInit;
Reader reader;
InsituStringStream iss;
LookaheadParserHandler(const char *str) : iss((char*)str)
{
reader.IterativeParseInit();
}
bool Null()
{
state = kHasNull;
val.SetNull();
return true;
}
bool Bool(bool b)
{
state = kHasBool;
val.SetBool(b);
return true;
}
bool Int(int i)
{
state = kHasNumber;
val.SetInt(i);
return true;
}
bool Uint(unsigned u)
{
state = kHasNumber;
val.SetUint(u);
return true;
}
bool Int64(int64_t i)
{
state = kHasNumber;
val.SetInt64(i);
return true;
}
bool Uint64(int64_t u)
{
state = kHasNumber;
val.SetUint64(u);
return true;
}
bool Double(double d)
{
state = kHasNumber;
val.SetDouble(d);
return true;
}
bool RawNumber(const char *, SizeType, TVG_UNUSED bool)
{
return false;
}
bool String(const char *str, SizeType length, TVG_UNUSED bool)
{
state = kHasString;
val.SetString(str, length);
return true;
}
bool StartObject()
{
state = kEnteringObject;
return true;
}
bool Key(const char *str, SizeType length, TVG_UNUSED bool)
{
state = kHasKey;
val.SetString(str, length);
return true;
}
bool EndObject(SizeType)
{
state = kExitingObject;
return true;
}
bool StartArray()
{
state = kEnteringArray;
return true;
}
bool EndArray(SizeType)
{
state = kExitingArray;
return true;
}
void Error()
{
TVGERR("LOTTIE", "Parsing Error!");
state = kError;
}
bool Invalid()
{
return state == kError;
}
bool enterObject();
bool enterArray();
bool nextArrayValue();
int getInt();
float getFloat();
const char* getString();
char* getStringCopy();
bool getBool();
void getNull();
bool parseNext();
const char* nextObjectKey();
void skip(const char* key);
void skipOut(int depth);
int peekType();
};
#endif //_TVG_LOTTIE_PARSER_HANDLER_H_

View file

@ -0,0 +1,449 @@
/*
* 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.
*/
#ifndef _TVG_LOTTIE_PROPERTY_H_
#define _TVG_LOTTIE_PROPERTY_H_
#include "tvgCommon.h"
#include "tvgArray.h"
#include "tvgMath.h"
#include "tvgBezier.h"
#include "tvgLottieInterpolator.h"
struct PathSet
{
Point* pts;
PathCommand* cmds;
uint16_t ptsCnt;
uint16_t cmdsCnt;
};
struct RGB24
{
int32_t rgb[3];
};
struct ColorStop
{
Fill::ColorStop* data;
};
static inline RGB24 operator-(const RGB24& lhs, const RGB24& rhs)
{
return {lhs.rgb[0] - rhs.rgb[0], lhs.rgb[1] - rhs.rgb[1], lhs.rgb[2] - rhs.rgb[2]};
}
static inline RGB24 operator+(const RGB24& lhs, const RGB24& rhs)
{
return {lhs.rgb[0] + rhs.rgb[0], lhs.rgb[1] + rhs.rgb[1], lhs.rgb[2] + rhs.rgb[2]};
}
static inline RGB24 operator*(const RGB24& lhs, float rhs)
{
return {(int32_t)lroundf(lhs.rgb[0] * rhs), (int32_t)lroundf(lhs.rgb[1] * rhs), (int32_t)lroundf(lhs.rgb[2] * rhs)};
}
static void copy(PathSet& pathset, Array<Point>& outPts)
{
Array<Point> inPts;
inPts.data = pathset.pts;
inPts.count = pathset.ptsCnt;
outPts.push(inPts);
inPts.data = nullptr;
}
static void copy(PathSet& pathset, Array<PathCommand>& outCmds)
{
Array<PathCommand> inCmds;
inCmds.data = pathset.cmds;
inCmds.count = pathset.cmdsCnt;
outCmds.push(inCmds);
inCmds.data = nullptr;
}
template<typename T>
struct LottieScalarFrame
{
T value; //keyframe value
int32_t no; //frame number
LottieInterpolator* interpolator;
T interpolate(LottieScalarFrame<T>* next, int32_t frameNo)
{
auto t = float(frameNo - no) / float(next->no - no);
if (interpolator) t = interpolator->progress(t);
return mathLerp(value, next->value, t);
}
};
template<typename T>
struct LottieVectorFrame
{
T value; //keyframe value
int32_t no; //frame number
LottieInterpolator* interpolator;
T outTangent, inTangent;
float length;
bool hasTangent = false;
T interpolate(LottieVectorFrame* next, int32_t frameNo)
{
auto t = float(frameNo - no) / float(next->no - no);
if (interpolator) t = interpolator->progress(t);
if (hasTangent) {
Bezier bz = {value, value + outTangent, next->value + inTangent, next->value};
t = bezAt(bz, t * length, length);
return bezPointAt(bz, t);
} else {
return mathLerp(value, next->value, t);
}
}
float angle(LottieVectorFrame* next, int32_t frameNo)
{
if (!hasTangent) return 0;
auto t = float(frameNo - no) / float(next->no - no);
if (interpolator) t = interpolator->progress(t);
Bezier bz = {value, value + outTangent, next->value + inTangent, next->value};
t = bezAt(bz, t * length, length);
return -bezAngleAt(bz, t);
}
void prepare(LottieVectorFrame* next)
{
Bezier bz = {value, value + outTangent, next->value + inTangent, next->value};
length = bezLength(bz);
}
};
template<typename T>
struct LottieProperty
{
//Property has an either keyframes or single value.
Array<LottieScalarFrame<T>>* frames = nullptr;
T value;
LottieProperty(T v) : value(v) {}
~LottieProperty()
{
delete(frames);
}
LottieScalarFrame<T>& newFrame()
{
if (!frames) frames = new Array<LottieScalarFrame<T>>;
if (frames->count + 1 >= frames->reserved) {
auto old = frames->reserved;
frames->grow(frames->count + 2);
memset((void*)(frames->data + old), 0x00, sizeof(LottieScalarFrame<T>) * (frames->reserved - old));
}
++frames->count;
return frames->last();
}
LottieScalarFrame<T>& nextFrame()
{
return frames->data[frames->count];
}
T operator()(int32_t frameNo)
{
if (!frames) return value;
if (frames->count == 1 || frameNo <= frames->first().no) return frames->first().value;
if (frameNo >= frames->last().no) return frames->last().value;
for (auto frame = frames->data + 1; frame < frames->end(); ++frame) {
if (frameNo > frame->no) continue;
if (frameNo == frame->no) return frame->value;
return (frame - 1)->interpolate(frame, frameNo);
}
return value;
}
float angle(int32_t frameNo) { return 0; }
void prepare() {}
};
struct LottiePathSet
{
Array<LottieScalarFrame<PathSet>>* frames = nullptr;
PathSet value;
LottiePathSet(PathSet v) : value(v)
{
}
~LottiePathSet()
{
free(value.cmds);
free(value.pts);
if (!frames) return;
for (auto p = frames->data; p < frames->end(); ++p) {
free((*p).value.cmds);
free((*p).value.pts);
}
free(frames->data);
free(frames);
}
LottieScalarFrame<PathSet>& newFrame()
{
if (!frames) {
frames = static_cast<Array<LottieScalarFrame<PathSet>>*>(calloc(1, sizeof(Array<LottieScalarFrame<PathSet>>)));
}
if (frames->count + 1 >= frames->reserved) {
auto old = frames->reserved;
frames->grow(frames->count + 2);
memset((void*)(frames->data + old), 0x00, sizeof(LottieScalarFrame<PathSet>) * (frames->reserved - old));
}
++frames->count;
return frames->last();
}
LottieScalarFrame<PathSet>& nextFrame()
{
return frames->data[frames->count];
}
bool operator()(int32_t frameNo, Array<PathCommand>& cmds, Array<Point>& pts)
{
if (!frames) {
copy(value, cmds);
copy(value, pts);
return true;
}
if (frames->count == 1 || frameNo <= frames->first().no) {
copy(frames->first().value, cmds);
copy(frames->first().value, pts);
return true;
}
if (frameNo >= frames->last().no) {
copy(frames->last().value, cmds);
copy(frames->last().value, pts);
return true;
}
for (auto frame = frames->data + 1; frame < frames->end(); ++frame) {
if (frameNo > frame->no) continue;
if (frameNo == frame->no) {
copy(frame->value, cmds);
copy(frame->value, pts);
return true;
}
//interpolate
auto pframe = frame - 1;
copy(pframe->value, cmds);
auto t = float(frameNo - pframe->no) / float(frame->no - pframe->no);
if (pframe->interpolator) t = pframe->interpolator->progress(t);
auto s = pframe->value.pts;
auto e = frame->value.pts;
for (auto i = 0; i < pframe->value.ptsCnt; ++i, ++s, ++e) {
pts.push(mathLerp(*s, *e, t));
}
return true;
}
return false;
}
void prepare() {}
};
struct LottieColorStop
{
Array<LottieScalarFrame<ColorStop>>* frames = nullptr;
ColorStop value;
uint16_t count = 0; //colorstop count
~LottieColorStop()
{
free(value.data);
if (!frames) return;
for (auto p = frames->data; p < frames->end(); ++p) {
free((*p).value.data);
}
free(frames->data);
free(frames);
}
LottieScalarFrame<ColorStop>& newFrame()
{
if (!frames) {
frames = static_cast<Array<LottieScalarFrame<ColorStop>>*>(calloc(1, sizeof(Array<LottieScalarFrame<ColorStop>>)));
}
if (frames->count + 1 >= frames->reserved) {
auto old = frames->reserved;
frames->grow(frames->count + 2);
memset((void*)(frames->data + old), 0x00, sizeof(LottieScalarFrame<ColorStop>) * (frames->reserved - old));
}
++frames->count;
return frames->last();
}
LottieScalarFrame<ColorStop>& nextFrame()
{
return frames->data[frames->count];
}
void operator()(int32_t frameNo, Fill* fill)
{
if (!frames) {
fill->colorStops(value.data, count);
return;
}
if (frames->count == 1 || frameNo <= frames->first().no) {
fill->colorStops(frames->first().value.data, count);
return;
}
if (frameNo >= frames->last().no) {
fill->colorStops(frames->last().value.data, count);
return;
}
for (auto frame = frames->data + 1; frame < frames->end(); ++frame) {
if (frameNo > frame->no) continue;
if (frameNo == frame->no) {
fill->colorStops(frame->value.data, count);
return;
}
//interpolate
auto pframe = frame - 1;
auto t = float(frameNo - pframe->no) / float(frame->no - pframe->no);
if (pframe->interpolator) t = pframe->interpolator->progress(t);
auto s = pframe->value.data;
auto e = frame->value.data;
Array<Fill::ColorStop> result;
for (auto i = 0; i < count; ++i, ++s, ++e) {
auto offset = mathLerp(s->offset, e->offset, t);
auto r = mathLerp(s->r, e->r, t);
auto g = mathLerp(s->g, e->g, t);
auto b = mathLerp(s->b, e->b, t);
result.push({offset, r, g, b, 255});
}
fill->colorStops(result.data, count);
return;
}
}
void prepare() {}
};
struct LottiePosition
{
Array<LottieVectorFrame<Point>>* frames = nullptr;
Point value;
LottiePosition(Point v) : value(v)
{
}
~LottiePosition()
{
delete(frames);
}
LottieVectorFrame<Point>& newFrame()
{
if (!frames) frames = new Array<LottieVectorFrame<Point>>;
if (frames->count + 1 >= frames->reserved) {
auto old = frames->reserved;
frames->grow(frames->count + 2);
memset((void*)(frames->data + old), 0x00, sizeof(LottieVectorFrame<Point>) * (frames->reserved - old));
}
++frames->count;
return frames->last();
}
LottieVectorFrame<Point>& nextFrame()
{
return frames->data[frames->count];
}
Point operator()(int32_t frameNo)
{
if (!frames) return value;
if (frames->count == 1 || frameNo <= frames->first().no) return frames->first().value;
if (frameNo >= frames->last().no) return frames->last().value;
for (auto frame = frames->data + 1; frame < frames->end(); ++frame) {
if (frameNo > frame->no) continue;
if (frameNo == frame->no) return frame->value;
return (frame - 1)->interpolate(frame, frameNo);
}
return value;
}
float angle(int32_t frameNo)
{
if (!frames) return 0;
if (frames->count == 1 || frameNo <= frames->first().no) return 0;
if (frameNo >= frames->last().no) return 0;
for (auto frame = frames->data + 1; frame < frames->end(); ++frame) {
if (frameNo > frame->no) continue;
return (frame - 1)->angle(frame, frameNo);
}
return 0;
}
void prepare()
{
if (!frames || frames->count < 2) return;
for (auto frame = frames->data + 1; frame < frames->end(); ++frame) {
(frame - 1)->prepare(frame);
}
}
};
using LottiePoint = LottieProperty<Point>;
using LottieFloat = LottieProperty<float>;
using LottieOpacity = LottieProperty<uint8_t>;
using LottieColor = LottieProperty<RGB24>;
#endif //_TVG_LOTTIE_PROPERTY_H_