tvg_saver: implementation of the Saver class
authorMira Grudzinska <67589014+mgrudzinska@users.noreply.github.com>
Mon, 19 Jul 2021 08:31:36 +0000 (10:31 +0200)
committerJunsuChoi <jsuya.choi@samsung.com>
Thu, 22 Jul 2021 08:24:22 +0000 (17:24 +0900)
The Saver class enables to save any Paint object (Scene, Shape, Picture)
in a binary file. To read the file the tvg loader should be used.
To save a paint a new API was introduced - tvg::Saver::save.

inc/thorvg.h
src/lib/meson.build
src/lib/tvgBinaryDesc.h
src/lib/tvgPictureImpl.h
src/lib/tvgSaver.cpp [new file with mode: 0644]
src/lib/tvgSaverImpl.h [new file with mode: 0644]

index 3a79822..e172570 100644 (file)
@@ -1378,6 +1378,39 @@ public:
     _TVG_DISABLE_CTOR(Initializer);
 };
 
+
+/**
+ * @class Saver
+ *
+ * @brief A class enabling saving a paint in a binary format.
+ *
+ * @BETA_API
+ */
+class TVG_EXPORT Saver
+{
+public:
+    ~Saver();
+
+    /**
+     * @brief Saves all the paints from the tree in a binary format.
+     *
+     * @param[in] paint The root paint to be saved with all its nodes.
+     * @param[in] path A path to the file, in which the data is to be saved.
+     *
+     * @retval Result::Success When succeed.
+     * @retval Result::InvalidArguments the @p path is empty or @c nullptr is passed as the @p paint.
+     * @retval Result::FailedAllocation An internal error with a memory allocation for the Saver object.
+     * @retval Result::MemoryCorruption When casting in the internal function implementation failed.
+     * @retval Result::Unknown Others.
+     *
+     * @BETA_API
+     */
+    static Result save(std::unique_ptr<Paint> paint, const std::string& path) noexcept;
+
+    _TVG_DECLARE_PRIVATE(Saver);
+};
+
+
 /** @}*/
 
 } //namespace
