From fe00e5425787963a8f4d70ae844d8a152034d8bc Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Wed, 21 Jul 2021 17:31:42 +0900 Subject: [PATCH] tvg_saver: introduce a new module tvg saver tvg saver is a new module to export tvg files. In this patch, it also contains the infrastructure of saver module to expand other types of savers such as png, jpg, etc. To save the tvg file from a paint, you can use the Saver feature, for example: auto saver = tvg::Saver::gen(); saver->save(paint, "sample.tvg"); saver->sync(); Later, you can read the "sample.tvg" using Picture. auto picture = tvg::Picture::gen(); picture->load("sample.tvg"); ... The behavior of the saver will work on sync/async based on the threading setting of the initializer. Thus if you wish to have a benefit of it, you must call sync() after the save() in the proper delayed time. Otherwise, you can call sync() immediately. Note that, the asynchronous tasking is depent on the saver module implementation. Also, you need to enable tvg saver/loader modules from meson option. (yet this feature is under the beta) @API Addition: Result Saver::save(std::unique_ptr paint, const std::string& path) noexcept; Result Saver::sync() noexcept; @Examples: tvgSaver @Co-author: Mira Grudzinska --- inc/thorvg.h | 5 +- meson.build | 16 +- meson_options.txt | 6 + src/examples/TvgSaver.cpp | 8 +- src/lib/meson.build | 2 +- src/lib/tvgCommon.h | 2 + src/lib/tvgLoaderMgr.h | 2 - src/lib/tvgPaint.cpp | 2 + src/lib/tvgPaint.h | 1 + src/lib/tvgPictureImpl.h | 4 +- src/lib/tvgSaver.cpp | 85 ++++++- src/lib/tvgSaver.h | 47 ++++ src/lib/tvgSaverImpl.h | 409 ------------------------------- src/loaders/meson.build | 1 - src/meson.build | 3 +- src/savers/meson.build | 10 + src/savers/tvg/meson.build | 9 + src/savers/tvg/tvgTvgSaver.cpp | 434 +++++++++++++++++++++++++++++++++ src/savers/tvg/tvgTvgSaver.h | 70 ++++++ 19 files changed, 679 insertions(+), 437 deletions(-) create mode 100644 src/lib/tvgSaver.h delete mode 100644 src/lib/tvgSaverImpl.h create mode 100644 src/savers/meson.build create mode 100644 src/savers/tvg/meson.build create mode 100644 src/savers/tvg/tvgTvgSaver.cpp create mode 100644 src/savers/tvg/tvgTvgSaver.h diff --git a/inc/thorvg.h b/inc/thorvg.h index b615375a..ff656548 100644 --- a/inc/thorvg.h +++ b/inc/thorvg.h @@ -44,7 +44,7 @@ protected: \ friend Canvas; \ friend Scene; \ friend Picture; \ - friend Saver; + friend SaveModule; \ #define _TVG_DECALRE_IDENTIFIER() \ @@ -56,10 +56,10 @@ namespace tvg { class RenderMethod; +class SaveModule; class Scene; class Picture; class Canvas; -class Saver; /** * @defgroup ThorVG ThorVG @@ -1369,7 +1369,6 @@ public: _TVG_DECLARE_PRIVATE(Saver); }; - /** @}*/ } //namespace diff --git a/meson.build b/meson.build index 9a3717c3..5563fbce 100644 --- a/meson.build +++ b/meson.build @@ -34,6 +34,10 @@ if get_option('loaders').contains('jpg') == true config_h.set10('THORVG_JPG_LOADER_SUPPORT', true) endif +if get_option('savers').contains('tvg') == true + config_h.set10('THORVG_TVG_SAVER_SUPPORT', true) +endif + if get_option('vectors').contains('avx') == true config_h.set10('THORVG_AVX_VECTOR_SUPPORT', true) endif @@ -73,11 +77,12 @@ Summary: Loader (SVG): @7@ Loader (PNG): @8@ Loader (JPG): @9@ - CAPI Binding: @10@ - Log Message: @11@ - Tests: @12@ - Examples: @13@ - Tool (Svg2Png): @14@ + Saver (TVG): @10@ + CAPI Binding: @11@ + Log Message: @12@ + Tests: @13@ + Examples: @14@ + Tool (Svg2Png): @15@ '''.format( meson.project_version(), @@ -90,6 +95,7 @@ Summary: get_option('loaders').contains('svg'), get_option('loaders').contains('png'), get_option('loaders').contains('jpg'), + get_option('savers').contains('tvg'), get_option('bindings').contains('capi'), get_option('log'), get_option('tests'), diff --git a/meson_options.txt b/meson_options.txt index d09b6b3f..582d1cc0 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -10,6 +10,12 @@ option('loaders', value: ['svg'], description: 'Enable File Loaders in thorvg') +option('savers', + type: 'array', + choices: ['', 'tvg'], + value: [''], + description: 'Enable File Savers in thorvg') + option('vectors', type: 'array', choices: ['', 'avx'], diff --git a/src/examples/TvgSaver.cpp b/src/examples/TvgSaver.cpp index 181b8e9a..1274460f 100644 --- a/src/examples/TvgSaver.cpp +++ b/src/examples/TvgSaver.cpp @@ -179,12 +179,12 @@ void exportTvg() //save the tvg file auto saver = tvg::Saver::gen(); - if (saver->save(move(scene), EXAMPLE_DIR"/test.tvg") != tvg::Result::Success) { - cout << "Problem with saving the test.tvg file." << endl; + if (saver->save(move(scene), EXAMPLE_DIR"/test.tvg") == tvg::Result::Success) { + saver->sync(); + cout << "Successfully exported to test.tvg, Please check the result using PictureTvg!" << endl; return; } - - cout << "Successfully exported to test.tvg, Please check the result using PictureTvg!" << endl; + cout << "Problem with saving the test.tvg file. Did you enable TVG Saver?" << endl; } diff --git a/src/lib/meson.build b/src/lib/meson.build index c535560d..372fe2dc 100644 --- a/src/lib/meson.build +++ b/src/lib/meson.build @@ -18,7 +18,7 @@ source_file = [ 'tvgLoaderMgr.h', 'tvgPictureImpl.h', 'tvgRender.h', - 'tvgSaverImpl.h', + 'tvgSaver.h', 'tvgSceneImpl.h', 'tvgShapeImpl.h', 'tvgTaskScheduler.h', diff --git a/src/lib/tvgCommon.h b/src/lib/tvgCommon.h index 2fed7b3c..a61670e5 100644 --- a/src/lib/tvgCommon.h +++ b/src/lib/tvgCommon.h @@ -36,6 +36,8 @@ using namespace tvg; #define TVG_CLASS_ID_LINEAR 4 #define TVG_CLASS_ID_RADIAL 5 +enum class FileType { Tvg = 0, Svg, Raw, Png, Jpg, Unknown }; + //for MSVC Compat #ifdef _MSC_VER #define TVG_UNUSED diff --git a/src/lib/tvgLoaderMgr.h b/src/lib/tvgLoaderMgr.h index 18a8e8ae..fb5893d4 100644 --- a/src/lib/tvgLoaderMgr.h +++ b/src/lib/tvgLoaderMgr.h @@ -24,8 +24,6 @@ #include "tvgLoader.h" -enum class FileType { Tvg = 0, Svg, Raw, Png, Jpg, Unknown }; - struct LoaderMgr { static bool init(); diff --git a/src/lib/tvgPaint.cpp b/src/lib/tvgPaint.cpp index 7c539fcd..2dbcadd7 100644 --- a/src/lib/tvgPaint.cpp +++ b/src/lib/tvgPaint.cpp @@ -27,6 +27,8 @@ /* Internal Class Implementation */ /************************************************************************/ + + static inline bool FLT_SAME(float a, float b) { return (fabsf(a - b) < FLT_EPSILON); diff --git a/src/lib/tvgPaint.h b/src/lib/tvgPaint.h index 6ce5d1ff..d4a67f64 100644 --- a/src/lib/tvgPaint.h +++ b/src/lib/tvgPaint.h @@ -27,6 +27,7 @@ namespace tvg { + struct Iterator { virtual ~Iterator() {} diff --git a/src/lib/tvgPictureImpl.h b/src/lib/tvgPictureImpl.h index 40e48e91..9a195dc3 100644 --- a/src/lib/tvgPictureImpl.h +++ b/src/lib/tvgPictureImpl.h @@ -34,9 +34,7 @@ struct PictureIterator : Iterator { Paint* paint = nullptr; - PictureIterator(Paint* p) : paint(p) - { - } + PictureIterator(Paint* p) : paint(p) {} const Paint* next() override { diff --git a/src/lib/tvgSaver.cpp b/src/lib/tvgSaver.cpp index 5f508076..212d9960 100644 --- a/src/lib/tvgSaver.cpp +++ b/src/lib/tvgSaver.cpp @@ -19,13 +19,69 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -#include "tvgSaverImpl.h" -#include +#include "tvgCommon.h" +#include "tvgSaver.h" + +#ifdef THORVG_TVG_SAVER_SUPPORT + #include "tvgTvgSaver.h" +#endif /************************************************************************/ /* Internal Class Implementation */ /************************************************************************/ +struct Saver::Impl +{ + SaveModule* saveModule = nullptr; + ~Impl() + { + if (saveModule) delete(saveModule); + } +}; + + +static SaveModule* _find(FileType type) +{ + switch(type) { + case FileType::Tvg: { +#ifdef THORVG_TVG_SAVER_SUPPORT + return new TvgSaver; +#endif + break; + } + default: { + break; + } + } + +#ifdef THORVG_LOG_ENABLED + const char *format; + switch(type) { + case FileType::Tvg: { + format = "TVG"; + break; + } + default: { + format = "???"; + break; + } + } + printf("SAVER: %s format is not supported\n", format); +#endif + return nullptr; +} + + +static SaveModule* _find(const string& path) +{ + auto ext = path.substr(path.find_last_of(".") + 1); + if (!ext.compare("tvg")) { + return _find(FileType::Tvg); + } + return nullptr; +} + + /************************************************************************/ /* External Class Implementation */ /************************************************************************/ @@ -41,21 +97,34 @@ Saver::~Saver() } -Result Saver::save(std::unique_ptr paint, const std::string& path) noexcept +Result Saver::save(std::unique_ptr paint, const string& path) noexcept { + //Already on saving an other resource. + if (pImpl->saveModule) return Result::InsufficientCondition; + auto p = paint.release(); if (!p) return Result::MemoryCorruption; - if (this->pImpl->save(p, path)) return Result::Success; - - return Result::Unknown; + if (auto saveModule = _find(path)) { + if (saveModule->save(p, path)) { + pImpl->saveModule = saveModule; + return Result::Success; + } else { + return Result::Unknown; + } + } + return Result::NonSupport; } Result Saver::sync() noexcept { - if (this->pImpl->sync()) return Result::Success; - return Result::Unknown; + if (!pImpl->saveModule) return Result::InsufficientCondition; + pImpl->saveModule->close(); + delete(pImpl->saveModule); + pImpl->saveModule = nullptr; + + return Result::Success; } diff --git a/src/lib/tvgSaver.h b/src/lib/tvgSaver.h new file mode 100644 index 00000000..3586442e --- /dev/null +++ b/src/lib/tvgSaver.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2021 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_SAVER_H_ +#define _TVG_SAVER_H_ + +#include "tvgPaint.h" + +namespace tvg +{ + +class SaveModule +{ +public: + virtual ~SaveModule() {} + + virtual bool save(Paint* paint, const string& path) = 0; + virtual bool close() = 0; + + //Utility Method: Iterator Delegator + Iterator* iterator(const Paint* paint) + { + return paint->pImpl->iterator(); + } +}; + +} + +#endif //_TVG_SAVER_H_ \ No newline at end of file diff --git a/src/lib/tvgSaverImpl.h b/src/lib/tvgSaverImpl.h deleted file mode 100644 index 9a83312d..00000000 --- a/src/lib/tvgSaverImpl.h +++ /dev/null @@ -1,409 +0,0 @@ -/* - * Copyright (c) 2021 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_SAVER_IMPL_H_ -#define _TVG_SAVER_IMPL_H_ - -#include -#include -#include -#include "tvgPaint.h" -#include "tvgBinaryDesc.h" - -#define SIZE(A) sizeof(A) - -/************************************************************************/ -/* Internal Class Implementation */ -/************************************************************************/ - -static inline TvgBinCounter SERIAL_DONE(TvgBinCounter cnt) -{ - return SIZE(TvgBinTag) + SIZE(TvgBinCounter) + cnt; -} - -struct Saver::Impl -{ - Paint* paint = nullptr; //TODO: replace with Array - Array buffer; - - ~Impl() - { - sync(); - } - - bool sync() - { - if (paint) delete(paint); - - buffer.reset(); - - return true; - } - - bool flushTo(const std::string& path) - { - FILE* fp = fopen(path.c_str(), "w+"); - if (!fp) return false; - - if (fwrite(buffer.data, sizeof(char), buffer.count, fp) == 0) return false; - - fclose(fp); - - return true; - } - - bool writeHeader() - { - buffer.grow(TVG_HEADER_SIGNATURE_LENGTH + TVG_HEADER_VERSION_LENGTH); - - auto ptr = buffer.ptr(); - memcpy(ptr, TVG_HEADER_SIGNATURE, TVG_HEADER_SIGNATURE_LENGTH); - ptr += TVG_HEADER_SIGNATURE_LENGTH; - memcpy(ptr, TVG_HEADER_VERSION, TVG_HEADER_VERSION_LENGTH); - ptr += TVG_HEADER_VERSION_LENGTH; - - buffer.count += (TVG_HEADER_SIGNATURE_LENGTH + TVG_HEADER_VERSION_LENGTH); - - return true; - } - - void writeTag(TvgBinTag tag) - { - buffer.grow(SIZE(TvgBinTag)); - memcpy(buffer.ptr(), &tag, SIZE(TvgBinTag)); - buffer.count += SIZE(TvgBinTag); - } - - void writeCount(TvgBinCounter cnt) - { - buffer.grow(SIZE(TvgBinCounter)); - memcpy(buffer.ptr(), &cnt, SIZE(TvgBinCounter)); - buffer.count += SIZE(TvgBinCounter); - } - - void writeReservedCount(TvgBinCounter cnt) - { - memcpy(buffer.ptr() - cnt - SIZE(TvgBinCounter), &cnt, SIZE(TvgBinCounter)); - } - - void reserveCount() - { - buffer.grow(SIZE(TvgBinCounter)); - buffer.count += SIZE(TvgBinCounter); - } - - TvgBinCounter writeData(const void* data, TvgBinCounter cnt) - { - buffer.grow(cnt); - memcpy(buffer.ptr(), data, cnt); - buffer.count += cnt; - - return cnt; - } - - TvgBinCounter writeTagProperty(TvgBinTag tag, TvgBinCounter cnt, const void* data) - { - TvgBinCounter growCnt = SERIAL_DONE(cnt); - - buffer.grow(growCnt); - - auto ptr = buffer.ptr(); - - *ptr = tag; - ++ptr; - - memcpy(ptr, &cnt, SIZE(TvgBinCounter)); - ptr += SIZE(TvgBinCounter); - - memcpy(ptr, data, cnt); - ptr += cnt; - - buffer.count += growCnt; - - return growCnt; - } - - TvgBinCounter serializePaint(const Paint* paint) - { - TvgBinCounter cnt = 0; - - //opacity - auto opacity = paint->opacity(); - if (opacity < 255) { - cnt += writeTagProperty(TVG_TAG_PAINT_OPACITY, sizeof(opacity), &opacity); - } - - //transform - auto m = const_cast(paint)->transform(); - if (fabs(m.e11 - 1) > FLT_EPSILON || fabs(m.e12) > FLT_EPSILON || fabs(m.e13) > FLT_EPSILON || - fabs(m.e21) > FLT_EPSILON || fabs(m.e22 - 1) > FLT_EPSILON || fabs(m.e23) > FLT_EPSILON || - fabs(m.e31) > FLT_EPSILON || fabs(m.e32) > FLT_EPSILON || fabs(m.e33 - 1) > FLT_EPSILON) { - cnt += writeTagProperty(TVG_TAG_PAINT_TRANSFORM, sizeof(m), &m); - } - - //composite - const Paint* cmpTarget = nullptr; - auto cmpMethod = paint->composite(&cmpTarget); - if (cmpMethod != CompositeMethod::None && cmpTarget) { - cnt += serializeComposite(cmpTarget, cmpMethod); - } - - return cnt; - } - - TvgBinCounter serializeScene(const Scene* scene) - { - writeTag(TVG_TAG_CLASS_SCENE); - reserveCount(); - - auto cnt = serializeChildren(scene) + serializePaint(scene); - - writeReservedCount(cnt); - - return SERIAL_DONE(cnt); - } - - TvgBinCounter serializeFill(const Fill* fill, TvgBinTag tag) - { - const Fill::ColorStop* stops = nullptr; - auto stopsCnt = fill->colorStops(&stops); - if (!stops || stopsCnt == 0) return 0; - - writeTag(tag); - reserveCount(); - - TvgBinCounter cnt = 0; - - //radial fill - if (fill->id() == TVG_CLASS_ID_RADIAL) { - float args[3]; - static_cast(fill)->radial(args, args + 1,args + 2); - cnt += writeTagProperty(TVG_TAG_FILL_RADIAL_GRADIENT, sizeof(args), args); - //linear fill - } else { - float args[4]; - static_cast(fill)->linear(args, args + 1, args + 2, args + 3); - cnt += writeTagProperty(TVG_TAG_FILL_LINEAR_GRADIENT, sizeof(args), args); - } - - auto flag = static_cast(fill->spread()); - cnt += writeTagProperty(TVG_TAG_FILL_FILLSPREAD, SIZE(TvgBinFlag), &flag); - cnt += writeTagProperty(TVG_TAG_FILL_COLORSTOPS, stopsCnt * sizeof(stops), stops); - - writeReservedCount(cnt); - - return SERIAL_DONE(cnt); - } - - TvgBinCounter serializeStroke(const Shape* shape) - { - writeTag(TVG_TAG_SHAPE_STROKE); - reserveCount(); - - //cap - auto flag = static_cast(shape->strokeCap()); - auto cnt = writeTagProperty(TVG_TAG_SHAPE_STROKE_CAP, SIZE(TvgBinFlag), &flag); - - //join - flag = static_cast(shape->strokeJoin()); - cnt += writeTagProperty(TVG_TAG_SHAPE_STROKE_JOIN, SIZE(TvgBinFlag), &flag); - - //width - auto width = shape->strokeWidth(); - cnt += writeTagProperty(TVG_TAG_SHAPE_STROKE_WIDTH, sizeof(width), &width); - - //fill - if (auto fill = shape->strokeFill()) { - cnt += serializeFill(fill, TVG_TAG_SHAPE_STROKE_FILL); - } else { - uint8_t color[4] = {0, 0, 0, 0}; - shape->strokeColor(color, color + 1, color + 2, color + 3); - cnt += writeTagProperty(TVG_TAG_SHAPE_STROKE_COLOR, sizeof(color), &color); - } - - //dash - const float* dashPattern = nullptr; - auto dashCnt = shape->strokeDash(&dashPattern); - if (dashPattern && dashCnt > 0) { - TvgBinCounter dashCntSize = sizeof(dashCnt); - TvgBinCounter dashPtrnSize = dashCnt * sizeof(dashPattern[0]); - - writeTag(TVG_TAG_SHAPE_STROKE_DASHPTRN); - writeCount(dashCntSize + dashPtrnSize); - cnt += writeData(&dashCnt, dashCntSize); - cnt += writeData(dashPattern, dashPtrnSize); - cnt += SIZE(TvgBinTag) + SIZE(TvgBinCounter); - } - - writeReservedCount(cnt); - - return SERIAL_DONE(cnt); - } - - TvgBinCounter serializePath(const Shape* shape) - { - const PathCommand* cmds = nullptr; - auto cmdCnt = shape->pathCommands(&cmds); - const Point* pts = nullptr; - auto ptsCnt = shape->pathCoords(&pts); - - if (!cmds || !pts || cmdCnt == 0 || ptsCnt == 0) return 0; - - writeTag(TVG_TAG_SHAPE_PATH); - reserveCount(); - - auto cnt = writeData(&cmdCnt, sizeof(cmdCnt)); - cnt += writeData(&ptsCnt, sizeof(ptsCnt)); - cnt += writeData(cmds, cmdCnt * sizeof(cmds[0])); - cnt += writeData(pts, ptsCnt * sizeof(pts[0])); - - writeReservedCount(cnt); - - return SERIAL_DONE(cnt); - } - - TvgBinCounter serializeShape(const Shape* shape) - { - writeTag(TVG_TAG_CLASS_SHAPE); - reserveCount(); - - //fill rule - auto flag = (shape->fillRule() == FillRule::EvenOdd) ? TVG_FLAG_SHAPE_FILLRULE_EVENODD : TVG_FLAG_SHAPE_FILLRULE_WINDING; - auto cnt = writeTagProperty(TVG_TAG_SHAPE_FILLRULE, SIZE(TvgBinFlag), &flag); - - //stroke - if (shape->strokeWidth() > 0) cnt += serializeStroke(shape); - - //fill - if (auto fill = shape->fill()) { - cnt += serializeFill(fill, TVG_TAG_SHAPE_FILL); - } else { - uint8_t color[4] = {0, 0, 0, 0}; - shape->fillColor(color, color + 1, color + 2, color + 3); - cnt += writeTagProperty(TVG_TAG_SHAPE_COLOR, sizeof(color), color); - } - - cnt += serializePath(shape); - cnt += serializePaint(shape); - - writeReservedCount(cnt); - - return SERIAL_DONE(cnt); - } - - TvgBinCounter serializePicture(const Picture* picture) - { - writeTag(TVG_TAG_CLASS_PICTURE); - reserveCount(); - - TvgBinCounter cnt = 0; - - //Bitmap Image - if (auto pixels = picture->data()) { - //TODO: Loader expects uints - float fw, fh; - picture->size(&fw, &fh); - - auto w = static_cast(fw); - auto h = static_cast(fh); - TvgBinCounter sizeCnt = sizeof(w); - TvgBinCounter imgSize = w * h * sizeof(pixels[0]); - - writeTag(TVG_TAG_PICTURE_RAW_IMAGE); - writeCount(2 * sizeCnt + imgSize); - - cnt += writeData(&w, sizeCnt); - cnt += writeData(&h, sizeCnt); - cnt += writeData(pixels, imgSize); - cnt += SIZE(TvgBinTag) + SIZE(TvgBinCounter); - //Vector Image - } else { - cnt += serializeChildren(picture); - } - - cnt += serializePaint(picture); - - writeReservedCount(cnt); - - return SERIAL_DONE(cnt); - } - - TvgBinCounter serializeComposite(const Paint* cmpTarget, CompositeMethod cmpMethod) - { - writeTag(TVG_TAG_PAINT_CMP_TARGET); - reserveCount(); - - auto flag = static_cast(cmpMethod); - auto cnt = writeTagProperty(TVG_TAG_PAINT_CMP_METHOD, SIZE(TvgBinFlag), &flag); - - cnt += serialize(cmpTarget); - - writeReservedCount(cnt); - - return SERIAL_DONE(cnt); - } - - TvgBinCounter serializeChildren(const Paint* paint) - { - auto it = paint->pImpl->iterator(); - if (!it) return 0; - - TvgBinCounter cnt = 0; - - while (auto p = it->next()) - cnt += serialize(p); - - delete(it); - - return cnt; - } - - TvgBinCounter serialize(const Paint* paint) - { - if (!paint) return 0; - - switch (paint->id()) { - case TVG_CLASS_ID_SHAPE: return serializeShape(static_cast(paint)); - case TVG_CLASS_ID_SCENE: return serializeScene(static_cast(paint)); - case TVG_CLASS_ID_PICTURE: return serializePicture(static_cast(paint)); - } - - return 0; - } - - bool save(Paint* paint, const std::string& path) - { - //FIXME: use Array and remove sync() here - sync(); - - //TODO: Validate path - - this->paint = paint; - - if (!writeHeader()) return false; - if (serialize(paint) == 0) return false; - if (!flushTo(path)) return false; - - return true; - } -}; - -#endif //_TVG_SAVER_IMPL_H_ diff --git a/src/loaders/meson.build b/src/loaders/meson.build index 0e580b9f..24b65448 100644 --- a/src/loaders/meson.build +++ b/src/loaders/meson.build @@ -14,7 +14,6 @@ endif if get_option('loaders').contains('jpg') == true subdir('jpg') - message('Enable JPG Loader') endif subdir('raw') diff --git a/src/meson.build b/src/meson.build index 119dec2d..3302e330 100644 --- a/src/meson.build +++ b/src/meson.build @@ -15,10 +15,11 @@ endif subdir('lib') subdir('loaders') +subdir('savers') subdir('bindings') thread_dep = meson.get_compiler('cpp').find_library('pthread') -thorvg_lib_dep = [common_dep, loader_dep, binding_dep, thread_dep] +thorvg_lib_dep = [common_dep, loader_dep, saver_dep, binding_dep, thread_dep] thorvg_lib = library( 'thorvg', diff --git a/src/savers/meson.build b/src/savers/meson.build new file mode 100644 index 00000000..2b35e381 --- /dev/null +++ b/src/savers/meson.build @@ -0,0 +1,10 @@ +subsaver_dep = [] + +if get_option('savers').contains('tvg') == true + subdir('tvg') +endif + +saver_dep = declare_dependency( + dependencies: subsaver_dep, + include_directories : include_directories('.'), +) diff --git a/src/savers/tvg/meson.build b/src/savers/tvg/meson.build new file mode 100644 index 00000000..87eb9927 --- /dev/null +++ b/src/savers/tvg/meson.build @@ -0,0 +1,9 @@ +source_file = [ + 'tvgTvgSaver.h', + 'tvgTvgSaver.cpp', +] + +subsaver_dep += [declare_dependency( + include_directories : include_directories('.'), + sources : source_file +)] diff --git a/src/savers/tvg/tvgTvgSaver.cpp b/src/savers/tvg/tvgTvgSaver.cpp new file mode 100644 index 00000000..ba9abbe4 --- /dev/null +++ b/src/savers/tvg/tvgTvgSaver.cpp @@ -0,0 +1,434 @@ +/* + * Copyright (c) 2021 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 +#include +#include +#include "tvgSaver.h" +#include "tvgTvgSaver.h" + +#define SIZE(A) sizeof(A) + +/************************************************************************/ +/* Internal Class Implementation */ +/************************************************************************/ + +static inline TvgBinCounter SERIAL_DONE(TvgBinCounter cnt) +{ + return SIZE(TvgBinTag) + SIZE(TvgBinCounter) + cnt; +} + + +bool TvgSaver::flushTo(const std::string& path) +{ + FILE* fp = fopen(path.c_str(), "w+"); + if (!fp) return false; + + if (fwrite(buffer.data, sizeof(char), buffer.count, fp) == 0) return false; + + fclose(fp); + + return true; +} + + +bool TvgSaver::writeHeader() +{ + buffer.grow(TVG_HEADER_SIGNATURE_LENGTH + TVG_HEADER_VERSION_LENGTH); + + auto ptr = buffer.ptr(); + memcpy(ptr, TVG_HEADER_SIGNATURE, TVG_HEADER_SIGNATURE_LENGTH); + ptr += TVG_HEADER_SIGNATURE_LENGTH; + memcpy(ptr, TVG_HEADER_VERSION, TVG_HEADER_VERSION_LENGTH); + ptr += TVG_HEADER_VERSION_LENGTH; + + buffer.count += (TVG_HEADER_SIGNATURE_LENGTH + TVG_HEADER_VERSION_LENGTH); + + return true; +} + + +void TvgSaver::writeTag(TvgBinTag tag) +{ + buffer.grow(SIZE(TvgBinTag)); + memcpy(buffer.ptr(), &tag, SIZE(TvgBinTag)); + buffer.count += SIZE(TvgBinTag); +} + + +void TvgSaver::writeCount(TvgBinCounter cnt) +{ + buffer.grow(SIZE(TvgBinCounter)); + memcpy(buffer.ptr(), &cnt, SIZE(TvgBinCounter)); + buffer.count += SIZE(TvgBinCounter); +} + + +void TvgSaver::writeReservedCount(TvgBinCounter cnt) +{ + memcpy(buffer.ptr() - cnt - SIZE(TvgBinCounter), &cnt, SIZE(TvgBinCounter)); +} + + +void TvgSaver::reserveCount() +{ + buffer.grow(SIZE(TvgBinCounter)); + buffer.count += SIZE(TvgBinCounter); +} + + +TvgBinCounter TvgSaver::writeData(const void* data, TvgBinCounter cnt) +{ + buffer.grow(cnt); + memcpy(buffer.ptr(), data, cnt); + buffer.count += cnt; + + return cnt; +} + + +TvgBinCounter TvgSaver::writeTagProperty(TvgBinTag tag, TvgBinCounter cnt, const void* data) +{ + auto growCnt = SERIAL_DONE(cnt); + + buffer.grow(growCnt); + + auto ptr = buffer.ptr(); + + *ptr = tag; + ++ptr; + + memcpy(ptr, &cnt, SIZE(TvgBinCounter)); + ptr += SIZE(TvgBinCounter); + + memcpy(ptr, data, cnt); + ptr += cnt; + + buffer.count += growCnt; + + return growCnt; +} + + +TvgBinCounter TvgSaver::serializePaint(const Paint* paint) +{ + TvgBinCounter cnt = 0; + + //opacity + auto opacity = paint->opacity(); + if (opacity < 255) { + cnt += writeTagProperty(TVG_TAG_PAINT_OPACITY, sizeof(opacity), &opacity); + } + + //transform + auto m = const_cast(paint)->transform(); + if (fabs(m.e11 - 1) > FLT_EPSILON || fabs(m.e12) > FLT_EPSILON || fabs(m.e13) > FLT_EPSILON || + fabs(m.e21) > FLT_EPSILON || fabs(m.e22 - 1) > FLT_EPSILON || fabs(m.e23) > FLT_EPSILON || + fabs(m.e31) > FLT_EPSILON || fabs(m.e32) > FLT_EPSILON || fabs(m.e33 - 1) > FLT_EPSILON) { + cnt += writeTagProperty(TVG_TAG_PAINT_TRANSFORM, sizeof(m), &m); + } + + //composite + const Paint* cmpTarget = nullptr; + auto cmpMethod = paint->composite(&cmpTarget); + if (cmpMethod != CompositeMethod::None && cmpTarget) { + cnt += serializeComposite(cmpTarget, cmpMethod); + } + + return cnt; +} + + +TvgBinCounter TvgSaver::serializeScene(const Scene* scene) +{ + writeTag(TVG_TAG_CLASS_SCENE); + reserveCount(); + + auto cnt = serializeChildren(scene) + serializePaint(scene); + + writeReservedCount(cnt); + + return SERIAL_DONE(cnt); +} + + +TvgBinCounter TvgSaver::serializeFill(const Fill* fill, TvgBinTag tag) +{ + const Fill::ColorStop* stops = nullptr; + auto stopsCnt = fill->colorStops(&stops); + if (!stops || stopsCnt == 0) return 0; + + writeTag(tag); + reserveCount(); + + TvgBinCounter cnt = 0; + + //radial fill + if (fill->id() == TVG_CLASS_ID_RADIAL) { + float args[3]; + static_cast(fill)->radial(args, args + 1,args + 2); + cnt += writeTagProperty(TVG_TAG_FILL_RADIAL_GRADIENT, sizeof(args), args); + //linear fill + } else { + float args[4]; + static_cast(fill)->linear(args, args + 1, args + 2, args + 3); + cnt += writeTagProperty(TVG_TAG_FILL_LINEAR_GRADIENT, sizeof(args), args); + } + + auto flag = static_cast(fill->spread()); + cnt += writeTagProperty(TVG_TAG_FILL_FILLSPREAD, SIZE(TvgBinFlag), &flag); + cnt += writeTagProperty(TVG_TAG_FILL_COLORSTOPS, stopsCnt * sizeof(stops), stops); + + writeReservedCount(cnt); + + return SERIAL_DONE(cnt); +} + + +TvgBinCounter TvgSaver::serializeStroke(const Shape* shape) +{ + writeTag(TVG_TAG_SHAPE_STROKE); + reserveCount(); + + //cap + auto flag = static_cast(shape->strokeCap()); + auto cnt = writeTagProperty(TVG_TAG_SHAPE_STROKE_CAP, SIZE(TvgBinFlag), &flag); + + //join + flag = static_cast(shape->strokeJoin()); + cnt += writeTagProperty(TVG_TAG_SHAPE_STROKE_JOIN, SIZE(TvgBinFlag), &flag); + + //width + auto width = shape->strokeWidth(); + cnt += writeTagProperty(TVG_TAG_SHAPE_STROKE_WIDTH, sizeof(width), &width); + + //fill + if (auto fill = shape->strokeFill()) { + cnt += serializeFill(fill, TVG_TAG_SHAPE_STROKE_FILL); + } else { + uint8_t color[4] = {0, 0, 0, 0}; + shape->strokeColor(color, color + 1, color + 2, color + 3); + cnt += writeTagProperty(TVG_TAG_SHAPE_STROKE_COLOR, sizeof(color), &color); + } + + //dash + const float* dashPattern = nullptr; + auto dashCnt = shape->strokeDash(&dashPattern); + if (dashPattern && dashCnt > 0) { + TvgBinCounter dashCntSize = sizeof(dashCnt); + TvgBinCounter dashPtrnSize = dashCnt * sizeof(dashPattern[0]); + + writeTag(TVG_TAG_SHAPE_STROKE_DASHPTRN); + writeCount(dashCntSize + dashPtrnSize); + cnt += writeData(&dashCnt, dashCntSize); + cnt += writeData(dashPattern, dashPtrnSize); + cnt += SIZE(TvgBinTag) + SIZE(TvgBinCounter); + } + + writeReservedCount(cnt); + + return SERIAL_DONE(cnt); +} + + +TvgBinCounter TvgSaver::serializePath(const Shape* shape) +{ + const PathCommand* cmds = nullptr; + auto cmdCnt = shape->pathCommands(&cmds); + const Point* pts = nullptr; + auto ptsCnt = shape->pathCoords(&pts); + + if (!cmds || !pts || cmdCnt == 0 || ptsCnt == 0) return 0; + + writeTag(TVG_TAG_SHAPE_PATH); + reserveCount(); + + auto cnt = writeData(&cmdCnt, sizeof(cmdCnt)); + cnt += writeData(&ptsCnt, sizeof(ptsCnt)); + cnt += writeData(cmds, cmdCnt * sizeof(cmds[0])); + cnt += writeData(pts, ptsCnt * sizeof(pts[0])); + + writeReservedCount(cnt); + + return SERIAL_DONE(cnt); +} + + +TvgBinCounter TvgSaver::serializeShape(const Shape* shape) +{ + writeTag(TVG_TAG_CLASS_SHAPE); + reserveCount(); + + //fill rule + auto flag = (shape->fillRule() == FillRule::EvenOdd) ? TVG_FLAG_SHAPE_FILLRULE_EVENODD : TVG_FLAG_SHAPE_FILLRULE_WINDING; + auto cnt = writeTagProperty(TVG_TAG_SHAPE_FILLRULE, SIZE(TvgBinFlag), &flag); + + //stroke + if (shape->strokeWidth() > 0) cnt += serializeStroke(shape); + + //fill + if (auto fill = shape->fill()) { + cnt += serializeFill(fill, TVG_TAG_SHAPE_FILL); + } else { + uint8_t color[4] = {0, 0, 0, 0}; + shape->fillColor(color, color + 1, color + 2, color + 3); + cnt += writeTagProperty(TVG_TAG_SHAPE_COLOR, sizeof(color), color); + } + + cnt += serializePath(shape); + cnt += serializePaint(shape); + + writeReservedCount(cnt); + + return SERIAL_DONE(cnt); +} + + +TvgBinCounter TvgSaver::serializePicture(const Picture* picture) +{ + writeTag(TVG_TAG_CLASS_PICTURE); + reserveCount(); + + TvgBinCounter cnt = 0; + + //Bitmap Image + if (auto pixels = picture->data()) { + //TODO: Loader expects uints + float fw, fh; + picture->size(&fw, &fh); + + auto w = static_cast(fw); + auto h = static_cast(fh); + TvgBinCounter sizeCnt = sizeof(w); + TvgBinCounter imgSize = w * h * sizeof(pixels[0]); + + writeTag(TVG_TAG_PICTURE_RAW_IMAGE); + writeCount(2 * sizeCnt + imgSize); + + cnt += writeData(&w, sizeCnt); + cnt += writeData(&h, sizeCnt); + cnt += writeData(pixels, imgSize); + cnt += SIZE(TvgBinTag) + SIZE(TvgBinCounter); + //Vector Image + } else { + cnt += serializeChildren(picture); + } + + cnt += serializePaint(picture); + + writeReservedCount(cnt); + + return SERIAL_DONE(cnt); +} + + +TvgBinCounter TvgSaver::serializeComposite(const Paint* cmpTarget, CompositeMethod cmpMethod) +{ + writeTag(TVG_TAG_PAINT_CMP_TARGET); + reserveCount(); + + auto flag = static_cast(cmpMethod); + auto cnt = writeTagProperty(TVG_TAG_PAINT_CMP_METHOD, SIZE(TvgBinFlag), &flag); + + cnt += serialize(cmpTarget); + + writeReservedCount(cnt); + + return SERIAL_DONE(cnt); +} + + +TvgBinCounter TvgSaver::serializeChildren(const Paint* paint) +{ + auto it = this->iterator(paint); + if (!it) return 0; + + TvgBinCounter cnt = 0; + + while (auto p = it->next()) + cnt += serialize(p); + + delete(it); + + return cnt; +} + + +TvgBinCounter TvgSaver::serialize(const Paint* paint) +{ + if (!paint) return 0; + + switch (paint->id()) { + case TVG_CLASS_ID_SHAPE: return serializeShape(static_cast(paint)); + case TVG_CLASS_ID_SCENE: return serializeScene(static_cast(paint)); + case TVG_CLASS_ID_PICTURE: return serializePicture(static_cast(paint)); + } + + return 0; +} + +void TvgSaver::run(unsigned tid) +{ + if (!writeHeader()) return; + if (serialize(paint) == 0) return; + if (!flushTo(path)) return; +} + + +/************************************************************************/ +/* External Class Implementation */ +/************************************************************************/ + +TvgSaver::~TvgSaver() +{ + close(); +} + +bool TvgSaver::close() +{ + this->done(); + + if (paint) { + delete(paint); + paint = nullptr; + } + if (path) { + free(path); + path = nullptr; + } + buffer.reset(); + return true; +} + + +bool TvgSaver::save(Paint* paint, const string& path) +{ + close(); + + this->path = strdup(path.c_str()); + if (!this->path) return false; + + this->paint = paint; + + TaskScheduler::request(this); + + return true; +} \ No newline at end of file diff --git a/src/savers/tvg/tvgTvgSaver.h b/src/savers/tvg/tvgTvgSaver.h new file mode 100644 index 00000000..908d94f1 --- /dev/null +++ b/src/savers/tvg/tvgTvgSaver.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2021 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_TVGSAVER_H_ +#define _TVG_TVGSAVER_H_ + +#include "tvgArray.h" +#include "tvgBinaryDesc.h" +#include "tvgTaskScheduler.h" + +namespace tvg +{ + +class TvgSaver : public SaveModule, public Task +{ +private: + Array buffer; + Paint* paint = nullptr; + char *path = nullptr; + + bool flushTo(const std::string& path); + void reserveCount(); + + bool writeHeader(); + void writeTag(TvgBinTag tag); + void writeCount(TvgBinCounter cnt); + void writeReservedCount(TvgBinCounter cnt); + TvgBinCounter writeData(const void* data, TvgBinCounter cnt); + TvgBinCounter writeTagProperty(TvgBinTag tag, TvgBinCounter cnt, const void* data); + + TvgBinCounter serialize(const Paint* paint); + TvgBinCounter serializePaint(const Paint* paint); + TvgBinCounter serializeScene(const Scene* scene); + TvgBinCounter serializeShape(const Shape* shape); + TvgBinCounter serializePicture(const Picture* picture); + TvgBinCounter serializeFill(const Fill* fill, TvgBinTag tag); + TvgBinCounter serializeStroke(const Shape* shape); + TvgBinCounter serializePath(const Shape* shape); + TvgBinCounter serializeComposite(const Paint* cmpTarget, CompositeMethod cmpMethod); + TvgBinCounter serializeChildren(const Paint* paint); + +public: + ~TvgSaver(); + + bool save(Paint* paint, const string& path) override; + bool close() override; + void run(unsigned tid) override; +}; + +} + +#endif //_TVG_SAVE_MODULE_H_ \ No newline at end of file