tvg_saver: introduce a new module tvg saver

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>
This commit is contained in:
Hermet Park 2021-07-21 17:31:42 +09:00 committed by Hermet Park
parent 87d00b4121
commit fe00e54257
19 changed files with 679 additions and 437 deletions

View 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
@ -1369,7 +1369,6 @@ public:
_TVG_DECLARE_PRIVATE(Saver);
};
/** @}*/
} //namespace

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

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

View 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;
}

View file

@ -18,7 +18,7 @@ source_file = [
'tvgLoaderMgr.h',
'tvgPictureImpl.h',
'tvgRender.h',
'tvgSaverImpl.h',
'tvgSaver.h',
'tvgSceneImpl.h',
'tvgShapeImpl.h',
'tvgTaskScheduler.h',

View 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

View file

@ -24,8 +24,6 @@
#include "tvgLoader.h"
enum class FileType { Tvg = 0, Svg, Raw, Png, Jpg, Unknown };
struct LoaderMgr
{
static bool init();

View file

@ -27,6 +27,8 @@
/* Internal Class Implementation */
/************************************************************************/
static inline bool FLT_SAME(float a, float b)
{
return (fabsf(a - b) < FLT_EPSILON);

View file

@ -27,6 +27,7 @@
namespace tvg
{
struct Iterator
{
virtual ~Iterator() {}

View 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
{

View file

@ -19,13 +19,69 @@
* 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;
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;
}

47
src/lib/tvgSaver.h Normal file
View file

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

View file

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

View file

@ -14,7 +14,6 @@ endif
if get_option('loaders').contains('jpg') == true
subdir('jpg')
message('Enable JPG Loader')
endif
subdir('raw')

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

10
src/savers/meson.build Normal file
View file

@ -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('.'),
)

View file

@ -0,0 +1,9 @@
source_file = [
'tvgTvgSaver.h',
'tvgTvgSaver.cpp',
]
subsaver_dep += [declare_dependency(
include_directories : include_directories('.'),
sources : source_file
)]

View file

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

View file

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