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> paint, const std::string& path) noexcept;
Result Saver::sync() noexcept;
@Examples: tvgSaver
@Co-author: Mira Grudzinska <m.grudzinska@samsung.com>
friend Canvas; \
friend Scene; \
friend Picture; \
- friend Saver;
+ friend SaveModule; \
#define _TVG_DECALRE_IDENTIFIER() \
{
class RenderMethod;
+class SaveModule;
class Scene;
class Picture;
class Canvas;
-class Saver;
/**
* @defgroup ThorVG ThorVG
_TVG_DECLARE_PRIVATE(Saver);
};
-
/** @}*/
} //namespace
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
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(),
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'),
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'],
//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;
}
'tvgLoaderMgr.h',
'tvgPictureImpl.h',
'tvgRender.h',
- 'tvgSaverImpl.h',
+ 'tvgSaver.h',
'tvgSceneImpl.h',
'tvgShapeImpl.h',
'tvgTaskScheduler.h',
#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
#include "tvgLoader.h"
-enum class FileType { Tvg = 0, Svg, Raw, Png, Jpg, Unknown };
-
struct LoaderMgr
{
static bool init();
/* Internal Class Implementation */
/************************************************************************/
+
+
static inline bool FLT_SAME(float a, float b)
{
return (fabsf(a - b) < FLT_EPSILON);
namespace tvg
{
+
struct Iterator
{
virtual ~Iterator() {}
{
Paint* paint = nullptr;
- PictureIterator(Paint* p) : paint(p)
- {
- }
+ PictureIterator(Paint* p) : paint(p) {}
const Paint* next() override
{
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
-#include "tvgSaverImpl.h"
-#include <iostream>
+#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 */
/************************************************************************/
}
-Result Saver::save(std::unique_ptr<Paint> paint, const std::string& path) noexcept
+Result Saver::save(std::unique_ptr<Paint> 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;
}
--- /dev/null
+/*
+ * 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
+++ /dev/null
-/*
- * 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 <float.h>
-#include <math.h>
-#include <stdio.h>
-#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<TvgBinByte> 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*>(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<const RadialGradient*>(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<const LinearGradient*>(fill)->linear(args, args + 1, args + 2, args + 3);
- cnt += writeTagProperty(TVG_TAG_FILL_LINEAR_GRADIENT, sizeof(args), args);
- }
-
- auto flag = static_cast<TvgBinFlag>(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<TvgBinFlag>(shape->strokeCap());
- auto cnt = writeTagProperty(TVG_TAG_SHAPE_STROKE_CAP, SIZE(TvgBinFlag), &flag);
-
- //join
- flag = static_cast<TvgBinFlag>(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<uint32_t>(fw);
- auto h = static_cast<uint32_t>(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<TvgBinFlag>(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<const Shape*>(paint));
- case TVG_CLASS_ID_SCENE: return serializeScene(static_cast<const Scene*>(paint));
- case TVG_CLASS_ID_PICTURE: return serializePicture(static_cast<const Picture*>(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_
if get_option('loaders').contains('jpg') == true
subdir('jpg')
- message('Enable JPG Loader')
endif
subdir('raw')
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',
--- /dev/null
+subsaver_dep = []
+
+if get_option('savers').contains('tvg') == true
+ subdir('tvg')
+endif
+
+saver_dep = declare_dependency(
+ dependencies: subsaver_dep,
+ include_directories : include_directories('.'),
+)
--- /dev/null
+source_file = [
+ 'tvgTvgSaver.h',
+ 'tvgTvgSaver.cpp',
+]
+
+subsaver_dep += [declare_dependency(
+ include_directories : include_directories('.'),
+ sources : source_file
+)]
--- /dev/null
+/*
+ * 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 <float.h>
+#include <math.h>
+#include <stdio.h>
+#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*>(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<const RadialGradient*>(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<const LinearGradient*>(fill)->linear(args, args + 1, args + 2, args + 3);
+ cnt += writeTagProperty(TVG_TAG_FILL_LINEAR_GRADIENT, sizeof(args), args);
+ }
+
+ auto flag = static_cast<TvgBinFlag>(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<TvgBinFlag>(shape->strokeCap());
+ auto cnt = writeTagProperty(TVG_TAG_SHAPE_STROKE_CAP, SIZE(TvgBinFlag), &flag);
+
+ //join
+ flag = static_cast<TvgBinFlag>(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<uint32_t>(fw);
+ auto h = static_cast<uint32_t>(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<TvgBinFlag>(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<const Shape*>(paint));
+ case TVG_CLASS_ID_SCENE: return serializeScene(static_cast<const Scene*>(paint));
+ case TVG_CLASS_ID_PICTURE: return serializePicture(static_cast<const Picture*>(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
--- /dev/null
+/*
+ * 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<TvgBinByte> 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