lottie/slot: Support image overriding

This commit is contained in:
Jinny You 2024-11-06 18:04:40 +09:00 committed by Hermet Park
parent ce3bee7a3f
commit 98fbc90f9b
6 changed files with 101 additions and 44 deletions

View file

@ -61,8 +61,6 @@ void LottieLoader::release()
free((char*)content); free((char*)content);
content = nullptr; content = nullptr;
} }
free(dirName);
dirName = nullptr;
} }
@ -85,6 +83,8 @@ LottieLoader::~LottieLoader()
//TODO: correct position? //TODO: correct position?
delete(comp); delete(comp);
delete(builder); delete(builder);
free(dirName);
} }

View file

@ -61,6 +61,14 @@ void LottieSlot::reset()
static_cast<LottieTextDoc*>(pair->prop)->frames = nullptr; static_cast<LottieTextDoc*>(pair->prop)->frames = nullptr;
break; break;
} }
case LottieProperty::Type::Image: {
static_cast<LottieImage*>(pair->obj)->data.release();
static_cast<LottieImage*>(pair->obj)->data = *static_cast<LottieBitmap*>(pair->prop);
static_cast<LottieBitmap*>(pair->prop)->b64Data = nullptr;
static_cast<LottieBitmap*>(pair->prop)->mimeType = nullptr;
static_cast<LottieImage*>(pair->obj)->prepare();
break;
}
default: break; default: break;
} }
delete(pair->prop); delete(pair->prop);
@ -108,6 +116,14 @@ void LottieSlot::assign(LottieObject* target)
pair->obj->override(&static_cast<LottieText*>(target)->doc); pair->obj->override(&static_cast<LottieText*>(target)->doc);
break; break;
} }
case LottieProperty::Type::Image: {
if (!overridden) {
pair->prop = new LottieBitmap;
*static_cast<LottieBitmap*>(pair->prop) = static_cast<LottieImage*>(pair->obj)->data;
}
pair->obj->override(&static_cast<LottieImage*>(target)->data);
break;
}
default: break; default: break;
} }
} }
@ -152,13 +168,6 @@ float LottieTextRange::factor(float frameNo, float totalLen, float idx)
} }
LottieImage::~LottieImage()
{
free(b64Data);
free(mimeType);
}
void LottieImage::prepare() void LottieImage::prepare()
{ {
LottieObject::type = LottieObject::Image; LottieObject::type = LottieObject::Image;
@ -168,14 +177,15 @@ void LottieImage::prepare()
//force to load a picture on the same thread //force to load a picture on the same thread
TaskScheduler::async(false); TaskScheduler::async(false);
if (size > 0) picture->load((const char*)b64Data, size, mimeType, false); if (data.size > 0) picture->load((const char*)data.b64Data, data.size, data.mimeType, false);
else picture->load(path); else picture->load(data.path);
TaskScheduler::async(true); TaskScheduler::async(true);
picture->size(width, height); picture->size(data.width, data.height);
PP(picture)->ref(); PP(picture)->ref();
pooler.reset();
pooler.push(picture); pooler.push(picture);
} }

View file

@ -649,16 +649,14 @@ struct LottieGradientStroke : LottieGradient, LottieStroke
struct LottieImage : LottieObject, LottieRenderPooler<tvg::Picture> struct LottieImage : LottieObject, LottieRenderPooler<tvg::Picture>
{ {
union { LottieBitmap data;
char* b64Data = nullptr;
char* path; void override(LottieProperty* prop) override
}; {
char* mimeType = nullptr; this->data = *static_cast<LottieBitmap*>(prop);
uint32_t size = 0; this->prepare();
float width = 0.0f; }
float height = 0.0f;
~LottieImage();
void prepare(); void prepare();
}; };

View file

