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