tvg_saver: introduce a new module tvg saver
authorHermet Park <chuneon.park@samsung.com>
Wed, 21 Jul 2021 08:31:42 +0000 (17:31 +0900)
committerJunsuChoi <jsuya.choi@samsung.com>
Thu, 22 Jul 2021 08:24:22 +0000 (17:24 +0900)
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>

19 files changed:
inc/thorvg.h
meson.build
meson_options.txt
src/examples/TvgSaver.cpp
src/lib/meson.build
src/lib/tvgCommon.h
src/lib/tvgLoaderMgr.h
src/lib/tvgPaint.cpp
src/lib/tvgPaint.h
src/lib/tvgPictureImpl.h
src/lib/tvgSaver.cpp
src/lib/tvgSaver.h [new file with mode: 0644]
src/lib/tvgSaverImpl.h [deleted file]
src/loaders/meson.build
src/meson.build
src/savers/meson.build [new file with mode: 0644]
src/savers/tvg/meson.build [new file with mode: 0644]
src/savers/tvg/tvgTvgSaver.cpp [new file with mode: 0644]
src/savers/tvg/tvgTvgSaver.h [new file with mode: 0644]

index 308a109..b48d0f6 100644 (file)
@@ -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
@@ -1370,7 +1370,6 @@ public:
     _TVG_DECLARE_PRIVATE(Saver);
 };
 
-
 /** @}*/
 
 } //namespace
index 9a3717c..5563fbc 100644 (file)
@@ -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'),
index 3ae98c0..3d72baf 100644 (file)
@@ -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'],
index 181b8e9..1274460 100644 (file)
@@ -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;
 }
 
 
index c535560..372fe2d 100644 (file)
@@ -18,7 +18,7 @@ source_file = [
    'tvgLoaderMgr.h',
    'tvgPictureImpl.h',
    'tvgRender.h',
-   'tvgSaverImpl.h',
+   'tvgSaver.h',
    'tvgSceneImpl.h',
    'tvgShapeImpl.h',
    'tvgTaskScheduler.h',
index 2fed7b3..a61670e 100644 (file)
@@ -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
index 18a8e8a..fb5893d 100644 (file)
@@ -24,8 +24,6 @@
 
 #include "tvgLoader.h"
 
-enum class FileType { Tvg = 0, Svg, Raw, Png, Jpg, Unknown };
-
 struct LoaderMgr
 {
     static bool init();
index 7c539fc..2dbcadd 100644 (file)
@@ -27,6 +27,8 @@
 /* Internal Class Implementation                                        */
 /************************************************************************/
 
+
+
 static inline bool FLT_SAME(float a, float b)
 {
     return (fabsf(a - b) < FLT_EPSILON);
index 6ce5d1f..d4a67f6 100644 (file)
@@ -27,6 +27,7 @@
 
 namespace tvg
 {
+
     struct Iterator
     {
         virtual ~Iterator() {}
index 40e48e9..9a195dc 100644 (file)
@@ -34,9 +34,7 @@ struct PictureIterator : Iterator
 {
     Paint* paint = nullptr;
 
-    PictureIterator(Paint* p) : paint(p)
-    {
-    }
+    PictureIterator(Paint* p) : paint(p) {}
 
     const Paint* next() override
     {
index 5f50807..212d996 100644 (file)
  * 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                                        */
 /************************************************************************/
@@ -41,21 +97,34 @@ Saver::~Saver()
 }
 
 
-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;
 }
 
 
diff --git a/src/lib/tvgSaver.h b/src/lib/tvgSaver.h
new file mode 100644 (file)
index 0000000..3586442
--- /dev/null
@@ -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 (file)
index 9a83312..0000000
+++ /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 <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_
index 0e580b9..24b6544 100644 (file)
@@ -14,7 +14,6 @@ endif
 
 if get_option('loaders').contains('jpg') == true
     subdir('jpg')
-    message('Enable JPG Loader')
 endif
 
 subdir('raw')
index 119dec2..3302e33 100644 (file)
@@ -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 (file)
index 0000000..2b35e38
--- /dev/null
@@ -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 (file)
index 0000000..87eb992
--- /dev/null
@@ -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 (file)
index 0000000..ba9abbe
--- /dev/null
@@ -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 <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
diff --git a/src/savers/tvg/tvgTvgSaver.h b/src/savers/tvg/tvgTvgSaver.h
new file mode 100644 (file)
index 0000000..908d94f
--- /dev/null
@@ -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<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