@ -486,10 +486,8 @@ void LottieParser::parsePropertyInternal(T& prop)
template<LottieProperty::Type type> template<LottieProperty::Type type>
void LottieParser::registerSlot(LottieObject* obj) void LottieParser::registerSlot(LottieObject* obj, char* sid)
{ {
auto sid = getStringCopy();
//append object if the slot already exists. //append object if the slot already exists.
for (auto slot = comp->slots.begin(); slot < comp->slots.end(); ++slot) { for (auto slot = comp->slots.begin(); slot < comp->slots.end(); ++slot) {
if (strcmp((*slot)->sid, sid)) continue; if (strcmp((*slot)->sid, sid)) continue;
@ -506,7 +504,7 @@ void LottieParser::parseProperty(T& prop, LottieObject* obj)
enterObject(); enterObject();
while (auto key = nextObjectKey()) { while (auto key = nextObjectKey()) {
if (KEY_AS("k")) parsePropertyInternal(prop); if (KEY_AS("k")) parsePropertyInternal(prop);
else if (obj && KEY_AS("sid")) registerSlot<type>(obj); else if (obj && KEY_AS("sid")) registerSlot<type>(obj, getStringCopy());
else if (KEY_AS("x")) prop.exp = _expression(getStringCopy(), comp, context.layer, context.parent, &prop); else if (KEY_AS("x")) prop.exp = _expression(getStringCopy(), comp, context.layer, context.parent, &prop);
else if (KEY_AS("ix")) prop.ix = getInt(); else if (KEY_AS("ix")) prop.ix = getInt();
else skip(key); else skip(key);
@ -761,7 +759,7 @@ void LottieParser::parseColorStop(LottieGradient* gradient)
while (auto key = nextObjectKey()) { while (auto key = nextObjectKey()) {
if (KEY_AS("p")) gradient->colorStops.count = getInt(); if (KEY_AS("p")) gradient->colorStops.count = getInt();
else if (KEY_AS("k")) parseProperty<LottieProperty::Type::ColorStop>(gradient->colorStops, gradient); else if (KEY_AS("k")) parseProperty<LottieProperty::Type::ColorStop>(gradient->colorStops, gradient);
else if (KEY_AS("sid")) registerSlot<LottieProperty::Type::ColorStop>(gradient); else if (KEY_AS("sid")) registerSlot<LottieProperty::Type::ColorStop>(gradient, getStringCopy());
else skip(key); else skip(key);
} }
} }
@ -931,45 +929,39 @@ void LottieParser::parseObject(Array<LottieObject*>& parent)
} }
LottieImage* LottieParser::parseImage(const char* data, const char* subPath, bool embedded, float width, float height) void LottieParser::parseImage(LottieImage* image, const char* data, const char* subPath, bool embedded, float width, float height)
{ {
//Used for Image Asset
auto image = new LottieImage;
//embedded image resource. should start with "data:" //embedded image resource. should start with "data:"
//header look like "data:image/png;base64," so need to skip till ','. //header look like "data:image/png;base64," so need to skip till ','.
if (embedded && !strncmp(data, "data:", 5)) { if (embedded && !strncmp(data, "data:", 5)) {
//figure out the mimetype //figure out the mimetype
auto mimeType = data + 11; auto mimeType = data + 11;
auto needle = strstr(mimeType, ";"); auto needle = strstr(mimeType, ";");
image->mimeType = strDuplicate(mimeType, needle - mimeType); image->data.mimeType = strDuplicate(mimeType, needle - mimeType);
//b64 data //b64 data
auto b64Data = strstr(data, ",") + 1; auto b64Data = strstr(data, ",") + 1;
size_t length = strlen(data) - (b64Data - data); size_t length = strlen(data) - (b64Data - data);
image->size = b64Decode(b64Data, length, &image->b64Data); image->data.size = b64Decode(b64Data, length, &image->data.b64Data);
//external image resource //external image resource
} else { } else {
auto len = strlen(dirName) + strlen(subPath) + strlen(data) + 1; auto len = strlen(dirName) + strlen(subPath) + strlen(data) + 1;
image->path = static_cast<char*>(malloc(len)); image->data.path = static_cast<char*>(malloc(len));
snprintf(image->path, len, "%s%s%s", dirName, subPath, data); snprintf(image->data.path, len, "%s%s%s", dirName, subPath, data);
} }
image->width = width; image->data.width = width;
image->height = height; image->data.height = height;
image->prepare(); image->prepare();
return image;
} }
LottieObject* LottieParser::parseAsset() LottieObject* LottieParser::parseAsset()
{ {
enterObject();
LottieObject* obj = nullptr; LottieObject* obj = nullptr;
unsigned long id = 0; unsigned long id = 0;
//Used for Image Asset //Used for Image Asset
char* sid = nullptr;
const char* data = nullptr; const char* data = nullptr;
const char* subPath = nullptr; const char* subPath = nullptr;
float width = 0.0f; float width = 0.0f;
@ -991,9 +983,14 @@ LottieObject* LottieParser::parseAsset()
else if (KEY_AS("w")) width = getFloat(); else if (KEY_AS("w")) width = getFloat();
else if (KEY_AS("h")) height = getFloat(); else if (KEY_AS("h")) height = getFloat();
else if (KEY_AS("e")) embedded = getInt(); else if (KEY_AS("e")) embedded = getInt();
else if (KEY_AS("sid")) sid = getStringCopy();
else skip(key); else skip(key);
} }
if (data) obj = parseImage(data, subPath, embedded, width, height); if (data) {
obj = new LottieImage;
parseImage(static_cast<LottieImage*>(obj), data, subPath, embedded, width, height);
if (sid) registerSlot<LottieProperty::Type::Image>(obj, sid);
}
if (obj) obj->id = id; if (obj) obj->id = id;
return obj; return obj;
} }
@ -1021,6 +1018,7 @@ void LottieParser::parseAssets()
{ {
enterArray(); enterArray();
while (nextArrayValue()) { while (nextArrayValue()) {
enterObject();
auto asset = parseAsset(); auto asset = parseAsset();
if (asset) comp->assets.push(asset); if (asset) comp->assets.push(asset);
else TVGERR("LOTTIE", "Invalid Asset!"); else TVGERR("LOTTIE", "Invalid Asset!");
@ -1478,6 +1476,11 @@ bool LottieParser::apply(LottieSlot* slot)
parseSlotProperty<LottieProperty::Type::TextDoc>(static_cast<LottieText*>(obj)->doc); parseSlotProperty<LottieProperty::Type::TextDoc>(static_cast<LottieText*>(obj)->doc);
break; break;
} }
case LottieProperty::Type::Image: {
obj = parseAsset();
context.parent = obj;
break;
}
default: break; default: break;
} }