index 089a8fc..c535560 100644 (file)
@@ -18,6 +18,7 @@ source_file = [
    'tvgLoaderMgr.h',
    'tvgPictureImpl.h',
    'tvgRender.h',
+   'tvgSaverImpl.h',
    'tvgSceneImpl.h',
    'tvgShapeImpl.h',
    'tvgTaskScheduler.h',
@@ -32,6 +33,7 @@ source_file = [
    'tvgPicture.cpp',
    'tvgRadialGradient.cpp',
    'tvgRender.cpp',
+   'tvgSaver.cpp',
    'tvgScene.cpp',
    'tvgShape.cpp',
    'tvgSwCanvas.cpp',
index d5e5ace..6d665cf 100644 (file)
@@ -54,6 +54,7 @@ struct tvgBlock
     #define TVG_BIN_HEADER_SIGNATURE_LENGTH 3
     #define TVG_BIN_HEADER_VERSION "000"
     #define TVG_BIN_HEADER_VERSION_LENGTH 3
+    #define TVG_BIN_HEADER_DATA_LENGTH 2
 #endif
 
 #define TVG_PICTURE_BEGIN_INDICATOR   (TvgIndicator)0xfc
index e2bf355..1178afb 100644 (file)
@@ -221,6 +221,7 @@ struct Picture::Impl
 
     Paint::Iterator begin()
     {
+        reload();
         return Paint::Iterator(picture, paint);
     }
 
diff --git a/src/lib/tvgSaver.cpp b/src/lib/tvgSaver.cpp
new file mode 100644 (file)
index 0000000..761c571
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * 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 "tvgSaverImpl.h"
+#include <iostream>
+
+/************************************************************************/
+/* Internal Class Implementation                                        */
+/************************************************************************/
+
+/************************************************************************/
+/* External Class Implementation                                        */
+/************************************************************************/
+
+Saver::Saver() : pImpl(new Impl(this))
+{
+}
+
+
+Saver::~Saver()
+{
+    delete(pImpl);
+}
+
+
+Result Saver::save(std::unique_ptr<Paint> paint, const std::string& path) noexcept
+{
+    if (!paint || path.empty()) return Result::InvalidArguments;
+
+    auto saver = unique_ptr<Saver>(new Saver());
+    if (!saver) return Result::FailedAllocation;
+
+    auto p = paint.release();
+    if (!p) return Result::MemoryCorruption;
+
+    if (saver->pImpl->save(p, path)) {
+        delete p;
+        return Result::Success;
+    }
+
+    delete p;
+    return Result::Unknown;
+}
diff --git a/src/lib/tvgSaverImpl.h b/src/lib/tvgSaverImpl.h
new file mode 100644 (file)
index 0000000..8013238
--- /dev/null
@@ -0,0 +1,494 @@
+/*
+ * 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 "tvgPaint.h"
+#include "tvgBinaryDesc.h"
+#include <float.h>
+#include <math.h>
+#include <fstream>
+
+struct Saver::Impl
+{
+    Saver* saver;
+    char* buffer = nullptr;
+    char* pointer = nullptr;
+    uint32_t size = 0;
+    uint32_t reserved = 0;
+
+
+    Impl(Saver* s) : saver(s)
+    {
+    }
+
+
+    ~Impl()
+    {
+        clearBuffer();
+    }
+
+
+    bool prepareBuffer()
+    {
+        reserved = TVG_BIN_HEADER_SIGNATURE_LENGTH + TVG_BIN_HEADER_VERSION_LENGTH + TVG_BIN_HEADER_DATA_LENGTH;
+        buffer = static_cast<char*>(malloc(reserved));
+        if (!buffer) {
+            reserved = 0;
+            return false;
+        }
+        pointer = buffer;
+        return true;
+    }
+
+
+    void resizeBuffer(uint32_t newSize)
+    {
+        //OPTIMIZE ME: find more optimal alg ? "*2" is not opt when raw/png is used
+        reserved += 100;
+        if (newSize > reserved) reserved = newSize + 100;
+
+        auto bufferOld = buffer;
+
+        buffer = static_cast<char*>(realloc(buffer, reserved));
+
+        if (buffer != bufferOld)
+            pointer = buffer + (pointer - bufferOld);
+    }
+
+
+    void rewindBuffer(ByteCounter bytesNum)
+    {
+        if (pointer - bytesNum < buffer) return;
+
+        pointer -= bytesNum;
+        size -= bytesNum;
+    }
+
+
+    void clearBuffer()
+    {
+        if (buffer) free(buffer);
+        buffer = nullptr;
+        pointer = nullptr;
+        size = 0;
+        reserved = 0;
+    }
+
+
+    bool saveBufferToFile(const std::string& path)
+    {
+        ofstream outFile;
+        outFile.open(path, ios::out | ios::trunc | ios::binary);
+        if (!outFile.is_open()) return false;
+        outFile.write(buffer, size);
+        outFile.close();
+
+        return true;
+    }
+
+
+    bool writeHeader()
+    {
+        const char *tvg = TVG_BIN_HEADER_SIGNATURE;
+        const char *version = TVG_BIN_HEADER_VERSION;
+        //TODO  - unused header data
+        uint16_t dataByteCnt = 0;
+        ByteCounter headerByteCnt = TVG_BIN_HEADER_SIGNATURE_LENGTH + TVG_BIN_HEADER_VERSION_LENGTH + TVG_BIN_HEADER_DATA_LENGTH;
+        if (size + headerByteCnt > reserved) resizeBuffer(headerByteCnt);
+
+        memcpy(pointer, tvg, TVG_BIN_HEADER_SIGNATURE_LENGTH);
+        pointer += TVG_BIN_HEADER_SIGNATURE_LENGTH;
+        memcpy(pointer, version, TVG_BIN_HEADER_VERSION_LENGTH);
+        pointer += TVG_BIN_HEADER_VERSION_LENGTH;
+        memcpy(pointer, &dataByteCnt, TVG_BIN_HEADER_DATA_LENGTH);
+        pointer += TVG_BIN_HEADER_DATA_LENGTH;
+
+        size += headerByteCnt;
+        return true;
+    }
+
+
+    void writeMemberIndicator(TvgIndicator ind)
+    {
+        if (size + TVG_INDICATOR_SIZE > reserved) resizeBuffer(size + TVG_INDICATOR_SIZE);
+
+        memcpy(pointer, &ind, TVG_INDICATOR_SIZE);
+        pointer += TVG_INDICATOR_SIZE;
+        size += TVG_INDICATOR_SIZE;
+    }
+
+
+    void writeMemberDataSize(ByteCounter byteCnt)
+    {
+        if (size + BYTE_COUNTER_SIZE > reserved) resizeBuffer(size + BYTE_COUNTER_SIZE);
+
+        memcpy(pointer, &byteCnt, BYTE_COUNTER_SIZE);
+        pointer += BYTE_COUNTER_SIZE;
+        size += BYTE_COUNTER_SIZE;
+    }
+
+
+    void writeMemberDataSizeAt(ByteCounter byteCnt)
+    {
+        memcpy(pointer - byteCnt - BYTE_COUNTER_SIZE, &byteCnt, BYTE_COUNTER_SIZE);
+    }
+
+
+    void skipInBufferMemberDataSize()
+    {
+        if (size + BYTE_COUNTER_SIZE > reserved) resizeBuffer(size + BYTE_COUNTER_SIZE);
+        pointer += BYTE_COUNTER_SIZE;
+        size += BYTE_COUNTER_SIZE;
+    }
+
+
+    ByteCounter writeMemberData(const void* data, ByteCounter byteCnt)
+    {
+        if (size + byteCnt > reserved) resizeBuffer(size + byteCnt);
+
+        memcpy(pointer, data, byteCnt);
+        pointer += byteCnt;
+        size += byteCnt;
+
+        return byteCnt;
+    }
+
+
+    ByteCounter writeMember(TvgIndicator ind, ByteCounter byteCnt, const void* data)
+    {
+        ByteCounter blockByteCnt = TVG_INDICATOR_SIZE + BYTE_COUNTER_SIZE + byteCnt;
+
+        if (size + blockByteCnt > reserved) resizeBuffer(size + blockByteCnt);
+
+        memcpy(pointer, &ind, TVG_INDICATOR_SIZE);
+        pointer += TVG_INDICATOR_SIZE;
+        memcpy(pointer, &byteCnt, BYTE_COUNTER_SIZE);
+        pointer += BYTE_COUNTER_SIZE;
+        memcpy(pointer, data, byteCnt);
+        pointer += byteCnt;
+
+        size += blockByteCnt;
+
+        return blockByteCnt;
+    }
+
+
+    ByteCounter serializePaint(const Paint* paint)
+    {
+        ByteCounter paintDataByteCnt = 0;
+
+        uint8_t opacity = paint->opacity();
+        if (opacity < 255) {
+            paintDataByteCnt += writeMember(TVG_PAINT_OPACITY_INDICATOR, sizeof(opacity), &opacity);
+        }
+
+        Matrix 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) {
+            paintDataByteCnt += writeMember(TVG_PAINT_TRANSFORM_MATRIX_INDICATOR, sizeof(m), &m);
+        }
+
+        const Paint* cmpTarget = nullptr;
+        auto cmpMethod = paint->composite(&cmpTarget);
+        if (cmpMethod != CompositeMethod::None && cmpTarget) {
+            paintDataByteCnt += serializeComposite(cmpTarget, cmpMethod);
+        }
+
+        return paintDataByteCnt;
+    }
+
+
+    ByteCounter serializeScene(const Paint* paint)
+    {
+        auto scene = static_cast<const Scene*>(paint);
+        if (!scene) return 0;
+
+        ByteCounter sceneDataByteCnt = 0;
+
+        writeMemberIndicator(TVG_SCENE_BEGIN_INDICATOR);
+        skipInBufferMemberDataSize();
+
+        sceneDataByteCnt += serializeChildren(paint);
+        sceneDataByteCnt += serializePaint(scene);
+
+        writeMemberDataSizeAt(sceneDataByteCnt);
+
+        return TVG_INDICATOR_SIZE + BYTE_COUNTER_SIZE + sceneDataByteCnt;
+    }
+
+
+    ByteCounter serializeShapeFill(const Fill* f, TvgIndicator fillTvgFlag)
+    {
+        ByteCounter fillDataByteCnt = 0;
+        const Fill::ColorStop* stops = nullptr;
+        auto stopsCnt = f->colorStops(&stops);
+        if (!stops || stopsCnt == 0) return 0;
+
+        writeMemberIndicator(fillTvgFlag);
+        skipInBufferMemberDataSize();
+
+        if (f->id() == FILL_ID_RADIAL) {
+            float argRadial[3];
+            auto radGrad = static_cast<const RadialGradient*>(f);
+            if (radGrad->radial(argRadial, argRadial + 1,argRadial + 2) != Result::Success) {
+                rewindBuffer(TVG_FLAG_SIZE + BYTE_COUNTER_SIZE);
+                return 0;
+            }
+            fillDataByteCnt += writeMember(TVG_FILL_RADIAL_GRADIENT_INDICATOR, sizeof(argRadial), argRadial);
+        }
+        else {
+            float argLinear[4];
+            auto linGrad = static_cast<const LinearGradient*>(f);
+            if (linGrad->linear(argLinear, argLinear + 1, argLinear + 2, argLinear + 3) != Result::Success) {
+                rewindBuffer(TVG_FLAG_SIZE + BYTE_COUNTER_SIZE);
+                return 0;
+            }
+            fillDataByteCnt += writeMember(TVG_FILL_LINEAR_GRADIENT_INDICATOR, sizeof(argLinear), argLinear);
+        }
+
+        auto flag = static_cast<TvgFlag>(f->spread());
+        fillDataByteCnt += writeMember(TVG_FILL_FILLSPREAD_INDICATOR, TVG_FLAG_SIZE, &flag);
+
+        fillDataByteCnt += writeMember(TVG_FILL_COLORSTOPS_INDICATOR, stopsCnt * sizeof(stops), stops);
+
+        writeMemberDataSizeAt(fillDataByteCnt);
+
+        return TVG_INDICATOR_SIZE + BYTE_COUNTER_SIZE + fillDataByteCnt;
+    }
+
+
+    ByteCounter serializeShapeStroke(const Shape* shape)
+    {
+        ByteCounter strokeDataByteCnt = 0;
+        TvgFlag flag;
+
+        writeMemberIndicator(TVG_SHAPE_STROKE_INDICATOR);
+        skipInBufferMemberDataSize();
+
+        flag = static_cast<TvgFlag>(shape->strokeCap());
+        strokeDataByteCnt += writeMember(TVG_SHAPE_STROKE_CAP_INDICATOR, TVG_FLAG_SIZE, &flag);
+
+        flag = static_cast<TvgFlag>(shape->strokeJoin());
+        strokeDataByteCnt += writeMember(TVG_SHAPE_STROKE_JOIN_INDICATOR, TVG_FLAG_SIZE, &flag);
+
+        float width = shape->strokeWidth();
+        strokeDataByteCnt += writeMember(TVG_SHAPE_STROKE_WIDTH_INDICATOR, sizeof(width), &width);
+
+        if (auto fill = shape->strokeFill()) {
+            strokeDataByteCnt += serializeShapeFill(fill, TVG_SHAPE_STROKE_FILL_INDICATOR);
+        } else {
+            uint8_t color[4] = {0, 0, 0, 0};
+            shape->strokeColor(color, color + 1, color + 2, color + 3);
+            strokeDataByteCnt += writeMember(TVG_SHAPE_STROKE_COLOR_INDICATOR, sizeof(color), &color);
+        }
+
+        const float* dashPattern = nullptr;
+        uint32_t dashCnt = shape->strokeDash(&dashPattern);
+        if (dashPattern && dashCnt > 0) {
+            ByteCounter dashCntByteCnt = sizeof(dashCnt);
+            ByteCounter dashPtrnByteCnt = dashCnt * sizeof(dashPattern[0]);
+
+            writeMemberIndicator(TVG_SHAPE_STROKE_DASHPTRN_INDICATOR);
+            writeMemberDataSize(dashCntByteCnt + dashPtrnByteCnt);
+            strokeDataByteCnt += writeMemberData(&dashCnt, dashCntByteCnt);
+            strokeDataByteCnt += writeMemberData(dashPattern, dashPtrnByteCnt);
+            strokeDataByteCnt += TVG_INDICATOR_SIZE + BYTE_COUNTER_SIZE;
+        }
+
+        writeMemberDataSizeAt(strokeDataByteCnt);
+
+        return TVG_INDICATOR_SIZE + BYTE_COUNTER_SIZE + strokeDataByteCnt;
+    }
+
+
+    ByteCounter serializeShapePath(const Shape* shape)
+    {
+        const PathCommand* cmds = nullptr;
+        uint32_t cmdCnt = shape->pathCommands(&cmds);
+        const Point* pts = nullptr;
+        uint32_t ptsCnt = shape->pathCoords(&pts);
+
+        if (!cmds || !pts || !cmdCnt || !ptsCnt) return 0;
+
+        ByteCounter pathDataByteCnt = 0;
+
+        writeMemberIndicator(TVG_SHAPE_PATH_INDICATOR);
+        skipInBufferMemberDataSize();
+
+        pathDataByteCnt += writeMemberData(&cmdCnt, sizeof(cmdCnt));
+        pathDataByteCnt += writeMemberData(&ptsCnt, sizeof(ptsCnt));
+        pathDataByteCnt += writeMemberData(cmds, cmdCnt * sizeof(cmds[0]));
+        pathDataByteCnt += writeMemberData(pts, ptsCnt * sizeof(pts[0]));
+
+        writeMemberDataSizeAt(pathDataByteCnt);
+
+        return TVG_INDICATOR_SIZE + BYTE_COUNTER_SIZE + pathDataByteCnt;
+    }
+
+
+    ByteCounter serializeShape(const Paint* paint)
+    {
+        auto shape = static_cast<const Shape*>(paint);
+        if (!shape) return 0;
+
+        ByteCounter shapeDataByteCnt = 0;
+
+        writeMemberIndicator(TVG_SHAPE_BEGIN_INDICATOR);
+        skipInBufferMemberDataSize();
+
+        TvgFlag ruleTvgFlag = (shape->fillRule() == FillRule::EvenOdd) ? TVG_SHAPE_FILLRULE_EVENODD_FLAG : TVG_SHAPE_FILLRULE_WINDING_FLAG;
+        shapeDataByteCnt += writeMember(TVG_SHAPE_FILLRULE_INDICATOR, TVG_FLAG_SIZE, &ruleTvgFlag);
+
+        if (shape->strokeWidth() > 0) {
+            shapeDataByteCnt += serializeShapeStroke(shape);
+        }
+
+        if (auto fill = shape->fill()) {
+            shapeDataByteCnt += serializeShapeFill(fill, TVG_SHAPE_FILL_INDICATOR);
+        } else {
+            uint8_t color[4] = {0, 0, 0, 0};
+            shape->fillColor(color, color + 1, color + 2, color + 3);
+            shapeDataByteCnt += writeMember(TVG_SHAPE_COLOR_INDICATOR, sizeof(color), color);
+        }
+
+        shapeDataByteCnt += serializeShapePath(shape);
+
+        shapeDataByteCnt += serializeChildren(paint);
+        shapeDataByteCnt += serializePaint(shape);
+
+        writeMemberDataSizeAt(shapeDataByteCnt);
+
+        return TVG_INDICATOR_SIZE + BYTE_COUNTER_SIZE + shapeDataByteCnt;
+    }
+
+
+    ByteCounter serializePicture(const Paint* paint)
+    {
+        auto picture = static_cast<const Picture*>(paint);
+        if (!picture) return 0;
+        auto pixels = picture->data();
+
+        ByteCounter pictureDataByteCnt = 0;
+
+        writeMemberIndicator(TVG_PICTURE_BEGIN_INDICATOR);
+        skipInBufferMemberDataSize();
+
+        if (pixels) {
+            //TODO - loader expects uints
+            float vw, vh;
+            picture->viewbox(nullptr, nullptr, &vw, &vh);
+
+            uint32_t w = static_cast<uint32_t>(vw);
+            uint32_t h = static_cast<uint32_t>(vh);
+            ByteCounter wByteCnt = sizeof(w); // same as h size
+            ByteCounter pixelsByteCnt = w * h * sizeof(pixels[0]);
+
+            writeMemberIndicator(TVG_RAW_IMAGE_BEGIN_INDICATOR);
+            writeMemberDataSize(2 * wByteCnt + pixelsByteCnt);
+            pictureDataByteCnt += writeMemberData(&w, wByteCnt);
+            pictureDataByteCnt += writeMemberData(&h, wByteCnt);
+            pictureDataByteCnt += writeMemberData(pixels, pixelsByteCnt);
+            pictureDataByteCnt += TVG_INDICATOR_SIZE + BYTE_COUNTER_SIZE;
+        } else {
+            pictureDataByteCnt += serializeChildren(paint);
+        }
+
+        pictureDataByteCnt += serializePaint(picture);
+
+        writeMemberDataSizeAt(pictureDataByteCnt);
+
+        return TVG_INDICATOR_SIZE + BYTE_COUNTER_SIZE + pictureDataByteCnt;
+    }
+
+
+    ByteCounter serializeComposite(const Paint* cmpTarget, CompositeMethod cmpMethod)
+    {
+        ByteCounter cmpDataByteCnt = 0;
+
+        writeMemberIndicator(TVG_PAINT_CMP_TARGET_INDICATOR);
+        skipInBufferMemberDataSize();
+
+        auto cmpMethodTvgFlag = static_cast<TvgFlag>(cmpMethod);
+        cmpDataByteCnt += writeMember(TVG_PAINT_CMP_METHOD_INDICATOR, TVG_FLAG_SIZE, &cmpMethodTvgFlag);
+
+        cmpDataByteCnt += serialize(cmpTarget);
+
+        writeMemberDataSizeAt(cmpDataByteCnt);
+
+        return TVG_INDICATOR_SIZE + BYTE_COUNTER_SIZE + cmpDataByteCnt;
+    }
+
+
+    ByteCounter serializeChildren(const Paint* paint)
+    {
+        if (!paint) return 0;
+        ByteCounter dataByteCnt = 0;
+
+        for (auto it = paint->begin(); it != paint->end(); ++it) {
+            dataByteCnt += serialize(&(*it));
+        }
+
+        return dataByteCnt;
+    }
+
+
+    ByteCounter serialize(const Paint* paint)
+    {
+        if (!paint) return 0;
+        ByteCounter dataByteCnt = 0;
+
+        switch (paint->id()) {
+            case PAINT_ID_SHAPE: {
+                dataByteCnt += serializeShape(paint);
+                break;
+            }
+            case PAINT_ID_SCENE: {
+                dataByteCnt += serializeScene(paint);
+                break;
+            }
+            case PAINT_ID_PICTURE: {
+                dataByteCnt += serializePicture(paint);
+                break;
+            }
+        }
+
+        return dataByteCnt;
+    }
+
+
+    bool save(const Paint* paint, const std::string& path)
+    {
+        if (!prepareBuffer()) return false;
+        if (!writeHeader()) return false;
+
+        if (serialize(paint) == 0) return false;
+
+        if (!saveBufferToFile(path)) return false;
+        clearBuffer();
+
+        return true;
+    }
+};
+
+#endif //_TVG_SAVER_IMPL_H_