wasm: Revise the wasm binding for lottie-player

- removed and replaced `tvgWasm`
This commit is contained in:
Jinny You 2023-12-11 11:56:20 +09:00 committed by Hermet Park
parent 003464b7e5
commit 87722991f7
2 changed files with 59 additions and 201 deletions

View file

@ -1,5 +1,5 @@
if (cc.get_id() == 'emscripten')
source_file = files('tvgWasm.cpp')
source_file = files('tvgWasmLottieAnimation.cpp')
thorvg_wasm_dep = declare_dependency(include_directories
: include_directories('.'), sources
: source_file)

View file

@ -29,28 +29,18 @@ using namespace tvg;
static const char* NoError = "None";
class __attribute__((visibility("default"))) TvgWasm
class __attribute__((visibility("default"))) TvgLottieAnimation
{
//This structure data should be aligned with the ThorVG Viewer implementation.
struct Layer
{
uint32_t paint; //cast of a paint pointer
uint32_t depth;
uint32_t type;
uint8_t opacity;
CompositeMethod method;
};
public:
~TvgWasm()
~TvgLottieAnimation()
{
free(buffer);
Initializer::term(CanvasEngine::Sw);
}
static unique_ptr<TvgWasm> create()
static unique_ptr<TvgLottieAnimation> create()
{
return unique_ptr<TvgWasm>(new TvgWasm());
return unique_ptr<TvgLottieAnimation>(new TvgLottieAnimation());
}
string error()
@ -58,7 +48,32 @@ public:
return errorMsg;
}
bool load(string data, string mimetype, int width, int height)
// Getter methods
val size()
{
return val(typed_memory_view(2, psize));
}
val duration()
{
if (!canvas || !animation) return val(0);
return val(animation->duration());
}
val totalFrame()
{
if (!canvas || !animation) return val(0);
return val(animation->totalFrame());
}
val curFrame()
{
if (!canvas || !animation) return val(0);
return val(animation->curFrame());
}
// Render methods
bool load(string data, string mimetype, int width, int height, string rpath = "")
{
errorMsg = NoError;
@ -96,20 +111,6 @@ public:
return true;
}
bool update()
{
if (!updated) return true;
errorMsg = NoError;
if (canvas->update() != Result::Success) {
errorMsg = "update() fail";
return false;
}
return true;
}
val render()
{
errorMsg = NoError;
@ -130,21 +131,18 @@ public:
return val(typed_memory_view(width * height * 4, buffer));
}
val size()
bool update()
{
return val(typed_memory_view(2, psize));
}
if (!updated) return true;
val duration()
{
if (!canvas || !animation) return val(0);
return val(animation->duration());
}
errorMsg = NoError;
val totalFrame()
{
if (!canvas || !animation) return val(0);
return val(animation->totalFrame());
if (canvas->update() != Result::Success) {
errorMsg = "update() fail";
return false;
}
return true;
}
bool frame(float no)
@ -183,35 +181,7 @@ public:
updated = true;
}
bool save2Tvg()
{
errorMsg = NoError;
if (!animation) return false;
auto duplicate = cast<Picture>(animation->picture()->duplicate());
if (!duplicate) {
errorMsg = "duplicate(), fail";
return false;
}
auto saver = Saver::gen();
if (!saver) {
errorMsg = "Invalid saver";
return false;
}
if (saver->save(std::move(duplicate), "output.tvg") != tvg::Result::Success) {
errorMsg = "save(), fail";
return false;
}
saver->sync();
return true;
}
// Saver methods
bool save2Gif(string data, string mimetype, int width, int height, int fps)
{
errorMsg = NoError;
@ -257,62 +227,10 @@ public:
return true;
}
val layers()
{
if (!canvas || !animation) return val(nullptr);
//returns an array of a structure Layer: [id] [depth] [type] [composite]
children.reset();
sublayers(&children, animation->picture(), 0);
return val(typed_memory_view(children.count * sizeof(Layer) / sizeof(uint32_t), (uint32_t *)(children.data)));
}
bool opacity(uint32_t id, uint8_t opacity)
{
if (!canvas || !animation) return false;
auto paint = findPaintById(animation->picture(), id, nullptr);
if (!paint) return false;
const_cast<Paint*>(paint)->opacity(opacity);
return true;
}
val geometry(uint32_t id)
{
if (!canvas || !animation) return val(typed_memory_view<float>(0, nullptr));
Array<const Paint*> parents;
auto paint = findPaintById(animation->picture(), id, &parents);
if (!paint) return val(typed_memory_view<float>(0, nullptr));
paint->bounds(&bounds[0], &bounds[1], &bounds[2], &bounds[3], false);
float points[8] = { //clockwise points
bounds[0], bounds[1], //(x1, y1)
bounds[0] + bounds[2], bounds[1], //(x2, y1)
bounds[0] + bounds[2], bounds[1] + bounds[3], //(x2, y2)
bounds[0], bounds[1] + bounds[3], //(x1, y2)
};
for (auto paint = parents.data; paint < parents.end(); ++paint) {
auto m = const_cast<Paint*>(*paint)->transform();
for (int i = 0; i < 8; i += 2) {
float x = points[i] * m.e11 + points[i+1] * m.e12 + m.e13;
points[i] = x;
points[i + 1] = points[i] * m.e21 + points[i + 1] * m.e22 + m.e23;
}
}
bounds[0] = points[0]; //x(p1)
bounds[1] = points[3]; //y(p2)
bounds[2] = points[4] - bounds[0]; //x(p3)
bounds[3] = points[7] - bounds[1]; //y(p4)
return val(typed_memory_view(4, bounds));
}
// TODO: Advanced APIs wrt Interactivty & theme methods...
private:
explicit TvgWasm()
explicit TvgLottieAnimation()
{
errorMsg = NoError;
@ -328,90 +246,30 @@ private:
if (!animation) errorMsg = "Invalid animation";
}
void sublayers(Array<Layer>* layers, const Paint* paint, uint32_t depth)
{
//paint
if (paint->identifier() != Shape::identifier()) {
auto it = IteratorAccessor::iterator(paint);
if (it->count() > 0) {
it->begin();
layers->grow(it->count());
while (auto child = it->next()) {
uint32_t type = child->identifier();
layers->push({.paint = reinterpret_cast<uint32_t>(child), .depth = depth + 1, .type = type, .opacity = child->opacity(), .method = CompositeMethod::None});
sublayers(layers, child, depth + 1);
}
}
}
//composite
const Paint* target = nullptr;
CompositeMethod method = paint->composite(&target);
if (target && method != CompositeMethod::None) {
layers->push({.paint = reinterpret_cast<uint32_t>(target), .depth = depth, .type = target->identifier(), .opacity = target->opacity(), .method = method});
sublayers(layers, target, depth);
}
}
const Paint* findPaintById(const Paint* parent, uint32_t id, Array<const Paint *>* parents) {
//validate paintId is correct and exists in the picture
if (reinterpret_cast<uint32_t>(parent) == id) {
if (parents) parents->push(parent);
return parent;
}
//paint
if (parent->identifier() != Shape::identifier()) {
auto it = IteratorAccessor::iterator(parent);
if (it->count() > 0) {
it->begin();
while (auto child = it->next()) {
if (auto paint = findPaintById(child, id, parents)) {
if (parents) parents->push(parent);
return paint;
}
}
}
}
//composite
const Paint* target = nullptr;
CompositeMethod method = parent->composite(&target);
if (target && method != CompositeMethod::None) {
if (auto paint = findPaintById(target, id, parents)) {
if (parents) parents->push(parent);
return paint;
}
}
return nullptr;
}
private:
string errorMsg;
unique_ptr<SwCanvas> canvas = nullptr;
unique_ptr<Animation> animation = nullptr;
uint8_t* buffer = nullptr;
Array<Layer> children;
uint32_t width = 0;
uint32_t height = 0;
float bounds[4];
float psize[2]; //picture size
bool updated = false;
};
EMSCRIPTEN_BINDINGS(thorvg_bindings) {
class_<TvgWasm>("TvgWasm")
.constructor(&TvgWasm::create)
.function("error", &TvgWasm::error, allow_raw_pointers())
.function("load", &TvgWasm::load)
.function("update", &TvgWasm::update)
.function("resize", &TvgWasm::resize)
.function("render", &TvgWasm::render)
.function("size", &TvgWasm::size)
.function("duration", &TvgWasm::duration)
.function("totalFrame", &TvgWasm::totalFrame)
.function("frame", &TvgWasm::frame)
.function("save2Tvg", &TvgWasm::save2Tvg)
.function("save2Gif", &TvgWasm::save2Gif)
.function("layers", &TvgWasm::layers)
.function("geometry", &TvgWasm::geometry)
.function("opacity", &TvgWasm::opacity);
}
EMSCRIPTEN_BINDINGS(thorvg_bindings)
{
class_<TvgLottieAnimation>("TvgLottieAnimation")
.constructor(&TvgLottieAnimation ::create)
.function("error", &TvgLottieAnimation ::error, allow_raw_pointers())
.function("size", &TvgLottieAnimation ::size)
.function("duration", &TvgLottieAnimation ::duration)
.function("totalFrame", &TvgLottieAnimation ::totalFrame)
.function("curFrame", &TvgLottieAnimation ::curFrame)
.function("render", &TvgLottieAnimation::render)
.function("load", &TvgLottieAnimation ::load)
.function("update", &TvgLottieAnimation ::update)
.function("frame", &TvgLottieAnimation ::frame)
.function("resize", &TvgLottieAnimation ::resize)
.function("save2Gif", &TvgLottieAnimation ::save2Gif);
}