mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-07 21:23:32 +00:00
spec out tvg binary format
issue: https://github.com/thorvg/thorvg/issues/2721
This commit is contained in:
parent
366cbfbc6b
commit
0e45fabb3d
104 changed files with 30 additions and 3133 deletions
|
@ -8,7 +8,6 @@
|
|||
/src/renderer/wg_engine @SergeyLebedkin
|
||||
/src/loaders/external_webp/ @JSUYA
|
||||
/src/loaders/raw/ @JSUYA
|
||||
/src/loaders/tvg/ @mgrudzinska
|
||||
/src/loaders/svg/ @JSUYA @mgrudzinska
|
||||
/src/loaders/webp/ @JSUYA
|
||||
/src/bindings/capi @mgrudzinska
|
||||
|
|
50
README.md
50
README.md
|
@ -27,7 +27,7 @@ The following list shows primitives that are supported by ThorVG: <br />
|
|||
- **Scene Management**: retainable scene graph and object transformations
|
||||
- **Composition**: various blending and masking
|
||||
- **Text**: unicode characters with horizontal text layout using scalable fonts (TTF)
|
||||
- **Images**: TVG, SVG, JPG, PNG, WebP, and raw bitmaps
|
||||
- **Images**: SVG, JPG, PNG, WebP, and raw bitmaps
|
||||
- **Animations**: Lottie
|
||||
|
||||
<p align="center">
|
||||
|
@ -64,7 +64,6 @@ The task scheduler has been meticulously crafted to conceal complexity, streamli
|
|||
- [Quick Start](#quick-start)
|
||||
- [SVG](#svg)
|
||||
- [Lottie](#lottie)
|
||||
- [TVG Picture](#tvg-picture)
|
||||
- [In Practice](#in-practice)
|
||||
- [Canva iOS](#canva-ios)
|
||||
- [dotLottie](#dotlottie)
|
||||
|
@ -77,7 +76,6 @@ The task scheduler has been meticulously crafted to conceal complexity, streamli
|
|||
- [ThorVG Viewer](#thorvg-viewer)
|
||||
- [Lottie to GIF](#lottie-to-gif)
|
||||
- [SVG to PNG](#svg-to-png)
|
||||
- [SVG to TVG](#svg-to-tvg)
|
||||
- [API Bindings](#api-bindings)
|
||||
- [Dependencies](#dependencies)
|
||||
- [Contributors](#contributors)
|
||||
|
@ -325,32 +323,6 @@ Let's suppose the progress variable determines the position of the animation, ra
|
|||
|
||||
Please check out the [ThorVG Test App](https://thorvg-perf-test.vercel.app/) to see the performance of various Lottie animations powered by ThorVG.
|
||||
|
||||
[Back to contents](#contents)
|
||||
<br />
|
||||
<br />
|
||||
## TVG Picture
|
||||
|
||||
ThorVG introduces the dedicated vector data format, known as TVG Picture, designed to efficiently store Paint node properties within a scene in binary form. This format is meticulously optimized in advance, ensuring compact file sizes and swift data loading processes. </br>
|
||||
</br>
|
||||
To leverage the TVG Picture format, ThorVG employs a specialized module called TVG Saver. This module is responsible for optimizing the data associated with all scene-tree nodes and storing them in binary form. During the optimization phase, TVG Saver intelligently eliminates unused information, eliminates duplicated properties, consolidates overlapping shapes, and employs data compression where feasible. Remarkably, these optimizations maintain compatibility with future versions of ThorVG libraries, with data compression utilizing the [Lempel-Ziv-Welchi](https://en.wikipedia.org/wiki/Lempel%E2%80%93Ziv%E2%80%93Welch) algorithm when applicable.</br>
|
||||
</br>
|
||||
As a result of these efforts, the final data size is notably smaller than other text-based vector data formats, such as SVG. This reduction in data size not only minimizes I/O operations but also mitigates memory bandwidth requirements during data loading. This aspect proves particularly beneficial for programs reliant on substantial vector resources. </br>
|
||||
</br>
|
||||
Furthermore, TVG Picture substantially streamlines resource loading tasks by circumventing the need for data interpretation, resulting in reduced runtime memory demands and rendering tasks that subsequently enhance performance. </br>
|
||||
</br>
|
||||
By adopting TVG Picture, you can achieve an average reduction of over 30% in data size and loading times (for more details, refer to "[See More](https://github.com/thorvg/thorvg/wiki/TVG-Picture-Binary-Size)"). Notably, the extent of performance improvement is contingent on resource size and complexity. </br>
|
||||
</br>
|
||||
<p align="center">
|
||||
<img width="909" height="auto" src="https://github.com/thorvg/thorvg/blob/main/res/example_tvgsize.png">
|
||||
</p>
|
||||
|
||||
As TVG Saver facilitates the export of the scene-tree into TVG Picture data files (TVG), the subsequent task of importing and restoring this data to programmable instances is efficiently handled by the TVG Loader. For seamless conversion from SVG to TVG, the ThorVG Viewer provides a swift solution.
|
||||
|
||||
<p align="center">
|
||||
<img width="710" height="auto" src="https://github.com/thorvg/thorvg/blob/main/res/example_tvgmodule.png">
|
||||
</p>
|
||||
|
||||
|
||||
[Back to contents](#contents)
|
||||
<br />
|
||||
<br />
|
||||
|
@ -423,7 +395,7 @@ The ThorVG API documentation can be accessed at [thorvg.org/apis](https://www.th
|
|||
<br />
|
||||
## Tools
|
||||
### ThorVG Viewer
|
||||
ThorVG provides the resource verification tool for the ThorVG Engine. [ThorVG viewer](https://thorvg.github.io/thorvg.viewer/) does immediate rendering via web browser running on the ThorVG web-assembly binary, allowing real-time editing of the vector elements on it. It doesn't upload your resources to any external server while allowing to export to supported formats such as TVG, so the designer resource copyright is protected.</br>
|
||||
ThorVG provides the resource verification tool for the ThorVG Engine. [ThorVG viewer](https://thorvg.github.io/thorvg.viewer/) does immediate rendering via web browser running on the ThorVG web-assembly binary, allowing real-time editing of the vector elements on it. It doesn't upload your resources to any external server while allowing to export to supported formats such as GIF, so the designer resource copyright is protected.</br>
|
||||
</br>
|
||||
|
||||
<p align="center">
|
||||
|
@ -494,24 +466,6 @@ Examples:
|
|||
$ svg2png . -r 200x200
|
||||
```
|
||||
|
||||
### SVG to TVG
|
||||
ThorVG provides an executable `svg2tvg` converter that generates a TVG file from an SVG file.
|
||||
|
||||
To use `svg2tvg`, you need to activate this feature in the build option:
|
||||
```
|
||||
meson setup builddir -Dtools=svg2tvg -Dsavers=tvg
|
||||
```
|
||||
|
||||
Examples of the usage of the `svg2tvg`:
|
||||
```
|
||||
Usage:
|
||||
svg2tvg [SVG file] or [SVG folder]
|
||||
|
||||
Examples:
|
||||
$ svg2tvg input.svg
|
||||
$ svg2tvg svgfolder
|
||||
```
|
||||
|
||||
[Back to contents](#contents)
|
||||
<br />
|
||||
<br />
|
||||
|
|
|
@ -1,62 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021 - 2024 the ThorVG project. 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 "Example.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* ThorVG Drawing Contents */
|
||||
/************************************************************************/
|
||||
|
||||
struct UserExample : tvgexam::Example
|
||||
{
|
||||
bool content(tvg::Canvas* canvas, uint32_t w, uint32_t h) override
|
||||
{
|
||||
if (!canvas) return false;
|
||||
|
||||
//load the tvg file
|
||||
auto picture = tvg::Picture::gen();
|
||||
if (!tvgexam::verify(picture->load(EXAMPLE_DIR"/tvg/test.tvg"), "You might need to run \"TvgSaver\" example first to generate the \"test.tvg\".")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
float w2, h2;
|
||||
picture->size(&w2, &h2);
|
||||
cout << "default tvg view size = " << w2 << " x " << h2 << endl;
|
||||
|
||||
picture->translate(w2 * 0.1f, h2 * 0.1f);
|
||||
picture->size(w2 * 0.8f, h2 * 0.8f);
|
||||
|
||||
canvas->push(std::move(picture));
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* Entry Point */
|
||||
/************************************************************************/
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
return tvgexam::main(new UserExample, argc, argv);
|
||||
}
|
115
examples/Tvg.cpp
115
examples/Tvg.cpp
|
@ -1,115 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2020 - 2024 the ThorVG project. 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 "Example.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* ThorVG Drawing Contents */
|
||||
/************************************************************************/
|
||||
|
||||
#define NUM_PER_ROW 9
|
||||
#define NUM_PER_COL 9
|
||||
|
||||
struct UserExample : tvgexam::Example
|
||||
{
|
||||
std::vector<unique_ptr<tvg::Picture>> pictures;
|
||||
uint32_t w, h;
|
||||
uint32_t size;
|
||||
|
||||
int counter = 0;
|
||||
|
||||
void populate(const char* path) override
|
||||
{
|
||||
if (counter >= NUM_PER_ROW * NUM_PER_COL) return;
|
||||
|
||||
//ignore if not tvg.
|
||||
const char *ext = path + strlen(path) - 3;
|
||||
if (strcmp(ext, "tvg")) return;
|
||||
|
||||
auto picture = tvg::Picture::gen();
|
||||
|
||||
if (!tvgexam::verify(picture->load(path))) return;
|
||||
|
||||
//image scaling preserving its aspect ratio
|
||||
float scale;
|
||||
float shiftX = 0.0f, shiftY = 0.0f;
|
||||
float w, h;
|
||||
picture->size(&w, &h);
|
||||
|
||||
if (w > h) {
|
||||
scale = size / w;
|
||||
shiftY = (size - h * scale) * 0.5f;
|
||||
} else {
|
||||
scale = size / h;
|
||||
shiftX = (size - w * scale) * 0.5f;
|
||||
}
|
||||
|
||||
picture->scale(scale);
|
||||
picture->translate((counter % NUM_PER_ROW) * size + shiftX, (counter / NUM_PER_ROW) * (this->h / NUM_PER_COL) + shiftY);
|
||||
|
||||
pictures.push_back(std::move(picture));
|
||||
|
||||
cout << "TVG: " << path << endl;
|
||||
|
||||
counter++;
|
||||
}
|
||||
|
||||
bool content(tvg::Canvas* canvas, uint32_t w, uint32_t h) override
|
||||
{
|
||||
if (!canvas) return false;
|
||||
|
||||
//Background
|
||||
auto shape = tvg::Shape::gen();
|
||||
shape->appendRect(0, 0, w, h);
|
||||
shape->fill(255, 255, 255);
|
||||
|
||||
canvas->push(std::move(shape));
|
||||
|
||||
this->w = w;
|
||||
this->h = h;
|
||||
this->size = w / NUM_PER_ROW;
|
||||
|
||||
this->scandir(EXAMPLE_DIR"/tvg");
|
||||
|
||||
/* This showcase demonstrates the asynchronous loading of tvg.
|
||||
For this, pictures are pushed at a certain sync time.
|
||||
This allows time for the tvg resources to finish loading;
|
||||
otherwise, you can push pictures immediately. */
|
||||
for (auto& paint : pictures) {
|
||||
canvas->push(std::move(paint));
|
||||
}
|
||||
|
||||
pictures.clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* Entry Point */
|
||||
/************************************************************************/
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
return tvgexam::main(new UserExample, argc, argv, 1280, 1280);
|
||||
}
|
|
@ -1,233 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021 - 2024 the ThorVG project. 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 "Example.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
/************************************************************************/
|
||||
/* ThorVG Saving Contents */
|
||||
/************************************************************************/
|
||||
|
||||
unique_ptr<tvg::Paint> tvgClippedImage(uint32_t * data, int width, int height)
|
||||
{
|
||||
auto image = tvg::Picture::gen();
|
||||
if (!tvgexam::verify(image->load(data, width, height, true, true))) return nullptr;
|
||||
image->translate(400, 0);
|
||||
image->scale(2);
|
||||
|
||||
auto imageClip = tvg::Shape::gen();
|
||||
imageClip->appendCircle(400, 200, 80, 180);
|
||||
imageClip->translate(200, 0);
|
||||
image->clip(std::move(imageClip));
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
unique_ptr<tvg::Paint> tvgMaskedSvg()
|
||||
{
|
||||
auto svg = tvg::Picture::gen();
|
||||
if (!tvgexam::verify(svg->load(EXAMPLE_DIR"/svg/tiger.svg"))) return nullptr;
|
||||
svg->opacity(200);
|
||||
svg->scale(0.3);
|
||||
svg->translate(50, 450);
|
||||
|
||||
auto svgMask = tvg::Shape::gen();
|
||||
|
||||
//star
|
||||
svgMask->moveTo(199, 34);
|
||||
svgMask->lineTo(253, 143);
|
||||
svgMask->lineTo(374, 160);
|
||||
svgMask->lineTo(287, 244);
|
||||
svgMask->lineTo(307, 365);
|
||||
svgMask->lineTo(199, 309);
|
||||
svgMask->lineTo(97, 365);
|
||||
svgMask->lineTo(112, 245);
|
||||
svgMask->lineTo(26, 161);
|
||||
svgMask->lineTo(146, 143);
|
||||
svgMask->close();
|
||||
|
||||
svgMask->fill(0, 0, 0);
|
||||
svgMask->translate(30, 440);
|
||||
svgMask->opacity(200);
|
||||
svgMask->scale(0.7);
|
||||
svg->composite(std::move(svgMask), tvg::CompositeMethod::AlphaMask);
|
||||
|
||||
return svg;
|
||||
}
|
||||
|
||||
unique_ptr<tvg::Paint> tvgNestedPaints(tvg::Fill::ColorStop* colorStops, int colorStopsCnt)
|
||||
{
|
||||
auto scene = tvg::Scene::gen();
|
||||
scene->translate(100, 100);
|
||||
|
||||
auto scene2 = tvg::Scene::gen();
|
||||
scene2->rotate(10);
|
||||
scene2->scale(2);
|
||||
scene2->translate(400,400);
|
||||
|
||||
auto shape = tvg::Shape::gen();
|
||||
shape->appendRect(50, 0, 50, 100, 10, 40);
|
||||
shape->fill(0, 0, 255, 125);
|
||||
scene2->push(std::move(shape));
|
||||
|
||||
scene->push(std::move(scene2));
|
||||
|
||||
auto shape2 = tvg::Shape::gen();
|
||||
shape2->appendRect(0, 0, 50, 100, 10, 40);
|
||||
auto fillShape = tvg::RadialGradient::gen();
|
||||
fillShape->radial(25, 50, 25);
|
||||
fillShape->colorStops(colorStops, colorStopsCnt);
|
||||
shape2->fill(std::move(fillShape));
|
||||
shape2->scale(2);
|
||||
shape2->opacity(200);
|
||||
shape2->translate(400, 400);
|
||||
scene->push(std::move(shape2));
|
||||
|
||||
return scene;
|
||||
}
|
||||
|
||||
unique_ptr<tvg::Paint> tvgGradientShape(tvg::Fill::ColorStop* colorStops, int colorStopsCnt)
|
||||
{
|
||||
float dashPattern[2] = {30, 40};
|
||||
|
||||
//gradient shape + dashed stroke
|
||||
auto fillStroke = tvg::LinearGradient::gen();
|
||||
fillStroke->linear(20, 120, 380, 280);
|
||||
fillStroke->colorStops(colorStops, colorStopsCnt);
|
||||
|
||||
auto fillShape = tvg::LinearGradient::gen();
|
||||
fillShape->linear(20, 120, 380, 280);
|
||||
fillShape->colorStops(colorStops, colorStopsCnt);
|
||||
|
||||
auto shape = tvg::Shape::gen();
|
||||
shape->appendCircle(200, 200, 180, 80);
|
||||
shape->fill(std::move(fillShape));
|
||||
shape->strokeWidth(20);
|
||||
shape->strokeDash(dashPattern, 2);
|
||||
shape->strokeFill(std::move(fillStroke));
|
||||
|
||||
return shape;
|
||||
}
|
||||
|
||||
unique_ptr<tvg::Paint> tvgCircle1(tvg::Fill::ColorStop* colorStops, int colorStopsCnt)
|
||||
{
|
||||
auto circ = tvg::Shape::gen();
|
||||
circ->appendCircle(400, 375, 50, 50);
|
||||
circ->fill(0, 255, 0, 155);
|
||||
|
||||
return circ;
|
||||
}
|
||||
|
||||
unique_ptr<tvg::Paint> tvgCircle2(tvg::Fill::ColorStop* colorStops, int colorStopsCnt)
|
||||
{
|
||||
auto circ = tvg::Shape::gen();
|
||||
circ->appendCircle(400, 425, 50, 50);
|
||||
auto fill = tvg::RadialGradient::gen();
|
||||
fill->radial(400, 425, 50);
|
||||
fill->colorStops(colorStops, colorStopsCnt);
|
||||
circ->fill(std::move(fill));
|
||||
|
||||
return circ;
|
||||
}
|
||||
|
||||
void exportTvg()
|
||||
{
|
||||
//prepare the main scene
|
||||
auto scene = tvg::Scene::gen();
|
||||
|
||||
//prepare image source
|
||||
const int width = 200;
|
||||
const int height = 300;
|
||||
ifstream file(EXAMPLE_DIR"/image/rawimage_200x300.raw", ios::binary);
|
||||
if (!file.is_open()) return;
|
||||
uint32_t *data = (uint32_t*) malloc(sizeof(uint32_t) * width * height);
|
||||
file.read(reinterpret_cast<char*>(data), sizeof(uint32_t) * width * height);
|
||||
file.close();
|
||||
|
||||
//clipped image
|
||||
auto image = tvgClippedImage(data, width, height);
|
||||
scene->push(std::move(image));
|
||||
|
||||
free(data);
|
||||
|
||||
//prepare gradient common data
|
||||
tvg::Fill::ColorStop colorStops1[3];
|
||||
colorStops1[0] = {0, 255, 0, 0, 255};
|
||||
colorStops1[1] = {0.5, 0, 0, 255, 127};
|
||||
colorStops1[2] = {1, 127, 127, 127, 127};
|
||||
|
||||
tvg::Fill::ColorStop colorStops2[2];
|
||||
colorStops2[0] = {0, 255, 0, 0, 255};
|
||||
colorStops2[1] = {1, 50, 0, 255, 255};
|
||||
|
||||
tvg::Fill::ColorStop colorStops3[2];
|
||||
colorStops3[0] = {0, 0, 0, 255, 155};
|
||||
colorStops3[1] = {1, 0, 255, 0, 155};
|
||||
|
||||
//gradient shape + dashed stroke
|
||||
auto shape1 = tvgGradientShape(colorStops1, 3);
|
||||
scene->push(std::move(shape1));
|
||||
|
||||
//nested paints
|
||||
auto scene2 = tvgNestedPaints(colorStops2, 2);
|
||||
scene->push(std::move(scene2));
|
||||
|
||||
//masked svg file
|
||||
auto svg = tvgMaskedSvg();
|
||||
scene->push(std::move(svg));
|
||||
|
||||
//solid top circle and gradient bottom circle
|
||||
auto circ1 = tvgCircle1(colorStops3, 2);
|
||||
scene->push(std::move(circ1));
|
||||
|
||||
auto circ2 = tvgCircle2(colorStops3, 2);
|
||||
scene->push(std::move(circ2));
|
||||
|
||||
//inv mask applied to the main scene
|
||||
auto mask = tvg::Shape::gen();
|
||||
mask->appendCircle(400, 400, 15, 15);
|
||||
mask->fill(0, 0, 0);
|
||||
scene->composite(std::move(mask), tvg::CompositeMethod::InvAlphaMask);
|
||||
|
||||
//save the tvg file
|
||||
auto saver = tvg::Saver::gen();
|
||||
if (!tvgexam::verify(saver->save(std::move(scene), EXAMPLE_DIR"/tvg/test.tvg"))) return;
|
||||
saver->sync();
|
||||
cout << "Successfully exported to test.tvg, Please check the result using PictureTvg!" << endl;
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* Entry Point */
|
||||
/************************************************************************/
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if (tvgexam::verify(tvg::Initializer::init(0))) {
|
||||
|
||||
exportTvg();
|
||||
|
||||
tvg::Initializer::term();
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -52,7 +52,6 @@ source_file = [
|
|||
'PicturePng.cpp',
|
||||
'PictureRaw.cpp',
|
||||
'PictureSvg.cpp',
|
||||
'PictureTvg.cpp',
|
||||
'PictureWebp.cpp',
|
||||
'RadialGradient.cpp',
|
||||
'Retaining.cpp',
|
||||
|
@ -68,8 +67,6 @@ source_file = [
|
|||
'Svg.cpp',
|
||||
'Text.cpp',
|
||||
'Transform.cpp',
|
||||
'Tvg.cpp',
|
||||
'TvgSaver.cpp',
|
||||
'Update.cpp',
|
||||
'Viewport.cpp'
|
||||
]
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
49
meson.build
49
meson.build
|
@ -41,14 +41,12 @@ endif
|
|||
|
||||
#Tools
|
||||
all_tools = get_option('tools').contains('all')
|
||||
svg2tvg = all_tools or get_option('tools').contains('svg2tvg')
|
||||
lottie2gif = all_tools or get_option('tools').contains('lottie2gif')
|
||||
svg2png = all_tools or get_option('tools').contains('svg2png')
|
||||
|
||||
#Loaders
|
||||
all_loaders = get_option('loaders').contains('all')
|
||||
svg_loader = all_loaders or get_option('loaders').contains('svg') or svg2tvg or svg2png
|
||||
tvg_loader = all_loaders or get_option('loaders').contains('tvg')
|
||||
svg_loader = all_loaders or get_option('loaders').contains('svg') or svg2png
|
||||
png_loader = all_loaders or get_option('loaders').contains('png')
|
||||
jpg_loader = all_loaders or get_option('loaders').contains('jpg')
|
||||
lottie_loader = all_loaders or get_option('loaders').contains('lottie') or lottie2gif
|
||||
|
@ -57,7 +55,6 @@ webp_loader = all_loaders or get_option('loaders').contains('webp')
|
|||
|
||||
#Savers
|
||||
all_savers = get_option('savers').contains('all')
|
||||
tvg_saver = all_savers or get_option('savers').contains('tvg') or svg2tvg
|
||||
gif_saver = all_savers or get_option('savers').contains('gif') or lottie2gif
|
||||
|
||||
#Loaders/savers/tools config
|
||||
|
@ -65,10 +62,6 @@ if svg_loader
|
|||
config_h.set10('THORVG_SVG_LOADER_SUPPORT', true)
|
||||
endif
|
||||
|
||||
if tvg_loader
|
||||
config_h.set10('THORVG_TVG_LOADER_SUPPORT', true)
|
||||
endif
|
||||
|
||||
if png_loader
|
||||
config_h.set10('THORVG_PNG_LOADER_SUPPORT', true)
|
||||
endif
|
||||
|
@ -89,10 +82,6 @@ if webp_loader
|
|||
config_h.set10('THORVG_WEBP_LOADER_SUPPORT', true)
|
||||
endif
|
||||
|
||||
if tvg_saver
|
||||
config_h.set10('THORVG_TVG_SAVER_SUPPORT', true)
|
||||
endif
|
||||
|
||||
if gif_saver
|
||||
config_h.set10('THORVG_GIF_SAVER_SUPPORT', true)
|
||||
endif
|
||||
|
@ -170,24 +159,21 @@ Summary:
|
|||
Raster Engine (SW): @5@
|
||||
Raster Engine (GL): @6@
|
||||
Raster Engine (WG): @7@
|
||||
Loader (TVG): @8@
|
||||
Loader (SVG): @9@
|
||||
Loader (TTF): @10@
|
||||
Loader (LOTTIE): @11@
|
||||
Loader (PNG): @12@
|
||||
Loader (JPG): @13@
|
||||
Loader (WEBP): @14@
|
||||
Saver (TVG): @15@
|
||||
Saver (GIF): @16@
|
||||
Binding (CAPI): @17@
|
||||
Binding (WASM_BETA): @18@
|
||||
Log Message: @19@
|
||||
Tests: @20@
|
||||
Examples: @21@
|
||||
Tool (Svg2Tvg): @22@
|
||||
Tool (Svg2Png): @23@
|
||||
Tool (Lottie2Gif): @24@
|
||||
Extra (Lottie Expressions): @25@
|
||||
Loader (SVG): @8@
|
||||
Loader (TTF): @9@
|
||||
Loader (LOTTIE): @10@
|
||||
Loader (PNG): @11@
|
||||
Loader (JPG): @12@
|
||||
Loader (WEBP): @13@
|
||||
Saver (GIF): @14@
|
||||
Binding (CAPI): @15@
|
||||
Binding (WASM_BETA): @16@
|
||||
Log Message: @17@
|
||||
Tests: @18@
|
||||
Examples: @19@
|
||||
Tool (Svg2Png): @20@
|
||||
Tool (Lottie2Gif): @21@
|
||||
Extra (Lottie Expressions): @22@
|
||||
|
||||
'''.format(
|
||||
meson.project_version(),
|
||||
|
@ -198,21 +184,18 @@ Summary:
|
|||
sw_engine,
|
||||
gl_engine,
|
||||
wg_engine,
|
||||
tvg_loader,
|
||||
svg_loader,
|
||||
ttf_loader,
|
||||
lottie_loader,
|
||||
png_loader,
|
||||
jpg_loader,
|
||||
webp_loader,
|
||||
tvg_saver,
|
||||
gif_saver,
|
||||
get_option('bindings').contains('capi'),
|
||||
get_option('bindings').contains('wasm_beta'),
|
||||
get_option('log'),
|
||||
get_option('tests'),
|
||||
get_option('examples'),
|
||||
svg2tvg,
|
||||
svg2png,
|
||||
lottie2gif,
|
||||
lottie_expressions
|
||||
|
|
|
@ -6,13 +6,13 @@ option('engines',
|
|||
|
||||
option('loaders',
|
||||
type: 'array',
|
||||
choices: ['', 'tvg', 'svg', 'png', 'jpg', 'lottie', 'ttf', 'webp', 'all'],
|
||||
value: ['svg', 'tvg', 'lottie', 'ttf'],
|
||||
choices: ['', 'svg', 'png', 'jpg', 'lottie', 'ttf', 'webp', 'all'],
|
||||
value: ['svg', 'lottie', 'ttf'],
|
||||
description: 'Enable File Loaders in thorvg')
|
||||
|
||||
option('savers',
|
||||
type: 'array',
|
||||
choices: ['', 'tvg', 'gif', 'all'],
|
||||
choices: ['', 'gif', 'all'],
|
||||
value: [''],
|
||||
description: 'Enable File Savers in thorvg')
|
||||
|
||||
|
@ -34,7 +34,7 @@ option('bindings',
|
|||
|
||||
option('tools',
|
||||
type: 'array',
|
||||
choices: ['', 'svg2tvg', 'svg2png', 'lottie2gif', 'all'],
|
||||
choices: ['', 'svg2png', 'lottie2gif', 'all'],
|
||||
value: [''],
|
||||
description: 'Enable building thorvg tools')
|
||||
|
||||
|
@ -62,4 +62,4 @@ option('extra',
|
|||
type: 'array',
|
||||
choices: ['', 'lottie_expressions'],
|
||||
value: ['lottie_expressions'],
|
||||
description: '"Enable support for exceptionally advanced features')
|
||||
description: '"Enable support for exceptionally advanced features')
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 106 KiB After Width: | Height: | Size: 62 KiB |
Binary file not shown.
Before Width: | Height: | Size: 57 KiB |
Binary file not shown.
Before Width: | Height: | Size: 200 KiB |
Binary file not shown.
Before Width: | Height: | Size: 17 KiB |
Binary file not shown.
Before Width: | Height: | Size: 14 KiB |
|
@ -348,11 +348,7 @@ public:
|
|||
// Saver methods
|
||||
bool save(string mimetype)
|
||||
{
|
||||
if (mimetype == "gif") {
|
||||
return save2Gif();
|
||||
} else if (mimetype == "tvg") {
|
||||
return save2Tvg();
|
||||
}
|
||||
if (mimetype == "gif") return save2Gif();
|
||||
|
||||
errorMsg = "Invalid mimetype";
|
||||
return false;
|
||||
|
@ -420,34 +416,6 @@ public:
|
|||
return true;
|
||||
}
|
||||
|
||||
bool save2Tvg()
|
||||
{
|
||||
errorMsg = NoError;
|
||||
|
||||
if (!animation) return false;
|
||||
|
||||
auto saver = Saver::gen();
|
||||
if (!saver) {
|
||||
errorMsg = "Invalid saver";
|
||||
return false;
|
||||
}
|
||||
|
||||
//preserve the picture using the reference counting
|
||||
PP(animation->picture())->ref();
|
||||
|
||||
if (saver->save(tvg::cast<Picture>(animation->picture()), "output.tvg") != tvg::Result::Success) {
|
||||
PP(animation->picture())->unref();
|
||||
errorMsg = "save(), fail";
|
||||
return false;
|
||||
}
|
||||
|
||||
saver->sync();
|
||||
|
||||
PP(animation->picture())->unref();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: Advanced APIs wrt Interactivity & theme methods...
|
||||
|
||||
private:
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
source_file = [
|
||||
'tvgArray.h',
|
||||
'tvgCompressor.h',
|
||||
'tvgFormat.h',
|
||||
'tvgInlist.h',
|
||||
'tvgLock.h',
|
||||
'tvgMath.h',
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
*/
|
||||
|
||||
/*
|
||||
* Lempel–Ziv–Welch (LZW) encoder/decoder by Guilherme R. Lampert(guilherme.ronaldo.lampert@gmail.com)
|
||||
* Lempel–Ziv–Welch (LZW) decoder by Guilherme R. Lampert(guilherme.ronaldo.lampert@gmail.com)
|
||||
|
||||
* This is the compression scheme used by the GIF image format and the Unix 'compress' tool.
|
||||
* Main differences from this implementation is that End Of Input (EOI) and Clear Codes (CC)
|
||||
|
@ -55,8 +55,6 @@
|
|||
*/
|
||||
#include "config.h"
|
||||
|
||||
|
||||
|
||||
#include <string>
|
||||
#include <memory.h>
|
||||
#include "tvgCompressor.h"
|
||||
|
@ -68,7 +66,6 @@ namespace tvg {
|
|||
/* LZW Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
|
||||
//LZW Dictionary helper:
|
||||
constexpr int Nil = -1;
|
||||
constexpr int MaxDictBits = 12;
|
||||
|
@ -76,128 +73,6 @@ constexpr int StartBits = 9;
|
|||
constexpr int FirstCode = (1 << (StartBits - 1)); // 256
|
||||
constexpr int MaxDictEntries = (1 << MaxDictBits); // 4096
|
||||
|
||||
|
||||
//Round up to the next power-of-two number, e.g. 37 => 64
|
||||
static int nextPowerOfTwo(int num)
|
||||
{
|
||||
--num;
|
||||
for (size_t i = 1; i < sizeof(num) * 8; i <<= 1) {
|
||||
num = num | num >> i;
|
||||
}
|
||||
return ++num;
|
||||
}
|
||||
|
||||
|
||||
struct BitStreamWriter
|
||||
{
|
||||
uint8_t* stream; //Growable buffer to store our bits. Heap allocated & owned by the class instance.
|
||||
int bytesAllocated; //Current size of heap-allocated stream buffer *in bytes*.
|
||||
int granularity; //Amount bytesAllocated multiplies by when auto-resizing in appendBit().
|
||||
int currBytePos; //Current byte being written to, from 0 to bytesAllocated-1.
|
||||
int nextBitPos; //Bit position within the current byte to access next. 0 to 7.
|
||||
int numBitsWritten; //Number of bits in use from the stream buffer, not including byte-rounding padding.
|
||||
|
||||
void internalInit()
|
||||
{
|
||||
stream = nullptr;
|
||||
bytesAllocated = 0;
|
||||
granularity = 2;
|
||||
currBytePos = 0;
|
||||
nextBitPos = 0;
|
||||
numBitsWritten = 0;
|
||||
}
|
||||
|
||||
uint8_t* allocBytes(const int bytesWanted, uint8_t * oldPtr, const int oldSize)
|
||||
{
|
||||
auto newMemory = static_cast<uint8_t *>(malloc(bytesWanted));
|
||||
memset(newMemory, 0, bytesWanted);
|
||||
|
||||
if (oldPtr) {
|
||||
memcpy(newMemory, oldPtr, oldSize);
|
||||
free(oldPtr);
|
||||
}
|
||||
return newMemory;
|
||||
}
|
||||
|
||||
BitStreamWriter()
|
||||
{
|
||||
/* 8192 bits for a start (1024 bytes). It will resize if needed.
|
||||
Default granularity is 2. */
|
||||
internalInit();
|
||||
allocate(8192);
|
||||
}
|
||||
|
||||
BitStreamWriter(const int initialSizeInBits, const int growthGranularity = 2)
|
||||
{
|
||||
internalInit();
|
||||
setGranularity(growthGranularity);
|
||||
allocate(initialSizeInBits);
|
||||
}
|
||||
|
||||
~BitStreamWriter()
|
||||
{
|
||||
free(stream);
|
||||
}
|
||||
|
||||
void allocate(int bitsWanted)
|
||||
{
|
||||
//Require at least a byte.
|
||||
if (bitsWanted <= 0) bitsWanted = 8;
|
||||
|
||||
//Round upwards if needed:
|
||||
if ((bitsWanted % 8) != 0) bitsWanted = nextPowerOfTwo(bitsWanted);
|
||||
|
||||
//We might already have the required count.
|
||||
const int sizeInBytes = bitsWanted / 8;
|
||||
if (sizeInBytes <= bytesAllocated) return;
|
||||
|
||||
stream = allocBytes(sizeInBytes, stream, bytesAllocated);
|
||||
bytesAllocated = sizeInBytes;
|
||||
}
|
||||
|
||||
void appendBit(const int bit)
|
||||
{
|
||||
const uint32_t mask = uint32_t(1) << nextBitPos;
|
||||
stream[currBytePos] = (stream[currBytePos] & ~mask) | (-bit & mask);
|
||||
++numBitsWritten;
|
||||
|
||||
if (++nextBitPos == 8) {
|
||||
nextBitPos = 0;
|
||||
if (++currBytePos == bytesAllocated) allocate(bytesAllocated * granularity * 8);
|
||||
}
|
||||
}
|
||||
|
||||
void appendBitsU64(const uint64_t num, const int bitCount)
|
||||
{
|
||||
for (int b = 0; b < bitCount; ++b) {
|
||||
const uint64_t mask = uint64_t(1) << b;
|
||||
const int bit = !!(num & mask);
|
||||
appendBit(bit);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t* release()
|
||||
{
|
||||
auto oldPtr = stream;
|
||||
internalInit();
|
||||
return oldPtr;
|
||||
}
|
||||
|
||||
void setGranularity(const int growthGranularity)
|
||||
{
|
||||
granularity = (growthGranularity >= 2) ? growthGranularity : 2;
|
||||
}
|
||||
|
||||
int getByteCount() const
|
||||
{
|
||||
int usedBytes = numBitsWritten / 8;
|
||||
int leftovers = numBitsWritten % 8;
|
||||
if (leftovers != 0) ++usedBytes;
|
||||
return usedBytes;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct BitStreamReader
|
||||
{
|
||||
const uint8_t* stream; // Pointer to the external bit stream. Not owned by the reader.
|
||||
|
@ -272,18 +147,6 @@ struct Dictionary
|
|||
}
|
||||
}
|
||||
|
||||
int findIndex(const int code, const int value) const
|
||||
{
|
||||
if (code == Nil) return value;
|
||||
|
||||
//Linear search for now.
|
||||
//TODO: Worth optimizing with a proper hash-table?
|
||||
for (int i = 0; i < size; ++i) {
|
||||
if (entries[i].code == code && entries[i].value == value) return i;
|
||||
}
|
||||
return Nil;
|
||||
}
|
||||
|
||||
bool add(const int code, const int value)
|
||||
{
|
||||
if (size == MaxDictEntries) return false;
|
||||
|
@ -379,52 +242,10 @@ uint8_t* lzwDecode(const uint8_t* compressed, uint32_t compressedSizeBytes, uint
|
|||
}
|
||||
|
||||
|
||||
uint8_t* lzwEncode(const uint8_t* uncompressed, uint32_t uncompressedSizeBytes, uint32_t* compressedSizeBytes, uint32_t* compressedSizeBits)
|
||||
{
|
||||
//LZW encoding context:
|
||||
int code = Nil;
|
||||
int codeBitsWidth = StartBits;
|
||||
Dictionary dictionary;
|
||||
|
||||
//Output bit stream we write to. This will allocate memory as needed to accommodate the encoded data.
|
||||
BitStreamWriter bitStream;
|
||||
|
||||
for (; uncompressedSizeBytes > 0; --uncompressedSizeBytes, ++uncompressed) {
|
||||
const int value = *uncompressed;
|
||||
const int index = dictionary.findIndex(code, value);
|
||||
|
||||
if (index != Nil) {
|
||||
code = index;
|
||||
continue;
|
||||
}
|
||||
|
||||
//Write the dictionary code using the minimum bit-with:
|
||||
bitStream.appendBitsU64(code, codeBitsWidth);
|
||||
|
||||
//Flush it when full so we can restart the sequences.
|
||||
if (!dictionary.flush(codeBitsWidth)) {
|
||||
//There's still space for this sequence.
|
||||
dictionary.add(code, value);
|
||||
}
|
||||
code = value;
|
||||
}
|
||||
|
||||
//Residual code at the end:
|
||||
if (code != Nil) bitStream.appendBitsU64(code, codeBitsWidth);
|
||||
|
||||
//Pass ownership of the compressed data buffer to the user pointer:
|
||||
*compressedSizeBytes = bitStream.getByteCount();
|
||||
*compressedSizeBits = bitStream.numBitsWritten;
|
||||
|
||||
return bitStream.release();
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* B64 Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
|
||||
size_t b64Decode(const char* encoded, const size_t len, char** decoded)
|
||||
{
|
||||
static constexpr const char B64_INDEX[256] =
|
||||
|
@ -473,7 +294,7 @@ size_t b64Decode(const char* encoded, const size_t len, char** decoded)
|
|||
|
||||
|
||||
/************************************************************************/
|
||||
/* DJB2 Implementation */
|
||||
/* DJB2 Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
unsigned long djb2Encode(const char* str)
|
||||
|
|
|
@ -27,7 +27,6 @@
|
|||
|
||||
namespace tvg
|
||||
{
|
||||
uint8_t* lzwEncode(const uint8_t* uncompressed, uint32_t uncompressedSizeBytes, uint32_t* compressedSizeBytes, uint32_t* compressedSizeBits);
|
||||
uint8_t* lzwDecode(const uint8_t* compressed, uint32_t compressedSizeBytes, uint32_t compressedSizeBits, uint32_t uncompressedSizeBytes);
|
||||
size_t b64Decode(const char* encoded, const size_t len, char** decoded);
|
||||
unsigned long djb2Encode(const char* str);
|
||||
|
|
|
@ -1,95 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021 - 2024 the ThorVG project. 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_FORMAT_H_
|
||||
#define _TVG_FORMAT_H_
|
||||
|
||||
/* TODO: Need to consider whether uin8_t is enough size for extension...
|
||||
Rather than optimal data, we can use enough size and data compress? */
|
||||
|
||||
using TvgBinByte = uint8_t;
|
||||
using TvgBinCounter = uint32_t;
|
||||
using TvgBinTag = TvgBinByte;
|
||||
using TvgBinFlag = TvgBinByte;
|
||||
|
||||
|
||||
//Header
|
||||
#define TVG_HEADER_SIZE 33 //TVG_HEADER_SIGNATURE_LENGTH + TVG_HEADER_VERSION_LENGTH + 2*SIZE(float) + TVG_HEADER_RESERVED_LENGTH + TVG_HEADER_COMPRESS_SIZE
|
||||
#define TVG_HEADER_SIGNATURE "ThorVG"
|
||||
#define TVG_HEADER_SIGNATURE_LENGTH 6
|
||||
#define TVG_HEADER_VERSION "010000" //Major 01, Minor 00, Micro 00
|
||||
#define TVG_HEADER_VERSION_LENGTH 6
|
||||
#define TVG_HEADER_RESERVED_LENGTH 1 //Storing flags for extensions
|
||||
#define TVG_HEADER_COMPRESS_SIZE 12 //TVG_HEADER_UNCOMPRESSED_SIZE + TVG_HEADER_COMPRESSED_SIZE + TVG_HEADER_COMPRESSED_SIZE_BITS
|
||||
//Compress Size
|
||||
#define TVG_HEADER_UNCOMPRESSED_SIZE 4 //SIZE (TvgBinCounter)
|
||||
#define TVG_HEADER_COMPRESSED_SIZE 4 //SIZE (TvgBinCounter)
|
||||
#define TVG_HEADER_COMPRESSED_SIZE_BITS 4 //SIZE (TvgBinCounter)
|
||||
//Reserved Flag
|
||||
#define TVG_HEAD_FLAG_COMPRESSED 0x01
|
||||
|
||||
//Paint Type
|
||||
#define TVG_TAG_CLASS_PICTURE (TvgBinTag)0xfc
|
||||
#define TVG_TAG_CLASS_SHAPE (TvgBinTag)0xfd
|
||||
#define TVG_TAG_CLASS_SCENE (TvgBinTag)0xfe
|
||||
|
||||
|
||||
//Paint
|
||||
#define TVG_TAG_PAINT_OPACITY (TvgBinTag)0x10
|
||||
#define TVG_TAG_PAINT_TRANSFORM (TvgBinTag)0x11
|
||||
#define TVG_TAG_PAINT_CMP_TARGET (TvgBinTag)0x01
|
||||
#define TVG_TAG_PAINT_CMP_METHOD (TvgBinTag)0x20
|
||||
|
||||
|
||||
//Shape
|
||||
#define TVG_TAG_SHAPE_PATH (TvgBinTag)0x40
|
||||
#define TVG_TAG_SHAPE_STROKE (TvgBinTag)0x41
|
||||
#define TVG_TAG_SHAPE_FILL (TvgBinTag)0x42
|
||||
#define TVG_TAG_SHAPE_COLOR (TvgBinTag)0x43
|
||||
#define TVG_TAG_SHAPE_FILLRULE (TvgBinTag)0x44
|
||||
|
||||
|
||||
//Stroke
|
||||
#define TVG_TAG_SHAPE_STROKE_CAP (TvgBinTag)0x50
|
||||
#define TVG_TAG_SHAPE_STROKE_JOIN (TvgBinTag)0x51
|
||||
#define TVG_TAG_SHAPE_STROKE_WIDTH (TvgBinTag)0x52
|
||||
#define TVG_TAG_SHAPE_STROKE_COLOR (TvgBinTag)0x53
|
||||
#define TVG_TAG_SHAPE_STROKE_FILL (TvgBinTag)0x54
|
||||
#define TVG_TAG_SHAPE_STROKE_DASHPTRN (TvgBinTag)0x55
|
||||
#define TVG_TAG_SHAPE_STROKE_MITERLIMIT (TvgBinTag)0x56
|
||||
#define TVG_TAG_SHAPE_STROKE_ORDER (TvgBinTag)0x57
|
||||
#define TVG_TAG_SHAPE_STROKE_DASH_OFFSET (TvgBinTag)0x58
|
||||
|
||||
|
||||
//Fill
|
||||
#define TVG_TAG_FILL_LINEAR_GRADIENT (TvgBinTag)0x60
|
||||
#define TVG_TAG_FILL_RADIAL_GRADIENT (TvgBinTag)0x61
|
||||
#define TVG_TAG_FILL_COLORSTOPS (TvgBinTag)0x62
|
||||
#define TVG_TAG_FILL_FILLSPREAD (TvgBinTag)0x63
|
||||
#define TVG_TAG_FILL_TRANSFORM (TvgBinTag)0x64
|
||||
#define TVG_TAG_FILL_RADIAL_GRADIENT_FOCAL (TvgBinTag)0x65
|
||||
|
||||
//Picture
|
||||
#define TVG_TAG_PICTURE_RAW_IMAGE (TvgBinTag)0x70
|
||||
#define TVG_TAG_PICTURE_MESH (TvgBinTag)0x71
|
||||
|
||||
#endif //_TVG_FORMAT_H_
|
|
@ -1,9 +1,5 @@
|
|||
subloader_dep = []
|
||||
|
||||
if tvg_loader
|
||||
subdir('tvg')
|
||||
endif
|
||||
|
||||
if svg_loader
|
||||
subdir('svg')
|
||||
endif
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
source_file = [
|
||||
'tvgTvgLoader.h',
|
||||
'tvgTvgCommon.h',
|
||||
'tvgTvgLoader.cpp',
|
||||
'tvgTvgBinInterpreter.cpp',
|
||||
]
|
||||
|
||||
subloader_dep += [declare_dependency(
|
||||
include_directories : include_directories('.'),
|
||||
sources : source_file
|
||||
)]
|
|
@ -1,487 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021 - 2024 the ThorVG project. 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 <memory.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <malloc.h>
|
||||
#elif defined(__linux__)
|
||||
#include <alloca.h>
|
||||
#else
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
|
||||
#include "tvgTvgCommon.h"
|
||||
#include "tvgShape.h"
|
||||
#include "tvgFill.h"
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
struct TvgBinBlock
|
||||
{
|
||||
TvgBinTag type;
|
||||
TvgBinCounter length;
|
||||
const char* data;
|
||||
const char* end;
|
||||
};
|
||||
|
||||
static Paint* _parsePaint(TvgBinBlock baseBlock);
|
||||
|
||||
|
||||
static TvgBinBlock _readBlock(const char *ptr)
|
||||
{
|
||||
TvgBinBlock block;
|
||||
block.type = *ptr;
|
||||
READ_UI32(&block.length, ptr + SIZE(TvgBinTag));
|
||||
block.data = ptr + SIZE(TvgBinTag) + SIZE(TvgBinCounter);
|
||||
block.end = block.data + block.length;
|
||||
return block;
|
||||
}
|
||||
|
||||
|
||||
static bool _parseCmpTarget(const char *ptr, const char *end, Paint *paint)
|
||||
{
|
||||
auto block = _readBlock(ptr);
|
||||
if (block.end > end) return false;
|
||||
|
||||
if (block.type != TVG_TAG_PAINT_CMP_METHOD) return false;
|
||||
if (block.length != SIZE(TvgBinFlag)) return false;
|
||||
|
||||
auto cmpMethod = static_cast<CompositeMethod>(*block.data);
|
||||
|
||||
ptr = block.end;
|
||||
|
||||
auto cmpBlock = _readBlock(ptr);
|
||||
if (cmpBlock.end > end) return false;
|
||||
|
||||
paint->composite(unique_ptr<Paint>(_parsePaint(cmpBlock)), cmpMethod);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool _parsePaintProperty(TvgBinBlock block, Paint *paint)
|
||||
{
|
||||
switch (block.type) {
|
||||
case TVG_TAG_PAINT_OPACITY: {
|
||||
if (block.length != SIZE(uint8_t)) return false;
|
||||
paint->opacity(*block.data);
|
||||
return true;
|
||||
}
|
||||
case TVG_TAG_PAINT_TRANSFORM: {
|
||||
if (block.length != SIZE(Matrix)) return false;
|
||||
Matrix matrix;
|
||||
memcpy(&matrix, block.data, SIZE(Matrix));
|
||||
paint->transform(matrix);
|
||||
return true;
|
||||
}
|
||||
case TVG_TAG_PAINT_CMP_TARGET: {
|
||||
if (block.length < SIZE(TvgBinTag) + SIZE(TvgBinCounter)) return false;
|
||||
return _parseCmpTarget(block.data, block.end, paint);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static bool _parseScene(TvgBinBlock block, Paint *paint)
|
||||
{
|
||||
auto scene = static_cast<Scene*>(paint);
|
||||
|
||||
//Case2: Base Paint Properties
|
||||
if (_parsePaintProperty(block, scene)) return true;
|
||||
|
||||
//Case3: A Child paint
|
||||
if (auto paint = _parsePaint(block)) {
|
||||
scene->push(unique_ptr<Paint>(paint));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static bool _parseShapePath(const char *ptr, const char *end, Shape *shape)
|
||||
{
|
||||
uint32_t cmdCnt, ptsCnt;
|
||||
|
||||
READ_UI32(&cmdCnt, ptr);
|
||||
ptr += SIZE(cmdCnt);
|
||||
|
||||
READ_UI32(&ptsCnt, ptr);
|
||||
ptr += SIZE(ptsCnt);
|
||||
|
||||
auto cmds = (TvgBinFlag*) ptr;
|
||||
ptr += SIZE(TvgBinFlag) * cmdCnt;
|
||||
|
||||
auto pts = (Point*) ptr;
|
||||
ptr += SIZE(Point) * ptsCnt;
|
||||
|
||||
if (ptr > end) return false;
|
||||
|
||||
/* Recover to PathCommand(4 bytes) from TvgBinFlag(1 byte) */
|
||||
PathCommand* inCmds = (PathCommand*)alloca(sizeof(PathCommand) * cmdCnt);
|
||||
for (uint32_t i = 0; i < cmdCnt; ++i) {
|
||||
inCmds[i] = static_cast<PathCommand>(cmds[i]);
|
||||
}
|
||||
|
||||
shape->appendPath(inCmds, cmdCnt, pts, ptsCnt);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static unique_ptr<Fill> _parseShapeFill(const char *ptr, const char *end)
|
||||
{
|
||||
unique_ptr<Fill> fillGrad;
|
||||
|
||||
while (ptr < end) {
|
||||
auto block = _readBlock(ptr);
|
||||
if (block.end > end) return nullptr;
|
||||
|
||||
switch (block.type) {
|
||||
case TVG_TAG_FILL_RADIAL_GRADIENT: {
|
||||
if (block.length != 3 * SIZE(float)) return nullptr;
|
||||
|
||||
auto ptr = block.data;
|
||||
float x, y, radius;
|
||||
|
||||
READ_FLOAT(&x, ptr);
|
||||
ptr += SIZE(float);
|
||||
READ_FLOAT(&y, ptr);
|
||||
ptr += SIZE(float);
|
||||
READ_FLOAT(&radius, ptr);
|
||||
|
||||
auto fillGradRadial = RadialGradient::gen();
|
||||
fillGradRadial->radial(x, y, radius);
|
||||
fillGrad = std::move(fillGradRadial);
|
||||
break;
|
||||
}
|
||||
case TVG_TAG_FILL_RADIAL_GRADIENT_FOCAL: {
|
||||
if (block.length != 3 * SIZE(float)) return nullptr;
|
||||
|
||||
auto ptr = block.data;
|
||||
float x, y, radius;
|
||||
|
||||
READ_FLOAT(&x, ptr);
|
||||
ptr += SIZE(float);
|
||||
READ_FLOAT(&y, ptr);
|
||||
ptr += SIZE(float);
|
||||
READ_FLOAT(&radius, ptr);
|
||||
|
||||
if (auto fillGradRadial = static_cast<RadialGradient*>(fillGrad.get())) {
|
||||
P(fillGradRadial)->fx = x;
|
||||
P(fillGradRadial)->fy = y;
|
||||
P(fillGradRadial)->fr = radius;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TVG_TAG_FILL_LINEAR_GRADIENT: {
|
||||
if (block.length != 4 * SIZE(float)) return nullptr;
|
||||
|
||||
auto ptr = block.data;
|
||||
float x1, y1, x2, y2;
|
||||
|
||||
READ_FLOAT(&x1, ptr);
|
||||
ptr += SIZE(float);
|
||||
READ_FLOAT(&y1, ptr);
|
||||
ptr += SIZE(float);
|
||||
READ_FLOAT(&x2, ptr);
|
||||
ptr += SIZE(float);
|
||||
READ_FLOAT(&y2, ptr);
|
||||
|
||||
auto fillGradLinear = LinearGradient::gen();
|
||||
fillGradLinear->linear(x1, y1, x2, y2);
|
||||
fillGrad = std::move(fillGradLinear);
|
||||
break;
|
||||
}
|
||||
case TVG_TAG_FILL_FILLSPREAD: {
|
||||
if (!fillGrad) return nullptr;
|
||||
if (block.length != SIZE(TvgBinFlag)) return nullptr;
|
||||
fillGrad->spread((FillSpread) *block.data);
|
||||
break;
|
||||
}
|
||||
case TVG_TAG_FILL_COLORSTOPS: {
|
||||
if (!fillGrad) return nullptr;
|
||||
if (block.length == 0 || block.length & 0x07) return nullptr;
|
||||
uint32_t stopsCnt = block.length >> 3; // 8 bytes per ColorStop
|
||||
if (stopsCnt > 1023) return nullptr;
|
||||
Fill::ColorStop* stops = (Fill::ColorStop*)alloca(sizeof(Fill::ColorStop) * stopsCnt);
|
||||
auto p = block.data;
|
||||
for (uint32_t i = 0; i < stopsCnt; i++, p += 8) {
|
||||
READ_FLOAT(&stops[i].offset, p);
|
||||
stops[i].r = p[4];
|
||||
stops[i].g = p[5];
|
||||
stops[i].b = p[6];
|
||||
stops[i].a = p[7];
|
||||
}
|
||||
fillGrad->colorStops(stops, stopsCnt);
|
||||
break;
|
||||
}
|
||||
case TVG_TAG_FILL_TRANSFORM: {
|
||||
if (!fillGrad || block.length != SIZE(Matrix)) return nullptr;
|
||||
Matrix gradTransform;
|
||||
memcpy(&gradTransform, block.data, SIZE(Matrix));
|
||||
fillGrad->transform(gradTransform);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
TVGLOG("TVG", "Unsupported tag %d (0x%x) used as one of the fill properties, %d bytes skipped", block.type, block.type, block.length);
|
||||
break;
|
||||
}
|
||||
}
|
||||
ptr = block.end;
|
||||
}
|
||||
return fillGrad;
|
||||
}
|
||||
|
||||
|
||||
static bool _parseShapeStrokeDashPattern(const char *ptr, const char *end, Shape *shape)
|
||||
{
|
||||
uint32_t dashPatternCnt;
|
||||
READ_UI32(&dashPatternCnt, ptr);
|
||||
ptr += SIZE(uint32_t);
|
||||
if (dashPatternCnt > 0) {
|
||||
float* dashPattern = static_cast<float*>(malloc(sizeof(float) * dashPatternCnt));
|
||||
if (!dashPattern) return false;
|
||||
memcpy(dashPattern, ptr, sizeof(float) * dashPatternCnt);
|
||||
ptr += SIZE(float) * dashPatternCnt;
|
||||
|
||||
if (ptr > end) {
|
||||
free(dashPattern);
|
||||
return false;
|
||||
}
|
||||
|
||||
shape->strokeDash(dashPattern, dashPatternCnt);
|
||||
free(dashPattern);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool _parseShapeStroke(const char *ptr, const char *end, Shape *shape)
|
||||
{
|
||||
while (ptr < end) {
|
||||
auto block = _readBlock(ptr);
|
||||
if (block.end > end) return false;
|
||||
|
||||
switch (block.type) {
|
||||
case TVG_TAG_SHAPE_STROKE_CAP: {
|
||||
if (block.length != SIZE(TvgBinFlag)) return false;
|
||||
shape->strokeCap((StrokeCap) *block.data);
|
||||
break;
|
||||
}
|
||||
case TVG_TAG_SHAPE_STROKE_JOIN: {
|
||||
if (block.length != SIZE(TvgBinFlag)) return false;
|
||||
shape->strokeJoin((StrokeJoin) *block.data);
|
||||
break;
|
||||
}
|
||||
case TVG_TAG_SHAPE_STROKE_ORDER: {
|
||||
if (block.length != SIZE(TvgBinFlag)) return false;
|
||||
P(shape)->strokeFirst((bool) *block.data);
|
||||
break;
|
||||
}
|
||||
case TVG_TAG_SHAPE_STROKE_WIDTH: {
|
||||
if (block.length != SIZE(float)) return false;
|
||||
float width;
|
||||
READ_FLOAT(&width, block.data);
|
||||
shape->strokeWidth(width);
|
||||
break;
|
||||
}
|
||||
case TVG_TAG_SHAPE_STROKE_COLOR: {
|
||||
if (block.length != 4) return false;
|
||||
shape->strokeFill(block.data[0], block.data[1], block.data[2], block.data[3]);
|
||||
break;
|
||||
}
|
||||
case TVG_TAG_SHAPE_STROKE_FILL: {
|
||||
auto fill = _parseShapeFill(block.data, block.end);
|
||||
if (!fill) return false;
|
||||
shape->strokeFill(std::move(fill));
|
||||
break;
|
||||
}
|
||||
case TVG_TAG_SHAPE_STROKE_DASHPTRN: {
|
||||
if (!_parseShapeStrokeDashPattern(block.data, block.end, shape)) return false;
|
||||
break;
|
||||
}
|
||||
case TVG_TAG_SHAPE_STROKE_MITERLIMIT: {
|
||||
if (block.length != SIZE(float)) return false;
|
||||
float miterlimit;
|
||||
READ_FLOAT(&miterlimit, block.data);
|
||||
shape->strokeMiterlimit(miterlimit);
|
||||
break;
|
||||
}
|
||||
case TVG_TAG_SHAPE_STROKE_DASH_OFFSET: {
|
||||
if (block.length != SIZE(float)) return false;
|
||||
float offset;
|
||||
READ_FLOAT(&offset, block.data);
|
||||
P(shape)->rs.stroke->dashOffset = offset;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
TVGLOG("TVG", "Unsupported tag %d (0x%x) used as one of stroke properties, %d bytes skipped", block.type, block.type, block.length);
|
||||
break;
|
||||
}
|
||||
}
|
||||
ptr = block.end;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool _parseShape(TvgBinBlock block, Paint* paint)
|
||||
{
|
||||
auto shape = static_cast<Shape*>(paint);
|
||||
|
||||
//Case1: Shape specific properties
|
||||
switch (block.type) {
|
||||
case TVG_TAG_SHAPE_PATH: {
|
||||
return _parseShapePath(block.data, block.end, shape);
|
||||
}
|
||||
case TVG_TAG_SHAPE_STROKE: {
|
||||
return _parseShapeStroke(block.data, block.end, shape);
|
||||
}
|
||||
case TVG_TAG_SHAPE_FILL: {
|
||||
auto fill = _parseShapeFill(block.data, block.end);
|
||||
if (!fill) return false;
|
||||
shape->fill(std::move(fill));
|
||||
return true;
|
||||
}
|
||||
case TVG_TAG_SHAPE_COLOR: {
|
||||
if (block.length != 4) return false;
|
||||
shape->fill(block.data[0], block.data[1], block.data[2], block.data[3]);
|
||||
return true;
|
||||
}
|
||||
case TVG_TAG_SHAPE_FILLRULE: {
|
||||
if (block.length != SIZE(TvgBinFlag)) return false;
|
||||
shape->fill((FillRule)*block.data);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
//Case2: Base Paint Properties
|
||||
return _parsePaintProperty(block, shape);
|
||||
}
|
||||
|
||||
|
||||
static bool _parsePicture(TvgBinBlock block, Paint* paint)
|
||||
{
|
||||
auto picture = static_cast<Picture*>(paint);
|
||||
|
||||
switch (block.type) {
|
||||
case TVG_TAG_PICTURE_RAW_IMAGE: {
|
||||
if (block.length < 2 * SIZE(uint32_t)) return false;
|
||||
|
||||
auto ptr = block.data;
|
||||
uint32_t w, h;
|
||||
|
||||
READ_UI32(&w, ptr);
|
||||
ptr += SIZE(uint32_t);
|
||||
READ_UI32(&h, ptr);
|
||||
ptr += SIZE(uint32_t);
|
||||
|
||||
auto size = w * h * SIZE(uint32_t);
|
||||
if (block.length != 2 * SIZE(uint32_t) + size) return false;
|
||||
|
||||
picture->load((uint32_t*) ptr, w, h, true, true);
|
||||
|
||||
return true;
|
||||
}
|
||||
//Base Paint Properties
|
||||
default: {
|
||||
if (_parsePaintProperty(block, picture)) return true;
|
||||
}
|
||||
}
|
||||
|
||||
//Vector Picture won't be requested since Saver replaces it with the Scene
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static Paint* _parsePaint(TvgBinBlock baseBlock)
|
||||
{
|
||||
bool (*parser)(TvgBinBlock, Paint*);
|
||||
Paint *paint;
|
||||
|
||||
//1. Decide the type of paint.
|
||||
switch (baseBlock.type) {
|
||||
case TVG_TAG_CLASS_SCENE: {
|
||||
paint = Scene::gen().release();
|
||||
parser = _parseScene;
|
||||
break;
|
||||
}
|
||||
case TVG_TAG_CLASS_SHAPE: {
|
||||
paint = Shape::gen().release();
|
||||
parser = _parseShape;
|
||||
break;
|
||||
}
|
||||
case TVG_TAG_CLASS_PICTURE: {
|
||||
paint = Picture::gen().release();
|
||||
parser = _parsePicture;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
TVGERR("TVG", "Invalid Paint Type %d (0x%x)", baseBlock.type, baseBlock.type);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
auto ptr = baseBlock.data;
|
||||
|
||||
//2. Read Subsequent properties of the current paint.
|
||||
while (ptr < baseBlock.end) {
|
||||
auto block = _readBlock(ptr);
|
||||
if (block.end > baseBlock.end) return paint;
|
||||
if (!parser(block, paint)) {
|
||||
TVGERR("TVG", "Encountered the wrong paint properties... Paint Class %d (0x%x)", baseBlock.type, baseBlock.type);
|
||||
return paint;
|
||||
}
|
||||
ptr = block.end;
|
||||
}
|
||||
return paint;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
Scene* TvgBinInterpreter::run(const char *ptr, const char* end)
|
||||
{
|
||||
auto scene = Scene::gen();
|
||||
if (!scene) return nullptr;
|
||||
|
||||
while (ptr < end) {
|
||||
auto block = _readBlock(ptr);
|
||||
if (block.end > end) {
|
||||
TVGERR("TVG", "Corrupted tvg file.");
|
||||
return nullptr;
|
||||
}
|
||||
scene->push(unique_ptr<Paint>(_parsePaint(block)));
|
||||
ptr = block.end;
|
||||
}
|
||||
|
||||
return scene.release();
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021 - 2024 the ThorVG project. 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_TVG_COMMON_H_
|
||||
#define _TVG_TVG_COMMON_H_
|
||||
|
||||
#include "tvgCommon.h"
|
||||
#include "tvgFormat.h"
|
||||
|
||||
#define SIZE(A) sizeof(A)
|
||||
#define READ_UI32(dst, src) memcpy(dst, (src), sizeof(uint32_t))
|
||||
#define READ_FLOAT(dst, src) memcpy(dst, (src), sizeof(float))
|
||||
|
||||
|
||||
/* Interface for Tvg Binary Interpreter */
|
||||
class TvgBinInterpreterBase
|
||||
{
|
||||
public:
|
||||
virtual ~TvgBinInterpreterBase() {}
|
||||
|
||||
/* ptr: points the tvg binary body (after header)
|
||||
end: end of the tvg binary data */
|
||||
virtual Scene* run(const char* ptr, const char* end) = 0;
|
||||
};
|
||||
|
||||
|
||||
/* Version 0 */
|
||||
class TvgBinInterpreter : public TvgBinInterpreterBase
|
||||
{
|
||||
public:
|
||||
Scene* run(const char* ptr, const char* end) override;
|
||||
};
|
||||
|
||||
|
||||
#endif //_TVG_TVG_COMMON_H_
|
|
@ -1,233 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021 - 2024 the ThorVG project. 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 <memory.h>
|
||||
#include <fstream>
|
||||
#include "tvgLoader.h"
|
||||
#include "tvgTvgLoader.h"
|
||||
#include "tvgCompressor.h"
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
|
||||
void TvgLoader::clear(bool all)
|
||||
{
|
||||
if (copy) free((char*)data);
|
||||
ptr = data = nullptr;
|
||||
size = 0;
|
||||
copy = false;
|
||||
|
||||
delete(interpreter);
|
||||
interpreter = nullptr;
|
||||
|
||||
if (all) delete(root);
|
||||
}
|
||||
|
||||
|
||||
/* WARNING: Header format shall not change! */
|
||||
bool TvgLoader::readHeader()
|
||||
{
|
||||
if (!ptr) return false;
|
||||
|
||||
//Make sure the size is large enough to hold the header
|
||||
if (size < TVG_HEADER_SIZE) return false;
|
||||
|
||||
//1. Signature
|
||||
if (memcmp(ptr, TVG_HEADER_SIGNATURE, TVG_HEADER_SIGNATURE_LENGTH)) return false;
|
||||
ptr += TVG_HEADER_SIGNATURE_LENGTH;
|
||||
|
||||
//2. Version
|
||||
char version[TVG_HEADER_VERSION_LENGTH + 1];
|
||||
memcpy(version, ptr, TVG_HEADER_VERSION_LENGTH);
|
||||
version[TVG_HEADER_VERSION_LENGTH - 1] = '\0';
|
||||
ptr += TVG_HEADER_VERSION_LENGTH;
|
||||
this->version = atoi(version);
|
||||
if (this->version > THORVG_VERSION_NUMBER()) {
|
||||
TVGLOG("TVG", "This TVG file expects a higher version(%d) of ThorVG symbol(%d)", this->version, THORVG_VERSION_NUMBER());
|
||||
}
|
||||
|
||||
//3. View Size
|
||||
READ_FLOAT(&w, ptr);
|
||||
ptr += SIZE(float);
|
||||
READ_FLOAT(&h, ptr);
|
||||
ptr += SIZE(float);
|
||||
|
||||
//4. Reserved
|
||||
if (*ptr & TVG_HEAD_FLAG_COMPRESSED) compressed = true;
|
||||
ptr += TVG_HEADER_RESERVED_LENGTH;
|
||||
|
||||
//5. Compressed Size if any
|
||||
if (compressed) {
|
||||
auto p = ptr;
|
||||
|
||||
//TVG_HEADER_UNCOMPRESSED_SIZE
|
||||
memcpy(&uncompressedSize, p, sizeof(uint32_t));
|
||||
p += SIZE(uint32_t);
|
||||
|
||||
//TVG_HEADER_COMPRESSED_SIZE
|
||||
memcpy(&compressedSize, p, sizeof(uint32_t));
|
||||
p += SIZE(uint32_t);
|
||||
|
||||
//TVG_HEADER_COMPRESSED_SIZE_BITS
|
||||
memcpy(&compressedSizeBits, p, sizeof(uint32_t));
|
||||
}
|
||||
|
||||
ptr += TVG_HEADER_COMPRESS_SIZE;
|
||||
|
||||
//Decide the proper Tvg Binary Interpreter based on the current file version
|
||||
if (this->version >= 0) interpreter = new TvgBinInterpreter;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void TvgLoader::run(unsigned tid)
|
||||
{
|
||||
auto data = const_cast<char*>(ptr);
|
||||
|
||||
if (compressed) {
|
||||
data = (char*) lzwDecode((uint8_t*) data, compressedSize, compressedSizeBits, uncompressedSize);
|
||||
root = interpreter->run(data, data + uncompressedSize);
|
||||
free(data);
|
||||
} else {
|
||||
root = interpreter->run(data, this->data + size);
|
||||
}
|
||||
|
||||
clear(false);
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
TvgLoader::TvgLoader() : ImageLoader(FileType::Tvg)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
TvgLoader::~TvgLoader()
|
||||
{
|
||||
this->done();
|
||||
clear();
|
||||
}
|
||||
|
||||
|
||||
bool TvgLoader::open(const string &path)
|
||||
{
|
||||
clear();
|
||||
|
||||
ifstream f;
|
||||
f.open(path, ifstream::in | ifstream::binary | ifstream::ate);
|
||||
|
||||
if (!f.is_open()) return false;
|
||||
|
||||
size = f.tellg();
|
||||
f.seekg(0, ifstream::beg);
|
||||
|
||||
copy = true;
|
||||
data = (char*)malloc(size);
|
||||
if (!data) {
|
||||
clear();
|
||||
f.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!f.read((char*)data, size))
|
||||
{
|
||||
clear();
|
||||
f.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
f.close();
|
||||
|
||||
ptr = data;
|
||||
|
||||
return readHeader();
|
||||
}
|
||||
|
||||
|
||||
bool TvgLoader::open(const char *data, uint32_t size, TVG_UNUSED const string& rpath, bool copy)
|
||||
{
|
||||
clear();
|
||||
|
||||
if (copy) {
|
||||
this->data = (char*)malloc(size);
|
||||
if (!this->data) return false;
|
||||
memcpy((char*)this->data, data, size);
|
||||
} else this->data = data;
|
||||
|
||||
this->ptr = this->data;
|
||||
this->size = size;
|
||||
this->copy = copy;
|
||||
|
||||
return readHeader();
|
||||
}
|
||||
|
||||
|
||||
bool TvgLoader::resize(Paint* paint, float w, float h)
|
||||
{
|
||||
if (!paint) return false;
|
||||
|
||||
auto sx = w / this->w;
|
||||
auto sy = h / this->h;
|
||||
|
||||
//Scale
|
||||
auto scale = sx < sy ? sx : sy;
|
||||
paint->scale(scale);
|
||||
|
||||
//Align
|
||||
float tx = 0, ty = 0;
|
||||
auto sw = this->w * scale;
|
||||
auto sh = this->h * scale;
|
||||
if (sw > sh) ty -= (h - sh) * 0.5f;
|
||||
else tx -= (w - sw) * 0.5f;
|
||||
paint->translate(-tx, -ty);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool TvgLoader::read()
|
||||
{
|
||||
if (!ptr || size == 0) return false;
|
||||
|
||||
//the loading has been already completed
|
||||
if (root || !LoadModule::read()) return true;
|
||||
|
||||
TaskScheduler::request(this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Paint* TvgLoader::paint()
|
||||
{
|
||||
this->done();
|
||||
auto ret = root;
|
||||
root = nullptr;
|
||||
return ret;
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021 - 2024 the ThorVG project. 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_TVG_LOADER_H_
|
||||
#define _TVG_TVG_LOADER_H_
|
||||
|
||||
#include "tvgTaskScheduler.h"
|
||||
#include "tvgTvgCommon.h"
|
||||
|
||||
|
||||
class TvgLoader : public ImageLoader, public Task
|
||||
{
|
||||
public:
|
||||
const char* data = nullptr;
|
||||
const char* ptr = nullptr;
|
||||
uint32_t size = 0;
|
||||
uint16_t version = 0;
|
||||
Scene* root = nullptr;
|
||||
TvgBinInterpreterBase* interpreter = nullptr;
|
||||
uint32_t uncompressedSize = 0;
|
||||
uint32_t compressedSize = 0;
|
||||
uint32_t compressedSizeBits = 0;
|
||||
bool copy = false;
|
||||
bool compressed = false;
|
||||
|
||||
TvgLoader();
|
||||
~TvgLoader();
|
||||
|
||||
bool open(const string &path) override;
|
||||
bool open(const char *data, uint32_t size, const string& rpath, bool copy) override;
|
||||
bool read() override;
|
||||
bool resize(Paint* paint, float w, float h) override;
|
||||
|
||||
Paint* paint() override;
|
||||
|
||||
private:
|
||||
bool readHeader();
|
||||
void run(unsigned tid) override;
|
||||
void clear(bool all = true);
|
||||
};
|
||||
|
||||
#endif //_TVG_TVG_LOADER_H_
|
|
@ -1639,7 +1639,7 @@ bool rasterCompositor(SwSurface* surface)
|
|||
surface->alphas[2] = _argbLuma;
|
||||
surface->alphas[3] = _argbInvLuma;
|
||||
} else {
|
||||
TVGERR("SW_ENGINE", "Unsupported Colorspace(%d) is expected!", surface->cs);
|
||||
TVGERR("SW_ENGINE", "Unsupported Colorspace(%d) is expected!", (int)surface->cs);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -54,7 +54,7 @@ using namespace tvg;
|
|||
#define strdup _strdup
|
||||
#endif
|
||||
|
||||
enum class FileType { Png = 0, Jpg, Webp, Tvg, Svg, Lottie, Ttf, Raw, Gif, Unknown };
|
||||
enum class FileType { Png = 0, Jpg, Webp, Svg, Lottie, Ttf, Raw, Gif, Unknown };
|
||||
|
||||
using Size = Point;
|
||||
|
||||
|
|
|
@ -34,10 +34,6 @@
|
|||
#include "tvgPngLoader.h"
|
||||
#endif
|
||||
|
||||
#ifdef THORVG_TVG_LOADER_SUPPORT
|
||||
#include "tvgTvgLoader.h"
|
||||
#endif
|
||||
|
||||
#ifdef THORVG_JPG_LOADER_SUPPORT
|
||||
#include "tvgJpgLoader.h"
|
||||
#endif
|
||||
|
@ -90,12 +86,6 @@ static LoadModule* _find(FileType type)
|
|||
case FileType::Webp: {
|
||||
#ifdef THORVG_WEBP_LOADER_SUPPORT
|
||||
return new WebpLoader;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case FileType::Tvg: {
|
||||
#ifdef THORVG_TVG_LOADER_SUPPORT
|
||||
return new TvgLoader;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
@ -129,10 +119,6 @@ static LoadModule* _find(FileType type)
|
|||
#ifdef THORVG_LOG_ENABLED
|
||||
const char *format;
|
||||
switch(type) {
|
||||
case FileType::Tvg: {
|
||||
format = "TVG";
|
||||
break;
|
||||
}
|
||||
case FileType::Svg: {
|
||||
format = "SVG";
|
||||
break;
|
||||
|
@ -175,7 +161,6 @@ static LoadModule* _find(FileType type)
|
|||
static LoadModule* _findByPath(const string& path)
|
||||
{
|
||||
auto ext = path.substr(path.find_last_of(".") + 1);
|
||||
if (!ext.compare("tvg")) return _find(FileType::Tvg);
|
||||
if (!ext.compare("svg")) return _find(FileType::Svg);
|
||||
if (!ext.compare("json")) return _find(FileType::Lottie);
|
||||
if (!ext.compare("png")) return _find(FileType::Png);
|
||||
|
@ -191,8 +176,7 @@ static FileType _convert(const string& mimeType)
|
|||
{
|
||||
auto type = FileType::Unknown;
|
||||
|
||||
if (mimeType == "tvg") type = FileType::Tvg;
|
||||
else if (mimeType == "svg" || mimeType == "svg+xml") type = FileType::Svg;
|
||||
if (mimeType == "svg" || mimeType == "svg+xml") type = FileType::Svg;
|
||||
else if (mimeType == "ttf" || mimeType == "otf") type = FileType::Ttf;
|
||||
else if (mimeType == "lottie") type = FileType::Lottie;
|
||||
else if (mimeType == "raw") type = FileType::Raw;
|
||||
|
|
|
@ -24,9 +24,6 @@
|
|||
#include "tvgSaveModule.h"
|
||||
#include "tvgPaint.h"
|
||||
|
||||
#ifdef THORVG_TVG_SAVER_SUPPORT
|
||||
#include "tvgTvgSaver.h"
|
||||
#endif
|
||||
#ifdef THORVG_GIF_SAVER_SUPPORT
|
||||
#include "tvgGifSaver.h"
|
||||
#endif
|
||||
|
@ -51,12 +48,6 @@ struct Saver::Impl
|
|||
static SaveModule* _find(FileType type)
|
||||
{
|
||||
switch(type) {
|
||||
case FileType::Tvg: {
|
||||
#ifdef THORVG_TVG_SAVER_SUPPORT
|
||||
return new TvgSaver;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case FileType::Gif: {
|
||||
#ifdef THORVG_GIF_SAVER_SUPPORT
|
||||
return new GifSaver;
|
||||
|
@ -71,10 +62,6 @@ static SaveModule* _find(FileType type)
|
|||
#ifdef THORVG_LOG_ENABLED
|
||||
const char *format;
|
||||
switch(type) {
|
||||
case FileType::Tvg: {
|
||||
format = "TVG";
|
||||
break;
|
||||
}
|
||||
case FileType::Gif: {
|
||||
format = "GIF";
|
||||
break;
|
||||
|
@ -93,11 +80,7 @@ static SaveModule* _find(FileType type)
|
|||
static SaveModule* _find(const string& path)
|
||||
{
|
||||
auto ext = path.substr(path.find_last_of(".") + 1);
|
||||
if (!ext.compare("tvg")) {
|
||||
return _find(FileType::Tvg);
|
||||
} else if (!ext.compare("gif")) {
|
||||
return _find(FileType::Gif);
|
||||
}
|
||||
if (!ext.compare("gif")) return _find(FileType::Gif);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,5 @@
|
|||
subsaver_dep = []
|
||||
|
||||
if tvg_saver
|
||||
subdir('tvg')
|
||||
endif
|
||||
|
||||
if gif_saver
|
||||
subdir('gif')
|
||||
endif
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
source_file = [
|
||||
'tvgTvgSaver.h',
|
||||
'tvgTvgSaver.cpp',
|
||||
]
|
||||
|
||||
subsaver_dep += [declare_dependency(
|
||||
include_directories : include_directories('.'),
|
||||
sources : source_file
|
||||
)]
|
|
@ -1,818 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021 - 2024 the ThorVG project. 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 <cstring>
|
||||
|
||||
#include "tvgSaveModule.h"
|
||||
#include "tvgTvgSaver.h"
|
||||
#include "tvgCompressor.h"
|
||||
#include "tvgShape.h"
|
||||
#include "tvgFill.h"
|
||||
#include "tvgPicture.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <malloc.h>
|
||||
#elif defined(__linux__)
|
||||
#include <alloca.h>
|
||||
#else
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
|
||||
static FILE* _fopen(const char* filename, const char* mode)
|
||||
{
|
||||
#if defined(_MSC_VER) && defined(__clang__)
|
||||
FILE *fp;
|
||||
auto err = fopen_s(&fp, filename, mode);
|
||||
if (err != 0) return nullptr;
|
||||
return fp;
|
||||
#else
|
||||
auto fp = fopen(filename, mode);
|
||||
if (!fp) return nullptr;
|
||||
return fp;
|
||||
#endif
|
||||
}
|
||||
|
||||
#define SIZE(A) sizeof(A)
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
static inline TvgBinCounter SERIAL_DONE(TvgBinCounter cnt)
|
||||
{
|
||||
return SIZE(TvgBinTag) + SIZE(TvgBinCounter) + cnt;
|
||||
}
|
||||
|
||||
|
||||
/* if the properties are identical, we can merge the shapes. */
|
||||
static bool _merge(Shape* from, Shape* to)
|
||||
{
|
||||
uint8_t r, g, b, a;
|
||||
uint8_t r2, g2, b2, a2;
|
||||
|
||||
//fill
|
||||
if (from->fill() || to->fill()) return false;
|
||||
|
||||
r = g = b = a = r2 = g2 = b2 = a2 = 0;
|
||||
|
||||
from->fillColor(&r, &g, &b, &a);
|
||||
to->fillColor(&r2, &g2, &b2, &a2);
|
||||
|
||||
if (r != r2 || g != g2 || b != b2 || a != a2 || a < 255) return false;
|
||||
|
||||
auto fromRule = from->fillRule();
|
||||
if (fromRule == FillRule::EvenOdd || fromRule != to->fillRule()) return false;
|
||||
|
||||
//composition
|
||||
if (from->composite(nullptr) != CompositeMethod::None) return false;
|
||||
if (to->composite(nullptr) != CompositeMethod::None) return false;
|
||||
|
||||
//opacity
|
||||
if (from->opacity() != to->opacity()) return false;
|
||||
|
||||
//transform
|
||||
auto t1 = from->transform();
|
||||
auto t2 = to->transform();
|
||||
|
||||
if (t1 != t2) return false;
|
||||
|
||||
//stroke
|
||||
if (P(from)->strokeFirst() != P(to)->strokeFirst()) return false;
|
||||
|
||||
r = g = b = a = r2 = g2 = b2 = a2 = 0;
|
||||
|
||||
from->strokeFill(&r, &g, &b, &a);
|
||||
to->strokeFill(&r2, &g2, &b2, &a2);
|
||||
|
||||
if (r != r2 || g != g2 || b != b2 || a != a2) return false;
|
||||
|
||||
if (fabs(from->strokeWidth() - to->strokeWidth()) > FLOAT_EPSILON) return false;
|
||||
|
||||
//OPTIMIZE: Yet we can't merge outlining shapes unless we can support merging shapes feature.
|
||||
if (from->strokeWidth() > 0 || to->strokeWidth() > 0) return false;
|
||||
|
||||
if (from->strokeCap() != to->strokeCap()) return false;
|
||||
if (from->strokeJoin() != to->strokeJoin()) return false;
|
||||
if (from->strokeDash(nullptr) > 0 || to->strokeDash(nullptr) > 0) return false;
|
||||
if (from->strokeFill() || to->strokeFill()) return false;
|
||||
|
||||
if (fabsf(from->strokeMiterlimit() - to->strokeMiterlimit()) > FLOAT_EPSILON) return false;
|
||||
|
||||
//fill rule
|
||||
if (from->fillRule() != to->fillRule()) return false;
|
||||
|
||||
//Good, identical shapes, we can merge them.
|
||||
const PathCommand* cmds = nullptr;
|
||||
auto cmdCnt = from->pathCommands(&cmds);
|
||||
|
||||
const Point* pts = nullptr;
|
||||
auto ptsCnt = from->pathCoords(&pts);
|
||||
|
||||
to->appendPath(cmds, cmdCnt, pts, ptsCnt);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool TvgSaver::saveEncoding(const std::string& path)
|
||||
{
|
||||
//Try encoding
|
||||
auto uncompressed = buffer.data + headerSize;
|
||||
auto uncompressedSize = buffer.count - headerSize;
|
||||
|
||||
uint32_t compressedSize, compressedSizeBits;
|
||||
|
||||
auto compressed = lzwEncode(uncompressed, uncompressedSize, &compressedSize, &compressedSizeBits);
|
||||
|
||||
//Failed compression.
|
||||
if (!compressed) return flushTo(path);
|
||||
|
||||
//Optimization is ineffective.
|
||||
if (compressedSize >= uncompressedSize) {
|
||||
free(compressed);
|
||||
return flushTo(path);
|
||||
}
|
||||
|
||||
TVGLOG("TVG_SAVER", "%s, compressed: %d -> %d, saved rate: %3.2f%%", path.c_str(), uncompressedSize, compressedSize, (1 - ((float) compressedSize / (float) uncompressedSize)) * 100);
|
||||
|
||||
//Update compress size in the header.
|
||||
uncompressed -= (TVG_HEADER_COMPRESS_SIZE + TVG_HEADER_RESERVED_LENGTH);
|
||||
|
||||
//Compression Flag
|
||||
*uncompressed |= TVG_HEAD_FLAG_COMPRESSED;
|
||||
uncompressed += TVG_HEADER_RESERVED_LENGTH;
|
||||
|
||||
//Uncompressed Size
|
||||
memcpy(uncompressed, &uncompressedSize, TVG_HEADER_UNCOMPRESSED_SIZE);
|
||||
uncompressed += TVG_HEADER_UNCOMPRESSED_SIZE;
|
||||
|
||||
//Compressed Size
|
||||
memcpy(uncompressed, &compressedSize, TVG_HEADER_COMPRESSED_SIZE);
|
||||
uncompressed += TVG_HEADER_COMPRESSED_SIZE;
|
||||
|
||||
//Compressed Size Bits
|
||||
memcpy(uncompressed, &compressedSizeBits, TVG_HEADER_COMPRESSED_SIZE_BITS);
|
||||
|
||||
//Good optimization, flush to file.
|
||||
auto fp = _fopen(path.c_str(), "wb+");
|
||||
if (!fp) goto fail;
|
||||
|
||||
//write header
|
||||
if (fwrite(buffer.data, SIZE(uint8_t), headerSize, fp) == 0) goto fail;
|
||||
|
||||
//write compressed data
|
||||
if (fwrite(compressed, SIZE(uint8_t), compressedSize, fp) == 0) goto fail;
|
||||
|
||||
fclose(fp);
|
||||
free(compressed);
|
||||
|
||||
return true;
|
||||
|
||||
fail:
|
||||
if (fp) fclose(fp);
|
||||
if (compressed) free(compressed);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool TvgSaver::flushTo(const std::string& path)
|
||||
{
|
||||
auto fp = _fopen(path.c_str(), "wb+");
|
||||
if (!fp) return false;
|
||||
|
||||
if (fwrite(buffer.data, SIZE(uint8_t), buffer.count, fp) == 0) {
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/* WARNING: Header format shall not changed! */
|
||||
bool TvgSaver::writeHeader()
|
||||
{
|
||||
headerSize = TVG_HEADER_SIGNATURE_LENGTH + TVG_HEADER_VERSION_LENGTH + SIZE(vsize) + TVG_HEADER_RESERVED_LENGTH + TVG_HEADER_COMPRESS_SIZE;
|
||||
|
||||
buffer.grow(headerSize);
|
||||
|
||||
//1. Signature
|
||||
auto ptr = buffer.end();
|
||||
memcpy(ptr, TVG_HEADER_SIGNATURE, TVG_HEADER_SIGNATURE_LENGTH);
|
||||
ptr += TVG_HEADER_SIGNATURE_LENGTH;
|
||||
|
||||
//2. Version
|
||||
memcpy(ptr, TVG_HEADER_VERSION, TVG_HEADER_VERSION_LENGTH);
|
||||
ptr += TVG_HEADER_VERSION_LENGTH;
|
||||
|
||||
buffer.count += (TVG_HEADER_SIGNATURE_LENGTH + TVG_HEADER_VERSION_LENGTH);
|
||||
|
||||
//3. View Size
|
||||
writeData(vsize, SIZE(vsize));
|
||||
ptr += SIZE(vsize);
|
||||
|
||||
//4. Reserved data + Compress size
|
||||
memset(ptr, 0x00, TVG_HEADER_RESERVED_LENGTH + TVG_HEADER_COMPRESS_SIZE);
|
||||
buffer.count += (TVG_HEADER_RESERVED_LENGTH + TVG_HEADER_COMPRESS_SIZE);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void TvgSaver::writeTag(TvgBinTag tag)
|
||||
{
|
||||
buffer.grow(SIZE(TvgBinTag));
|
||||
memcpy(buffer.end(), &tag, SIZE(TvgBinTag));
|
||||
buffer.count += SIZE(TvgBinTag);
|
||||
}
|
||||
|
||||
|
||||
void TvgSaver::writeCount(TvgBinCounter cnt)
|
||||
{
|
||||
buffer.grow(SIZE(TvgBinCounter));
|
||||
memcpy(buffer.end(), &cnt, SIZE(TvgBinCounter));
|
||||
buffer.count += SIZE(TvgBinCounter);
|
||||
}
|
||||
|
||||
|
||||
void TvgSaver::writeReservedCount(TvgBinCounter cnt)
|
||||
{
|
||||
memcpy(buffer.end() - 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.end(), 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.end();
|
||||
|
||||
*ptr = tag;
|
||||
++ptr;
|
||||
|
||||
memcpy(ptr, &cnt, SIZE(TvgBinCounter));
|
||||
ptr += SIZE(TvgBinCounter);
|
||||
|
||||
memcpy(ptr, data, cnt);
|
||||
ptr += cnt;
|
||||
|
||||
buffer.count += growCnt;
|
||||
|
||||
return growCnt;
|
||||
}
|
||||
|
||||
|
||||
TvgBinCounter TvgSaver::writeTransform(const Matrix* transform, TvgBinTag tag)
|
||||
{
|
||||
if (!identity(transform)) return writeTagProperty(tag, SIZE(Matrix), transform);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
TvgBinCounter TvgSaver::serializePaint(const Paint* paint, const Matrix* pTransform)
|
||||
{
|
||||
TvgBinCounter cnt = 0;
|
||||
|
||||
//opacity
|
||||
auto opacity = paint->opacity();
|
||||
if (opacity < 255) {
|
||||
cnt += writeTagProperty(TVG_TAG_PAINT_OPACITY, SIZE(opacity), &opacity);
|
||||
}
|
||||
|
||||
//composite
|
||||
const Paint* cmpTarget = nullptr;
|
||||
auto cmpMethod = paint->composite(&cmpTarget);
|
||||
if (cmpMethod != CompositeMethod::None && cmpTarget) {
|
||||
cnt += serializeComposite(cmpTarget, cmpMethod, pTransform);
|
||||
}
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
|
||||
/* Propagate parents properties to the child so that we can skip saving the parent. */
|
||||
TvgBinCounter TvgSaver::serializeChild(const Paint* parent, const Paint* child, const Matrix* transform)
|
||||
{
|
||||
const Paint* compTarget = nullptr;
|
||||
auto compMethod = parent->composite(&compTarget);
|
||||
|
||||
/* If the parent & the only child have composition, we can't skip the parent...
|
||||
Or if the parent has the transform and composition, we can't skip the parent... */
|
||||
if (compMethod != CompositeMethod::None) {
|
||||
if (transform || child->composite(nullptr) != CompositeMethod::None) return 0;
|
||||
}
|
||||
|
||||
//propagate opacity
|
||||
uint32_t opacity = parent->opacity();
|
||||
|
||||
if (opacity < 255) {
|
||||
uint32_t tmp = (child->opacity() * opacity);
|
||||
if (tmp > 0) tmp /= 255;
|
||||
const_cast<Paint*>(child)->opacity(tmp);
|
||||
}
|
||||
|
||||
//propagate composition
|
||||
if (compTarget) const_cast<Paint*>(child)->composite(cast<Paint>(compTarget->duplicate()), compMethod);
|
||||
|
||||
return serialize(child, transform);
|
||||
}
|
||||
|
||||
|
||||
TvgBinCounter TvgSaver::serializeScene(const Scene* scene, const Matrix* pTransform, const Matrix* cTransform)
|
||||
{
|
||||
auto it = IteratorAccessor::iterator(scene);
|
||||
if (it->count() == 0) {
|
||||
delete(it);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//Case - Only Child: Skip saving this scene.
|
||||
if (it->count() == 1) {
|
||||
auto cnt = serializeChild(scene, it->next(), cTransform);
|
||||
if (cnt > 0) {
|
||||
delete(it);
|
||||
return cnt;
|
||||
}
|
||||
}
|
||||
|
||||
it->begin();
|
||||
|
||||
//Case - Serialize Scene & its children
|
||||
writeTag(TVG_TAG_CLASS_SCENE);
|
||||
reserveCount();
|
||||
|
||||
auto cnt = serializeChildren(it, cTransform) + serializePaint(scene, pTransform);
|
||||
|
||||
delete(it);
|
||||
|
||||
writeReservedCount(cnt);
|
||||
|
||||
return SERIAL_DONE(cnt);
|
||||
}
|
||||
|
||||
|
||||
TvgBinCounter TvgSaver::serializeFill(const Fill* fill, TvgBinTag tag, const Matrix* pTransform)
|
||||
{
|
||||
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->type() == Type::RadialGradient) {
|
||||
const RadialGradient* radial = static_cast<const RadialGradient*>(fill);
|
||||
float args[3];
|
||||
radial->radial(args, args + 1, args + 2);
|
||||
cnt += writeTagProperty(TVG_TAG_FILL_RADIAL_GRADIENT, SIZE(args), args);
|
||||
//focal
|
||||
if (!tvg::zero(P(radial)->fx)|| !tvg::zero(P(radial)->fy) || P(radial)->fr > 0.0f) {
|
||||
args[0] = P(radial)->fx;
|
||||
args[1] = P(radial)->fy;
|
||||
args[2] = P(radial)->fr;
|
||||
cnt += writeTagProperty(TVG_TAG_FILL_RADIAL_GRADIENT_FOCAL, SIZE(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, SIZE(args), args);
|
||||
}
|
||||
|
||||
if (auto flag = static_cast<TvgBinFlag>(fill->spread()))
|
||||
cnt += writeTagProperty(TVG_TAG_FILL_FILLSPREAD, SIZE(TvgBinFlag), &flag);
|
||||
cnt += writeTagProperty(TVG_TAG_FILL_COLORSTOPS, stopsCnt * SIZE(Fill::ColorStop), stops);
|
||||
|
||||
auto gTransform = fill->transform();
|
||||
if (pTransform) gTransform = *pTransform * gTransform;
|
||||
|
||||
cnt += writeTransform(&gTransform, TVG_TAG_FILL_TRANSFORM);
|
||||
|
||||
writeReservedCount(cnt);
|
||||
|
||||
return SERIAL_DONE(cnt);
|
||||
}
|
||||
|
||||
|
||||
TvgBinCounter TvgSaver::serializeStroke(const Shape* shape, const Matrix* pTransform, bool preTransform)
|
||||
{
|
||||
writeTag(TVG_TAG_SHAPE_STROKE);
|
||||
reserveCount();
|
||||
|
||||
//width
|
||||
auto width = shape->strokeWidth();
|
||||
if (preTransform) width *= sqrtf(powf(pTransform->e11, 2.0f) + powf(pTransform->e21, 2.0f)); //we know x/y scaling factors are same.
|
||||
auto cnt = writeTagProperty(TVG_TAG_SHAPE_STROKE_WIDTH, SIZE(width), &width);
|
||||
|
||||
//cap
|
||||
if (auto flag = static_cast<TvgBinFlag>(shape->strokeCap()))
|
||||
cnt += writeTagProperty(TVG_TAG_SHAPE_STROKE_CAP, SIZE(TvgBinFlag), &flag);
|
||||
|
||||
//join
|
||||
if (auto flag = static_cast<TvgBinFlag>(shape->strokeJoin()))
|
||||
cnt += writeTagProperty(TVG_TAG_SHAPE_STROKE_JOIN, SIZE(TvgBinFlag), &flag);
|
||||
|
||||
//order
|
||||
if (auto flag = static_cast<TvgBinFlag>(P(shape)->strokeFirst()))
|
||||
cnt += writeTagProperty(TVG_TAG_SHAPE_STROKE_ORDER, SIZE(TvgBinFlag), &flag);
|
||||
|
||||
//fill
|
||||
if (auto fill = shape->strokeFill()) {
|
||||
cnt += serializeFill(fill, TVG_TAG_SHAPE_STROKE_FILL, (preTransform ? pTransform : nullptr));
|
||||
} else {
|
||||
uint8_t color[4] = {0, 0, 0, 0};
|
||||
shape->strokeFill(color, color + 1, color + 2, color + 3);
|
||||
cnt += writeTagProperty(TVG_TAG_SHAPE_STROKE_COLOR, SIZE(color), &color);
|
||||
}
|
||||
|
||||
//dash
|
||||
const float* dashPattern = nullptr;
|
||||
float offset = 0.0f;
|
||||
auto dashCnt = shape->strokeDash(&dashPattern, &offset);
|
||||
if (dashPattern && dashCnt > 0) {
|
||||
TvgBinCounter dashCntSize = SIZE(dashCnt);
|
||||
TvgBinCounter dashPtrnSize = dashCnt * SIZE(dashPattern[0]);
|
||||
|
||||
writeTag(TVG_TAG_SHAPE_STROKE_DASHPTRN);
|
||||
writeCount(dashCntSize + dashPtrnSize);
|
||||
cnt += writeData(&dashCnt, dashCntSize);
|
||||
cnt += writeData(dashPattern, dashPtrnSize);
|
||||
cnt += SIZE(TvgBinTag) + SIZE(TvgBinCounter);
|
||||
}
|
||||
|
||||
//miterlimit (the default value is 4)
|
||||
auto miterlimit = shape->strokeMiterlimit();
|
||||
if (fabsf(miterlimit - 4.0f) > FLOAT_EPSILON) {
|
||||
cnt += writeTagProperty(TVG_TAG_SHAPE_STROKE_MITERLIMIT, SIZE(miterlimit), &miterlimit);
|
||||
}
|
||||
|
||||
//dash offset
|
||||
if (!tvg::zero(offset)) {
|
||||
cnt += writeTagProperty(TVG_TAG_SHAPE_STROKE_DASH_OFFSET, SIZE(offset), &offset);
|
||||
}
|
||||
|
||||
writeReservedCount(cnt);
|
||||
|
||||
return SERIAL_DONE(cnt);
|
||||
}
|
||||
|
||||
|
||||
TvgBinCounter TvgSaver::serializePath(const Shape* shape, const Matrix* transform, bool preTransform)
|
||||
{
|
||||
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();
|
||||
|
||||
/* Reduce the binary size.
|
||||
Convert PathCommand(4 bytes) to TvgBinFlag(1 byte) */
|
||||
TvgBinFlag* outCmds = (TvgBinFlag*)alloca(SIZE(TvgBinFlag) * cmdCnt);
|
||||
for (uint32_t i = 0; i < cmdCnt; ++i) {
|
||||
outCmds[i] = static_cast<TvgBinFlag>(cmds[i]);
|
||||
}
|
||||
|
||||
auto cnt = writeData(&cmdCnt, SIZE(cmdCnt));
|
||||
cnt += writeData(&ptsCnt, SIZE(ptsCnt));
|
||||
cnt += writeData(outCmds, SIZE(TvgBinFlag) * cmdCnt);
|
||||
|
||||
//transform?
|
||||
if (preTransform) {
|
||||
if (!tvg::equal(transform->e11, 1.0f) || !tvg::zero(transform->e12) || !tvg::zero(transform->e13) ||
|
||||
!tvg::zero(transform->e21) || !tvg::equal(transform->e22, 1.0f) || !tvg::zero(transform->e23) ||
|
||||
!tvg::zero(transform->e31) || !tvg::zero(transform->e32) || !tvg::equal(transform->e33, 1.0f)) {
|
||||
auto p = const_cast<Point*>(pts);
|
||||
for (uint32_t i = 0; i < ptsCnt; ++i) {
|
||||
*p *= *transform;
|
||||
++p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cnt += writeData(pts, ptsCnt * SIZE(pts[0]));
|
||||
|
||||
writeReservedCount(cnt);
|
||||
|
||||
return SERIAL_DONE(cnt);
|
||||
}
|
||||
|
||||
|
||||
TvgBinCounter TvgSaver::serializeShape(const Shape* shape, const Matrix* pTransform, const Matrix* cTransform)
|
||||
{
|
||||
writeTag(TVG_TAG_CLASS_SHAPE);
|
||||
reserveCount();
|
||||
TvgBinCounter cnt = 0;
|
||||
|
||||
//fill rule
|
||||
if (auto flag = static_cast<TvgBinFlag>(shape->fillRule())) {
|
||||
cnt = writeTagProperty(TVG_TAG_SHAPE_FILLRULE, SIZE(TvgBinFlag), &flag);
|
||||
}
|
||||
|
||||
//the pre-transformation can't be applied in the case when the stroke is dashed or irregularly scaled
|
||||
bool preTransform = true;
|
||||
|
||||
//stroke
|
||||
if (shape->strokeWidth() > 0) {
|
||||
uint8_t color[4] = {0, 0, 0, 0};
|
||||
shape->strokeFill(color, color + 1, color + 2, color + 3);
|
||||
auto fill = shape->strokeFill();
|
||||
if (fill || color[3] > 0) {
|
||||
if (!tvg::equal(cTransform->e11, cTransform->e22) || (tvg::zero(cTransform->e11) && !tvg::equal(cTransform->e12, cTransform->e21)) || shape->strokeDash(nullptr) > 0) preTransform = false;
|
||||
cnt += serializeStroke(shape, cTransform, preTransform);
|
||||
}
|
||||
}
|
||||
|
||||
//fill
|
||||
if (auto fill = shape->fill()) {
|
||||
cnt += serializeFill(fill, TVG_TAG_SHAPE_FILL, (preTransform ? cTransform : nullptr));
|
||||
} else {
|
||||
uint8_t color[4] = {0, 0, 0, 0};
|
||||
shape->fillColor(color, color + 1, color + 2, color + 3);
|
||||
if (color[3] > 0) cnt += writeTagProperty(TVG_TAG_SHAPE_COLOR, SIZE(color), color);
|
||||
}
|
||||
|
||||
cnt += serializePath(shape, cTransform, preTransform);
|
||||
|
||||
if (!preTransform) cnt += writeTransform(cTransform, TVG_TAG_PAINT_TRANSFORM);
|
||||
cnt += serializePaint(shape, pTransform);
|
||||
|
||||
writeReservedCount(cnt);
|
||||
|
||||
return SERIAL_DONE(cnt);
|
||||
}
|
||||
|
||||
|
||||
/* Picture has either a vector scene or a bitmap. */
|
||||
TvgBinCounter TvgSaver::serializePicture(const Picture* picture, const Matrix* pTransform, const Matrix* cTransform)
|
||||
{
|
||||
auto it = IteratorAccessor::iterator(picture);
|
||||
|
||||
//Case - Vector Scene:
|
||||
if (it->count() == 1) {
|
||||
auto cnt = serializeChild(picture, it->next(), cTransform);
|
||||
//Only child, Skip to save Picture...
|
||||
if (cnt > 0) {
|
||||
delete(it);
|
||||
return cnt;
|
||||
/* Unfortunately, we can't skip the Picture because it might have a compositor,
|
||||
Serialize Scene(instead of the Picture) & its scene. */
|
||||
} else {
|
||||
writeTag(TVG_TAG_CLASS_SCENE);
|
||||
reserveCount();
|
||||
auto cnt = serializeChildren(it, cTransform) + serializePaint(picture, pTransform);
|
||||
writeReservedCount(cnt);
|
||||
delete(it);
|
||||
return SERIAL_DONE(cnt);
|
||||
}
|
||||
}
|
||||
delete(it);
|
||||
|
||||
//Case - Bitmap Image:
|
||||
uint32_t w, h;
|
||||
auto pixels = P(picture)->data(&w, &h);
|
||||
if (!pixels) return 0;
|
||||
|
||||
writeTag(TVG_TAG_CLASS_PICTURE);
|
||||
reserveCount();
|
||||
|
||||
TvgBinCounter cnt = 0;
|
||||
TvgBinCounter sizeCnt = SIZE(w);
|
||||
TvgBinCounter imgSize = w * h * SIZE(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);
|
||||
|
||||
//Bitmap picture needs the transform info.
|
||||
cnt += writeTransform(cTransform, TVG_TAG_PAINT_TRANSFORM);
|
||||
|
||||
cnt += serializePaint(picture, pTransform);
|
||||
|
||||
writeReservedCount(cnt);
|
||||
|
||||
return SERIAL_DONE(cnt);
|
||||
}
|
||||
|
||||
|
||||
TvgBinCounter TvgSaver::serializeComposite(const Paint* cmpTarget, CompositeMethod cmpMethod, const Matrix* pTransform)
|
||||
{
|
||||
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, pTransform, true);
|
||||
|
||||
writeReservedCount(cnt);
|
||||
|
||||
return SERIAL_DONE(cnt);
|
||||
}
|
||||
|
||||
|
||||
TvgBinCounter TvgSaver::serializeChildren(Iterator* it, const Matrix* pTransform)
|
||||
{
|
||||
TvgBinCounter cnt = 0;
|
||||
|
||||
//Merging shapes. the result is written in the children.
|
||||
Array<const Paint*> children(it->count());
|
||||
children.push(it->next());
|
||||
|
||||
while (auto child = it->next()) {
|
||||
if (child->type() == Type::Shape) {
|
||||
//only dosable if the previous child is a shape.
|
||||
auto target = children.last();
|
||||
if (target->type() == Type::Shape) {
|
||||
if (_merge((Shape*)child, (Shape*)target)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
children.push(child);
|
||||
}
|
||||
|
||||
//Serialize merged children.
|
||||
auto child = children.data;
|
||||
for (uint32_t i = 0; i < children.count; ++i, ++child) {
|
||||
cnt += serialize(*child, pTransform);
|
||||
}
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
|
||||
TvgBinCounter TvgSaver::serialize(const Paint* paint, const Matrix* pTransform, bool compTarget)
|
||||
{
|
||||
if (!paint) return 0;
|
||||
|
||||
//Invisible paint, no point to save it if the paint is not the composition target...
|
||||
if (!compTarget && paint->opacity() == 0) return 0;
|
||||
|
||||
auto transform = const_cast<Paint*>(paint)->transform();
|
||||
if (pTransform) transform = *pTransform * transform;
|
||||
|
||||
switch (paint->type()) {
|
||||
case Type::Shape: return serializeShape(static_cast<const Shape*>(paint), pTransform, &transform);
|
||||
case Type::Scene: return serializeScene(static_cast<const Scene*>(paint), pTransform, &transform);
|
||||
case Type::Picture: return serializePicture(static_cast<const Picture*>(paint), pTransform, &transform);
|
||||
case Type::Text: {
|
||||
TVGERR("TVG", "TODO: Text Serialization!");
|
||||
return 0;
|
||||
}
|
||||
default: return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void TvgSaver::run(unsigned tid)
|
||||
{
|
||||
if (!writeHeader()) return;
|
||||
|
||||
//Serialize Root Paint, without its transform.
|
||||
Matrix transform = {1, 0, 0, 0, 1, 0, 0, 0, 1};
|
||||
|
||||
if (paint->opacity() > 0) {
|
||||
switch (paint->type()) {
|
||||
case Type::Shape: {
|
||||
serializeShape(static_cast<const Shape*>(paint), nullptr, &transform);
|
||||
break;
|
||||
}
|
||||
case Type::Scene: {
|
||||
serializeScene(static_cast<const Scene*>(paint), nullptr, &transform);
|
||||
break;
|
||||
}
|
||||
case Type::Picture: {
|
||||
serializePicture(static_cast<const Picture*>(paint), nullptr, &transform);
|
||||
break;
|
||||
}
|
||||
case Type::Text: {
|
||||
TVGERR("TVG", "TODO: Text Serialization!");
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!saveEncoding(path)) return;
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
TvgSaver::~TvgSaver()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
|
||||
bool TvgSaver::close()
|
||||
{
|
||||
this->done();
|
||||
|
||||
if (paint && P(paint)->refCnt == 0) delete(paint);
|
||||
paint = nullptr;
|
||||
|
||||
free(path);
|
||||
path = nullptr;
|
||||
|
||||
buffer.reset();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool TvgSaver::save(Paint* paint, Paint* bg, const string& path, TVG_UNUSED uint32_t quality)
|
||||
{
|
||||
close();
|
||||
|
||||
float x, y;
|
||||
x = y = 0;
|
||||
paint->bounds(&x, &y, &vsize[0], &vsize[1], false);
|
||||
|
||||
//cut off the negative space
|
||||
if (x < 0) vsize[0] += x;
|
||||
if (y < 0) vsize[1] += y;
|
||||
|
||||
if (vsize[0] < FLOAT_EPSILON || vsize[1] < FLOAT_EPSILON) {
|
||||
TVGLOG("TVG_SAVER", "Saving paint(%p) has zero view size.", paint);
|
||||
return false;
|
||||
}
|
||||
|
||||
this->path = strdup(path.c_str());
|
||||
if (!this->path) return false;
|
||||
|
||||
if (bg) {
|
||||
auto scene = Scene::gen();
|
||||
scene->push(cast(bg->duplicate()));
|
||||
scene->push(cast(paint));
|
||||
this->paint = scene.release();
|
||||
} else {
|
||||
this->paint = paint;
|
||||
}
|
||||
|
||||
TaskScheduler::request(this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool TvgSaver::save(TVG_UNUSED Animation* animation, TVG_UNUSED Paint* bg, TVG_UNUSED const string& path, TVG_UNUSED uint32_t quality, TVG_UNUSED uint32_t fps)
|
||||
{
|
||||
TVGLOG("TVG_SAVER", "Animation is not supported.");
|
||||
return false;
|
||||
}
|
||||
|
|
@ -1,80 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021 - 2024 the ThorVG project. 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 "tvgFormat.h"
|
||||
#include "tvgTaskScheduler.h"
|
||||
|
||||
namespace tvg
|
||||
{
|
||||
|
||||
class TvgSaver : public SaveModule, public Task
|
||||
{
|
||||
private:
|
||||
Array<TvgBinByte> buffer;
|
||||
Paint* paint = nullptr;
|
||||
Paint* bg = nullptr;
|
||||
char *path = nullptr;
|
||||
uint32_t headerSize;
|
||||
float vsize[2] = {0.0f, 0.0f};
|
||||
|
||||
bool flushTo(const std::string& path);
|
||||
bool saveEncoding(const std::string& path);
|
||||
void reserveCount();
|
||||
|
||||
bool writeHeader();
|
||||
bool writeViewSize();
|
||||
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 writeTransform(const Matrix* transform, TvgBinTag tag);
|
||||
|
||||
TvgBinCounter serialize(const Paint* paint, const Matrix* pTransform, bool compTarget = false);
|
||||
TvgBinCounter serializeScene(const Scene* scene, const Matrix* pTransform, const Matrix* cTransform);
|
||||
TvgBinCounter serializeShape(const Shape* shape, const Matrix* pTransform, const Matrix* cTransform);
|
||||
TvgBinCounter serializePicture(const Picture* picture, const Matrix* pTransform, const Matrix* cTransform);
|
||||
TvgBinCounter serializePaint(const Paint* paint, const Matrix* pTransform);
|
||||
TvgBinCounter serializeFill(const Fill* fill, TvgBinTag tag, const Matrix* pTransform);
|
||||
TvgBinCounter serializeStroke(const Shape* shape, const Matrix* pTransform, bool preTransform);
|
||||
TvgBinCounter serializePath(const Shape* shape, const Matrix* transform, bool preTransform);
|
||||
TvgBinCounter serializeComposite(const Paint* cmpTarget, CompositeMethod cmpMethod, const Matrix* pTransform);
|
||||
TvgBinCounter serializeChildren(Iterator* it, const Matrix* transform);
|
||||
TvgBinCounter serializeChild(const Paint* parent, const Paint* child, const Matrix* pTransform);
|
||||
|
||||
void run(unsigned tid) override;
|
||||
|
||||
public:
|
||||
~TvgSaver();
|
||||
|
||||
bool save(Paint* paint, Paint* bg, const string& path, uint32_t quality) override;
|
||||
bool save(Animation* animation, Paint* bg, const string& path, uint32_t quality, uint32_t fps) override;
|
||||
bool close() override;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //_TVG_SAVE_MODULE_H_
|
|
@ -180,32 +180,6 @@ TEST_CASE("Load Jpg file in Picture", "[capiPicture]")
|
|||
|
||||
#endif
|
||||
|
||||
#ifdef THORVG_TVG_LOADER_SUPPORT
|
||||
|
||||
TEST_CASE("Load Tvg file in Picture", "[capiPicture]")
|
||||
{
|
||||
Tvg_Paint* picture = tvg_picture_new();
|
||||
REQUIRE(picture);
|
||||
|
||||
//Invalid file
|
||||
REQUIRE(tvg_picture_load(picture, "invalid.tvg") == TVG_RESULT_INVALID_ARGUMENT);
|
||||
|
||||
//Load Png file
|
||||
REQUIRE(tvg_picture_load(picture, TEST_DIR"/test.tvg") == TVG_RESULT_SUCCESS);
|
||||
|
||||
//Verify Size
|
||||
float wNew = 500.0f, hNew = 500.0f;
|
||||
float w = 0.0f, h = 0.0f;
|
||||
REQUIRE(tvg_picture_set_size(picture, wNew, hNew) == TVG_RESULT_SUCCESS);
|
||||
REQUIRE(tvg_picture_get_size(picture, &w, &h) == TVG_RESULT_SUCCESS);
|
||||
REQUIRE(w == Approx(wNew).epsilon(0.0000001));
|
||||
REQUIRE(h == Approx(hNew).epsilon(0.0000001));
|
||||
|
||||
REQUIRE(tvg_paint_del(picture) == TVG_RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef THORVG_WEBP_LOADER_SUPPORT
|
||||
|
||||
TEST_CASE("Load Webp file in Picture", "[capiPicture]")
|
||||
|
|
|
@ -34,121 +34,6 @@ TEST_CASE("Create and delete a Saver", "[capiSaver]")
|
|||
REQUIRE(tvg_saver_del(saver) == TVG_RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
#ifdef THORVG_TVG_SAVER_SUPPORT
|
||||
|
||||
TEST_CASE("Save a paint into tvg", "[capiSaver]")
|
||||
{
|
||||
Tvg_Saver* saver = tvg_saver_new();
|
||||
REQUIRE(saver);
|
||||
|
||||
Tvg_Paint* paint_empty = tvg_shape_new();
|
||||
REQUIRE(paint_empty);
|
||||
|
||||
Tvg_Paint* paint1 = tvg_shape_new();
|
||||
REQUIRE(paint1);
|
||||
REQUIRE(tvg_shape_append_rect(paint1, 11.1f, 22.2f, 33.3f, 44.4f, 5.5f, 6.6f) == TVG_RESULT_SUCCESS);
|
||||
|
||||
Tvg_Paint* paint2 = tvg_paint_duplicate(paint1);
|
||||
REQUIRE(paint2);
|
||||
|
||||
Tvg_Paint* paint3 = tvg_paint_duplicate(paint1);
|
||||
REQUIRE(paint3);
|
||||
|
||||
//An invalid argument
|
||||
REQUIRE(tvg_saver_save(nullptr, paint_empty, TEST_DIR"/test.tvg", 50) == TVG_RESULT_INVALID_ARGUMENT);
|
||||
REQUIRE(tvg_saver_save(saver, nullptr, TEST_DIR"/test.tvg", 999999) == TVG_RESULT_INVALID_ARGUMENT);
|
||||
REQUIRE(tvg_saver_save(saver, paint_empty, nullptr, 100) == TVG_RESULT_INVALID_ARGUMENT);
|
||||
|
||||
//Save an empty paint
|
||||
REQUIRE(tvg_saver_save(saver, paint_empty, TEST_DIR"/test.tvg", 0) == TVG_RESULT_UNKNOWN);
|
||||
|
||||
//Unsupported target file format
|
||||
REQUIRE(tvg_saver_save(saver, paint1, TEST_DIR"/test.err", 0) == TVG_RESULT_NOT_SUPPORTED);
|
||||
|
||||
//Correct call
|
||||
REQUIRE(tvg_saver_save(saver, paint2, TEST_DIR"/test.tvg", 100) == TVG_RESULT_SUCCESS);
|
||||
|
||||
//Busy - saving some resources
|
||||
REQUIRE(tvg_saver_save(saver, paint3, TEST_DIR"/test.tvg", 100) == TVG_RESULT_INSUFFICIENT_CONDITION);
|
||||
|
||||
REQUIRE(tvg_saver_del(saver) == TVG_RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
TEST_CASE("Synchronize a Saver", "[capiSaver]")
|
||||
{
|
||||
Tvg_Saver* saver = tvg_saver_new();
|
||||
REQUIRE(saver);
|
||||
|
||||
Tvg_Paint* paint1 = tvg_shape_new();
|
||||
REQUIRE(paint1);
|
||||
REQUIRE(tvg_shape_append_rect(paint1, 11.1f, 22.2f, 33.3f, 44.4f, 5.5f, 6.6f) == TVG_RESULT_SUCCESS);
|
||||
|
||||
Tvg_Paint* paint2 = tvg_paint_duplicate(paint1);
|
||||
REQUIRE(paint2);
|
||||
|
||||
//An invalid argument
|
||||
REQUIRE(tvg_saver_sync(nullptr) == TVG_RESULT_INVALID_ARGUMENT);
|
||||
|
||||
//Nothing to be synced
|
||||
REQUIRE(tvg_saver_sync(saver) == TVG_RESULT_INSUFFICIENT_CONDITION);
|
||||
|
||||
REQUIRE(tvg_saver_save(saver, paint1, TEST_DIR"/test.tvg", 100) == TVG_RESULT_SUCCESS);
|
||||
|
||||
//Releasing the saving task
|
||||
REQUIRE(tvg_saver_sync(saver) == TVG_RESULT_SUCCESS);
|
||||
REQUIRE(tvg_saver_save(saver, paint2, TEST_DIR"/test.tvg", 100) == TVG_RESULT_SUCCESS);
|
||||
|
||||
REQUIRE(tvg_saver_del(saver) == TVG_RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
TEST_CASE("Save scene into tvg", "[capiSaver]")
|
||||
{
|
||||
Tvg_Paint* picture = tvg_picture_new();
|
||||
REQUIRE(picture);
|
||||
|
||||
Tvg_Saver* saver = tvg_saver_new();
|
||||
REQUIRE(saver);
|
||||
|
||||
FILE* file = fopen(TEST_DIR"/rawimage_200x300.raw", "r");
|
||||
REQUIRE(file);
|
||||
uint32_t* data = (uint32_t*)malloc(sizeof(uint32_t) * (200*300));
|
||||
|
||||
if (data && fread(data, sizeof(uint32_t), 200 * 300, file) > 0) {
|
||||
REQUIRE(tvg_picture_load_raw(picture, data, 200, 300, true, true) == TVG_RESULT_SUCCESS);
|
||||
REQUIRE(tvg_saver_save(saver, picture, TEST_DIR"/test.tvg", 88) == TVG_RESULT_SUCCESS);
|
||||
REQUIRE(tvg_saver_sync(saver) == TVG_RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
REQUIRE(tvg_saver_del(saver) == TVG_RESULT_SUCCESS);
|
||||
|
||||
fclose(file);
|
||||
free(data);
|
||||
}
|
||||
|
||||
#ifdef THORVG_SVG_LOADER_SUPPORT
|
||||
|
||||
TEST_CASE("Save svg into tvg", "[capiSaver]")
|
||||
{
|
||||
Tvg_Paint* picture = tvg_picture_new();
|
||||
REQUIRE(picture);
|
||||
REQUIRE(tvg_picture_load(picture, TEST_DIR"/logo.svg") == TVG_RESULT_SUCCESS);
|
||||
REQUIRE(tvg_picture_set_size(picture, 222, 333) == TVG_RESULT_SUCCESS);
|
||||
REQUIRE(tvg_paint_translate(picture, 123.45f, 54.321f) == TVG_RESULT_SUCCESS);
|
||||
|
||||
Tvg_Saver* saver = tvg_saver_new();
|
||||
REQUIRE(saver);
|
||||
|
||||
REQUIRE(tvg_saver_save(saver, picture, TEST_DIR"/test.tvg", 100) == TVG_RESULT_SUCCESS);
|
||||
REQUIRE(tvg_saver_sync(saver) == TVG_RESULT_SUCCESS);
|
||||
|
||||
REQUIRE(tvg_saver_del(saver) == TVG_RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(THORVG_GIF_SAVER_SUPPORT) && defined(THORVG_LOTTIE_LOADER_SUPPORT)
|
||||
|
||||
TEST_CASE("Save a lottie into gif", "[capiSavers]")
|
||||
|
|
|
@ -365,81 +365,6 @@ TEST_CASE("Load JPG file and render", "[tvgPicture]")
|
|||
|
||||
#endif
|
||||
|
||||
#ifdef THORVG_TVG_LOADER_SUPPORT
|
||||
|
||||
TEST_CASE("Load TVG file from path", "[tvgPicture]")
|
||||
{
|
||||
auto picture = Picture::gen();
|
||||
REQUIRE(picture);
|
||||
|
||||
//Invalid file
|
||||
REQUIRE(picture->load("invalid.tvg") == Result::InvalidArguments);
|
||||
|
||||
REQUIRE(picture->load(TEST_DIR"/tag.tvg") == Result::Success);
|
||||
|
||||
float w, h;
|
||||
REQUIRE(picture->size(&w, &h) == Result::Success);
|
||||
|
||||
REQUIRE(w == 1000);
|
||||
REQUIRE(h == 1000);
|
||||
}
|
||||
|
||||
TEST_CASE("Load TVG file from data", "[tvgPicture]")
|
||||
{
|
||||
auto picture = Picture::gen();
|
||||
REQUIRE(picture);
|
||||
|
||||
//Open file
|
||||
ifstream file(TEST_DIR"/tag.tvg", ios::in | ios::binary);
|
||||
REQUIRE(file.is_open());
|
||||
auto begin = file.tellg();
|
||||
file.seekg(0, ios::end);
|
||||
auto size = file.tellg() - begin;
|
||||
auto data = (char*)malloc(size);
|
||||
file.seekg(0, ios::beg);
|
||||
file.read(data, size);
|
||||
file.close();
|
||||
|
||||
REQUIRE(picture->load(data, size, "") == Result::Success);
|
||||
REQUIRE(picture->load(data, size, "tvg", "", true) == Result::Success);
|
||||
|
||||
float w, h;
|
||||
REQUIRE(picture->size(&w, &h) == Result::Success);
|
||||
REQUIRE(w == 1000);
|
||||
REQUIRE(h == 1000);
|
||||
|
||||
free(data);
|
||||
}
|
||||
|
||||
TEST_CASE("Load TVG file and render", "[tvgPicture]")
|
||||
{
|
||||
REQUIRE(Initializer::init(0) == Result::Success);
|
||||
|
||||
auto canvas = SwCanvas::gen();
|
||||
REQUIRE(canvas);
|
||||
|
||||
auto buffer = new uint32_t[1000*1000];
|
||||
if (!buffer) return;
|
||||
|
||||
REQUIRE(canvas->target(buffer, 1000, 1000, 1000, ColorSpace::ABGR8888) == Result::Success);
|
||||
|
||||
auto pictureTag = Picture::gen();
|
||||
REQUIRE(pictureTag);
|
||||
REQUIRE(pictureTag->load(TEST_DIR"/tag.tvg") == Result::Success);
|
||||
REQUIRE(canvas->push(std::move(pictureTag)) == Result::Success);
|
||||
|
||||
auto pictureTest = Picture::gen();
|
||||
REQUIRE(pictureTest);
|
||||
REQUIRE(pictureTest->load(TEST_DIR"/test.tvg") == Result::Success);
|
||||
REQUIRE(canvas->push(std::move(pictureTest)) == Result::Success);
|
||||
|
||||
REQUIRE(Initializer::term() == Result::Success);
|
||||
|
||||
delete[] buffer;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef THORVG_WEBP_LOADER_SUPPORT
|
||||
|
||||
TEST_CASE("Load WEBP file from path", "[tvgPicture]")
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue