matterbridge/vendor/github.com/Benau/go_rlottie/lottie_lottiemodel.h

1149 lines
35 KiB
C
Raw Permalink Normal View History

/*
* 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 LOTModel_H
#define LOTModel_H
#include <algorithm>
#include <cmath>
#include <cstring>
#include <functional>
#include <memory>
#include <unordered_map>
#include <vector>
#include "vector_varenaalloc.h"
#include "vector_vbezier.h"
#include "vector_vbrush.h"
#include "vector_vinterpolator.h"
#include "vector_vmatrix.h"
#include "vector_vpath.h"
#include "vector_vpoint.h"
#include "vector_vrect.h"
V_USE_NAMESPACE
namespace rlottie {
namespace internal {
using Marker = std::tuple<std::string, int, int>;
using LayerInfo = Marker;
template <typename T>
inline T lerp(const T &start, const T &end, float t)
{
return start + t * (end - start);
}
namespace model {
enum class MatteType : uchar { None = 0, Alpha = 1, AlphaInv, Luma, LumaInv };
enum class BlendMode : uchar {
Normal = 0,
Multiply = 1,
Screen = 2,
OverLay = 3
};
class Color {
public:
Color() = default;
Color(float red, float green, float blue) : r(red), g(green), b(blue) {}
VColor toColor(float a = 1)
{
return VColor(uchar(255 * r), uchar(255 * g), uchar(255 * b),
uchar(255 * a));
}
friend inline Color operator+(const Color &c1, const Color &c2);
friend inline Color operator-(const Color &c1, const Color &c2);
public:
float r{1};
float g{1};
float b{1};
};
inline Color operator-(const Color &c1, const Color &c2)
{
return Color(c1.r - c2.r, c1.g - c2.g, c1.b - c2.b);
}
inline Color operator+(const Color &c1, const Color &c2)
{
return Color(c1.r + c2.r, c1.g + c2.g, c1.b + c2.b);
}
inline const Color operator*(const Color &c, float m)
{
return Color(c.r * m, c.g * m, c.b * m);
}
inline const Color operator*(float m, const Color &c)
{
return Color(c.r * m, c.g * m, c.b * m);
}
struct PathData {
std::vector<VPointF> mPoints;
bool mClosed = false; /* "c" */
void reserve(size_t size) { mPoints.reserve(mPoints.size() + size); }
static void lerp(const PathData &start, const PathData &end, float t,
VPath &result)
{
result.reset();
// test for empty animation data.
if (start.mPoints.empty() || end.mPoints.empty())
{
return;
}
auto size = std::min(start.mPoints.size(), end.mPoints.size());
/* reserve exact memory requirement at once
* ptSize = size + 1(size + close)
* elmSize = size/3 cubic + 1 move + 1 close
*/
result.reserve(size + 1, size / 3 + 2);
result.moveTo(start.mPoints[0] +
t * (end.mPoints[0] - start.mPoints[0]));
for (size_t i = 1; i < size; i += 3) {
result.cubicTo(
start.mPoints[i] + t * (end.mPoints[i] - start.mPoints[i]),
start.mPoints[i + 1] +
t * (end.mPoints[i + 1] - start.mPoints[i + 1]),
start.mPoints[i + 2] +
t * (end.mPoints[i + 2] - start.mPoints[i + 2]));
}
if (start.mClosed) result.close();
}
void toPath(VPath &path) const
{
path.reset();
if (mPoints.empty()) return;
auto size = mPoints.size();
auto points = mPoints.data();
/* reserve exact memory requirement at once
* ptSize = size + 1(size + close)
* elmSize = size/3 cubic + 1 move + 1 close
*/
path.reserve(size + 1, size / 3 + 2);
path.moveTo(points[0]);
for (size_t i = 1; i < size; i += 3) {
path.cubicTo(points[i], points[i + 1], points[i + 2]);
}
if (mClosed) path.close();
}
};
template <typename T, typename Tag = void>
struct Value {
T start_;
T end_;
T at(float t) const { return lerp(start_, end_, t); }
float angle(float) const { return 0; }
void cache() {}
};
struct Position;
template <typename T>
struct Value<T, Position> {
T start_;
T end_;
T inTangent_;
T outTangent_;
float length_{0};
bool hasTangent_{false};
void cache()
{
if (hasTangent_) {
inTangent_ = end_ + inTangent_;
outTangent_ = start_ + outTangent_;
length_ = VBezier::fromPoints(start_, outTangent_, inTangent_, end_)
.length();
if (vIsZero(length_)) {
// this segment has zero length.
// so disable expensive path computaion.
hasTangent_ = false;
}
}
}
T at(float t) const
{
if (hasTangent_) {
/*
* position along the path calcualated
* using bezier at progress length (t * bezlen)
*/
VBezier b =
VBezier::fromPoints(start_, outTangent_, inTangent_, end_);
return b.pointAt(b.tAtLength(t * length_, length_));
}
return lerp(start_, end_, t);
}
float angle(float t) const
{
if (hasTangent_) {
VBezier b =
VBezier::fromPoints(start_, outTangent_, inTangent_, end_);
return b.angleAt(b.tAtLength(t * length_, length_));
}
return 0;
}
};
template <typename T, typename Tag>
class KeyFrames {
public:
struct Frame {
float progress(int frameNo) const
{
return interpolator_ ? interpolator_->value((frameNo - start_) /
(end_ - start_))
: 0;
}
T value(int frameNo) const { return value_.at(progress(frameNo)); }
float angle(int frameNo) const
{
return value_.angle(progress(frameNo));
}
float start_{0};
float end_{0};
VInterpolator *interpolator_{nullptr};
Value<T, Tag> value_;
};
T value(int frameNo) const
{
if (frames_.front().start_ >= frameNo)
return frames_.front().value_.start_;
if (frames_.back().end_ <= frameNo) return frames_.back().value_.end_;
for (const auto &keyFrame : frames_) {
if (frameNo >= keyFrame.start_ && frameNo < keyFrame.end_)
return keyFrame.value(frameNo);
}
return {};
}
float angle(int frameNo) const
{
if ((frames_.front().start_ >= frameNo) ||
(frames_.back().end_ <= frameNo))
return 0;
for (const auto &frame : frames_) {
if (frameNo >= frame.start_ && frameNo < frame.end_)
return frame.angle(frameNo);
}
return 0;
}
bool changed(int prevFrame, int curFrame) const
{
auto first = frames_.front().start_;
auto last = frames_.back().end_;
return !((first > prevFrame && first > curFrame) ||
(last < prevFrame && last < curFrame));
}
void cache()
{
for (auto &e : frames_) e.value_.cache();
}
public:
std::vector<Frame> frames_;
};
template <typename T, typename Tag = void>
class Property {
public:
using Animation = KeyFrames<T, Tag>;
Property() { construct(impl_.value_, {}); }
explicit Property(T value) { construct(impl_.value_, std::move(value)); }
const Animation &animation() const { return *(impl_.animation_.get()); }
const T & value() const { return impl_.value_; }
Animation &animation()
{
if (isValue_) {
destroy();
construct(impl_.animation_, std::make_unique<Animation>());
isValue_ = false;
}
return *(impl_.animation_.get());
}
T &value()
{
assert(isValue_);
return impl_.value_;
}
Property(Property &&other) noexcept
{
if (!other.isValue_) {
construct(impl_.animation_, std::move(other.impl_.animation_));
isValue_ = false;
} else {
construct(impl_.value_, std::move(other.impl_.value_));
isValue_ = true;
}
}
// delete special member functions
Property(const Property &) = delete;
Property &operator=(const Property &) = delete;
Property &operator=(Property &&) = delete;
~Property() { destroy(); }
bool isStatic() const { return isValue_; }
T value(int frameNo) const
{
return isStatic() ? value() : animation().value(frameNo);
}
// special function only for type T=PathData
template <typename forT = PathData>
auto value(int frameNo, VPath &path) const ->
typename std::enable_if_t<std::is_same<T, forT>::value, void>
{
if (isStatic()) {
value().toPath(path);
} else {
const auto &vec = animation().frames_;
if (vec.front().start_ >= frameNo)
return vec.front().value_.start_.toPath(path);
if (vec.back().end_ <= frameNo)
return vec.back().value_.end_.toPath(path);
for (const auto &keyFrame : vec) {
if (frameNo >= keyFrame.start_ && frameNo < keyFrame.end_) {
T::lerp(keyFrame.value_.start_, keyFrame.value_.end_,
keyFrame.progress(frameNo), path);
}
}
}
}
float angle(int frameNo) const
{
return isStatic() ? 0 : animation().angle(frameNo);
}
bool changed(int prevFrame, int curFrame) const
{
return isStatic() ? false : animation().changed(prevFrame, curFrame);
}
void cache()
{
if (!isStatic()) animation().cache();
}
private:
template <typename Tp>
void construct(Tp &member, Tp &&val)
{
new (&member) Tp(std::move(val));
}
void destroy()
{
if (isValue_) {
impl_.value_.~T();
} else {
using std::unique_ptr;
impl_.animation_.~unique_ptr<Animation>();
}
}
union details {
std::unique_ptr<Animation> animation_;
T value_;
details(){};
details(const details &) = delete;
details(details &&) = delete;
details &operator=(details &&) = delete;
details &operator=(const details &) = delete;
~details() noexcept {};
} impl_;
bool isValue_{true};
};
class Path;
struct PathData;
struct Dash {
std::vector<Property<float>> mData;
bool empty() const { return mData.empty(); }
size_t size() const { return mData.size(); }
bool isStatic() const
{
for (const auto &elm : mData)
if (!elm.isStatic()) return false;
return true;
}
void getDashInfo(int frameNo, std::vector<float> &result) const;
};
class Mask {
public:
enum class Mode { None, Add, Substarct, Intersect, Difference };
float opacity(int frameNo) const
{
return mOpacity.value(frameNo) / 100.0f;
}
bool isStatic() const { return mIsStatic; }
public:
Property<PathData> mShape;
Property<float> mOpacity{100};
bool mInv{false};
bool mIsStatic{true};
Mask::Mode mMode;
};
class Object {
public:
enum class Type : unsigned char {
Composition = 1,
Layer,
Group,
Transform,
Fill,
Stroke,
GFill,
GStroke,
Rect,
Ellipse,
Path,
Polystar,
Trim,
Repeater,
RoundedCorner
};
explicit Object(Object::Type type) : mPtr(nullptr)
{
mData._type = type;
mData._static = true;
mData._shortString = true;
mData._hidden = false;
}
~Object() noexcept
{
if (!shortString() && mPtr) free(mPtr);
}
Object(const Object &) = delete;
Object &operator=(const Object &) = delete;
void setStatic(bool value) { mData._static = value; }
bool isStatic() const { return mData._static; }
bool hidden() const { return mData._hidden; }
void setHidden(bool value) { mData._hidden = value; }
void setType(Object::Type type) { mData._type = type; }
Object::Type type() const { return mData._type; }
void setName(const char *name)
{
if (name) {
auto len = strlen(name);
if (len < maxShortStringLength) {
setShortString(true);
strncpy(mData._buffer, name, len + 1);
} else {
setShortString(false);
mPtr = strdup(name);
}
}
}
const char *name() const { return shortString() ? mData._buffer : mPtr; }
private:
static constexpr unsigned char maxShortStringLength = 14;
void setShortString(bool value) { mData._shortString = value; }
bool shortString() const { return mData._shortString; }
struct Data {
char _buffer[maxShortStringLength];
Object::Type _type;
bool _static : 1;
bool _hidden : 1;
bool _shortString : 1;
};
union {
Data mData;
char *mPtr{nullptr};
};
};
struct Asset {
enum class Type : unsigned char { Precomp, Image, Char };
bool isStatic() const { return mStatic; }
void setStatic(bool value) { mStatic = value; }
VBitmap bitmap() const { return mBitmap; }
void loadImageData(std::string data);
void loadImagePath(std::string Path);
Type mAssetType{Type::Precomp};
bool mStatic{true};
std::string mRefId; // ref id
std::vector<Object *> mLayers;
// image asset data
int mWidth{0};
int mHeight{0};
VBitmap mBitmap;
};
class Layer;
class Composition : public Object {
public:
Composition() : Object(Object::Type::Composition) {}
std::vector<LayerInfo> layerInfoList() const;
const std::vector<Marker> &markers() const { return mMarkers; }
double duration() const
{
return frameDuration() / frameRate(); // in second
}
size_t frameAtPos(double pos) const
{
if (pos < 0) pos = 0;
if (pos > 1) pos = 1;
return size_t(round(pos * frameDuration()));
}
long frameAtTime(double timeInSec) const
{
return long(frameAtPos(timeInSec / duration()));
}
size_t totalFrame() const { return mEndFrame - mStartFrame; }
long frameDuration() const { return mEndFrame - mStartFrame - 1; }
float frameRate() const { return mFrameRate; }
size_t startFrame() const { return mStartFrame; }
size_t endFrame() const { return mEndFrame; }
VSize size() const { return mSize; }
void processRepeaterObjects();
void updateStats();
public:
struct Stats {
uint16_t precompLayerCount{0};
uint16_t solidLayerCount{0};
uint16_t shapeLayerCount{0};
uint16_t imageLayerCount{0};
uint16_t nullLayerCount{0};
};
public:
std::string mVersion;
VSize mSize;
long mStartFrame{0};
long mEndFrame{0};
float mFrameRate{60};
BlendMode mBlendMode{BlendMode::Normal};
Layer * mRootLayer{nullptr};
std::unordered_map<std::string, Asset *> mAssets;
std::vector<Marker> mMarkers;
VArenaAlloc mArenaAlloc{2048};
Stats mStats;
};
class Transform : public Object {
public:
struct Data {
struct Extra {
Property<float> m3DRx{0};
Property<float> m3DRy{0};
Property<float> m3DRz{0};
Property<float> mSeparateX{0};
Property<float> mSeparateY{0};
bool mSeparate{false};
bool m3DData{false};
};
VMatrix matrix(int frameNo, bool autoOrient = false) const;
float opacity(int frameNo) const
{
return mOpacity.value(frameNo) / 100.0f;
}
void createExtraData()
{
if (!mExtra) mExtra = std::make_unique<Extra>();
}
Property<float> mRotation{0}; /* "r" */
Property<VPointF> mScale{{100, 100}}; /* "s" */
Property<VPointF, Position> mPosition; /* "p" */
Property<VPointF> mAnchor; /* "a" */
Property<float> mOpacity{100}; /* "o" */
std::unique_ptr<Extra> mExtra;
};
Transform() : Object(Object::Type::Transform) {}
void set(Transform::Data *data, bool staticFlag)
{
setStatic(staticFlag);
if (isStatic()) {
new (&impl.mStaticData)
StaticData(data->matrix(0), data->opacity(0));
} else {
impl.mData = data;
}
}
VMatrix matrix(int frameNo, bool autoOrient = false) const
{
if (isStatic()) return impl.mStaticData.mMatrix;
return impl.mData->matrix(frameNo, autoOrient);
}
float opacity(int frameNo) const
{
if (isStatic()) return impl.mStaticData.mOpacity;
return impl.mData->opacity(frameNo);
}
Transform(const Transform &) = delete;
Transform(Transform &&) = delete;
Transform &operator=(Transform &) = delete;
Transform &operator=(Transform &&) = delete;
~Transform() noexcept { destroy(); }
private:
void destroy()
{
if (isStatic()) {
impl.mStaticData.~StaticData();
}
}
struct StaticData {
StaticData(VMatrix &&m, float opacity)
: mOpacity(opacity), mMatrix(std::move(m))
{
}
float mOpacity;
VMatrix mMatrix;
};
union details {
Data * mData{nullptr};
StaticData mStaticData;
details(){};
details(const details &) = delete;
details(details &&) = delete;
details &operator=(details &&) = delete;
details &operator=(const details &) = delete;
~details() noexcept {};
} impl;
};
class Group : public Object {
public:
Group() : Object(Object::Type::Group) {}
explicit Group(Object::Type type) : Object(type) {}
public:
std::vector<Object *> mChildren;
Transform * mTransform{nullptr};
};
class Layer : public Group {
public:
enum class Type : uchar {
Precomp = 0,
Solid = 1,
Image = 2,
Null = 3,
Shape = 4,
Text = 5
};
Layer() : Group(Object::Type::Layer) {}
bool hasRoundedCorner() const noexcept { return mHasRoundedCorner; }
bool hasPathOperator() const noexcept { return mHasPathOperator; }
bool hasGradient() const noexcept { return mHasGradient; }
bool hasMask() const noexcept { return mHasMask; }
bool hasRepeater() const noexcept { return mHasRepeater; }
int id() const noexcept { return mId; }
int parentId() const noexcept { return mParentId; }
bool hasParent() const noexcept { return mParentId != -1; }
int inFrame() const noexcept { return mInFrame; }
int outFrame() const noexcept { return mOutFrame; }
int startFrame() const noexcept { return mStartFrame; }
Color solidColor() const noexcept
{
return mExtra ? mExtra->mSolidColor : Color();
}
bool autoOrient() const noexcept { return mAutoOrient; }
int timeRemap(int frameNo) const;
VSize layerSize() const { return mLayerSize; }
bool precompLayer() const { return mLayerType == Type::Precomp; }
VMatrix matrix(int frameNo) const
{
return mTransform ? mTransform->matrix(frameNo, autoOrient())
: VMatrix{};
}
float opacity(int frameNo) const
{
return mTransform ? mTransform->opacity(frameNo) : 1.0f;
}
Asset *asset() const { return mExtra ? mExtra->mAsset : nullptr; }
struct Extra {
Color mSolidColor;
std::string mPreCompRefId;
Property<float> mTimeRemap; /* "tm" */
Composition * mCompRef{nullptr};
Asset * mAsset{nullptr};
std::vector<Mask *> mMasks;
};
Layer::Extra *extra()
{
if (!mExtra) mExtra = std::make_unique<Layer::Extra>();
return mExtra.get();
}
public:
MatteType mMatteType{MatteType::None};
Type mLayerType{Layer::Type::Null};
BlendMode mBlendMode{BlendMode::Normal};
bool mHasRoundedCorner{false};
bool mHasPathOperator{false};
bool mHasMask{false};
bool mHasRepeater{false};
bool mHasGradient{false};
bool mAutoOrient{false};
VSize mLayerSize;
int mParentId{-1}; // Lottie the id of the parent in the composition
int mId{-1}; // Lottie the group id used for parenting.
float mTimeStreatch{1.0f};
int mInFrame{0};
int mOutFrame{0};
int mStartFrame{0};
std::unique_ptr<Extra> mExtra{nullptr};
};
/**
* TimeRemap 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.
*/
inline int Layer::timeRemap(int frameNo) const
{
/*
* only consider startFrame() when there is no timeRemap.
* when a layer has timeremap bodymovin updates the startFrame()
* of all child layer so we don't have to take care of it.
*/
if (!mExtra || mExtra->mTimeRemap.isStatic())
frameNo = frameNo - startFrame();
else
frameNo =
mExtra->mCompRef->frameAtTime(mExtra->mTimeRemap.value(frameNo));
/* Apply time streatch if it has any.
* Time streatch is just a factor by which the animation will speedup or
* slow down with respect to the overal animation. Time streach factor is
* already applied to the layers inFrame and outFrame.
* @TODO need to find out if timestreatch also affects the in and out frame
* of the child layers or not. */
return int(frameNo / mTimeStreatch);
}
class Stroke : public Object {
public:
Stroke() : Object(Object::Type::Stroke) {}
Color color(int frameNo) const { return mColor.value(frameNo); }
float opacity(int frameNo) const
{
return mOpacity.value(frameNo) / 100.0f;
}
float strokeWidth(int frameNo) const { return mWidth.value(frameNo); }
CapStyle capStyle() const { return mCapStyle; }
JoinStyle joinStyle() const { return mJoinStyle; }
float miterLimit() const { return mMiterLimit; }
bool hasDashInfo() const { return !mDash.empty(); }
void getDashInfo(int frameNo, std::vector<float> &result) const
{
return mDash.getDashInfo(frameNo, result);
}
public:
Property<Color> mColor; /* "c" */
Property<float> mOpacity{100}; /* "o" */
Property<float> mWidth{0}; /* "w" */
CapStyle mCapStyle{CapStyle::Flat}; /* "lc" */
JoinStyle mJoinStyle{JoinStyle::Miter}; /* "lj" */
float mMiterLimit{0}; /* "ml" */
Dash mDash;
bool mEnabled{true}; /* "fillEnabled" */
};
class Gradient : public Object {
public:
class Data {
public:
friend inline Gradient::Data operator+(const Gradient::Data &g1,
const Gradient::Data &g2);
friend inline Gradient::Data operator-(const Gradient::Data &g1,
const Gradient::Data &g2);
friend inline Gradient::Data operator*(float m,
const Gradient::Data &g);
public:
std::vector<float> mGradient;
};
explicit Gradient(Object::Type type) : Object(type) {}
inline float opacity(int frameNo) const
{
return mOpacity.value(frameNo) / 100.0f;
}
void update(std::unique_ptr<VGradient> &grad, int frameNo);
private:
void populate(VGradientStops &stops, int frameNo);
public:
int mGradientType{1}; /* "t" Linear=1 , Radial = 2*/
Property<VPointF> mStartPoint; /* "s" */
Property<VPointF> mEndPoint; /* "e" */
Property<float> mHighlightLength{0}; /* "h" */
Property<float> mHighlightAngle{0}; /* "a" */
Property<float> mOpacity{100}; /* "o" */
Property<Gradient::Data> mGradient; /* "g" */
int mColorPoints{-1};
bool mEnabled{true}; /* "fillEnabled" */
};
class GradientStroke : public Gradient {
public:
GradientStroke() : Gradient(Object::Type::GStroke) {}
float width(int frameNo) const { return mWidth.value(frameNo); }
CapStyle capStyle() const { return mCapStyle; }
JoinStyle joinStyle() const { return mJoinStyle; }
float miterLimit() const { return mMiterLimit; }
bool hasDashInfo() const { return !mDash.empty(); }
void getDashInfo(int frameNo, std::vector<float> &result) const
{
return mDash.getDashInfo(frameNo, result);
}
public:
Property<float> mWidth; /* "w" */
CapStyle mCapStyle{CapStyle::Flat}; /* "lc" */
JoinStyle mJoinStyle{JoinStyle::Miter}; /* "lj" */
float mMiterLimit{0}; /* "ml" */
Dash mDash;
};
class GradientFill : public Gradient {
public:
GradientFill() : Gradient(Object::Type::GFill) {}
FillRule fillRule() const { return mFillRule; }
public:
FillRule mFillRule{FillRule::Winding}; /* "r" */
};
class Fill : public Object {
public:
Fill() : Object(Object::Type::Fill) {}
Color color(int frameNo) const { return mColor.value(frameNo); }
float opacity(int frameNo) const
{
return mOpacity.value(frameNo) / 100.0f;
}
FillRule fillRule() const { return mFillRule; }
public:
FillRule mFillRule{FillRule::Winding}; /* "r" */
bool mEnabled{true}; /* "fillEnabled" */
Property<Color> mColor; /* "c" */
Property<float> mOpacity{100}; /* "o" */
};
class Shape : public Object {
public:
explicit Shape(Object::Type type) : Object(type) {}
VPath::Direction direction()
{
return (mDirection == 3) ? VPath::Direction::CCW : VPath::Direction::CW;
}
public:
int mDirection{1};
};
class Path : public Shape {
public:
Path() : Shape(Object::Type::Path) {}
public:
Property<PathData> mShape;
};
class RoundedCorner : public Object {
public:
RoundedCorner() : Object(Object::Type::RoundedCorner) {}
float radius(int frameNo) const { return mRadius.value(frameNo);}
public:
Property<float> mRadius{0};
};
class Rect : public Shape {
public:
Rect() : Shape(Object::Type::Rect) {}
float roundness(int frameNo)
{
return mRoundedCorner ? mRoundedCorner->radius(frameNo) :
mRound.value(frameNo);
}
bool roundnessChanged(int prevFrame, int curFrame)
{
return mRoundedCorner ? mRoundedCorner->mRadius.changed(prevFrame, curFrame) :
mRound.changed(prevFrame, curFrame);
}
public:
RoundedCorner* mRoundedCorner{nullptr};
Property<VPointF> mPos;
Property<VPointF> mSize;
Property<float> mRound{0};
};
class Ellipse : public Shape {
public:
Ellipse() : Shape(Object::Type::Ellipse) {}
public:
Property<VPointF> mPos;
Property<VPointF> mSize;
};
class Polystar : public Shape {
public:
enum class PolyType { Star = 1, Polygon = 2 };
Polystar() : Shape(Object::Type::Polystar) {}
public:
Polystar::PolyType mPolyType{PolyType::Polygon};
Property<VPointF> mPos;
Property<float> mPointCount{0};
Property<float> mInnerRadius{0};
Property<float> mOuterRadius{0};
Property<float> mInnerRoundness{0};
Property<float> mOuterRoundness{0};
Property<float> mRotation{0};
};
class Repeater : public Object {
public:
struct Transform {
VMatrix matrix(int frameNo, float multiplier) const;
float startOpacity(int frameNo) const
{
return mStartOpacity.value(frameNo) / 100;
}
float endOpacity(int frameNo) const
{
return mEndOpacity.value(frameNo) / 100;
}
bool isStatic() const
{
return mRotation.isStatic() && mScale.isStatic() &&
mPosition.isStatic() && mAnchor.isStatic() &&
mStartOpacity.isStatic() && mEndOpacity.isStatic();
}
Property<float> mRotation{0}; /* "r" */
Property<VPointF> mScale{{100, 100}}; /* "s" */
Property<VPointF> mPosition; /* "p" */
Property<VPointF> mAnchor; /* "a" */
Property<float> mStartOpacity{100}; /* "so" */
Property<float> mEndOpacity{100}; /* "eo" */
};
Repeater() : Object(Object::Type::Repeater) {}
Group *content() const { return mContent ? mContent : nullptr; }
void setContent(Group *content) { mContent = content; }
int maxCopies() const { return int(mMaxCopies); }
float copies(int frameNo) const { return mCopies.value(frameNo); }
float offset(int frameNo) const { return mOffset.value(frameNo); }
bool processed() const { return mProcessed; }
void markProcessed() { mProcessed = true; }
public:
Group * mContent{nullptr};
Transform mTransform;
Property<float> mCopies{0};
Property<float> mOffset{0};
float mMaxCopies{0.0};
bool mProcessed{false};
};
class Trim : public Object {
public:
struct Segment {
float start{0};
float end{0};
Segment() = default;
explicit Segment(float s, float e) : start(s), end(e) {}
};
enum class TrimType { Simultaneously, Individually };
Trim() : Object(Object::Type::Trim) {}
/*
* if start > end vector trims the path as a loop ( 2 segment)
* if start < end vector trims the path without loop ( 1 segment).
* if no offset then there is no loop.
*/
Segment segment(int frameNo) const
{
float start = mStart.value(frameNo) / 100.0f;
float end = mEnd.value(frameNo) / 100.0f;
float offset = std::fmod(mOffset.value(frameNo), 360.0f) / 360.0f;
float diff = std::abs(start - end);
if (vCompare(diff, 0.0f)) return Segment(0, 0);
if (vCompare(diff, 1.0f)) return Segment(0, 1);
if (offset > 0) {
start += offset;
end += offset;
if (start <= 1 && end <= 1) {
return noloop(start, end);
} else if (start > 1 && end > 1) {
return noloop(start - 1, end - 1);
} else {
return (start > 1) ? loop(start - 1, end)
: loop(start, end - 1);
}
} else {
start += offset;
end += offset;
if (start >= 0 && end >= 0) {
return noloop(start, end);
} else if (start < 0 && end < 0) {
return noloop(1 + start, 1 + end);
} else {
return (start < 0) ? loop(1 + start, end)
: loop(start, 1 + end);
}
}
}
Trim::TrimType type() const { return mTrimType; }
private:
Segment noloop(float start, float end) const
{
assert(start >= 0);
assert(end >= 0);
Segment s;
s.start = std::min(start, end);
s.end = std::max(start, end);
return s;
}
Segment loop(float start, float end) const
{
assert(start >= 0);
assert(end >= 0);
Segment s;
s.start = std::max(start, end);
s.end = std::min(start, end);
return s;
}
public:
Property<float> mStart{0};
Property<float> mEnd{0};
Property<float> mOffset{0};
Trim::TrimType mTrimType{TrimType::Simultaneously};
};
inline Gradient::Data operator+(const Gradient::Data &g1,
const Gradient::Data &g2)
{
if (g1.mGradient.size() != g2.mGradient.size()) return g1;
Gradient::Data newG;
newG.mGradient = g1.mGradient;
auto g2It = g2.mGradient.begin();
for (auto &i : newG.mGradient) {
i = i + *g2It;
g2It++;
}
return newG;
}
inline Gradient::Data operator-(const Gradient::Data &g1,
const Gradient::Data &g2)
{
if (g1.mGradient.size() != g2.mGradient.size()) return g1;
Gradient::Data newG;
newG.mGradient = g1.mGradient;
auto g2It = g2.mGradient.begin();
for (auto &i : newG.mGradient) {
i = i - *g2It;
g2It++;
}
return newG;
}
inline Gradient::Data operator*(float m, const Gradient::Data &g)
{
Gradient::Data newG;
newG.mGradient = g.mGradient;
for (auto &i : newG.mGradient) {
i = i * m;
}
return newG;
}
using ColorFilter = std::function<void(float &, float &, float &)>;
void configureModelCacheSize(size_t cacheSize);
std::shared_ptr<model::Composition> loadFromFile(const std::string &filePath,
bool cachePolicy);
std::shared_ptr<model::Composition> loadFromData(std::string jsonData,
const std::string &key,
std::string resourcePath,
bool cachePolicy);
std::shared_ptr<model::Composition> loadFromData(std::string jsonData,
std::string resourcePath,
ColorFilter filter);
std::shared_ptr<model::Composition> parse(char *str, std::string dir_path,
ColorFilter filter = {});
} // namespace model
} // namespace internal
} // namespace rlottie
#endif // LOTModel_H