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:
Mira Grudzinska 2021-07-19 10:31:36 +02:00 committed by GitHub
parent f23cba89f3
commit 0150391f03
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 592 additions and 0 deletions

View file

@ -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

View 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',

View 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

View file

@ -221,6 +221,7 @@ struct Picture::Impl
Paint::Iterator begin()
{
reload();
return Paint::Iterator(picture, paint);
}

61
src/lib/tvgSaver.cpp Normal file
View 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
View 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_