binding wasm: revise the code.

rewrite the code following the thorvg convention.
This commit is contained in:
Hermet Park 2023-08-01 17:36:05 +09:00 committed by Hermet Park
parent 3eca77b280
commit cee59dcd0e

View file

@ -21,69 +21,83 @@
*/ */
#include <thorvg.h> #include <thorvg.h>
#include <emscripten/bind.h>
#include "tvgIteratorAccessor.h" #include "tvgIteratorAccessor.h"
#include <emscripten/bind.h>
using namespace emscripten; using namespace emscripten;
using namespace std; using namespace std;
using namespace tvg; using namespace tvg;
class __attribute__((visibility("default"))) ThorvgWasm static const char* NoError = "None";
class __attribute__((visibility("default"))) TvgWasm
{ {
struct Layer
{
uint32_t paint; //cast of a paint pointer
uint32_t depth;
uint32_t type;
uint8_t opacity;
CompositeMethod method;
};
public: public:
static unique_ptr<ThorvgWasm> create() ~TvgWasm()
{ {
return unique_ptr<ThorvgWasm>(new ThorvgWasm()); free(buffer);
Initializer::term(CanvasEngine::Sw);
} }
string getError() static unique_ptr<TvgWasm> create()
{ {
return mErrorMsg; return unique_ptr<TvgWasm>(new TvgWasm());
}
string error()
{
return errorMsg;
} }
bool load(string data, string mimetype, int width, int height) bool load(string data, string mimetype, int width, int height)
{ {
mErrorMsg = "None"; errorMsg = NoError;
if (!mSwCanvas) { if (!canvas) {
mErrorMsg = "Canvas is NULL"; errorMsg = "Invalid canvas";
return false; return false;
} }
if (data.empty()) { if (data.empty()) {
mErrorMsg = "Data is empty"; errorMsg = "Invalid data";
return false; return false;
} }
mPicture = Picture::gen().release(); picture = Picture::gen().release();
if (!mPicture) { if (!picture) {
mErrorMsg = "Picture get failed"; errorMsg = "Invalid picture";
return false; return false;
} }
mSwCanvas->clear(); canvas->clear();
if (mPicture->load(data.c_str(), data.size(), mimetype, false) != Result::Success) { if (picture->load(data.c_str(), data.size(), mimetype, false) != Result::Success) {
/* mPicture is not handled as unique_ptr yet, so delete here */ delete(picture);
delete(mPicture); picture = nullptr;
mPicture = nullptr; errorMsg = "Fail, load()";
mErrorMsg = "Load failed";
return false; return false;
} }
mPicture->size(&mOriginalSize[0], &mOriginalSize[1]); picture->size(&psize[0], &psize[1]);
/* need to reset size to calculate scale in Picture.size internally /* need to reset size to calculate scale in Picture.size internally before calling resize() */
before calling updateSize */ this->width = 0;
mWidth = 0; this->height = 0;
mHeight = 0;
updateSize(width, height); resize(width, height);
if (mSwCanvas->push(unique_ptr<Picture>(mPicture)) != Result::Success) { if (canvas->push(cast<Picture>(picture)) != Result::Success) {
mErrorMsg = "Push failed"; errorMsg = "Fail, push()";
return false; return false;
} }
@ -92,21 +106,21 @@ public:
bool update(int width, int height, bool force) bool update(int width, int height, bool force)
{ {
mErrorMsg = "None"; errorMsg = NoError;
if (!mSwCanvas || !mPicture) { if (!canvas || !picture) {
mErrorMsg = "Invalid Conditions"; errorMsg = "Invalid Conditions";
return false; return false;
} }
if (!force && mWidth == width && mHeight == height) { if (!force && this->width == width && this->height == height) {
return true; return true;
} }
updateSize(width, height); resize(width, height);
if (mSwCanvas->update(mPicture) != Result::Success) { if (canvas->update(picture) != Result::Success) {
mErrorMsg = "Update failed"; errorMsg = "Fail, update()";
return false; return false;
} }
return true; return true;
@ -114,200 +128,164 @@ public:
val render() val render()
{ {
mErrorMsg = "None"; errorMsg = NoError;
if (!mSwCanvas) { if (!canvas) {
mErrorMsg = "Canvas is NULL"; errorMsg = "Invalid canvas";
return val(typed_memory_view<uint8_t>(0, nullptr)); return val(typed_memory_view<uint8_t>(0, nullptr));
} }
if (mSwCanvas->draw() != Result::Success) { if (canvas->draw() != Result::Success) {
mErrorMsg = "Draw failed"; errorMsg = "Fail, draw()";
return val(typed_memory_view<uint8_t>(0, nullptr)); return val(typed_memory_view<uint8_t>(0, nullptr));
} }
mSwCanvas->sync(); canvas->sync();
//Change ARGB into ABGR and unpremultiply return val(typed_memory_view(width * height * 4, buffer));
unpremultiplyBuffer();
return val(typed_memory_view(mWidth * mHeight * 4, mBuffer.get()));
} }
val originalSize() val size()
{ {
return val(typed_memory_view(2, mOriginalSize)); return val(typed_memory_view(2, psize));
} }
bool saveTvg(bool compress) bool save(bool compress)
{ {
mErrorMsg = "None"; errorMsg = NoError;
auto saver = tvg::Saver::gen(); auto saver = Saver::gen();
auto duplicate = tvg::cast<tvg::Picture>(mPicture->duplicate()); if (!saver) {
if (!saver || !duplicate) { errorMsg = "Invalid saver";
mErrorMsg = "Saving initialization failed";
return false; return false;
} }
if (saver->save(std::move(duplicate), "file.tvg", compress) != tvg::Result::Success) { auto duplicate = cast<Picture>(picture->duplicate());
mErrorMsg = "Tvg saving failed";
if (!duplicate) {
errorMsg = "Invalid dupliate";
return false;
}
if (saver->save(std::move(duplicate), "output.tvg", compress) != tvg::Result::Success) {
errorMsg = "Fail, save()";
return false; return false;
} }
saver->sync(); saver->sync();
return true; return true;
} }
val layers() val layers()
{ {
//returns an array of a structure Layer: [id] [depth] [type] [composite] //returns an array of a structure Layer: [id] [depth] [type] [composite]
mLayers.reset(); children.reset();
sublayers(&mLayers, mPicture, 0); sublayers(&children, picture, 0);
return val(typed_memory_view(children.count * sizeof(Layer) / sizeof(uint32_t), (uint32_t *)(children.data)));
return val(typed_memory_view(mLayers.count * sizeof(Layer) / sizeof(uint32_t), (uint32_t *)(mLayers.data)));
} }
bool setOpacity(uint32_t paintId, uint8_t opacity) bool opacity(uint32_t pid, uint8_t opacity)
{ {
const Paint* paint = findPaintById(mPicture, paintId, nullptr); auto paint = findPaintById(picture, pid, nullptr);
if (!paint) return false; if (!paint) return false;
const_cast<Paint*>(paint)->opacity(opacity); const_cast<Paint*>(paint)->opacity(opacity);
return true; return true;
} }
val bounds(uint32_t paintId) val geometry(uint32_t pid)
{ {
Array<const Paint*> parents; Array<const Paint*> parents;
const Paint* paint = findPaintById(mPicture, paintId, &parents); auto paint = findPaintById(picture, pid, &parents);
if (!paint) return val(typed_memory_view<float>(0, nullptr)); if (!paint) return val(typed_memory_view<float>(0, nullptr));
paint->bounds(&mBounds[0], &mBounds[1], &mBounds[2], &mBounds[3], false); paint->bounds(&bounds[0], &bounds[1], &bounds[2], &bounds[3], false);
float points[8] = { //clockwise points float points[8] = { //clockwise points
mBounds[0], mBounds[1], //(x1, y1) bounds[0], bounds[1], //(x1, y1)
mBounds[0] + mBounds[2], mBounds[1], //(x2, y1) bounds[0] + bounds[2], bounds[1], //(x2, y1)
mBounds[0] + mBounds[2], mBounds[1] + mBounds[3], //(x2, y2) bounds[0] + bounds[2], bounds[1] + bounds[3], //(x2, y2)
mBounds[0], mBounds[1] + mBounds[3], //(x1, y2) bounds[0], bounds[1] + bounds[3], //(x1, y2)
}; };
for (auto paint = parents.data; paint < parents.end(); ++paint) { for (auto paint = parents.data; paint < parents.end(); ++paint) {
auto m = const_cast<Paint*>(*paint)->transform(); auto m = const_cast<Paint*>(*paint)->transform();
for (int i = 0; i < 8; i += 2) { for (int i = 0; i < 8; i += 2) {
float x = points[i] * m.e11 + points[i+1] * m.e12 + m.e13; float x = points[i] * m.e11 + points[i+1] * m.e12 + m.e13;
points[i+1] = points[i] * m.e21 + points[i+1] * m.e22 + m.e23;
points[i] = x; points[i] = x;
points[i + 1] = points[i] * m.e21 + points[i + 1] * m.e22 + m.e23;
} }
} }
mBounds[0] = points[0];//x(p1) bounds[0] = points[0]; //x(p1)
mBounds[1] = points[3];//y(p2) bounds[1] = points[3]; //y(p2)
mBounds[2] = points[4] - mBounds[0];//x(p3) bounds[2] = points[4] - bounds[0]; //x(p3)
mBounds[3] = points[7] - mBounds[1];//y(p4) bounds[3] = points[7] - bounds[1]; //y(p4)
return val(typed_memory_view(4, mBounds)); return val(typed_memory_view(4, bounds));
} }
private: private:
explicit ThorvgWasm() explicit TvgWasm()
{ {
mErrorMsg = "None"; errorMsg = NoError;
Initializer::init(CanvasEngine::Sw, 0); Initializer::init(CanvasEngine::Sw, 0);
mSwCanvas = SwCanvas::gen(); canvas = SwCanvas::gen();
if (!mSwCanvas) { if (!canvas) {
mErrorMsg = "Canvas get failed"; errorMsg = "Invalid canvas";
return; return;
} }
} }
void unpremultiplyBuffer() { void resize(int width, int height)
for (uint32_t y = 0; y < mHeight; y++) {
auto buffer = (uint32_t *) mBuffer.get() + mWidth * y;
for (uint32_t x = 0; x < mWidth; ++x) {
uint8_t a = buffer[x] >> 24;
if (a == 255) {
uint8_t b = (buffer[x] & 0xff);
uint8_t r = ((buffer[x] >> 16) & 0xff);
buffer[x] = (buffer[x] & 0xff00ff00) | (b << 16) | (r);
} else if (a == 0) {
buffer[x] = 0x00ffffff;
} else {
uint16_t b = ((buffer[x] << 8) & 0xff00) / a;
uint16_t g = ((buffer[x]) & 0xff00) / a;
uint16_t r = ((buffer[x] >> 8) & 0xff00) / a;
if (b > 0xff) b = 0xff;
if (g > 0xff) g = 0xff;
if (r > 0xff) r = 0xff;
buffer[x] = (a << 24) | (b << 16) | (g << 8) | (r);
}
}
}
}
void updateSize(int width, int height)
{ {
if (!mSwCanvas) return; if (!canvas) return;
if (mWidth == width && mHeight == height) return; if (this->width == width && this->height == height) return;
mWidth = width; this->width = width;
mHeight = height; this->height = height;
mBuffer = make_unique<uint8_t[]>(mWidth * mHeight * 4);
mSwCanvas->target((uint32_t *)mBuffer.get(), mWidth, mWidth, mHeight, SwCanvas::ARGB8888);
if (mPicture) { buffer = (uint8_t*)malloc(width * height * sizeof(uint32_t));
canvas->target((uint32_t *)buffer, width, width, height, SwCanvas::ABGR8888S);
if (picture) {
float scale; float scale;
float shiftX = 0.0f, shiftY = 0.0f; float shiftX = 0.0f, shiftY = 0.0f;
if (mOriginalSize[0] > mOriginalSize[1]) { if (psize[0] > psize[1]) {
scale = width / mOriginalSize[0]; scale = width / psize[0];
shiftY = (height - mOriginalSize[1] * scale) * 0.5f; shiftY = (height - psize[1] * scale) * 0.5f;
} else { } else {
scale = height / mOriginalSize[1]; scale = height / psize[1];
shiftX = (width - mOriginalSize[0] * scale) * 0.5f; shiftX = (width - psize[0] * scale) * 0.5f;
} }
mPicture->scale(scale); picture->scale(scale);
mPicture->translate(shiftX, shiftY); picture->translate(shiftX, shiftY);
} }
} }
struct Layer
{
uint32_t paint; //cast of a paint pointer
uint32_t depth;
uint32_t type;
uint32_t composite;
uint32_t opacity;
};
void sublayers(Array<Layer>* layers, const Paint* paint, uint32_t depth) void sublayers(Array<Layer>* layers, const Paint* paint, uint32_t depth)
{ {
//paint //paint
if (paint->identifier() != Shape::identifier()) { if (paint->identifier() != Shape::identifier()) {
auto it = IteratorAccessor::iterator(paint); auto it = IteratorAccessor::iterator(paint);
if (it->count() > 0) { if (it->count() > 0) {
layers->reserve(layers->count + it->count());
it->begin(); it->begin();
layers->grow(it->count());
while (auto child = it->next()) { while (auto child = it->next()) {
uint32_t type = child->identifier(); uint32_t type = child->identifier();
uint32_t opacity = child->opacity(); layers->push({.paint = reinterpret_cast<uint32_t>(child), .depth = depth + 1, .type = type, .opacity = child->opacity(), .method = CompositeMethod::None});
layers->push({.paint = reinterpret_cast<uint32_t>(child), .depth = depth + 1, .type = type, .composite = static_cast<uint32_t>(CompositeMethod::None), .opacity = opacity});
sublayers(layers, child, depth + 1); sublayers(layers, child, depth + 1);
} }
} }
} }
//composite //composite
const Paint* compositeTarget = nullptr; const Paint* target = nullptr;
CompositeMethod composite = paint->composite(&compositeTarget); CompositeMethod method = paint->composite(&target);
if (compositeTarget && composite != CompositeMethod::None) { if (target && method != CompositeMethod::None) {
uint32_t type = compositeTarget->identifier(); layers->push({.paint = reinterpret_cast<uint32_t>(target), .depth = depth, .type = target->identifier(), .opacity = target->opacity(), .method = method});
uint32_t opacity = compositeTarget->opacity(); sublayers(layers, target, depth);
layers->push({.paint = reinterpret_cast<uint32_t>(compositeTarget), .depth = depth, .type = type, .composite = static_cast<uint32_t>(composite), .opacity = opacity});
sublayers(layers, compositeTarget, depth);
} }
} }
const Paint* findPaintById(const Paint* parent, uint32_t paintId, Array<const Paint *>* parents) { const Paint* findPaintById(const Paint* parent, uint32_t id, Array<const Paint *>* parents) {
//validate paintId is correct and exists in the picture //validate paintId is correct and exists in the picture
if (reinterpret_cast<uint32_t>(parent) == paintId) { if (reinterpret_cast<uint32_t>(parent) == id) {
if (parents) parents->push(parent); if (parents) parents->push(parent);
return parent; return parent;
} }
@ -317,7 +295,7 @@ private:
if (it->count() > 0) { if (it->count() > 0) {
it->begin(); it->begin();
while (auto child = it->next()) { while (auto child = it->next()) {
if (auto paint = findPaintById(child, paintId, parents)) { if (auto paint = findPaintById(child, id, parents)) {
if (parents) parents->push(parent); if (parents) parents->push(parent);
return paint; return paint;
} }
@ -325,10 +303,10 @@ private:
} }
} }
//composite //composite
const Paint* compositeTarget = nullptr; const Paint* target = nullptr;
CompositeMethod composite = parent->composite(&compositeTarget); CompositeMethod method = parent->composite(&target);
if (compositeTarget && composite != CompositeMethod::None) { if (target && method != CompositeMethod::None) {
if (auto paint = findPaintById(compositeTarget, paintId, parents)) { if (auto paint = findPaintById(target, id, parents)) {
if (parents) parents->push(parent); if (parents) parents->push(parent);
return paint; return paint;
} }
@ -337,32 +315,28 @@ private:
} }
private: private:
string mErrorMsg; string errorMsg;
unique_ptr< SwCanvas > mSwCanvas = nullptr; unique_ptr<SwCanvas> canvas = nullptr;
Picture* mPicture = nullptr; Picture* picture = nullptr;
unique_ptr<uint8_t[]> mBuffer = nullptr; uint8_t* buffer = nullptr;
Array<Layer> children;
uint32_t mWidth{0}; uint32_t width = 0;
uint32_t mHeight{0}; uint32_t height = 0;
float bounds[4];
Array<Layer> mLayers; float psize[2]; //picture size
float mBounds[4];
float mOriginalSize[2];
}; };
//Binding code
EMSCRIPTEN_BINDINGS(thorvg_bindings) { EMSCRIPTEN_BINDINGS(thorvg_bindings) {
class_<ThorvgWasm>("ThorvgWasm") class_<TvgWasm>("TvgWasm")
.constructor(&ThorvgWasm::create) .constructor(&TvgWasm::create)
.function("getError", &ThorvgWasm::getError, allow_raw_pointers()) .function("error", &TvgWasm::error, allow_raw_pointers())
.function("load", &ThorvgWasm::load) .function("load", &TvgWasm::load)
.function("update", &ThorvgWasm::update) .function("update", &TvgWasm::update)
.function("render", &ThorvgWasm::render) .function("render", &TvgWasm::render)
.function("originalSize", &ThorvgWasm::originalSize) .function("size", &TvgWasm::size)
.function("save", &TvgWasm::save)
.function("saveTvg", &ThorvgWasm::saveTvg) .function("layers", &TvgWasm::layers)
.function("geometry", &TvgWasm::geometry)
.function("layers", &ThorvgWasm::layers) .function("opacity", &TvgWasm::opacity);
.function("bounds", &ThorvgWasm::bounds)
.function("setOpacity", &ThorvgWasm::setOpacity);
} }