View file

@ -39,7 +39,7 @@ public:
bool apply(LottieSlot* slot); bool apply(LottieSlot* slot);
const char* sid(bool first = false); const char* sid(bool first = false);
void captureSlots(const char* key); void captureSlots(const char* key);
template<LottieProperty::Type type = LottieProperty::Type::Invalid> void registerSlot(LottieObject* obj); template<LottieProperty::Type type = LottieProperty::Type::Invalid> void registerSlot(LottieObject* obj, char* sid);
LottieComposition* comp = nullptr; LottieComposition* comp = nullptr;
const char* dirName = nullptr; //base resource directory const char* dirName = nullptr; //base resource directory
@ -77,7 +77,7 @@ private:
LottieObject* parseObject(); LottieObject* parseObject();
LottieObject* parseAsset(); LottieObject* parseAsset();
LottieImage* parseImage(const char* data, const char* subPath, bool embedded, float width, float height); void parseImage(LottieImage* image, const char* data, const char* subPath, bool embedded, float width, float height);
LottieLayer* parseLayer(LottieLayer* precomp); LottieLayer* parseLayer(LottieLayer* precomp);
LottieObject* parseGroup(); LottieObject* parseGroup();
LottieRect* parseRect(); LottieRect* parseRect();

View file

@ -112,7 +112,7 @@ struct LottieVectorFrame
//Property would have an either keyframes or single value. //Property would have an either keyframes or single value.
struct LottieProperty struct LottieProperty
{ {
enum class Type : uint8_t { Point = 0, Float, Opacity, Color, PathSet, ColorStop, Position, TextDoc, Invalid }; enum class Type : uint8_t { Point = 0, Float, Opacity, Color, PathSet, ColorStop, Position, TextDoc, Image, Invalid };
LottieExpression* exp = nullptr; LottieExpression* exp = nullptr;
Type type; Type type;
@ -828,6 +828,52 @@ struct LottieTextDoc : LottieProperty
}; };
struct LottieBitmap : LottieProperty
{
union {
char* b64Data = nullptr;
char* path;
};
char* mimeType = nullptr;
uint32_t size = 0;
float width = 0.0f;
float height = 0.0f;
~LottieBitmap()
{
release();
}
void release()
{
free(b64Data);
free(mimeType);
b64Data = nullptr;
mimeType = nullptr;
}
uint32_t frameCnt() override { return 0; }
uint32_t nearest(float time) override { return 0; }
float frameNo(int32_t key) override { return 0; }
LottieBitmap& operator=(const LottieBitmap& other)
{
//shallow copy, used for slot overriding
if (other.mimeType) b64Data = other.b64Data;
else path = other.path;
mimeType = other.mimeType;
size = other.size;
width = other.width;
height = other.height;
const_cast<LottieBitmap&>(other).b64Data = nullptr;
const_cast<LottieBitmap&>(other).mimeType = nullptr;
return *this;
}
};
using LottiePoint = LottieGenericProperty<Point>; using LottiePoint = LottieGenericProperty<Point>;
using LottieFloat = LottieGenericProperty<float>; using LottieFloat = LottieGenericProperty<float>;
using LottieOpacity = LottieGenericProperty<uint8_t>; using LottieOpacity = LottieGenericProperty<uint8_t>;