mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-07 21:23:32 +00:00
tvg_saver: implementation of the Saver class
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.
This commit is contained in:
parent
f23cba89f3
commit
0150391f03
6 changed files with 592 additions and 0 deletions
33
inc/thorvg.h
33
inc/thorvg.h
|
@ -1377,6 +1377,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
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -221,6 +221,7 @@ struct Picture::Impl
|
|||
|
||||
Paint::Iterator begin()
|
||||
{
|
||||
reload();
|
||||
return Paint::Iterator(picture, paint);
|
||||
}
|
||||
|
||||
|
|
61
src/lib/tvgSaver.cpp
Normal file
61
src/lib/tvgSaver.cpp
Normal file
|
@ -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;
|
||||
}
|
494
src/lib/tvgSaverImpl.h
Normal file
494
src/lib/tvgSaverImpl.h
Normal file
|
@ -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_
|
Loading…
Add table
Reference in a new issue