Initial Draft

This commit is contained in:
Hermet Park 2020-08-24 14:30:08 +09:00
commit 90024e92a6
122 changed files with 18881 additions and 2 deletions

25
.gitignore vendored Normal file
View file

@ -0,0 +1,25 @@
build
.vscode
*.swp
testShape
testMultiShapes
testBoundary
testPath
testPathCopy
testBlending
testUpdate
testDirectUpdate
testScene
testTransform
testCustomTransform
testSceneTransform
testStroke
testStrokeLine
testLinearGradient
testRadialGradient
testGradientTransform
testSvg
testSvg2
testAsync
testCapi
testArc

7
AUTHORS Normal file
View file

@ -0,0 +1,7 @@
Hermet Park <chuneon.park@samsung.com>
Prudhvi Raj Vasireddi <prudhvi.raj@samsung.com>
Junsu Choi <jsuya.choi@samsung.com>
Pranay Samanta <pranay.ks@samsung.com>
Mateusz Palkowski <m.palkowski@samsung.com>
Subhransu Mohanty <sub.mohanty@samsung.com>
Mira Grudzinska <m.grudzinska@samsung.com>

7
LICENSE Normal file
View file

@ -0,0 +1,7 @@
Copyright 2020 (see AUTHORS)
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.

View file

@ -1,2 +1,62 @@
# thorvg
This is a lightweight vector graphics engine which provides a full set of shape drawing functionalities.
# ThorVG
ThorVG is a platform independent lightweight standalone C++ library for drawing vector-based shapes and SVG.
#
## Contents
- [Building ThorVG](#building-thorvg)
- [Meson Build](#meson-build)
- [Quick Start](#quick-start)
- [Issues or Feature Requests?](#issues-or-feature-requests)
#
## Building ThorVG
thorvg supports [meson](https://mesonbuild.com/) build system.
#
### Meson Build
install [meson](http://mesonbuild.com/Getting-meson.html) and [ninja](https://ninja-build.org/) if not already installed.
Run meson to configure ThorVG.
```
meson build
```
Run ninja to build & install ThorVG.
```
ninja -C build install
```
[Back to contents](#contents)
#
## Quick Start
ThorVG renders vector shapes on a given canvas buffer.
You can initialize ThorVG engine first:
```cpp
tvg::Initializer::init(tvg::CanvasEngine::Sw);
```
You can prepare a empty canvas for drawing on it.
```cpp
static uint32_t buffer[WIDTH * HEIGHT]; //canvas target buffer
auto canvas = tvg::SwCanvas::gen(); //generate a canvas
canvas->target(buffer, WIDTH, WIDTH, HEIGHT); //stride, w, h
```
Next you can draw shapes onto the canvas.
```cpp
auto shape = tvg::Shape::gen(); //generate a shape
shape->appendRect(0, 0, 200, 200, 0, 0); //x, y, w, h, rx, ry
shape->appendCircle(400, 400, 100, 100); //cx, cy, radiusW, radiusH
shape->fill(255, 255, 0, 255); //r, g, b, a
canvas->push(move(shape)); //push shape drawing command
```
Begin rendering & finish it at a particular time.
```cpp
canvas->draw();
canvas->sync();
```
Lastly, you can acquire the rendered image in buffer memory.
[Back to contents](#contents)
#
## Issues or Feature Requests?
For immidiate assistant or support please reach us in [Gitter](https://gitter.im/thorvg-dev/community#)

7
inc/meson.build Normal file
View file

@ -0,0 +1,7 @@
header_files = ['thorvg.h']
if get_option('bindings').contains('capi') == true
header_files += ['thorvg_capi.h']
endif
install_headers(header_files)

381
inc/thorvg.h Normal file
View file

@ -0,0 +1,381 @@
#ifndef _THORVG_H_
#define _THORVG_H_
#include <memory>
#ifdef TVG_BUILD
#define TVG_EXPORT __attribute__ ((visibility ("default")))
#else
#define TVG_EXPORT
#endif
#ifdef LOG_TAG
#undef LOG_TAG
#endif
#define LOG_TAG "TVG"
#ifdef __cplusplus
extern "C" {
#endif
#define _TVG_DECLARE_PRIVATE(A) \
protected: \
struct Impl; \
std::unique_ptr<Impl> pImpl; \
A(const A&) = delete; \
const A& operator=(const A&) = delete; \
A()
#define _TVG_DISABLE_CTOR(A) \
A() = delete; \
~A() = delete
#define _TVG_DECLARE_ACCESSOR() \
friend Canvas; \
friend Scene; \
friend Picture
#define _TVG_DECALRE_IDENTIFIER() \
auto id() const { return _id; } \
protected: \
unsigned _id
namespace tvg
{
class RenderMethod;
class Scene;
class Picture;
class Canvas;
enum class TVG_EXPORT Result { Success = 0, InvalidArguments, InsufficientCondition, FailedAllocation, MemoryCorruption, NonSupport, Unknown };
enum class TVG_EXPORT PathCommand { Close = 0, MoveTo, LineTo, CubicTo };
enum class TVG_EXPORT StrokeCap { Square = 0, Round, Butt };
enum class TVG_EXPORT StrokeJoin { Bevel = 0, Round, Miter };
enum class TVG_EXPORT FillSpread { Pad = 0, Reflect, Repeat };
enum class TVG_EXPORT CanvasEngine { Sw = (1 << 1), Gl = (1 << 2)};
struct Point
{
float x, y;
};
struct Matrix
{
float e11, e12, e13;
float e21, e22, e23;
float e31, e32, e33;
};
/**
* @class Paint
*
* @ingroup ThorVG
*
* @brief description...
*
*/
class TVG_EXPORT Paint
{
public:
virtual ~Paint();
Result rotate(float degree) noexcept;
Result scale(float factor) noexcept;
Result translate(float x, float y) noexcept;
Result transform(const Matrix& m) noexcept;
Result bounds(float* x, float* y, float* w, float* h) const noexcept;
_TVG_DECLARE_ACCESSOR();
_TVG_DECLARE_PRIVATE(Paint);
};
/**
* @class Fill
*
* @ingroup ThorVG
*
* @brief description...
*
*/
class TVG_EXPORT Fill
{
public:
struct ColorStop
{
float offset;
uint8_t r, g, b, a;
};
virtual ~Fill();
Result colorStops(const ColorStop* colorStops, uint32_t cnt) noexcept;
Result spread(FillSpread s) noexcept;
uint32_t colorStops(const ColorStop** colorStops) const noexcept;
FillSpread spread() const noexcept;
_TVG_DECALRE_IDENTIFIER();
_TVG_DECLARE_PRIVATE(Fill);
};
/**
* @class Canvas
*
* @ingroup ThorVG
*
* @brief description...
*
*/
class TVG_EXPORT Canvas
{
public:
Canvas(RenderMethod*);
virtual ~Canvas();
Result reserve(uint32_t n) noexcept;
virtual Result push(std::unique_ptr<Paint> paint) noexcept;
virtual Result clear() noexcept;
virtual Result update(Paint* paint) noexcept;
virtual Result draw() noexcept;
virtual Result sync() noexcept;
_TVG_DECLARE_PRIVATE(Canvas);
};
/**
* @class LinearGradient
*
* @ingroup ThorVG
*
* @brief description...
*
*/
class TVG_EXPORT LinearGradient final : public Fill
{
public:
~LinearGradient();
Result linear(float x1, float y1, float x2, float y2) noexcept;
Result linear(float* x1, float* y1, float* x2, float* y2) const noexcept;
static std::unique_ptr<LinearGradient> gen() noexcept;
_TVG_DECLARE_PRIVATE(LinearGradient);
};
/**
* @class RadialGradient
*
* @ingroup ThorVG
*
* @brief description...
*
*/
class TVG_EXPORT RadialGradient final : public Fill
{
public:
~RadialGradient();
Result radial(float cx, float cy, float radius) noexcept;
Result radial(float* cx, float* cy, float* radius) const noexcept;
static std::unique_ptr<RadialGradient> gen() noexcept;
_TVG_DECLARE_PRIVATE(RadialGradient);
};
/**
* @class Shape
*
* @ingroup ThorVG
*
* @brief description...
*
*/
class TVG_EXPORT Shape final : public Paint
{
public:
~Shape();
Result reset() noexcept;
//Path
Result moveTo(float x, float y) noexcept;
Result lineTo(float x, float y) noexcept;
Result cubicTo(float cx1, float cy1, float cx2, float cy2, float x, float y) noexcept;
Result close() noexcept;
//Shape
Result appendRect(float x, float y, float w, float h, float rx, float ry) noexcept;
Result appendCircle(float cx, float cy, float rx, float ry) noexcept;
Result appendArc(float cx, float cy, float radius, float startAngle, float sweep, bool pie) noexcept;
Result appendPath(const PathCommand* cmds, uint32_t cmdCnt, const Point* pts, uint32_t ptsCnt) noexcept;
//Stroke
Result stroke(float width) noexcept;
Result stroke(uint8_t r, uint8_t g, uint8_t b, uint8_t a) noexcept;
Result stroke(const float* dashPattern, uint32_t cnt) noexcept;
Result stroke(StrokeCap cap) noexcept;
Result stroke(StrokeJoin join) noexcept;
//Fill
Result fill(uint8_t r, uint8_t g, uint8_t b, uint8_t a) noexcept;
Result fill(std::unique_ptr<Fill> f) noexcept;
//Getters
uint32_t pathCommands(const PathCommand** cmds) const noexcept;
uint32_t pathCoords(const Point** pts) const noexcept;
Result fill(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const noexcept;
const Fill* fill() const noexcept;
float strokeWidth() const noexcept;
Result strokeColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const noexcept;
uint32_t strokeDash(const float** dashPattern) const noexcept;
StrokeCap strokeCap() const noexcept;
StrokeJoin strokeJoin() const noexcept;
static std::unique_ptr<Shape> gen() noexcept;
_TVG_DECLARE_PRIVATE(Shape);
};
/**
* @class Picture
*
* @ingroup ThorVG
*
* @brief description...
*
*/
class TVG_EXPORT Picture final : public Paint
{
public:
~Picture();
Result load(const std::string& path) noexcept;
Result load(const char* data, uint32_t size) noexcept;
Result viewbox(float* x, float* y, float* w, float* h) const noexcept;
static std::unique_ptr<Picture> gen() noexcept;
_TVG_DECLARE_PRIVATE(Picture);
};
/**
* @class Scene
*
* @ingroup ThorVG
*
* @brief description...
*
*/
class TVG_EXPORT Scene final : public Paint
{
public:
~Scene();
Result push(std::unique_ptr<Paint> paint) noexcept;
Result reserve(uint32_t size) noexcept;
static std::unique_ptr<Scene> gen() noexcept;
_TVG_DECLARE_PRIVATE(Scene);
};
/**
* @class SwCanvas
*
* @ingroup ThorVG
*
@brief description...
*
*/
class TVG_EXPORT SwCanvas final : public Canvas
{
public:
~SwCanvas();
enum Colorspace { ABGR8888 = 0, ARGB8888 };
Result target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h, Colorspace cs) noexcept;
static std::unique_ptr<SwCanvas> gen() noexcept;
_TVG_DECLARE_PRIVATE(SwCanvas);
};
/**
* @class GlCanvas
*
* @ingroup ThorVG
*
* @brief description...
*
*/
class TVG_EXPORT GlCanvas final : public Canvas
{
public:
~GlCanvas();
//TODO: Gl Specific methods. Need gl backend configuration methods as well.
Result target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h) noexcept;
static std::unique_ptr<GlCanvas> gen() noexcept;
_TVG_DECLARE_PRIVATE(GlCanvas);
};
/**
* @class Engine
*
* @ingroup ThorVG
*
* @brief description...
*
*/
class TVG_EXPORT Initializer final
{
public:
/**
* @brief ...
*
* @param[in] arg ...
*
* @note ...
*
* @return ...
*
* @see ...
*/
static Result init(CanvasEngine engine, uint32_t threads) noexcept;
static Result term(CanvasEngine engine) noexcept;
_TVG_DISABLE_CTOR(Initializer);
};
} //namespace
#ifdef __cplusplus
}
#endif
#endif //_THORVG_H_

171
inc/thorvg_capi.h Normal file
View file

@ -0,0 +1,171 @@
#ifndef __THORVG_CAPI_H__
#define __THORVG_CAPI_H__
#ifdef TVG_EXPORT
#undef TVG_EXPORT
#endif
#ifdef TVG_BUILD
#define TVG_EXPORT __attribute__ ((visibility ("default")))
#else
#define TVG_EXPORT
#endif
#ifdef __cplusplus
extern "C" {
#endif
typedef struct _Tvg_Canvas Tvg_Canvas;
typedef struct _Tvg_Paint Tvg_Paint;
typedef struct _Tvg_Gradient Tvg_Gradient;
#define TVG_ENGINE_SW (1 << 1)
#define TVG_ENGINE_GL (1 << 2)
#define TVG_COLORSPACE_ABGR8888 0
#define TVG_COLORSPACE_ARGB8888 1
typedef enum {
TVG_RESULT_SUCCESS = 0,
TVG_RESULT_INVALID_ARGUMENT,
TVG_RESULT_INSUFFICIENT_CONDITION,
TVG_RESULT_FAILED_ALLOCATION,
TVG_RESULT_MEMORY_CORRUPTION,
TVG_RESULT_NOT_SUPPORTED,
TVG_RESULT_UNKNOWN
} Tvg_Result;
typedef enum {
TVG_PATH_COMMAND_CLOSE = 0,
TVG_PATH_COMMAND_MOVE_TO,
TVG_PATH_COMMAND_LINE_TO,
TVG_PATH_COMMAND_CUBIC_TO
} Tvg_Path_Command;
typedef enum {
TVG_STROKE_CAP_SQUARE = 0,
TVG_STROKE_CAP_ROUND,
TVG_STROKE_CAP_BUTT
} Tvg_Stroke_Cap;
typedef enum {
TVG_STROKE_JOIN_BEVEL = 0,
TVG_STROKE_JOIN_ROUND,
TVG_STROKE_JOIN_MITER
} Tvg_Stroke_Join;
typedef enum {
TVG_STROKE_FILL_PAD = 0,
TVG_STROKE_FILL_REFLECT,
TVG_STROKE_FILL_REPEAT
} Tvg_Stroke_Fill;
typedef struct
{
float x, y;
} Tvg_Point;
typedef struct
{
float e11, e12, e13;
float e21, e22, e23;
float e31, e32, e33;
} Tvg_Matrix;
typedef struct
{
float offset;
uint8_t r, g, b, a;
} Tvg_Color_Stop;
/************************************************************************/
/* Engine API */
/************************************************************************/
TVG_EXPORT Tvg_Result tvg_engine_init(unsigned engine_method, unsigned threads);
TVG_EXPORT Tvg_Result tvg_engine_term(unsigned engine_method);
/************************************************************************/
/* SwCanvas API */
/************************************************************************/
TVG_EXPORT Tvg_Canvas* tvg_swcanvas_create();
TVG_EXPORT Tvg_Result tvg_swcanvas_set_target(Tvg_Canvas* canvas, uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h, uint32_t cs);
/************************************************************************/
/* Common Canvas API */
/************************************************************************/
TVG_EXPORT Tvg_Result tvg_canvas_destroy(Tvg_Canvas* canvas);
TVG_EXPORT Tvg_Result tvg_canvas_push(Tvg_Canvas* canvas, Tvg_Paint* paint);
TVG_EXPORT Tvg_Result tvg_canvas_reserve(Tvg_Canvas* canvas, uint32_t n);
TVG_EXPORT Tvg_Result tvg_canvas_clear(Tvg_Canvas* canvas);
TVG_EXPORT Tvg_Result tvg_canvas_update(Tvg_Canvas* canvas);
TVG_EXPORT Tvg_Result tvg_canvas_update_paint(Tvg_Canvas* canvas, Tvg_Paint* paint);
TVG_EXPORT Tvg_Result tvg_canvas_draw(Tvg_Canvas* canvas);
TVG_EXPORT Tvg_Result tvg_canvas_sync(Tvg_Canvas* canvas);
/************************************************************************/
/* Paint API */
/************************************************************************/
TVG_EXPORT Tvg_Result tvg_paint_del(Tvg_Paint* paint);
TVG_EXPORT Tvg_Result tvg_paint_scale(Tvg_Paint* paint, float factor);
TVG_EXPORT Tvg_Result tvg_paint_rotate(Tvg_Paint* paint, float degree);
TVG_EXPORT Tvg_Result tvg_paint_translate(Tvg_Paint* paint, float x, float y);
TVG_EXPORT Tvg_Result tvg_paint_transform(Tvg_Paint* paint, const Tvg_Matrix* m);
/************************************************************************/
/* Shape API */
/************************************************************************/
TVG_EXPORT Tvg_Paint* tvg_shape_new();
TVG_EXPORT Tvg_Result tvg_shape_reset(Tvg_Paint* paint);
TVG_EXPORT Tvg_Result tvg_shape_move_to(Tvg_Paint* paint, float x, float y);
TVG_EXPORT Tvg_Result tvg_shape_line_to(Tvg_Paint* paint, float x, float y);
TVG_EXPORT Tvg_Result tvg_shape_cubic_to(Tvg_Paint* paint, float cx1, float cy1, float cx2, float cy2, float x, float y);
TVG_EXPORT Tvg_Result tvg_shape_close(Tvg_Paint* paint);
TVG_EXPORT Tvg_Result tvg_shape_append_rect(Tvg_Paint* paint, float x, float y, float w, float h, float rx, float ry);
TVG_EXPORT Tvg_Result tvg_shape_append_circle(Tvg_Paint* paint, float cx, float cy, float rx, float ry);
TVG_EXPORT Tvg_Result tvg_shape_append_arc(Tvg_Paint* paint, float cx, float cy, float radius, float startAngle, float sweep, uint8_t pie);
TVG_EXPORT Tvg_Result tvg_shape_append_path(Tvg_Paint* paint, const Tvg_Path_Command* cmds, uint32_t cmdCnt, const Tvg_Point* pts, uint32_t ptsCnt);
TVG_EXPORT Tvg_Result tvg_shape_set_stroke_width(Tvg_Paint* paint, float width);
TVG_EXPORT Tvg_Result tvg_shape_set_stroke_color(Tvg_Paint* paint, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
TVG_EXPORT Tvg_Result tvg_shape_set_stroke_dash(Tvg_Paint* paint, const float* dashPattern, uint32_t cnt);
TVG_EXPORT Tvg_Result tvg_shape_set_stroke_cap(Tvg_Paint* paint, Tvg_Stroke_Cap cap);
TVG_EXPORT Tvg_Result tvg_shape_set_stroke_join(Tvg_Paint* paint, Tvg_Stroke_Join join);
TVG_EXPORT Tvg_Result tvg_shape_fill_color(Tvg_Paint* paint, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
TVG_EXPORT Tvg_Result tvg_shape_linear_gradient_set(Tvg_Paint* paint, Tvg_Gradient *grad);
TVG_EXPORT Tvg_Result tvg_shape_radial_gradient_set(Tvg_Paint* paint, Tvg_Gradient *grad);
/************************************************************************/
/* Gradient API */
/************************************************************************/
TVG_EXPORT Tvg_Gradient* tvg_linear_gradient_new();
TVG_EXPORT Tvg_Gradient* tvg_radial_gradient_new();
TVG_EXPORT Tvg_Result tvg_gradient_del(Tvg_Gradient* grad);
TVG_EXPORT Tvg_Result tvg_linear_gradient_set(Tvg_Gradient* grad, float x1, float y1, float x2, float y2);
TVG_EXPORT Tvg_Result tvg_radial_gradient_set(Tvg_Gradient* grad, float cx, float cy, float radius);
TVG_EXPORT Tvg_Result tvg_gradient_color_stops(Tvg_Gradient* grad, const Tvg_Color_Stop* color_stop, uint32_t cnt);
TVG_EXPORT Tvg_Result tvg_gradient_spread(Tvg_Gradient* grad, const Tvg_Stroke_Fill);
/************************************************************************/
/* Picture API */
/************************************************************************/
TVG_EXPORT Tvg_Paint* tvg_picture_new();
TVG_EXPORT Tvg_Result tvg_picture_load(Tvg_Paint* paint, const char* path);
TVG_EXPORT Tvg_Result tvg_picture_get_viewbox(Tvg_Paint* paint, float* x, float* y, float* w, float* h);
#ifdef __cplusplus
}
#endif
#endif //_THORVG_CAPI_H_

51
meson.build Normal file
View file

@ -0,0 +1,51 @@
project('thorvg',
'cpp',
default_options : ['buildtype=debugoptimized', 'werror=false', 'optimization=s'],
version : '0.1.0',
license : 'MIT')
config_h = configuration_data()
if get_option('engines').contains('sw') == true
config_h.set10('THORVG_SW_RASTER_SUPPORT', true)
endif
if get_option('engines').contains('gl') == true
config_h.set10('THORVG_GL_RASTER_SUPPORT', true)
endif
if get_option('loaders').contains('svg') == true
config_h.set10('THORVG_SVG_LOADER_SUPPORT', true)
endif
if get_option('vectors').contains('avx') == true
config_h.set10('THORVG_AVX_VECTOR_SUPPORT', true)
endif
if get_option('bindings').contains('capi') == true
config_h.set10('THORVG_CAPI_BINDING_SUPPORT', true)
endif
configure_file(
output: 'config.h',
configuration: config_h
)
headers = [include_directories('inc'), include_directories('.')]
subdir('inc')
subdir('src')
summary = '''
Summary:
thorvg version : @0@
Build type : @1@
Prefix : @2@
'''.format(
meson.project_version(),
get_option('buildtype'),
get_option('prefix'),
)
message(summary)

23
meson_options.txt Normal file
View file

@ -0,0 +1,23 @@
option('engines',
type: 'array',
choices: ['sw', 'gl'],
value: ['sw'],
description: 'Enable Rasterizer Engine in thorvg')
option('loaders',
type: 'array',
choices: ['', 'svg'],
value: ['svg'],
description: 'Enable Vector File Loader in thorvg')
option('vectors',
type: 'array',
choices: ['', 'avx'],
value: [''],
description: 'Enable CPU Vectorization(SIMD) in thorvg')
option('bindings',
type: 'array',
choices: ['', 'capi'],
value: ['capi'],
description: 'Enable C API binding')

View file

@ -0,0 +1,5 @@
<manifest>
<request>
<domain name="_"/>
</request>
</manifest>

64
packaging/thorvg.spec Normal file
View file

@ -0,0 +1,64 @@
Name: thorvg
Summary: Thor Vector Graphics Library
Version: 0.0.1
Release: 1
Group: Graphics System/Rendering Engine
License: MIT
URL: https://github.com/samsung/thorvg
Source0: %{name}-%{version}.tar.gz
BuildRequires: pkgconfig
BuildRequires: pkgconfig(glesv2)
BuildRequires: meson
BuildRequires: ninja
Requires(post): /sbin/ldconfig
Requires(postun): /sbin/ldconfig
%description
Thor Vector Graphics Library
%package devel
Summary: Thor Vector Graphics Library (devel)
Group: Development/Libraries
Requires: %{name} = %{version}-%{release}
%description devel
Thor Vector Graphics Library (devel)
%prep
%setup -q
%build
export DESTDIR=%{buildroot}
meson setup \
--prefix /usr \
--libdir %{_libdir} \
builddir 2>&1
ninja \
-C builddir \
-j %(echo "`/usr/bin/getconf _NPROCESSORS_ONLN`")
%install
export DESTDIR=%{buildroot}
ninja -C builddir install
%files
%defattr(-,root,root,-)
%{_libdir}/libthorvg.so.*
%manifest packaging/thorvg.manifest
%files devel
%defattr(-,root,root,-)
%{_includedir}/*.h
%{_libdir}/libthorvg.so
%{_libdir}/pkgconfig/thorvg.pc

10
pc/thorvg.pc.in Normal file
View file

@ -0,0 +1,10 @@
prefix=@PREFIX@
libdir=@LIBDIR@
includedir=@INCDIR@
Name: Thor Vector Graphics
Description: Thor Vector Graphics Library
Version: @VERSION@
Requires:
Libs: -L${libdir} -lthorvg
Cflags: -I${includedir}/thorvg

View file

@ -0,0 +1,8 @@
source_file = [
'tvgCapi.cpp',
]
subbinding_dep += [declare_dependency(
include_directories : include_directories('.'),
sources : source_file
)]

View file

@ -0,0 +1,349 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <thorvg.h>
#include "thorvg_capi.h"
using namespace std;
using namespace tvg;
#ifdef __cplusplus
extern "C" {
#endif
struct _Tvg_Canvas
{
//Dummy for Direct Casting
};
struct _Tvg_Paint
{
//Dummy for Direct Casting
};
struct _Tvg_Gradient
{
//Dummy for Direct Casting
};
/************************************************************************/
/* Engine API */
/************************************************************************/
TVG_EXPORT Tvg_Result tvg_engine_init(unsigned engine_method, unsigned threads) {
Result ret = Result::Success;
if (engine_method & TVG_ENGINE_SW) ret = tvg::Initializer::init(tvg::CanvasEngine::Sw, threads);
if (ret != Result::Success) return (Tvg_Result) ret;
if (engine_method & TVG_ENGINE_GL) ret = tvg::Initializer::init(tvg::CanvasEngine::Gl, threads);
return (Tvg_Result) ret;
}
TVG_EXPORT Tvg_Result tvg_engine_term(unsigned engine_method) {
Result ret = Result::Success;
if (engine_method & TVG_ENGINE_SW) ret = tvg::Initializer::term(tvg::CanvasEngine::Sw);
if (ret != Result::Success) return (Tvg_Result) ret;
if (engine_method & TVG_ENGINE_GL) ret = tvg::Initializer::term(tvg::CanvasEngine::Gl);
return (Tvg_Result) ret;
}
/************************************************************************/
/* Canvas API */
/************************************************************************/
TVG_EXPORT Tvg_Canvas* tvg_swcanvas_create()
{
return (Tvg_Canvas*) SwCanvas::gen().release();
}
TVG_EXPORT Tvg_Result tvg_canvas_destroy(Tvg_Canvas* canvas)
{
delete(canvas);
return TVG_RESULT_SUCCESS;
}
TVG_EXPORT Tvg_Result tvg_swcanvas_set_target(Tvg_Canvas* canvas, uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h, uint32_t cs)
{
return (Tvg_Result) reinterpret_cast<SwCanvas*>(canvas)->target(buffer, stride, w, h, static_cast<SwCanvas::Colorspace>(cs));
}
TVG_EXPORT Tvg_Result tvg_canvas_push(Tvg_Canvas* canvas, Tvg_Paint* paint)
{
return (Tvg_Result) reinterpret_cast<Canvas*>(canvas)->push(unique_ptr<Paint>((Paint*)paint));
}
TVG_EXPORT Tvg_Result tvg_canvas_reserve(Tvg_Canvas* canvas, uint32_t n)
{
return (Tvg_Result) reinterpret_cast<Canvas*>(canvas)->reserve(n);
}
TVG_EXPORT Tvg_Result tvg_canvas_clear(Tvg_Canvas* canvas)
{
return (Tvg_Result) reinterpret_cast<Canvas*>(canvas)->clear();
}
TVG_EXPORT Tvg_Result tvg_canvas_update(Tvg_Canvas* canvas)
{
return (Tvg_Result) reinterpret_cast<Canvas*>(canvas)->update(nullptr);
}
TVG_EXPORT Tvg_Result tvg_canvas_update_paint(Tvg_Canvas* canvas, Tvg_Paint* paint)
{
return (Tvg_Result) reinterpret_cast<Canvas*>(canvas)->update((Paint*) paint);
}
TVG_EXPORT Tvg_Result tvg_canvas_draw(Tvg_Canvas* canvas)
{
return (Tvg_Result) reinterpret_cast<Canvas*>(canvas)->draw();
}
TVG_EXPORT Tvg_Result tvg_canvas_sync(Tvg_Canvas* canvas)
{
return (Tvg_Result) reinterpret_cast<Canvas*>(canvas)->sync();
}
/************************************************************************/
/* Paint API */
/************************************************************************/
TVG_EXPORT Tvg_Result tvg_paint_del(Tvg_Paint* paint)
{
delete(paint);
return TVG_RESULT_SUCCESS;
}
TVG_EXPORT Tvg_Result tvg_paint_scale(Tvg_Paint* paint, float factor)
{
return (Tvg_Result) reinterpret_cast<Paint*>(paint)->scale(factor);
}
TVG_EXPORT Tvg_Result tvg_paint_rotate(Tvg_Paint* paint, float degree)
{
return (Tvg_Result) reinterpret_cast<Paint*>(paint)->rotate(degree);
}
TVG_EXPORT Tvg_Result tvg_paint_translate(Tvg_Paint* paint, float x, float y)
{
return (Tvg_Result) reinterpret_cast<Paint*>(paint)->translate(x, y);
}
TVG_EXPORT Tvg_Result tvg_paint_transform(Tvg_Paint* paint, const Tvg_Matrix* m)
{
return (Tvg_Result) reinterpret_cast<Paint*>(paint)->transform(*(reinterpret_cast<const Matrix*>(m)));
}
/************************************************************************/
/* Shape API */
/************************************************************************/
TVG_EXPORT Tvg_Paint* tvg_shape_new()
{
return (Tvg_Paint*) Shape::gen().release();
}
TVG_EXPORT Tvg_Result tvg_shape_reset(Tvg_Paint* paint)
{
return (Tvg_Result) reinterpret_cast<Shape*>(paint)->reset();
}
TVG_EXPORT Tvg_Result tvg_shape_move_to(Tvg_Paint* paint, float x, float y)
{
return (Tvg_Result) reinterpret_cast<Shape*>(paint)->moveTo(x, y);
}
TVG_EXPORT Tvg_Result tvg_shape_line_to(Tvg_Paint* paint, float x, float y)
{
return (Tvg_Result) reinterpret_cast<Shape*>(paint)->lineTo(x, y);
}
TVG_EXPORT Tvg_Result tvg_shape_cubic_to(Tvg_Paint* paint, float cx1, float cy1, float cx2, float cy2, float x, float y)
{
return (Tvg_Result) reinterpret_cast<Shape*>(paint)->cubicTo(cx1, cy1, cx2, cy2, x, y);
}
TVG_EXPORT Tvg_Result tvg_shape_close(Tvg_Paint* paint)
{
return (Tvg_Result) reinterpret_cast<Shape*>(paint)->close();
}
TVG_EXPORT Tvg_Result tvg_shape_append_rect(Tvg_Paint* paint, float x, float y, float w, float h, float rx, float ry)
{
return (Tvg_Result) reinterpret_cast<Shape*>(paint)->appendRect(x, y, w, h, rx, ry);
}
TVG_EXPORT Tvg_Result tvg_shape_append_arc(Tvg_Paint* paint, float cx, float cy, float radius, float startAngle, float sweep, uint8_t pie)
{
return (Tvg_Result) reinterpret_cast<Shape*>(paint)->appendArc(cx, cy, radius, startAngle, sweep, pie);
}
TVG_EXPORT Tvg_Result tvg_shape_append_circle(Tvg_Paint* paint, float cx, float cy, float rx, float ry)
{
return (Tvg_Result) reinterpret_cast<Shape*>(paint)->appendCircle(cx, cy, rx, ry);
}
TVG_EXPORT Tvg_Result tvg_shape_append_path(Tvg_Paint* paint, const Tvg_Path_Command* cmds, uint32_t cmdCnt, const Tvg_Point* pts, uint32_t ptsCnt)
{
return (Tvg_Result) reinterpret_cast<Shape*>(paint)->appendPath((PathCommand*)cmds, cmdCnt, (Point*)pts, ptsCnt);
}
TVG_EXPORT Tvg_Result tvg_shape_set_stroke_width(Tvg_Paint* paint, float width)
{
return (Tvg_Result) reinterpret_cast<Shape*>(paint)->stroke(width);
}
TVG_EXPORT Tvg_Result tvg_shape_set_stroke_color(Tvg_Paint* paint, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
{
return (Tvg_Result) reinterpret_cast<Shape*>(paint)->stroke(r, g, b, a);
}
TVG_EXPORT Tvg_Result tvg_shape_set_stroke_dash(Tvg_Paint* paint, const float* dashPattern, uint32_t cnt)
{
return (Tvg_Result) reinterpret_cast<Shape*>(paint)->stroke(dashPattern, cnt);
}
TVG_EXPORT Tvg_Result tvg_shape_set_stroke_cap(Tvg_Paint* paint, Tvg_Stroke_Cap cap)
{
return (Tvg_Result) reinterpret_cast<Shape*>(paint)->stroke((StrokeCap)cap);
}
TVG_EXPORT Tvg_Result tvg_shape_set_stroke_join(Tvg_Paint* paint, Tvg_Stroke_Join join)
{
return (Tvg_Result) reinterpret_cast<Shape*>(paint)->stroke((StrokeJoin)join);
}
TVG_EXPORT Tvg_Result tvg_shape_fill_color(Tvg_Paint* paint, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
{
return (Tvg_Result) reinterpret_cast<Shape*>(paint)->fill(r, g, b, a);
}
TVG_EXPORT Tvg_Result tvg_shape_linear_gradient_set(Tvg_Paint* paint, Tvg_Gradient *gradient)
{
return (Tvg_Result) reinterpret_cast<Shape*>(paint)->fill(unique_ptr<LinearGradient>((LinearGradient*)(gradient)));
}
TVG_EXPORT Tvg_Result tvg_shape_radial_gradient_set(Tvg_Paint* paint, Tvg_Gradient *gradient)
{
return (Tvg_Result) reinterpret_cast<Shape*>(paint)->fill(unique_ptr<RadialGradient>((RadialGradient*)(gradient)));
}
/************************************************************************/
/* Picture API */
/************************************************************************/
TVG_EXPORT Tvg_Paint* tvg_picture_new()
{
return (Tvg_Paint*) Picture::gen().release();
}
TVG_EXPORT Tvg_Result tvg_picture_load(Tvg_Paint* paint, const char* path)
{
return (Tvg_Result) reinterpret_cast<Picture*>(paint)->load(path);
}
TVG_EXPORT Tvg_Result tvg_picture_get_viewbox(Tvg_Paint* paint, float* x, float* y, float* w, float* h)
{
return (Tvg_Result) reinterpret_cast<Picture*>(paint)->viewbox(x, y, w, h);
}
/************************************************************************/
/* Gradient API */
/************************************************************************/
TVG_EXPORT Tvg_Gradient* tvg_linear_gradient_new()
{
return (Tvg_Gradient*)LinearGradient::gen().release();
}
TVG_EXPORT Tvg_Gradient* tvg_radial_gradient_new()
{
return (Tvg_Gradient*)RadialGradient::gen().release();
}
TVG_EXPORT Tvg_Result tvg_gradient_del(Tvg_Gradient* grad)
{
delete(grad);
return TVG_RESULT_SUCCESS;
}
TVG_EXPORT Tvg_Result tvg_linear_gradient_set(Tvg_Gradient* grad, float x1, float y1, float x2, float y2)
{
return (Tvg_Result) reinterpret_cast<LinearGradient*>(grad)->linear(x1, y1, x2, y2);
}
TVG_EXPORT Tvg_Result tvg_radial_gradient_set(Tvg_Gradient* grad, float cx, float cy, float radius)
{
return (Tvg_Result) reinterpret_cast<RadialGradient*>(grad)->radial(cx, cy, radius);
}
TVG_EXPORT Tvg_Result tvg_gradient_color_stops(Tvg_Gradient* grad, const Tvg_Color_Stop* color_stop, uint32_t cnt)
{
return (Tvg_Result) reinterpret_cast<Fill*>(grad)->colorStops(reinterpret_cast<const Fill::ColorStop*>(color_stop), cnt);
}
TVG_EXPORT Tvg_Result tvg_gradient_spread(Tvg_Gradient* grad, const Tvg_Stroke_Fill spread)
{
return (Tvg_Result) reinterpret_cast<Fill*>(grad)->spread((FillSpread)spread);
}
#ifdef __cplusplus
}
#endif

11
src/bindings/meson.build Normal file
View file

@ -0,0 +1,11 @@
subbinding_dep = []
if get_option('bindings').contains('capi') == true
subdir('capi')
message('Enable CAPI Bindings')
endif
binding_dep = declare_dependency(
dependencies: subbinding_dep,
include_directories : include_directories('.'),
)

11
src/examples/main.cpp Normal file
View file

@ -0,0 +1,11 @@
#include <iostream>
using namespace std;
int
main(int argc, char *argv[])
{
cout << "test thorvg!" << endl;
return 0;
}

1
src/examples/meson.build Normal file
View file

@ -0,0 +1 @@
executable('thorvg_sample', 'main.cpp')

View file

@ -0,0 +1,25 @@
source_file = [
'tvgGlCommon.h',
'tvgGlGeometry.h',
'tvgGlGpuBuffer.h',
'tvgGlProgram.h',
'tvgGlRenderer.h',
'tvgGlShader.h',
'tvgGlShaderSrc.h',
'tvgGlGeometry.cpp',
'tvgGlGpuBuffer.cpp',
'tvgGlProgram.cpp',
'tvgGlRenderer.cpp',
'tvgGlShader.cpp',
'tvgGlShaderSrc.cpp',
]
egl_dep = meson.get_compiler('cpp').find_library('EGL')
gles_dep = meson.get_compiler('cpp').find_library('GLESv2')
external_dep = [egl_dep, gles_dep]
engine_dep += [declare_dependency(
dependencies : external_dep,
include_directories : include_directories('.'),
sources : source_file,
)]

View file

@ -0,0 +1,61 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef _TVG_GL_COMMON_H_
#define _TVG_GL_COMMON_H_
#include "tvgCommon.h"
#define GL_CHECK(x) \
x; \
do { \
GLenum glError = glGetError(); \
if(glError != GL_NO_ERROR) { \
printf("glGetError() = %i (0x%.8x) at line %s : %i\n", glError, glError, __FILE__, __LINE__); \
assert(0); \
} \
} while(0)
#define EGL_CHECK(x) \
x; \
do { \
EGLint eglError = eglGetError(); \
if(eglError != EGL_SUCCESS) { \
printf("eglGetError() = %i (0x%.8x) at line %s : %i\n", eglError, eglError, __FILE__, __LINE__); \
assert(0); \
} \
} while(0)
class GlGeometry;
struct GlShape
{
float viewWd;
float viewHt;
RenderUpdateFlag updateFlag;
unique_ptr<GlGeometry> geometry;
};
#endif /* _TVG_GL_COMMON_H_ */

View file

@ -0,0 +1,342 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "tvgGlGpuBuffer.h"
#include "tvgGlGeometry.h"
#include "tvgGlCommon.h"
#include <GLES2/gl2.h>
uint32_t GlGeometry::getPrimitiveCount()
{
return mPrimitives.size();
}
bool GlGeometry::decomposeOutline(const Shape &shape)
{
const PathCommand *cmds = nullptr;
auto cmdCnt = shape.pathCommands(&cmds);
Point *pts = nullptr;
auto ptsCnt = shape.pathCoords(const_cast<const Point**>(&pts));
//No actual shape data
if (cmdCnt == 0 || ptsCnt == 0)
return false;
GlPrimitive* curPrimitive = nullptr;
for (size_t i = 0; i < cmdCnt; ++i)
{
switch (*(cmds + i))
{
case PathCommand::Close:
{
if (curPrimitive && curPrimitive->mAAPoints.size() > 0 &&
(curPrimitive->mAAPoints[0].orgPt != curPrimitive->mAAPoints.back().orgPt) )
{
curPrimitive->mAAPoints.push_back(curPrimitive->mAAPoints[0].orgPt);
}
break;
}
case PathCommand::MoveTo:
mPrimitives.push_back(GlPrimitive());
curPrimitive = &mPrimitives.back();
__attribute__ ((fallthrough));
case PathCommand::LineTo:
{
if (curPrimitive)
{
addPoint(*curPrimitive, pts[0]);
}
pts++;
break;
}
case PathCommand::CubicTo:
{
if (curPrimitive)
{
decomposeCubicCurve(*curPrimitive, curPrimitive->mAAPoints.back().orgPt, pts[0], pts[1], pts[2]);
}
pts += 3;
break;
}
}
}
return true;
}
bool GlGeometry::generateAAPoints(TVG_UNUSED const Shape &shape, float strokeWd, RenderUpdateFlag flag)
{
for (auto& shapeGeometry : mPrimitives)
{
std::vector<PointNormals> normalInfo;
constexpr float blurDir = -1.0f;
float antiAliasWidth = 1.0f;
vector<SmoothPoint>& aaPts = shapeGeometry.mAAPoints;
const float stroke = (strokeWd > 1) ? strokeWd - antiAliasWidth : strokeWd;
size_t nPoints = aaPts.size();
if (nPoints < 2)
{
return false;
}
normalInfo.resize(nPoints);
size_t fPoint = 0;
size_t sPoint = 1;
for (size_t i = 0; i < nPoints; ++i)
{
fPoint = i;
sPoint = i + 1;
if (fPoint == nPoints - 1)
sPoint = 0;
GlPoint normal = getNormal(aaPts[fPoint].orgPt, aaPts[sPoint].orgPt);
normalInfo[fPoint].normal1 = normal;
normalInfo[sPoint].normal2 = normal;
}
normalInfo[0].normal2 = normalInfo[0].normal1;
normalInfo[nPoints - 1].normal1 = normalInfo[nPoints - 1].normal2;
for (uint32_t i = 0; i < nPoints; ++i)
{
normalInfo[i].normalF = normalInfo[i].normal1 + normalInfo[i].normal2;
normalInfo[i].normalF.normalize();
float angle = dotProduct(normalInfo[i].normal2, normalInfo[i].normalF);
if (angle != 0)
normalInfo[i].normalF = normalInfo[i].normalF / angle;
else
normalInfo[i].normalF = GlPoint(0, 0);
if (flag & RenderUpdateFlag::Color)
{
aaPts[i].fillOuterBlur = extendEdge(aaPts[i].orgPt, normalInfo[i].normalF, blurDir * stroke);
aaPts[i].fillOuter = extendEdge(aaPts[i].fillOuterBlur, normalInfo[i].normalF, blurDir*antiAliasWidth);
}
if (flag & RenderUpdateFlag::Stroke)
{
aaPts[i].strokeOuterBlur = aaPts[i].orgPt;
aaPts[i].strokeOuter = extendEdge(aaPts[i].strokeOuterBlur, normalInfo[i].normalF, blurDir*antiAliasWidth);
aaPts[i].strokeInner = extendEdge(aaPts[i].strokeOuter, normalInfo[i].normalF, blurDir * stroke);
aaPts[i].strokeInnerBlur = extendEdge(aaPts[i].strokeInner, normalInfo[i].normalF, blurDir*antiAliasWidth);
}
}
}
return true;
}
bool GlGeometry::tesselate(TVG_UNUSED const Shape &shape, float viewWd, float viewHt, RenderUpdateFlag flag)
{
for (auto& shapeGeometry : mPrimitives)
{
constexpr float opaque = 1.0f;
constexpr float transparent = 0.0f;
vector<SmoothPoint>& aaPts = shapeGeometry.mAAPoints;
VertexDataArray& fill = shapeGeometry.mFill;
VertexDataArray& stroke = shapeGeometry.mStroke;
if (flag & RenderUpdateFlag::Color)
{
uint32_t i = 0;
for (size_t pt = 0; pt < aaPts.size(); ++pt)
{
addGeometryPoint(fill, aaPts[pt].fillOuter, viewWd, viewHt, opaque);
if (i > 1)
{
addTriangleFanIndices(i, fill.indices);
}
++i;
}
for (size_t pt = 1; pt < aaPts.size(); ++pt)
{
addGeometryPoint(fill, aaPts[pt - 1].fillOuterBlur, viewWd, viewHt, transparent);
addGeometryPoint(fill, aaPts[pt - 1].fillOuter, viewWd, viewHt, opaque);
addGeometryPoint(fill, aaPts[pt].fillOuterBlur, viewWd, viewHt, transparent);
addGeometryPoint(fill, aaPts[pt].fillOuter, viewWd, viewHt, opaque);
addQuadIndices(i, fill.indices);
}
}
if (flag & RenderUpdateFlag::Stroke)
{
uint32_t i = 0;
for (size_t pt = 1; pt < aaPts.size(); ++pt)
{
addGeometryPoint(stroke, aaPts[pt - 1].strokeOuter, viewWd, viewHt, opaque);
addGeometryPoint(stroke, aaPts[pt - 1].strokeInner, viewWd, viewHt, opaque);
addGeometryPoint(stroke, aaPts[pt].strokeOuter, viewWd, viewHt, opaque);
addGeometryPoint(stroke, aaPts[pt].strokeInner, viewWd, viewHt, opaque);
addQuadIndices(i, stroke.indices);
}
for (size_t pt = 1; pt < aaPts.size(); ++pt)
{
addGeometryPoint(stroke, aaPts[pt - 1].strokeOuterBlur, viewWd, viewHt, transparent);
addGeometryPoint(stroke, aaPts[pt - 1].strokeOuter, viewWd, viewHt, opaque);
addGeometryPoint(stroke, aaPts[pt].strokeOuterBlur, viewWd, viewHt, transparent);
addGeometryPoint(stroke, aaPts[pt].strokeOuter, viewWd, viewHt, opaque);
addQuadIndices(i, stroke.indices);
}
for (size_t pt = 1; pt < aaPts.size(); ++pt)
{
addGeometryPoint(stroke, aaPts[pt - 1].strokeInner, viewWd, viewHt, opaque);
addGeometryPoint(stroke, aaPts[pt - 1].strokeInnerBlur, viewWd, viewHt, transparent);
addGeometryPoint(stroke, aaPts[pt].strokeInner, viewWd, viewHt, opaque);
addGeometryPoint(stroke, aaPts[pt].strokeInnerBlur, viewWd, viewHt, transparent);
addQuadIndices(i, stroke.indices);
}
}
aaPts.clear();
}
return true;
}
void GlGeometry::disableVertex(uint32_t location)
{
GL_CHECK(glDisableVertexAttribArray(location));
mGpuBuffer->unbind(GlGpuBuffer::Target::ARRAY_BUFFER);
}
void GlGeometry::draw(const uint32_t location, const uint32_t primitiveIndex, RenderUpdateFlag flag)
{
if (primitiveIndex >= mPrimitives.size())
{
return;
}
VertexDataArray& geometry = (flag == RenderUpdateFlag::Color) ? mPrimitives[primitiveIndex].mFill : mPrimitives[primitiveIndex].mStroke;
updateBuffer(location, geometry);
GL_CHECK(glDrawElements(GL_TRIANGLES, geometry.indices.size(), GL_UNSIGNED_INT, geometry.indices.data()));
}
void GlGeometry::updateBuffer(uint32_t location, const VertexDataArray& vertexArray)
{
if (mGpuBuffer.get() == nullptr)
{
mGpuBuffer = make_unique<GlGpuBuffer>();
}
mGpuBuffer->updateBufferData(GlGpuBuffer::Target::ARRAY_BUFFER, vertexArray.vertices.size() * sizeof(VertexData), vertexArray.vertices.data());
GL_CHECK(glVertexAttribPointer(location, 3, GL_FLOAT, GL_FALSE, sizeof(VertexData), 0));
GL_CHECK(glEnableVertexAttribArray(location));
}
GlPoint GlGeometry::normalizePoint(const GlPoint &pt, float viewWd, float viewHt)
{
GlPoint p;
p.x = (pt.x * 2.0f / viewWd) - 1.0f;
p.y = -1.0f * ((pt.y * 2.0f / viewHt) - 1.0f);
return p;
}
void GlGeometry::addGeometryPoint(VertexDataArray &geometry, const GlPoint &pt, float viewWd, float viewHt, float opacity)
{
VertexData tv = { normalizePoint(pt, viewWd, viewHt), opacity};
geometry.vertices.push_back(tv);
}
GlPoint GlGeometry::getNormal(const GlPoint &p1, const GlPoint &p2)
{
GlPoint normal = p1 - p2;
normal.normalize();
return GlPoint(-normal.y, normal.x);
}
float GlGeometry::dotProduct(const GlPoint &p1, const GlPoint &p2)
{
return (p1.x * p2.x + p1.y * p2.y);
}
GlPoint GlGeometry::extendEdge(const GlPoint &pt, const GlPoint &normal, float scalar)
{
GlPoint tmp = (normal * scalar);
return (pt + tmp);
}
void GlGeometry::addPoint(GlPrimitive& primitve, const GlPoint &pt)
{
primitve.mAAPoints.push_back(GlPoint(pt.x, pt.y));
}
void GlGeometry::addTriangleFanIndices(uint32_t &curPt, std::vector<uint32_t> &indices)
{
indices.push_back(0);
indices.push_back(curPt - 1);
indices.push_back(curPt);
}
void GlGeometry::addQuadIndices(uint32_t &curPt, std::vector<uint32_t> &indices)
{
indices.push_back(curPt);
indices.push_back(curPt + 1);
indices.push_back(curPt + 2);
indices.push_back(curPt + 1);
indices.push_back(curPt + 3);
indices.push_back(curPt + 2);
curPt += 4;
}
bool GlGeometry::isBezierFlat(const GlPoint &p1, const GlPoint &c1, const GlPoint &c2, const GlPoint &p2)
{
GlPoint diff1 = (c1 * 3.0f) - (p1 * 2.0f) - p2;
GlPoint diff2 = (c2 * 3.0f) - (p2 * 2.0f) - p1;
diff1.mod();
diff2.mod();
if (diff1.x < diff2.x)
diff1.x = diff2.x;
if (diff1.y < diff2.y)
diff1.y = diff2.y;
if (diff1.x + diff1.y <= 0.5f)
return true;
return false;
}
void GlGeometry::decomposeCubicCurve(GlPrimitive& primitve, const GlPoint &pt1, const GlPoint &cpt1, const GlPoint &cpt2, const GlPoint &pt2)
{
if (isBezierFlat(pt1, cpt1, cpt2, pt2))
{
addPoint(primitve, pt2);
return;
}
GlPoint p12 = (pt1 + cpt1) * 0.5f;
GlPoint p23 = (cpt1 + cpt2) * 0.5f;
GlPoint p34 = (cpt2 + pt2) * 0.5f;
GlPoint p123 = (p12 + p23) * 0.5f;
GlPoint p234 = (p23 + p34) * 0.5f;
GlPoint p1234 = (p123 + p234) * 0.5f;
decomposeCubicCurve(primitve, pt1, p12, p123, p1234);
decomposeCubicCurve(primitve, p1234, p234, p34, pt2);
}

View file

@ -0,0 +1,210 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef _TVG_GL_GEOMETRY_H_
#define _TVG_GL_GEOMETRY_H_
#include "tvgGlCommon.h"
class GlPoint
{
public:
float x = 0.0f;
float y = 0.0f;
GlPoint() = default;
GlPoint(float pX, float pY):x(pX), y(pY)
{}
GlPoint(const Point& rhs):GlPoint(rhs.x, rhs.y)
{}
GlPoint(const GlPoint& rhs) = default;
GlPoint(GlPoint&& rhs) = default;
GlPoint& operator= (const GlPoint& rhs) = default;
GlPoint& operator= (GlPoint&& rhs) = default;
GlPoint& operator= (const Point& rhs)
{
x = rhs.x;
y = rhs.y;
return *this;
}
bool operator== (const GlPoint& rhs)
{
if (&rhs == this)
return true;
if (rhs.x == this->x && rhs.y == this->y)
return true;
return false;
}
bool operator!= (const GlPoint& rhs)
{
if (&rhs == this)
return true;
if (rhs.x != this->x || rhs.y != this->y)
return true;
return false;
}
GlPoint operator+ (const GlPoint& rhs) const
{
return GlPoint(x + rhs.x, y + rhs.y);
}
GlPoint operator+ (const float c) const
{
return GlPoint(x + c, y + c);
}
GlPoint operator- (const GlPoint& rhs) const
{
return GlPoint(x - rhs.x, y - rhs.y);
}
GlPoint operator- (const float c) const
{
return GlPoint(x - c, y - c);
}
GlPoint operator* (const GlPoint& rhs) const
{
return GlPoint(x * rhs.x, y * rhs.y);
}
GlPoint operator* (const float c) const
{
return GlPoint(x * c, y * c);
}
GlPoint operator/ (const GlPoint& rhs) const
{
return GlPoint(x / rhs.x, y / rhs.y);
}
GlPoint operator/ (const float c) const
{
return GlPoint(x / c, y / c);
}
void mod()
{
x = fabsf(x);
y = fabsf(y);
}
void normalize()
{
auto length = sqrtf( (x * x) + (y * y) );
if (length != 0.0f)
{
const auto inverseLen = 1.0f / length;
x *= inverseLen;
y *= inverseLen;
}
}
};
struct SmoothPoint
{
GlPoint orgPt;
GlPoint fillOuterBlur;
GlPoint fillOuter;
GlPoint strokeOuterBlur;
GlPoint strokeOuter;
GlPoint strokeInnerBlur;
GlPoint strokeInner;
SmoothPoint(GlPoint pt)
:orgPt(pt),
fillOuterBlur(pt),
fillOuter(pt),
strokeOuterBlur(pt),
strokeOuter(pt),
strokeInnerBlur(pt),
strokeInner(pt)
{
}
};
struct PointNormals
{
GlPoint normal1;
GlPoint normal2;
GlPoint normalF;
};
struct VertexData
{
GlPoint point;
float opacity = 0.0f;
};
struct VertexDataArray
{
vector<VertexData> vertices;
vector<uint32_t> indices;
};
struct GlPrimitive
{
vector<SmoothPoint> mAAPoints;
VertexDataArray mFill;
VertexDataArray mStroke;
};
class GlGpuBuffer;
class GlGeometry
{
public:
uint32_t getPrimitiveCount();
bool decomposeOutline(const Shape& shape);
bool generateAAPoints(const Shape& shape, float strokeWd, RenderUpdateFlag flag);
bool tesselate(const Shape &shape, float viewWd, float viewHt, RenderUpdateFlag flag);
void disableVertex(uint32_t location);
void draw(const uint32_t location, const uint32_t primitiveIndex, RenderUpdateFlag flag);
private:
GlPoint normalizePoint(const GlPoint &pt, float viewWd, float viewHt);
void addGeometryPoint(VertexDataArray &geometry, const GlPoint &pt, float viewWd, float viewHt, float opacity);
GlPoint getNormal(const GlPoint &p1, const GlPoint &p2);
float dotProduct(const GlPoint &p1, const GlPoint &p2);
GlPoint extendEdge(const GlPoint &pt, const GlPoint &normal, float scalar);
void addPoint(GlPrimitive& primitve, const GlPoint &pt);
void addTriangleFanIndices(uint32_t &curPt, vector<uint32_t> &indices);
void addQuadIndices(uint32_t &curPt, vector<uint32_t> &indices);
bool isBezierFlat(const GlPoint &p1, const GlPoint &c1, const GlPoint &c2, const GlPoint &p2);
void decomposeCubicCurve(GlPrimitive& primitve, const GlPoint &pt1, const GlPoint &cpt1, const GlPoint &cpt2, const GlPoint &pt2);
void updateBuffer(const uint32_t location, const VertexDataArray& vertexArray);
unique_ptr<GlGpuBuffer> mGpuBuffer;
vector<GlPrimitive> mPrimitives;
};
#endif /* _TVG_GL_GEOMETRY_H_ */

View file

@ -0,0 +1,54 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "tvgGlCommon.h"
#include "tvgGlGpuBuffer.h"
#include <assert.h>
GlGpuBuffer::GlGpuBuffer()
{
GL_CHECK(glGenBuffers(1, &mGlBufferId));
assert(mGlBufferId != GL_INVALID_VALUE);
}
GlGpuBuffer::~GlGpuBuffer()
{
if (mGlBufferId)
{
GL_CHECK(glDeleteBuffers(1, &mGlBufferId));
}
}
void GlGpuBuffer::updateBufferData(Target target, uint32_t size, const void* data)
{
GL_CHECK(glBindBuffer(static_cast<uint32_t>(target), mGlBufferId));
GL_CHECK(glBufferData(static_cast<uint32_t>(target), size, data, GL_STATIC_DRAW));
}
void GlGpuBuffer::unbind(Target target)
{
GL_CHECK(glBindBuffer(static_cast<uint32_t>(target), 0));
}

View file

@ -0,0 +1,48 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef _TVG_GL_GPU_BUFFER_H_
#define _TVG_GL_GPU_BUFFER_H_
#include <stdlib.h>
#include <GLES2/gl2.h>
class GlGpuBuffer
{
public:
enum class Target
{
ARRAY_BUFFER = GL_ARRAY_BUFFER,
ELEMENT_ARRAY_BUFFER = GL_ARRAY_BUFFER
};
GlGpuBuffer();
~GlGpuBuffer();
void updateBufferData(Target target, uint32_t size, const void* data);
void unbind(Target target);
private:
uint32_t mGlBufferId = 0;
};
#endif /* _TVG_GL_GPU_BUFFER_H_ */

View file

@ -0,0 +1,166 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "tvgGlCommon.h"
#include "tvgGlProgram.h"
#include <GLES2/gl2.h>
static std::vector<string> gStdAttributes = {
"aLocation"
};
static std::vector<string> gStdUniforms = {
"uColor"
};
uint32_t GlProgram::mCurrentProgram = 0;
map<string, int32_t> GlProgram::mAttributeBuffer;
map<string, int32_t> GlProgram::mUniformBuffer;
unique_ptr<GlProgram> GlProgram::gen(std::shared_ptr<GlShader> shader)
{
return make_unique<GlProgram>(shader);
}
GlProgram::GlProgram(std::shared_ptr<GlShader> shader)
{
linkProgram(shader);
load();
for (auto name : gStdAttributes)
{
getAttributeLocation(name.c_str());
}
for (auto name : gStdUniforms)
{
getUniformLocation(name.c_str());
}
}
GlProgram::~GlProgram()
{
if (mCurrentProgram == mProgramObj)
{
unload();
}
glDeleteProgram(mProgramObj);
}
void GlProgram::load()
{
if (mCurrentProgram == mProgramObj)
{
return;
}
mCurrentProgram = mProgramObj;
GL_CHECK(glUseProgram(mProgramObj));
}
void GlProgram::unload()
{
mCurrentProgram = 0;
}
int32_t GlProgram::getAttributeLocation(const char* name)
{
if (mAttributeBuffer.find(name) != mAttributeBuffer.end())
{
return mAttributeBuffer[name];
}
GL_CHECK(int32_t location = glGetAttribLocation(mCurrentProgram, name));
if (location != -1)
{
mAttributeBuffer[name] = location;
}
return location;
}
int32_t GlProgram::getUniformLocation(const char* name)
{
if (mUniformBuffer.find(name) != mUniformBuffer.end())
{
return mUniformBuffer[name];
}
GL_CHECK(int32_t location = glGetUniformLocation(mCurrentProgram, name));
if (location != -1)
{
mUniformBuffer[name] = location;
}
return location;
}
void GlProgram::setUniformValue(int32_t location, float r, float g, float b, float a)
{
glUniform4f(location, r, g, b, a);
}
void GlProgram::linkProgram(std::shared_ptr<GlShader> shader)
{
GLint linked;
// Create the program object
uint32_t progObj = glCreateProgram();
assert(progObj);
glAttachShader(progObj, shader->getVertexShader());
glAttachShader(progObj, shader->getFragmentShader());
// Link the program
glLinkProgram(progObj);
// Check the link status
glGetProgramiv(progObj, GL_LINK_STATUS, &linked);
if (!linked)
{
GLint infoLen = 0;
glGetProgramiv(progObj, GL_INFO_LOG_LENGTH, &infoLen);
if (infoLen > 0)
{
char* infoLog = new char[infoLen];
glGetProgramInfoLog(progObj, infoLen, NULL, infoLog);
std::cout << "Error linking shader: " << infoLog << std::endl;
delete[] infoLog;
}
glDeleteProgram(progObj);
progObj = 0;
assert(0);
}
mProgramObj = progObj;
}

View file

@ -0,0 +1,54 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef _TVG_GL_PROGRAM_H_
#define _TVG_GL_PROGRAM_H_
#include "tvgGlShader.h"
#include <memory>
#include <map>
class GlProgram
{
public:
static std::unique_ptr<GlProgram> gen(std::shared_ptr<GlShader> shader);
GlProgram(std::shared_ptr<GlShader> shader);
~GlProgram();
void load();
void unload();
int32_t getAttributeLocation(const char* name);
int32_t getUniformLocation(const char* name);
void setUniformValue(int32_t location, float r, float g, float b, float a);
private:
void linkProgram(std::shared_ptr<GlShader> shader);
uint32_t mProgramObj;
static uint32_t mCurrentProgram;
static std::map<string, int32_t> mAttributeBuffer;
static std::map<string, int32_t> mUniformBuffer;
};
#endif /* _TVG_GL_PROGRAM_H_ */

View file

@ -0,0 +1,215 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "tvgGlShaderSrc.h"
#include "tvgGlGpuBuffer.h"
#include "tvgGlGeometry.h"
#include "tvgGlCommon.h"
#include "tvgGlRenderer.h"
/************************************************************************/
/* Internal Class Implementation */
/************************************************************************/
static RenderInitializer renderInit;
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
bool GlRenderer::clear()
{
//TODO: (Request) to clear target
// Will be adding glClearColor for input buffer
return true;
}
bool GlRenderer::target(TVG_UNUSED uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h)
{
assert(w > 0 && h > 0);
surface.stride = stride;
surface.w = w;
surface.h = h;
return true;
}
bool GlRenderer::flush()
{
GL_CHECK(glFinish());
mColorProgram->unload();
return true;
}
bool GlRenderer::preRender()
{
// Blend function for pre multiplied alpha
GL_CHECK(glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA));
GL_CHECK(glEnable(GL_BLEND));
return true;
}
bool GlRenderer::postRender()
{
//TODO: called just after render()
return true;
}
bool GlRenderer::render(const Shape& shape, void *data)
{
GlShape* sdata = static_cast<GlShape*>(data);
if (!sdata) return false;
uint8_t r, g, b, a;
size_t flags = static_cast<size_t>(sdata->updateFlag);
GL_CHECK(glViewport(0, 0, sdata->viewWd, sdata->viewHt));
uint32_t geometryCnt = sdata->geometry->getPrimitiveCount();
for (uint32_t i = 0; i < geometryCnt; ++i)
{
mColorProgram->load();
if (flags & RenderUpdateFlag::Color)
{
shape.fill(&r, &g, &b, &a);
drawPrimitive(*(sdata->geometry), (float)r / 255.0f, (float)g / 255.0f, (float)b / 255.0f, (float)a / 255.0f, i, RenderUpdateFlag::Color);
}
if (flags & RenderUpdateFlag::Stroke)
{
shape.strokeColor(&r, &g, &b, &a);
drawPrimitive(*(sdata->geometry), (float)r / 255.0f, (float)g / 255.0f, (float)b / 255.0f, (float)a / 255.0f, i, RenderUpdateFlag::Stroke);
}
}
return true;
}
bool GlRenderer::dispose(TVG_UNUSED const Shape& shape, void *data)
{
GlShape* sdata = static_cast<GlShape*>(data);
if (!sdata) return false;
delete sdata;
return true;
}
void* GlRenderer::prepare(const Shape& shape, void* data, TVG_UNUSED const RenderTransform* transform, RenderUpdateFlag flags)
{
//prepare shape data
GlShape* sdata = static_cast<GlShape*>(data);
if (!sdata) {
sdata = new GlShape;
if (!sdata) return nullptr;
}
sdata->viewWd = static_cast<float>(surface.w);
sdata->viewHt = static_cast<float>(surface.h);
sdata->updateFlag = flags;
if (sdata->updateFlag == RenderUpdateFlag::None) return sdata;
initShaders();
sdata->geometry = make_unique<GlGeometry>();
//invisible?
uint8_t alphaF, alphaS;
shape.fill(nullptr, nullptr, nullptr, &alphaF);
shape.strokeColor(nullptr, nullptr, nullptr, &alphaS);
auto strokeWd = shape.strokeWidth();
if (alphaF == 0 && alphaS == 0) return sdata;
if (sdata->updateFlag & (RenderUpdateFlag::Color | RenderUpdateFlag::Stroke) )
{
if (!sdata->geometry->decomposeOutline(shape)) return sdata;
if (!sdata->geometry->generateAAPoints(shape, static_cast<float>(strokeWd), sdata->updateFlag)) return sdata;
if (!sdata->geometry->tesselate(shape, sdata->viewWd, sdata->viewHt, sdata->updateFlag)) return sdata;
}
return sdata;
}
int GlRenderer::init()
{
return RenderInitializer::init(renderInit, new GlRenderer);
}
int GlRenderer::term()
{
if (inst()->mColorProgram.get())
{
inst()->mColorProgram.reset(nullptr);
}
return RenderInitializer::term(renderInit);
}
uint32_t GlRenderer::unref()
{
return RenderInitializer::unref(renderInit);
}
uint32_t GlRenderer::ref()
{
return RenderInitializer::ref(renderInit);
}
GlRenderer* GlRenderer::inst()
{
//We know renderer type, avoid dynamic_cast for performance.
return static_cast<GlRenderer*>(RenderInitializer::inst(renderInit));
}
void GlRenderer::initShaders()
{
if (!mColorProgram.get())
{
shared_ptr<GlShader> shader = GlShader::gen(COLOR_VERT_SHADER, COLOR_FRAG_SHADER);
mColorProgram = GlProgram::gen(shader);
}
mColorProgram->load();
mColorUniformLoc = mColorProgram->getUniformLocation("uColor");
mVertexAttrLoc = mColorProgram->getAttributeLocation("aLocation");
}
void GlRenderer::drawPrimitive(GlGeometry& geometry, float r, float g, float b, float a, uint32_t primitiveIndex, RenderUpdateFlag flag)
{
mColorProgram->setUniformValue(mColorUniformLoc, r, g, b, a);
geometry.draw(mVertexAttrLoc, primitiveIndex, flag);
geometry.disableVertex(mVertexAttrLoc);
}

View file

@ -0,0 +1,62 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef _TVG_GL_RENDERER_H_
#define _TVG_GL_RENDERER_H_
#include "tvgGlCommon.h"
#include "tvgGlProgram.h"
class GlRenderer : public RenderMethod
{
public:
Surface surface = {nullptr, 0, 0, 0};
void* prepare(const Shape& shape, void* data, const RenderTransform* transform, RenderUpdateFlag flags) override;
bool dispose(const Shape& shape, void *data) override;
bool preRender() override;
bool render(const Shape& shape, void *data) override;
bool postRender() override;
bool target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h);
bool flush() override;
bool clear() override;
uint32_t ref() override;
uint32_t unref() override;
static GlRenderer* inst();
static int init();
static int term();
private:
GlRenderer(){};
~GlRenderer(){};
void initShaders();
void drawPrimitive(GlGeometry& geometry, float r, float g, float b, float a, uint32_t primitiveIndex, RenderUpdateFlag flag);
unique_ptr<GlProgram> mColorProgram = nullptr;
int32_t mColorUniformLoc = 0;
uint32_t mVertexAttrLoc = 0;
};
#endif /* _TVG_GL_RENDERER_H_ */

View file

@ -0,0 +1,99 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "tvgGlCommon.h"
#include "tvgGlShader.h"
#include <GLES2/gl2.h>
shared_ptr<GlShader> GlShader::gen(const char * vertSrc, const char * fragSrc)
{
shared_ptr<GlShader> shader = make_shared<GlShader>();
shader->createShader(vertSrc, fragSrc);
return shader;
}
GlShader::~GlShader()
{
glDeleteShader(mVtShader);
glDeleteShader(mFrShader);
}
uint32_t GlShader::getVertexShader()
{
return mVtShader;
}
uint32_t GlShader::getFragmentShader()
{
return mFrShader;
}
void GlShader::createShader(const char* vertSrc, const char* fragSrc)
{
mVtShader = complileShader(GL_VERTEX_SHADER, const_cast<char*>(vertSrc));
mFrShader = complileShader(GL_FRAGMENT_SHADER, const_cast<char*>(fragSrc));
}
uint32_t GlShader::complileShader(uint32_t type, char* shaderSrc)
{
GLuint shader;
GLint compiled;
// Create the shader object
shader = glCreateShader(type);
assert(shader);
// Load the shader source
glShaderSource(shader, 1, &shaderSrc, NULL);
// Compile the shader
glCompileShader(shader);
// Check the compile status
glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
if (!compiled)
{
GLint infoLen = 0;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
if (infoLen > 0)
{
char* infoLog = new char[infoLen];
glGetShaderInfoLog(shader, infoLen, NULL, infoLog);
std::cout << "Error compiling shader: " << infoLog << std::endl;
delete[] infoLog;
}
glDeleteShader(shader);
assert(0);
}
return shader;
}

View file

@ -0,0 +1,45 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef _TVG_GL_SHADER_H_
#define _TVG_GL_SHADER_H_
#include <memory>
class GlShader
{
public:
static std::shared_ptr<GlShader> gen(const char * vertSrc, const char * fragSrc);
~GlShader();
uint32_t getVertexShader();
uint32_t getFragmentShader();
private:
void createShader(const char* vertSrc, const char* fragSrc);
uint32_t complileShader(uint32_t type, char* shaderSrc);
uint32_t mVtShader;
uint32_t mFrShader;
};
#endif /* _TVG_GL_SHADER_H_ */

View file

@ -0,0 +1,44 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "tvgGlShaderSrc.h"
const char* COLOR_VERT_SHADER =
"attribute highp vec4 aLocation; \n"
"uniform highp vec4 uColor; \n"
"varying highp vec4 vcolor; \n"
"varying highp float vOpacity; \n"
"void main() \n"
"{ \n"
" gl_Position = vec4(aLocation.xy, 0.0, 1.0); \n"
" vcolor = uColor; \n"
" vOpacity = aLocation.z; \n"
"} \n";
const char* COLOR_FRAG_SHADER =
"varying highp vec4 vcolor; \n"
"varying highp float vOpacity; \n"
"void main() \n"
"{ \n"
" gl_FragColor = vec4(vcolor.xyz, vcolor.w*vOpacity); \n"
"} \n";

View file

@ -0,0 +1,29 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef _TVG_GL_SHADERSRC_H_
#define _TVG_GL_SHADERSRC_H_
extern const char* COLOR_VERT_SHADER;
extern const char* COLOR_FRAG_SHADER;
#endif /* _TVG_GL_SHADERSRC_H_ */

46
src/lib/meson.build Normal file
View file

@ -0,0 +1,46 @@
engine_dep = []
if get_option('engines').contains('sw') == true
subdir('sw_engine')
message('Enable SW Raster Engine')
endif
if get_option('engines').contains('gl') == true
subdir('gl_engine')
message('Enable GL Raster Engine')
endif
source_file = [
'tvgCanvasImpl.h',
'tvgCommon.h',
'tvgBezier.h',
'tvgLoader.h',
'tvgLoaderMgr.h',
'tvgPictureImpl.h',
'tvgRender.h',
'tvgSceneImpl.h',
'tvgShapePath.h',
'tvgShapeImpl.h',
'tvgTaskScheduler.h',
'tvgBezier.cpp',
'tvgCanvas.cpp',
'tvgFill.cpp',
'tvgGlCanvas.cpp',
'tvgInitializer.cpp',
'tvgLinearGradient.cpp',
'tvgLoaderMgr.cpp',
'tvgPaint.cpp',
'tvgPicture.cpp',
'tvgRadialGradient.cpp',
'tvgRender.cpp',
'tvgScene.cpp',
'tvgShape.cpp',
'tvgSwCanvas.cpp',
'tvgTaskScheduler.cpp',
]
common_dep = declare_dependency(
dependencies : engine_dep,
include_directories : include_directories('.'),
sources : source_file
)

View file

@ -0,0 +1,16 @@
source_file = [
'tvgSwCommon.h',
'tvgSwFill.cpp',
'tvgSwMath.cpp',
'tvgSwRenderer.h',
'tvgSwRaster.cpp',
'tvgSwRenderer.cpp',
'tvgSwRle.cpp',
'tvgSwShape.cpp',
'tvgSwStroke.cpp',
]
engine_dep += [declare_dependency(
include_directories : include_directories('.'),
sources : source_file
)]

View file

@ -0,0 +1,323 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef _TVG_SW_COMMON_H_
#define _TVG_SW_COMMON_H_
#include "tvgCommon.h"
#ifdef THORVG_AVX_VECTOR_SUPPORT
#include <immintrin.h>
#endif
#if 0
#include <sys/time.h>
static double timeStamp()
{
struct timeval tv;
gettimeofday(&tv, NULL);
return (tv.tv_sec + tv.tv_usec / 1000000.0);
}
#endif
#define SW_CURVE_TYPE_POINT 0
#define SW_CURVE_TYPE_CUBIC 1
#define SW_OUTLINE_FILL_WINDING 0
#define SW_OUTLINE_FILL_EVEN_ODD 1
#define SW_ANGLE_PI (180L << 16)
#define SW_ANGLE_2PI (SW_ANGLE_PI << 1)
#define SW_ANGLE_PI2 (SW_ANGLE_PI >> 1)
#define SW_ANGLE_PI4 (SW_ANGLE_PI >> 2)
using SwCoord = signed long;
using SwFixed = signed long long;
struct SwPoint
{
SwCoord x, y;
SwPoint& operator+=(const SwPoint& rhs) {
x += rhs.x;
y += rhs.y;
return *this;
}
SwPoint operator+(const SwPoint& rhs) const {
return {x + rhs.x, y + rhs.y};
}
SwPoint operator-(const SwPoint& rhs) const {
return {x - rhs.x, y - rhs.y};
}
bool operator==(const SwPoint& rhs ) const {
return (x == rhs.x && y == rhs.y);
}
bool operator!=(const SwPoint& rhs) const {
return (x != rhs.x || y != rhs.y);
}
bool zero()
{
if (x == 0 && y == 0) return true;
else return false;
}
bool small()
{
//2 is epsilon...
if (abs(x) < 2 && abs(y) < 2) return true;
else return false;
}
};
struct SwSize
{
SwCoord w, h;
};
struct SwOutline
{
uint32_t* cntrs; //the contour end points
uint32_t cntrsCnt; //number of contours in glyph
uint32_t reservedCntrsCnt;
SwPoint* pts; //the outline's points
uint32_t ptsCnt; //number of points in the glyph
uint32_t reservedPtsCnt;
uint8_t* types; //curve type
uint8_t fillMode; //outline fill mode
bool opened; //opened path?
};
struct SwSpan
{
int16_t x, y;
uint16_t len;
uint8_t coverage;
};
struct SwRleData
{
SwSpan *spans;
uint32_t alloc;
uint32_t size;
};
struct SwBBox
{
SwPoint min, max;
};
struct SwStrokeBorder
{
uint32_t ptsCnt;
uint32_t maxPts;
SwPoint* pts;
uint8_t* tags;
int32_t start; //index of current sub-path start point
bool movable; //true: for ends of lineto borders
bool valid;
};
struct SwStroke
{
SwFixed angleIn;
SwFixed angleOut;
SwPoint center;
SwFixed lineLength;
SwFixed subPathAngle;
SwPoint ptStartSubPath;
SwFixed subPathLineLength;
SwFixed width;
StrokeCap cap;
StrokeJoin join;
StrokeJoin joinSaved;
SwStrokeBorder borders[2];
float sx, sy;
bool firstPt;
bool openSubPath;
bool handleWideStrokes;
};
struct SwDashStroke
{
SwOutline* outline;
int32_t curLen;
int32_t curIdx;
Point ptStart;
Point ptCur;
float* pattern;
uint32_t cnt;
bool curOpGap;
};
struct SwFill
{
struct SwLinear {
float dx, dy;
float len;
float offset;
};
struct SwRadial {
float cx, cy;
float a;
float inv2a;
};
union {
SwLinear linear;
SwRadial radial;
};
uint32_t* ctable;
FillSpread spread;
float sx, sy;
bool translucent;
};
struct SwShape
{
SwOutline* outline;
SwStroke* stroke;
SwFill* fill;
SwRleData* rle;
SwRleData* strokeRle;
SwBBox bbox;
bool rect; //Fast Track: Othogonal rectangle?
};
struct SwCompositor
{
uint32_t (*join)(uint8_t r, uint8_t g, uint8_t b, uint8_t a);
uint32_t (*alpha)(uint32_t rgba);
};
struct SwSurface : Surface
{
SwCompositor comp;
};
static inline SwCoord TO_SWCOORD(float val)
{
return SwCoord(val * 64);
}
static inline uint32_t ALPHA_BLEND(uint32_t c, uint32_t a)
{
return (((((c >> 8) & 0x00ff00ff) * a) & 0xff00ff00) +
((((c & 0x00ff00ff) * a) >> 8) & 0x00ff00ff));
}
static inline uint32_t COLOR_INTERPOLATE(uint32_t c1, uint32_t a1, uint32_t c2, uint32_t a2)
{
auto t = (((c1 & 0xff00ff) * a1 + (c2 & 0xff00ff) * a2) >> 8) & 0xff00ff;
c1 = (((c1 >> 8) & 0xff00ff) * a1 + ((c2 >> 8) & 0xff00ff) * a2) & 0xff00ff00;
return (c1 |= t);
}
static inline uint8_t ALPHA_MULTIPLY(uint32_t c, uint32_t a)
{
return (c * a) >> 8;
}
int64_t mathMultiply(int64_t a, int64_t b);
int64_t mathDivide(int64_t a, int64_t b);
int64_t mathMulDiv(int64_t a, int64_t b, int64_t c);
void mathRotate(SwPoint& pt, SwFixed angle);
SwFixed mathTan(SwFixed angle);
SwFixed mathAtan(const SwPoint& pt);
SwFixed mathCos(SwFixed angle);
SwFixed mathSin(SwFixed angle);
void mathSplitCubic(SwPoint* base);
SwFixed mathDiff(SwFixed angle1, SwFixed angle2);
SwFixed mathLength(SwPoint& pt);
bool mathSmallCubic(SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, SwFixed& angleOut);
SwFixed mathMean(SwFixed angle1, SwFixed angle2);
void shapeReset(SwShape* shape);
bool shapeGenOutline(SwShape* shape, const Shape* sdata, const Matrix* transform);
bool shapePrepare(SwShape* shape, const Shape* sdata, const SwSize& clip, const Matrix* transform);
bool shapeGenRle(SwShape* shape, const Shape* sdata, const SwSize& clip, bool antiAlias);
void shapeDelOutline(SwShape* shape);
void shapeResetStroke(SwShape* shape, const Shape* sdata, const Matrix* transform);
bool shapeGenStrokeRle(SwShape* shape, const Shape* sdata, const Matrix* transform, const SwSize& clip);
void shapeFree(SwShape* shape);
void shapeDelStroke(SwShape* shape);
bool shapeGenFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, bool ctable);
void shapeResetFill(SwShape* shape);
void shapeDelFill(SwShape* shape);
void strokeReset(SwStroke* stroke, const Shape* shape, const Matrix* transform);
bool strokeParseOutline(SwStroke* stroke, const SwOutline& outline);
SwOutline* strokeExportOutline(SwStroke* stroke);
void strokeFree(SwStroke* stroke);
bool fillGenColorTable(SwFill* fill, const Fill* fdata, const Matrix* transform, SwSurface* surface, bool ctable);
void fillReset(SwFill* fill);
void fillFree(SwFill* fill);
void fillFetchLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t offset, uint32_t len);
void fillFetchRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len);
SwRleData* rleRender(const SwOutline* outline, const SwBBox& bbox, const SwSize& clip, bool antiAlias);
void rleFree(SwRleData* rle);
bool rasterCompositor(SwSurface* surface);
bool rasterGradientShape(SwSurface* surface, SwShape* shape, unsigned id);
bool rasterSolidShape(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
bool rasterStroke(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
bool rasterClear(SwSurface* surface);
static inline void rasterRGBA32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len)
{
#ifdef THORVG_AVX_VECTOR_SUPPORT
int32_t align = (8 - (offset % 8)) % 8;
//Vectorization
auto avxDst = (__m256i*)(dst + offset + align);
int32_t i = (len - align);
for (;i > 7; i -= 8, ++avxDst) {
*avxDst = _mm256_set1_epi32(val);
}
//Alignment
if (align > 0) {
if (align > len) align -= (align - len);
auto tmp = dst + offset;
for (; align > 0; --align, ++tmp) *tmp = val;
}
//Pack Leftovers
dst += offset + (len - i);
while (i-- > 0) *(dst++) = val;
#else
dst += offset;
while (len--) *dst++ = val;
#endif
}
#endif /* _TVG_SW_COMMON_H_ */

View file

@ -0,0 +1,314 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "tvgSwCommon.h"
/************************************************************************/
/* Internal Class Implementation */
/************************************************************************/
#define GRADIENT_STOP_SIZE 1024
#define FIXPT_BITS 8
#define FIXPT_SIZE (1<<FIXPT_BITS)
static bool _updateColorTable(SwFill* fill, const Fill* fdata, SwSurface* surface)
{
if (!fill->ctable) {
fill->ctable = static_cast<uint32_t*>(malloc(GRADIENT_STOP_SIZE * sizeof(uint32_t)));
if (!fill->ctable) return false;
}
const Fill::ColorStop* colors;
auto cnt = fdata->colorStops(&colors);
if (cnt == 0 || !colors) return false;
auto pColors = colors;
if (pColors->a < 255) fill->translucent = true;
auto r = ALPHA_MULTIPLY(pColors->r, pColors->a);
auto g = ALPHA_MULTIPLY(pColors->g, pColors->a);
auto b = ALPHA_MULTIPLY(pColors->b, pColors->a);
auto rgba = surface->comp.join(r, g, b, pColors->a);
auto inc = 1.0f / static_cast<float>(GRADIENT_STOP_SIZE);
auto pos = 1.5f * inc;
uint32_t i = 0;
fill->ctable[i++] = rgba;
while (pos <= pColors->offset) {
fill->ctable[i] = fill->ctable[i - 1];
++i;
pos += inc;
}
for (uint32_t j = 0; j < cnt - 1; ++j) {
auto curr = colors + j;
auto next = curr + 1;
auto delta = 1.0f / (next->offset - curr->offset);
if (next->a < 255) fill->translucent = true;
auto r = ALPHA_MULTIPLY(next->r, next->a);
auto g = ALPHA_MULTIPLY(next->g, next->a);
auto b = ALPHA_MULTIPLY(next->b, next->a);
auto rgba2 = surface->comp.join(r, g, b, next->a);
while (pos < next->offset && i < GRADIENT_STOP_SIZE) {
auto t = (pos - curr->offset) * delta;
auto dist = static_cast<int32_t>(256 * t);
auto dist2 = 256 - dist;
fill->ctable[i] = COLOR_INTERPOLATE(rgba, dist2, rgba2, dist);
++i;
pos += inc;
}
rgba = rgba2;
}
for (; i < GRADIENT_STOP_SIZE; ++i)
fill->ctable[i] = rgba;
//Make sure the lat color stop is represented at the end of the table
fill->ctable[GRADIENT_STOP_SIZE - 1] = rgba;
return true;
}
bool _prepareLinear(SwFill* fill, const LinearGradient* linear, const Matrix* transform)
{
float x1, x2, y1, y2;
if (linear->linear(&x1, &y1, &x2, &y2) != Result::Success) return false;
if (transform) {
auto sx = sqrt(pow(transform->e11, 2) + pow(transform->e21, 2));
auto sy = sqrt(pow(transform->e12, 2) + pow(transform->e22, 2));
auto cx = (x2 - x1) * 0.5f + x1;
auto cy = (y2 - y1) * 0.5f + y1;
auto dx = x1 - cx;
auto dy = y1 - cy;
x1 = dx * transform->e11 + dy * transform->e12 + transform->e13 + (cx * sx);
y1 = dx * transform->e21 + dy * transform->e22 + transform->e23 + (cy * sy);
dx = x2 - cx;
dy = y2 - cy;
x2 = dx * transform->e11 + dy * transform->e12 + transform->e13 + (cx * sx);
y2 = dx * transform->e21 + dy * transform->e22 + transform->e23 + (cy * sy);
}
fill->linear.dx = x2 - x1;
fill->linear.dy = y2 - y1;
fill->linear.len = fill->linear.dx * fill->linear.dx + fill->linear.dy * fill->linear.dy;
if (fill->linear.len < FLT_EPSILON) return true;
fill->linear.dx /= fill->linear.len;
fill->linear.dy /= fill->linear.len;
fill->linear.offset = -fill->linear.dx * x1 -fill->linear.dy * y1;
return true;
}
bool _prepareRadial(SwFill* fill, const RadialGradient* radial, const Matrix* transform)
{
float radius;
if (radial->radial(&fill->radial.cx, &fill->radial.cy, &radius) != Result::Success) return false;
if (radius < FLT_EPSILON) return true;
fill->sx = 1.0f;
fill->sy = 1.0f;
if (transform) {
auto tx = fill->radial.cx * transform->e11 + fill->radial.cy * transform->e12 + transform->e13;
auto ty = fill->radial.cx * transform->e21 + fill->radial.cy * transform->e22 + transform->e23;
fill->radial.cx = tx;
fill->radial.cy = ty;
auto sx = sqrt(pow(transform->e11, 2) + pow(transform->e21, 2));
auto sy = sqrt(pow(transform->e12, 2) + pow(transform->e22, 2));
//FIXME; Scale + Rotation is not working properly
radius *= sx;
if (fabsf(sx - sy) > FLT_EPSILON) {
fill->sx = sx;
fill->sy = sy;
}
}
fill->radial.a = radius * radius;
fill->radial.inv2a = pow(1 / (2 * fill->radial.a), 2);
return true;
}
static inline uint32_t _clamp(const SwFill* fill, int32_t pos)
{
switch (fill->spread) {
case FillSpread::Pad: {
if (pos >= GRADIENT_STOP_SIZE) pos = GRADIENT_STOP_SIZE - 1;
else if (pos < 0) pos = 0;
break;
}
case FillSpread::Repeat: {
if (pos < 0) pos = GRADIENT_STOP_SIZE + pos;
pos = pos % GRADIENT_STOP_SIZE;
break;
}
case FillSpread::Reflect: {
auto limit = GRADIENT_STOP_SIZE * 2;
pos = pos % limit;
if (pos < 0) pos = limit + pos;
if (pos >= GRADIENT_STOP_SIZE) pos = (limit - pos - 1);
break;
}
}
return pos;
}
static inline uint32_t _fixedPixel(const SwFill* fill, int32_t pos)
{
int32_t i = (pos + (FIXPT_SIZE / 2)) >> FIXPT_BITS;
return fill->ctable[_clamp(fill, i)];
}
static inline uint32_t _pixel(const SwFill* fill, float pos)
{
auto i = static_cast<int32_t>(pos * (GRADIENT_STOP_SIZE - 1) + 0.5f);
return fill->ctable[_clamp(fill, i)];
}
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
void fillFetchRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len)
{
if (fill->radial.a < FLT_EPSILON) return;
//Rotation
auto rx = (x + 0.5f - fill->radial.cx) * fill->sy;
auto ry = (y + 0.5f - fill->radial.cy) * fill->sx;
auto inv2a = fill->radial.inv2a;
auto rxy = rx * rx + ry * ry;
auto rxryPlus = 2 * rx;
auto det = (-4 * fill->radial.a * -rxy) * inv2a;
auto detDelta = (4 * fill->radial.a * (rxryPlus + 1.0f)) * inv2a;
auto detDelta2 = (4 * fill->radial.a * 2.0f) * inv2a;
for (uint32_t i = 0 ; i < len ; ++i) {
*dst = _pixel(fill, sqrt(det));
++dst;
det += detDelta;
detDelta += detDelta2;
}
}
void fillFetchLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t offset, uint32_t len)
{
if (fill->linear.len < FLT_EPSILON) return;
//Rotation
float rx = x + 0.5f;
float ry = y + 0.5f;
float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (GRADIENT_STOP_SIZE - 1);
float inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1);
if (fabsf(inc) < FLT_EPSILON) {
auto color = _fixedPixel(fill, static_cast<int32_t>(t * FIXPT_SIZE));
rasterRGBA32(dst, color, offset, len);
return;
}
dst += offset;
auto vMax = static_cast<float>(INT32_MAX >> (FIXPT_BITS + 1));
auto vMin = -vMax;
auto v = t + (inc * len);
//we can use fixed point math
if (v < vMax && v > vMin) {
auto t2 = static_cast<uint32_t>(t * FIXPT_SIZE);
auto inc2 = static_cast<uint32_t>(inc * FIXPT_SIZE);
for (uint32_t j = 0; j < len; ++j) {
*dst = _fixedPixel(fill, t2);
++dst;
t2 += inc2;
}
//we have to fallback to float math
} else {
while (dst < dst + len) {
*dst = _pixel(fill, t / GRADIENT_STOP_SIZE);
++dst;
t += inc;
}
}
}
bool fillGenColorTable(SwFill* fill, const Fill* fdata, const Matrix* transform, SwSurface* surface, bool ctable)
{
if (!fill) return false;
fill->spread = fdata->spread();
if (ctable) {
if (!_updateColorTable(fill, fdata, surface)) return false;
}
if (fdata->id() == FILL_ID_LINEAR) {
return _prepareLinear(fill, static_cast<const LinearGradient*>(fdata), transform);
} else if (fdata->id() == FILL_ID_RADIAL) {
return _prepareRadial(fill, static_cast<const RadialGradient*>(fdata), transform);
}
//LOG: What type of gradient?!
return false;
}
void fillReset(SwFill* fill)
{
if (fill->ctable) {
free(fill->ctable);
fill->ctable = nullptr;
}
fill->translucent = false;
}
void fillFree(SwFill* fill)
{
if (!fill) return;
if (fill->ctable) free(fill->ctable);
free(fill);
}

View file

@ -0,0 +1,417 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "tvgSwCommon.h"
/************************************************************************/
/* Internal Class Implementation */
/************************************************************************/
constexpr SwFixed CORDIC_FACTOR = 0xDBD95B16UL; //the Cordic shrink factor 0.858785336480436 * 2^32
//this table was generated for SW_FT_PI = 180L << 16, i.e. degrees
constexpr static auto ATAN_MAX = 23;
constexpr static SwFixed ATAN_TBL[] = {
1740967L, 919879L, 466945L, 234379L, 117304L, 58666L, 29335L,
14668L, 7334L, 3667L, 1833L, 917L, 458L, 229L, 115L,
57L, 29L, 14L, 7L, 4L, 2L, 1L};
static inline SwCoord SATURATE(const SwCoord x)
{
return (x >> (sizeof(SwCoord) * 8 - 1));
}
static inline SwFixed PAD_ROUND(const SwFixed x, int32_t n)
{
return (((x) + ((n)/2)) & ~((n)-1));
}
static SwCoord _downscale(SwFixed x)
{
//multiply a give value by the CORDIC shrink factor
auto s = abs(x);
int64_t t = (s * static_cast<int64_t>(CORDIC_FACTOR)) + 0x100000000UL;
s = static_cast<SwFixed>(t >> 32);
if (x < 0) s = -s;
return s;
}
static int32_t _normalize(SwPoint& pt)
{
/* the highest bit in overflow-safe vector components
MSB of 0.858785336480436 * sqrt(0.5) * 2^30 */
constexpr auto SAFE_MSB = 29;
auto v = pt;
//High order bit(MSB)
//clz: count leading zeros
auto shift = 31 - __builtin_clz(abs(v.x) | abs(v.y));
if (shift <= SAFE_MSB) {
shift = SAFE_MSB - shift;
pt.x = static_cast<SwCoord>((unsigned long)v.x << shift);
pt.y = static_cast<SwCoord>((unsigned long)v.y << shift);
} else {
shift -= SAFE_MSB;
pt.x = v.x >> shift;
pt.y = v.y >> shift;
shift = -shift;
}
return shift;
}
static void _polarize(SwPoint& pt)
{
auto v = pt;
SwFixed theta;
//Get the vector into [-PI/4, PI/4] sector
if (v.y > v.x) {
if (v.y > -v.x) {
auto tmp = v.y;
v.y = -v.x;
v.x = tmp;
theta = SW_ANGLE_PI2;
} else {
theta = v.y > 0 ? SW_ANGLE_PI : -SW_ANGLE_PI;
v.x = -v.x;
v.y = -v.y;
}
} else {
if (v.y < -v.x) {
theta = -SW_ANGLE_PI2;
auto tmp = -v.y;
v.y = v.x;
v.x = tmp;
} else {
theta = 0;
}
}
auto atan = ATAN_TBL;
uint32_t i;
SwFixed j;
//Pseudorotations. with right shifts
for (i = 1, j = 1; i < ATAN_MAX; j <<= 1, ++i) {
if (v.y > 0) {
auto tmp = v.x + ((v.y + j) >> i);
v.y = v.y - ((v.x + j) >> i);
v.x = tmp;
theta += *atan++;
} else {
auto tmp = v.x - ((v.y + j) >> i);
v.y = v.y + ((v.x + j) >> i);
v.x = tmp;
theta -= *atan++;
}
}
//round theta
if (theta >= 0) theta = PAD_ROUND(theta, 32);
else theta = -PAD_ROUND(-theta, 32);
pt.x = v.x;
pt.y = theta;
}
static void _rotate(SwPoint& pt, SwFixed theta)
{
SwFixed x = pt.x;
SwFixed y = pt.y;
//Rotate inside [-PI/4, PI/4] sector
while (theta < -SW_ANGLE_PI4) {
auto tmp = y;
y = -x;
x = tmp;
theta += SW_ANGLE_PI2;
}
while (theta > SW_ANGLE_PI4) {
auto tmp = -y;
y = x;
x = tmp;
theta -= SW_ANGLE_PI2;
}
auto atan = ATAN_TBL;
uint32_t i;
SwFixed j;
for (i = 1, j = 1; i < ATAN_MAX; j <<= 1, ++i) {
if (theta < 0) {
auto tmp = x + ((y + j) >> i);
y = y - ((x + j) >> i);
x = tmp;
theta += *atan++;
} else {
auto tmp = x - ((y + j) >> i);
y = y + ((x + j) >> i);
x = tmp;
theta -= *atan++;
}
}
pt.x = static_cast<SwCoord>(x);
pt.y = static_cast<SwCoord>(y);
}
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
SwFixed mathMean(SwFixed angle1, SwFixed angle2)
{
return angle1 + mathDiff(angle1, angle2) / 2;
}
bool mathSmallCubic(SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, SwFixed& angleOut)
{
auto d1 = base[2] - base[3];
auto d2 = base[1] - base[2];
auto d3 = base[0] - base[1];
if (d1.small()) {
if (d2.small()) {
if (d3.small()) {
//basically a point.
//do nothing to retain original direction
} else {
angleIn = angleMid = angleOut = mathAtan(d3);
}
} else {
if (d3.small()) {
angleIn = angleMid = angleOut = mathAtan(d2);
} else {
angleIn = angleMid = mathAtan(d2);
angleOut = mathAtan(d3);
}
}
} else {
if (d2.small()) {
if (d3.small()) {
angleIn = angleMid = angleOut = mathAtan(d1);
} else {
angleIn = mathAtan(d1);
angleOut = mathAtan(d3);
angleMid = mathMean(angleIn, angleOut);
}
} else {
if (d3.small()) {
angleIn = mathAtan(d1);
angleMid = angleOut = mathAtan(d2);
} else {
angleIn = mathAtan(d1);
angleMid = mathAtan(d2);
angleOut = mathAtan(d3);
}
}
}
auto theta1 = abs(mathDiff(angleIn, angleMid));
auto theta2 = abs(mathDiff(angleMid, angleOut));
if ((theta1 < (SW_ANGLE_PI / 8)) && (theta2 < (SW_ANGLE_PI / 8))) return true;
return false;
}
int64_t mathMultiply(int64_t a, int64_t b)
{
int32_t s = 1;
//move sign
if (a < 0) {
a = -a;
s = -s;
}
if (b < 0) {
b = -b;
s = -s;
}
int64_t c = (a * b + 0x8000L ) >> 16;
return (s > 0) ? c : -c;
}
int64_t mathDivide(int64_t a, int64_t b)
{
int32_t s = 1;
//move sign
if (a < 0) {
a = -a;
s = -s;
}
if (b < 0) {
b = -b;
s = -s;
}
int64_t q = b > 0 ? ((a << 16) + (b >> 1)) / b : 0x7FFFFFFFL;
return (s < 0 ? -q : q);
}
int64_t mathMulDiv(int64_t a, int64_t b, int64_t c)
{
int32_t s = 1;
//move sign
if (a < 0) {
a = -a;
s = -s;
}
if (b < 0) {
b = -b;
s = -s;
}
if (c < 0) {
c = -c;
s = -s;
}
int64_t d = c > 0 ? (a * b + (c >> 1)) / c : 0x7FFFFFFFL;
return (s > 0 ? d : -d);
}
void mathRotate(SwPoint& pt, SwFixed angle)
{
if (angle == 0 || (pt.x == 0 && pt.y == 0)) return;
auto v = pt;
auto shift = _normalize(v);
auto theta = angle;
_rotate(v, theta);
v.x = _downscale(v.x);
v.y = _downscale(v.y);
if (shift > 0) {
auto half = static_cast<int32_t>(1L << (shift - 1));
pt.x = (v.x + half + SATURATE(v.x)) >> shift;
pt.y = (v.y + half + SATURATE(v.y)) >> shift;
} else {
shift = -shift;
pt.x = static_cast<SwCoord>((unsigned long)v.x << shift);
pt.y = static_cast<SwCoord>((unsigned long)v.y << shift);
}
}
SwFixed mathTan(SwFixed angle)
{
SwPoint v = {CORDIC_FACTOR >> 8, 0};
_rotate(v, angle);
return mathDivide(v.y, v.x);
}
SwFixed mathAtan(const SwPoint& pt)
{
if (pt.x == 0 && pt.y == 0) return 0;
auto v = pt;
_normalize(v);
_polarize(v);
return v.y;
}
SwFixed mathSin(SwFixed angle)
{
return mathCos(SW_ANGLE_PI2 - angle);
}
SwFixed mathCos(SwFixed angle)
{
SwPoint v = {CORDIC_FACTOR >> 8, 0};
_rotate(v, angle);
return (v.x + 0x80L) >> 8;
}
SwFixed mathLength(SwPoint& pt)
{
auto v = pt;
//trivial case
if (v.x == 0) return abs(v.y);
if (v.y == 0) return abs(v.x);
//general case
auto shift = _normalize(v);
_polarize(v);
v.x = _downscale(v.x);
if (shift > 0) return (v.x + (static_cast<SwFixed>(1) << (shift -1))) >> shift;
return static_cast<SwFixed>((uint32_t)v.x << -shift);
}
void mathSplitCubic(SwPoint* base)
{
SwCoord a, b, c, d;
base[6].x = base[3].x;
c = base[1].x;
d = base[2].x;
base[1].x = a = (base[0].x + c) / 2;
base[5].x = b = (base[3].x + d) / 2;
c = (c + d) / 2;
base[2].x = a = (a + c) / 2;
base[4].x = b = (b + c) / 2;
base[3].x = (a + b) / 2;
base[6].y = base[3].y;
c = base[1].y;
d = base[2].y;
base[1].y = a = (base[0].y + c) / 2;
base[5].y = b = (base[3].y + d) / 2;
c = (c + d) / 2;
base[2].y = a = (a + c) / 2;
base[4].y = b = (b + c) / 2;
base[3].y = (a + b) / 2;
}
SwFixed mathDiff(SwFixed angle1, SwFixed angle2)
{
auto delta = angle2 - angle1;
delta %= SW_ANGLE_2PI;
if (delta < 0) delta += SW_ANGLE_2PI;
if (delta > SW_ANGLE_PI) delta -= SW_ANGLE_2PI;
return delta;
}

View file

@ -0,0 +1,369 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "tvgSwCommon.h"
/************************************************************************/
/* Internal Class Implementation */
/************************************************************************/
static uint32_t _colorAlpha(uint32_t c)
{
return (c >> 24) & 0xff;
}
static uint32_t _abgrJoin(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
{
return (a << 24 | b << 16 | g << 8 | r);
}
static uint32_t _argbJoin(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
{
return (a << 24 | r << 16 | g << 8 | b);
}
static SwBBox _clipRegion(Surface* surface, SwBBox& in)
{
auto bbox = in;
if (bbox.min.x < 0) bbox.min.x = 0;
if (bbox.min.y < 0) bbox.min.y = 0;
if (bbox.max.x > static_cast<SwCoord>(surface->w)) bbox.max.x = surface->w;
if (bbox.max.y > static_cast<SwCoord>(surface->h)) bbox.max.y = surface->h;
return bbox;
}
static bool _rasterTranslucentRect(SwSurface* surface, const SwBBox& region, uint32_t color)
{
auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x;
auto h = static_cast<uint32_t>(region.max.y - region.min.y);
auto w = static_cast<uint32_t>(region.max.x - region.min.x);
auto ialpha = 255 - surface->comp.alpha(color);
for (uint32_t y = 0; y < h; ++y) {
auto dst = &buffer[y * surface->stride];
for (uint32_t x = 0; x < w; ++x) {
dst[x] = color + ALPHA_BLEND(dst[x], ialpha);
}
}
return true;
}
static bool _rasterSolidRect(SwSurface* surface, const SwBBox& region, uint32_t color)
{
auto buffer = surface->buffer + (region.min.y * surface->stride);
auto w = static_cast<uint32_t>(region.max.x - region.min.x);
auto h = static_cast<uint32_t>(region.max.y - region.min.y);
for (uint32_t y = 0; y < h; ++y) {
rasterRGBA32(buffer + y * surface->stride, color, region.min.x, w);
}
return true;
}
static bool _rasterTranslucentRle(SwSurface* surface, SwRleData* rle, uint32_t color)
{
if (!rle) return false;
auto span = rle->spans;
uint32_t src;
for (uint32_t i = 0; i < rle->size; ++i) {
auto dst = &surface->buffer[span->y * surface->stride + span->x];
if (span->coverage < 255) src = ALPHA_BLEND(color, span->coverage);
else src = color;
auto ialpha = 255 - surface->comp.alpha(src);
for (uint32_t i = 0; i < span->len; ++i) {
dst[i] = src + ALPHA_BLEND(dst[i], ialpha);
}
++span;
}
return true;
}
static bool _rasterSolidRle(SwSurface* surface, SwRleData* rle, uint32_t color)
{
if (!rle) return false;
auto span = rle->spans;
for (uint32_t i = 0; i < rle->size; ++i) {
if (span->coverage == 255) {
rasterRGBA32(surface->buffer + span->y * surface->stride, color, span->x, span->len);
} else {
auto dst = &surface->buffer[span->y * surface->stride + span->x];
auto src = ALPHA_BLEND(color, span->coverage);
auto ialpha = 255 - span->coverage;
for (uint32_t i = 0; i < span->len; ++i) {
dst[i] = src + ALPHA_BLEND(dst[i], ialpha);
}
}
++span;
}
return true;
}
static bool _rasterLinearGradientRect(SwSurface* surface, const SwBBox& region, const SwFill* fill)
{
if (!fill) return false;
auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x;
auto h = static_cast<uint32_t>(region.max.y - region.min.y);
auto w = static_cast<uint32_t>(region.max.x - region.min.x);
//Translucent Gradient
if (fill->translucent) {
auto tmpBuf = static_cast<uint32_t*>(alloca(surface->w * sizeof(uint32_t)));
if (!tmpBuf) return false;
for (uint32_t y = 0; y < h; ++y) {
auto dst = &buffer[y * surface->stride];
fillFetchLinear(fill, tmpBuf, region.min.y + y, region.min.x, 0, w);
for (uint32_t x = 0; x < w; ++x) {
dst[x] = tmpBuf[x] + ALPHA_BLEND(dst[x], 255 - surface->comp.alpha(tmpBuf[x]));
}
}
//Opaque Gradient
} else {
for (uint32_t y = 0; y < h; ++y) {
fillFetchLinear(fill, buffer + y * surface->stride, region.min.y + y, region.min.x, 0, w);
}
}
return true;
}
static bool _rasterRadialGradientRect(SwSurface* surface, const SwBBox& region, const SwFill* fill)
{
if (!fill) return false;
auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x;
auto h = static_cast<uint32_t>(region.max.y - region.min.y);
auto w = static_cast<uint32_t>(region.max.x - region.min.x);
//Translucent Gradient
if (fill->translucent) {
auto tmpBuf = static_cast<uint32_t*>(alloca(surface->w * sizeof(uint32_t)));
if (!tmpBuf) return false;
for (uint32_t y = 0; y < h; ++y) {
auto dst = &buffer[y * surface->stride];
fillFetchRadial(fill, tmpBuf, region.min.y + y, region.min.x, w);
for (uint32_t x = 0; x < w; ++x) {
dst[x] = tmpBuf[x] + ALPHA_BLEND(dst[x], 255 - surface->comp.alpha(tmpBuf[x]));
}
}
//Opaque Gradient
} else {
for (uint32_t y = 0; y < h; ++y) {
auto dst = &buffer[y * surface->stride];
fillFetchRadial(fill, dst, region.min.y + y, region.min.x, w);
}
}
return true;
}
static bool _rasterLinearGradientRle(SwSurface* surface, SwRleData* rle, const SwFill* fill)
{
if (!rle || !fill) return false;
auto buf = static_cast<uint32_t*>(alloca(surface->w * sizeof(uint32_t)));
if (!buf) return false;
auto span = rle->spans;
//Translucent Gradient
if (fill->translucent) {
for (uint32_t i = 0; i < rle->size; ++i) {
auto dst = &surface->buffer[span->y * surface->stride + span->x];
fillFetchLinear(fill, buf, span->y, span->x, 0, span->len);
if (span->coverage == 255) {
for (uint32_t i = 0; i < span->len; ++i) {
dst[i] = buf[i] + ALPHA_BLEND(dst[i], 255 - surface->comp.alpha(buf[i]));
}
} else {
for (uint32_t i = 0; i < span->len; ++i) {
auto tmp = ALPHA_BLEND(buf[i], span->coverage);
dst[i] = tmp + ALPHA_BLEND(dst[i], 255 - surface->comp.alpha(tmp));
}
}
++span;
}
//Opaque Gradient
} else {
for (uint32_t i = 0; i < rle->size; ++i) {
if (span->coverage == 255) {
fillFetchLinear(fill, surface->buffer + span->y * surface->stride, span->y, span->x, span->x, span->len);
} else {
auto dst = &surface->buffer[span->y * surface->stride + span->x];
fillFetchLinear(fill, buf, span->y, span->x, 0, span->len);
auto ialpha = 255 - span->coverage;
for (uint32_t i = 0; i < span->len; ++i) {
dst[i] = ALPHA_BLEND(buf[i], span->coverage) + ALPHA_BLEND(dst[i], ialpha);
}
}
++span;
}
}
return true;
}
static bool _rasterRadialGradientRle(SwSurface* surface, SwRleData* rle, const SwFill* fill)
{
if (!rle || !fill) return false;
auto buf = static_cast<uint32_t*>(alloca(surface->w * sizeof(uint32_t)));
if (!buf) return false;
auto span = rle->spans;
//Translucent Gradient
if (fill->translucent) {
for (uint32_t i = 0; i < rle->size; ++i) {
auto dst = &surface->buffer[span->y * surface->stride + span->x];
fillFetchRadial(fill, buf, span->y, span->x, span->len);
if (span->coverage == 255) {
for (uint32_t i = 0; i < span->len; ++i) {
dst[i] = buf[i] + ALPHA_BLEND(dst[i], 255 - surface->comp.alpha(buf[i]));
}
} else {
for (uint32_t i = 0; i < span->len; ++i) {
auto tmp = ALPHA_BLEND(buf[i], span->coverage);
dst[i] = tmp + ALPHA_BLEND(dst[i], 255 - surface->comp.alpha(tmp));
}
}
++span;
}
//Opaque Gradient
} else {
for (uint32_t i = 0; i < rle->size; ++i) {
auto dst = &surface->buffer[span->y * surface->stride + span->x];
if (span->coverage == 255) {
fillFetchRadial(fill, dst, span->y, span->x, span->len);
} else {
fillFetchRadial(fill, buf, span->y, span->x, span->len);
auto ialpha = 255 - span->coverage;
for (uint32_t i = 0; i < span->len; ++i) {
dst[i] = ALPHA_BLEND(buf[i], span->coverage) + ALPHA_BLEND(dst[i], ialpha);
}
}
++span;
}
}
return true;
}
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
bool rasterCompositor(SwSurface* surface)
{
if (surface->cs == SwCanvas::ABGR8888) {
surface->comp.alpha = _colorAlpha;
surface->comp.join = _abgrJoin;
} else if (surface->cs == SwCanvas::ARGB8888) {
surface->comp.alpha = _colorAlpha;
surface->comp.join = _argbJoin;
} else {
//What Color Space ???
return false;
}
return true;
}
bool rasterGradientShape(SwSurface* surface, SwShape* shape, unsigned id)
{
//Fast Track
if (shape->rect) {
auto region = _clipRegion(surface, shape->bbox);
if (id == FILL_ID_LINEAR) return _rasterLinearGradientRect(surface, region, shape->fill);
return _rasterRadialGradientRect(surface, region, shape->fill);
} else {
if (id == FILL_ID_LINEAR) return _rasterLinearGradientRle(surface, shape->rle, shape->fill);
return _rasterRadialGradientRle(surface, shape->rle, shape->fill);
}
return false;
}
bool rasterSolidShape(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
{
r = ALPHA_MULTIPLY(r, a);
g = ALPHA_MULTIPLY(g, a);
b = ALPHA_MULTIPLY(b, a);
auto color = surface->comp.join(r, g, b, a);
//Fast Track
if (shape->rect) {
auto region = _clipRegion(surface, shape->bbox);
if (a == 255) return _rasterSolidRect(surface, region, color);
return _rasterTranslucentRect(surface, region, color);
} else{
if (a == 255) return _rasterSolidRle(surface, shape->rle, color);
return _rasterTranslucentRle(surface, shape->rle, color);
}
return false;
}
bool rasterStroke(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
{
r = ALPHA_MULTIPLY(r, a);
g = ALPHA_MULTIPLY(g, a);
b = ALPHA_MULTIPLY(b, a);
auto color = surface->comp.join(r, g, b, a);
if (a == 255) return _rasterSolidRle(surface, shape->strokeRle, color);
return _rasterTranslucentRle(surface, shape->strokeRle, color);
}
bool rasterClear(SwSurface* surface)
{
if (!surface || !surface->buffer || surface->stride <= 0 || surface->w <= 0 || surface->h <= 0) return false;
if (surface->w == surface->stride) {
rasterRGBA32(surface->buffer, 0x00000000, 0, surface->w * surface->h);
} else {
for (uint32_t i = 0; i < surface->h; i++) {
rasterRGBA32(surface->buffer + surface->stride * i, 0x00000000, 0, surface->w);
}
}
return true;
}

View file

@ -0,0 +1,185 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "tvgSwCommon.h"
#include "tvgSwRenderer.h"
/************************************************************************/
/* Internal Class Implementation */
/************************************************************************/
static RenderInitializer renderInit;
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
SwRenderer::~SwRenderer()
{
if (surface) delete(surface);
}
bool SwRenderer::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h, uint32_t cs)
{
if (!buffer || stride == 0 || w == 0 || h == 0) return false;
if (!surface) {
surface = new SwSurface;
if (!surface) return false;
}
surface->buffer = buffer;
surface->stride = stride;
surface->w = w;
surface->h = h;
surface->cs = cs;
return rasterCompositor(surface);
}
bool SwRenderer::preRender()
{
return rasterClear(surface);
}
bool SwRenderer::render(const Shape& sdata, TVG_UNUSED void *data)
{
auto shape = static_cast<SwShape*>(data);
if (!shape) return false;
uint8_t r, g, b, a;
if (auto fill = sdata.fill()) {
rasterGradientShape(surface, shape, fill->id());
} else{
sdata.fill(&r, &g, &b, &a);
if (a > 0) rasterSolidShape(surface, shape, r, g, b, a);
}
sdata.strokeColor(&r, &g, &b, &a);
if (a > 0) rasterStroke(surface, shape, r, g, b, a);
return true;
}
bool SwRenderer::dispose(TVG_UNUSED const Shape& sdata, void *data)
{
auto shape = static_cast<SwShape*>(data);
if (!shape) return false;
shapeFree(shape);
return true;
}
void* SwRenderer::prepare(const Shape& sdata, void* data, const RenderTransform* transform, RenderUpdateFlag flags)
{
//prepare shape data
auto shape = static_cast<SwShape*>(data);
if (!shape) {
shape = static_cast<SwShape*>(calloc(1, sizeof(SwShape)));
if (!shape) return nullptr;
}
if (flags == RenderUpdateFlag::None) return shape;
SwSize clip = {static_cast<SwCoord>(surface->w), static_cast<SwCoord>(surface->h)};
//Valid Stroking?
uint8_t strokeAlpha = 0;
auto strokeWidth = sdata.strokeWidth();
if (strokeWidth > FLT_EPSILON) {
sdata.strokeColor(nullptr, nullptr, nullptr, &strokeAlpha);
}
const Matrix* matrix = (transform ? &transform->m : nullptr);
//Shape
if (flags & (RenderUpdateFlag::Path | RenderUpdateFlag::Transform)) {
shapeReset(shape);
uint8_t alpha = 0;
sdata.fill(nullptr, nullptr, nullptr, &alpha);
bool renderShape = (alpha > 0 || sdata.fill());
if (renderShape || strokeAlpha) {
if (!shapePrepare(shape, &sdata, clip, matrix)) return shape;
if (renderShape) {
auto antiAlias = (strokeAlpha > 0 && strokeWidth >= 2) ? false : true;
if (!shapeGenRle(shape, &sdata, clip, antiAlias)) return shape;
}
}
//Fill
if (flags & (RenderUpdateFlag::Gradient | RenderUpdateFlag::Transform)) {
auto fill = sdata.fill();
if (fill) {
auto ctable = (flags & RenderUpdateFlag::Gradient) ? true : false;
if (ctable) shapeResetFill(shape);
if (!shapeGenFillColors(shape, fill, matrix, surface, ctable)) return shape;
} else {
shapeDelFill(shape);
}
}
//Stroke
if (flags & (RenderUpdateFlag::Stroke | RenderUpdateFlag::Transform)) {
if (strokeAlpha > 0) {
shapeResetStroke(shape, &sdata, matrix);
if (!shapeGenStrokeRle(shape, &sdata, matrix, clip)) return shape;
} else {
shapeDelStroke(shape);
}
}
shapeDelOutline(shape);
}
return shape;
}
int SwRenderer::init()
{
return RenderInitializer::init(renderInit, new SwRenderer);
}
int SwRenderer::term()
{
return RenderInitializer::term(renderInit);
}
uint32_t SwRenderer::unref()
{
return RenderInitializer::unref(renderInit);
}
uint32_t SwRenderer::ref()
{
return RenderInitializer::ref(renderInit);
}
SwRenderer* SwRenderer::inst()
{
//We know renderer type, avoid dynamic_cast for performance.
return static_cast<SwRenderer*>(RenderInitializer::inst(renderInit));
}

View file

@ -0,0 +1,54 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef _TVG_SW_RENDERER_H_
#define _TVG_SW_RENDERER_H_
struct SwSurface;
namespace tvg
{
class SwRenderer : public RenderMethod
{
public:
void* prepare(const Shape& shape, void* data, const RenderTransform* transform, RenderUpdateFlag flags) override;
bool dispose(const Shape& shape, void *data) override;
bool preRender() override;
bool render(const Shape& shape, void *data) override;
bool target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h, uint32_t cs);
uint32_t ref() override;
uint32_t unref() override;
static SwRenderer* inst();
static int init();
static int term();
private:
SwSurface* surface = nullptr;
SwRenderer(){};
~SwRenderer();
};
}
#endif /* _TVG_SW_RENDERER_H_ */

View file

@ -0,0 +1,754 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <setjmp.h>
#include <limits.h>
#include <memory.h>
#include "tvgSwCommon.h"
/************************************************************************/
/* Internal Class Implementation */
/************************************************************************/
constexpr auto MAX_SPANS = 256;
constexpr auto PIXEL_BITS = 8; //must be at least 6 bits!
constexpr auto ONE_PIXEL = (1L << PIXEL_BITS);
using Area = long;
struct Band
{
SwCoord min, max;
};
struct Cell
{
SwCoord x;
SwCoord cover;
Area area;
Cell *next;
};
struct RleWorker
{
SwRleData* rle;
SwPoint cellPos;
SwPoint cellMin;
SwPoint cellMax;
SwCoord cellXCnt;
SwCoord cellYCnt;
Area area;
SwCoord cover;
Cell* cells;
ptrdiff_t maxCells;
ptrdiff_t cellsCnt;
SwPoint pos;
SwPoint bezStack[32 * 3 + 1];
int levStack[32];
SwOutline* outline;
SwSpan spans[MAX_SPANS];
int spansCnt;
int ySpan;
int bandSize;
int bandShoot;
jmp_buf jmpBuf;
void* buffer;
long bufferSize;
Cell** yCells;
SwCoord yCnt;
SwSize clip;
bool invalid;
bool antiAlias;
};
static inline SwPoint UPSCALE(const SwPoint& pt)
{
return {pt.x << (PIXEL_BITS - 6), pt.y << (PIXEL_BITS - 6)};
}
static inline SwPoint DOWNSCALE(const SwPoint& pt)
{
return {pt.x >> (PIXEL_BITS - 6), pt.y >> (PIXEL_BITS - 6)};
}
static inline SwPoint TRUNC(const SwPoint& pt)
{
return {pt.x >> PIXEL_BITS, pt.y >> PIXEL_BITS};
}
static inline SwCoord TRUNC(const SwCoord x)
{
return x >> PIXEL_BITS;
}
static inline SwPoint SUBPIXELS(const SwPoint& pt)
{
return {pt.x << PIXEL_BITS, pt.y << PIXEL_BITS};
}
static inline SwCoord SUBPIXELS(const SwCoord x)
{
return (x << PIXEL_BITS);
}
/*
* Approximate sqrt(x*x+y*y) using the `alpha max plus beta min'
* algorithm. We use alpha = 1, beta = 3/8, giving us results with a
* largest error less than 7% compared to the exact value.
*/
static inline SwCoord HYPOT(SwPoint pt)
{
if (pt.x < 0) pt.x = -pt.x;
if (pt.y < 0) pt.y = -pt.y;
return ((pt.x > pt.y) ? (pt.x + (3 * pt.y >> 3)) : (pt.y + (3 * pt.x >> 3)));
}
static void _genSpan(SwRleData* rle, SwSpan* spans, uint32_t count)
{
auto newSize = rle->size + count;
/* allocate enough memory for new spans */
/* alloc is required to prevent free and reallocation */
/* when the rle needs to be regenerated because of attribute change. */
if (rle->alloc < newSize) {
rle->alloc = (newSize * 2);
rle->spans = static_cast<SwSpan*>(realloc(rle->spans, rle->alloc * sizeof(SwSpan)));
}
//copy the new spans to the allocated memory
SwSpan* lastSpan = rle->spans + rle->size;
memcpy(lastSpan, spans, count * sizeof(SwSpan));
rle->size = newSize;
}
static void _horizLine(RleWorker& rw, SwCoord x, SwCoord y, SwCoord area, SwCoord acount)
{
x += rw.cellMin.x;
y += rw.cellMin.y;
//Clip Y range
if (y < 0) return;
if (y >= rw.clip.h) return;
/* compute the coverage line's coverage, depending on the outline fill rule */
/* the coverage percentage is area/(PIXEL_BITS*PIXEL_BITS*2) */
auto coverage = static_cast<int>(area >> (PIXEL_BITS * 2 + 1 - 8)); //range 0 - 256
if (coverage < 0) coverage = -coverage;
if (rw.outline->fillMode == SW_OUTLINE_FILL_EVEN_ODD) {
coverage &= 511;
if (coverage > 256) coverage = 512 - coverage;
else if (coverage == 256) coverage = 255;
} else {
//normal non-zero winding rule
if (coverage >= 256) coverage = 255;
}
//span has ushort coordinates. check limit overflow
if (x >= SHRT_MAX) {
//LOG: x coordinate overflow!
x = SHRT_MAX;
}
if (y >= SHRT_MAX) {
//LOG: y coordinate overflow!
y = SHRT_MAX;
}
if (coverage > 0) {
if (!rw.antiAlias) coverage = 255;
auto count = rw.spansCnt;
auto span = rw.spans + count - 1;
//see whether we can add this span to the current list
if ((count > 0) && (rw.ySpan == y) &&
(span->x + span->len == x) && (span->coverage == coverage)) {
//Clip x range
SwCoord xOver = 0;
if (x + acount >= rw.clip.w) xOver -= (x + acount - rw.clip.w);
if (x < 0) xOver += x;
//span->len += (acount + xOver) - 1;
span->len += (acount + xOver);
return;
}
if (count >= MAX_SPANS) {
_genSpan(rw.rle, rw.spans, count);
rw.spansCnt = 0;
rw.ySpan = 0;
span = rw.spans;
} else {
++span;
}
//Clip x range
SwCoord xOver = 0;
if (x + acount >= rw.clip.w) xOver -= (x + acount - rw.clip.w);
if (x < 0) {
xOver += x;
x = 0;
}
//Nothing to draw
if (acount + xOver <= 0) return;
//add a span to the current list
span->x = x;
span->y = y;
span->len = (acount + xOver);
span->coverage = coverage;
++rw.spansCnt;
rw.ySpan = y;
}
}
static void _sweep(RleWorker& rw)
{
if (rw.cellsCnt == 0) return;
rw.spansCnt = 0;
rw.ySpan = 0;
for (int y = 0; y < rw.yCnt; ++y) {
auto cover = 0;
auto x = 0;
auto cell = rw.yCells[y];
while (cell) {
if (cell->x > x && cover != 0) _horizLine(rw, x, y, cover * (ONE_PIXEL * 2), cell->x - x);
cover += cell->cover;
auto area = cover * (ONE_PIXEL * 2) - cell->area;
if (area != 0 && cell->x >= 0) _horizLine(rw, cell->x, y, area, 1);
x = cell->x + 1;
cell = cell->next;
}
if (cover != 0) _horizLine(rw, x, y, cover * (ONE_PIXEL * 2), rw.cellXCnt - x);
}
if (rw.spansCnt > 0) _genSpan(rw.rle, rw.spans, rw.spansCnt);
}
static Cell* _findCell(RleWorker& rw)
{
auto x = rw.cellPos.x;
if (x > rw.cellXCnt) x = rw.cellXCnt;
auto pcell = &rw.yCells[rw.cellPos.y];
while(true) {
Cell* cell = *pcell;
if (!cell || cell->x > x) break;
if (cell->x == x) return cell;
pcell = &cell->next;
}
if (rw.cellsCnt >= rw.maxCells) longjmp(rw.jmpBuf, 1);
auto cell = rw.cells + rw.cellsCnt++;
cell->x = x;
cell->area = 0;
cell->cover = 0;
cell->next = *pcell;
*pcell = cell;
return cell;
}
static void _recordCell(RleWorker& rw)
{
if (rw.area | rw.cover) {
auto cell = _findCell(rw);
cell->area += rw.area;
cell->cover += rw.cover;
}
}
static void _setCell(RleWorker& rw, SwPoint pos)
{
/* Move the cell pointer to a new position. We set the `invalid' */
/* flag to indicate that the cell isn't part of those we're interested */
/* in during the render phase. This means that: */
/* */
/* . the new vertical position must be within min_ey..max_ey-1. */
/* . the new horizontal position must be strictly less than max_ex */
/* */
/* Note that if a cell is to the left of the clipping region, it is */
/* actually set to the (min_ex-1) horizontal position. */
/* All cells that are on the left of the clipping region go to the
min_ex - 1 horizontal position. */
pos.y -= rw.cellMin.y;
if (pos.x > rw.cellMax.x) pos.x = rw.cellMax.x;
pos.x -= rw.cellMin.x;
if (pos.x < 0) pos.x = -1;
//Are we moving to a different cell?
if (pos != rw.cellPos) {
//Record the current one if it is valid
if (!rw.invalid) _recordCell(rw);
}
rw.area = 0;
rw.cover = 0;
rw.cellPos = pos;
rw.invalid = ((unsigned)pos.y >= (unsigned)rw.cellYCnt || pos.x >= rw.cellXCnt);
}
static void _startCell(RleWorker& rw, SwPoint pos)
{
if (pos.x > rw.cellMax.x) pos.x = rw.cellMax.x;
if (pos.x < rw.cellMin.x) pos.x = rw.cellMin.x;
//if (pos.x < rw.cellMin.x) pos.x = (rw.cellMin.x - 1);
rw.area = 0;
rw.cover = 0;
rw.cellPos = pos - rw.cellMin;
rw.invalid = false;
_setCell(rw, pos);
}
static void _moveTo(RleWorker& rw, const SwPoint& to)
{
//record current cell, if any */
if (!rw.invalid) _recordCell(rw);
//start to a new position
_startCell(rw, TRUNC(to));
rw.pos = to;
}
static void _lineTo(RleWorker& rw, const SwPoint& to)
{
#define SW_UDIV(a, b) \
static_cast<SwCoord>(((unsigned long)(a) * (unsigned long)(b)) >> \
(sizeof(long) * CHAR_BIT - PIXEL_BITS))
auto e1 = TRUNC(rw.pos);
auto e2 = TRUNC(to);
//vertical clipping
if ((e1.y >= rw.cellMax.y && e2.y >= rw.cellMax.y) ||
(e1.y < rw.cellMin.y && e2.y < rw.cellMin.y)) {
rw.pos = to;
return;
}
auto diff = to - rw.pos;
auto f1 = rw.pos - SUBPIXELS(e1);
SwPoint f2;
//inside one cell
if (e1 == e2) {
;
//any horizontal line
} else if (diff.y == 0) {
e1.x = e2.x;
_setCell(rw, e1);
} else if (diff.x == 0) {
//vertical line up
if (diff.y > 0) {
do {
f2.y = ONE_PIXEL;
rw.cover += (f2.y - f1.y);
rw.area += (f2.y - f1.y) * f1.x * 2;
f1.y = 0;
++e1.y;
_setCell(rw, e1);
} while(e1.y != e2.y);
//vertical line down
} else {
do {
f2.y = 0;
rw.cover += (f2.y - f1.y);
rw.area += (f2.y - f1.y) * f1.x * 2;
f1.y = ONE_PIXEL;
--e1.y;
_setCell(rw, e1);
} while(e1.y != e2.y);
}
//any other line
} else {
Area prod = diff.x * f1.y - diff.y * f1.x;
/* These macros speed up repetitive divisions by replacing them
with multiplications and right shifts. */
auto dx_r = static_cast<long>(ULONG_MAX >> PIXEL_BITS) / (diff.x);
auto dy_r = static_cast<long>(ULONG_MAX >> PIXEL_BITS) / (diff.y);
/* The fundamental value `prod' determines which side and the */
/* exact coordinate where the line exits current cell. It is */
/* also easily updated when moving from one cell to the next. */
do {
auto px = diff.x * ONE_PIXEL;
auto py = diff.y * ONE_PIXEL;
//left
if (prod <= 0 && prod - px > 0) {
f2 = {0, SW_UDIV(-prod, -dx_r)};
prod -= py;
rw.cover += (f2.y - f1.y);
rw.area += (f2.y - f1.y) * (f1.x + f2.x);
f1 = {ONE_PIXEL, f2.y};
--e1.x;
//up
} else if (prod - px <= 0 && prod - px + py > 0) {
prod -= px;
f2 = {SW_UDIV(-prod, dy_r), ONE_PIXEL};
rw.cover += (f2.y - f1.y);
rw.area += (f2.y - f1.y) * (f1.x + f2.x);
f1 = {f2.x, 0};
++e1.y;
//right
} else if (prod - px + py <= 0 && prod + py >= 0) {
prod += py;
f2 = {ONE_PIXEL, SW_UDIV(prod, dx_r)};
rw.cover += (f2.y - f1.y);
rw.area += (f2.y - f1.y) * (f1.x + f2.x);
f1 = {0, f2.y};
++e1.x;
//down
} else {
f2 = {SW_UDIV(prod, -dy_r), 0};
prod += px;
rw.cover += (f2.y - f1.y);
rw.area += (f2.y - f1.y) * (f1.x + f2.x);
f1 = {f2.x, ONE_PIXEL};
--e1.y;
}
_setCell(rw, e1);
} while(e1 != e2);
}
f2 = {to.x - SUBPIXELS(e2.x), to.y - SUBPIXELS(e2.y)};
rw.cover += (f2.y - f1.y);
rw.area += (f2.y - f1.y) * (f1.x + f2.x);
rw.pos = to;
}
static void _cubicTo(RleWorker& rw, const SwPoint& ctrl1, const SwPoint& ctrl2, const SwPoint& to)
{
auto arc = rw.bezStack;
arc[0] = to;
arc[1] = ctrl2;
arc[2] = ctrl1;
arc[3] = rw.pos;
//Short-cut the arc that crosses the current band
auto min = arc[0].y;
auto max = arc[0].y;
SwCoord y;
for (auto i = 1; i < 4; ++i) {
y = arc[i].y;
if (y < min) min = y;
if (y > max) max = y;
}
if (TRUNC(min) >= rw.cellMax.y || TRUNC(max) < rw.cellMin.y) goto draw;
/* Decide whether to split or draw. See `Rapid Termination */
/* Evaluation for Recursive Subdivision of Bezier Curves' by Thomas */
/* F. Hain, at */
/* http://www.cis.southalabama.edu/~hain/general/Publications/Bezier/Camera-ready%20CISST02%202.pdf */
while (true) {
{
//diff is the P0 - P3 chord vector
auto diff = arc[3] - arc[0];
auto L = HYPOT(diff);
//avoid possible arithmetic overflow below by splitting
if (L > SHRT_MAX) goto split;
//max deviation may be as much as (s/L) * 3/4 (if Hain's v = 1)
auto sLimit = L * (ONE_PIXEL / 6);
auto diff1 = arc[1] - arc[0];
auto s = diff.y * diff1.x - diff.x * diff1.y;
if (s < 0) s = -s;
if (s > sLimit) goto split;
//s is L * the perpendicular distance from P2 to the line P0 - P3
auto diff2 = arc[2] - arc[0];
s = diff.y * diff2.x - diff.x * diff2.y;
if (s < 0) s = -s;
if (s > sLimit) goto split;
/* Split super curvy segments where the off points are so far
from the chord that the angles P0-P1-P3 or P0-P2-P3 become
acute as detected by appropriate dot products */
if (diff1.x * (diff1.x - diff.x) + diff1.y * (diff1.y - diff.y) > 0 ||
diff2.x * (diff2.x - diff.x) + diff2.y * (diff2.y - diff.y) > 0)
goto split;
//no reason to split
goto draw;
}
split:
mathSplitCubic(arc);
arc += 3;
continue;
draw:
_lineTo(rw, arc[0]);
if (arc == rw.bezStack) return;
arc -= 3;
}
}
static bool _decomposeOutline(RleWorker& rw)
{
auto outline = rw.outline;
auto first = 0; //index of first point in contour
for (uint32_t n = 0; n < outline->cntrsCnt; ++n) {
auto last = outline->cntrs[n];
auto limit = outline->pts + last;
auto start = UPSCALE(outline->pts[first]);
auto pt = outline->pts + first;
auto types = outline->types + first;
/* A contour cannot start with a cubic control point! */
if (types[0] == SW_CURVE_TYPE_CUBIC) goto invalid_outline;
_moveTo(rw, UPSCALE(outline->pts[first]));
while (pt < limit) {
++pt;
++types;
//emit a single line_to
if (types[0] == SW_CURVE_TYPE_POINT) {
_lineTo(rw, UPSCALE(*pt));
//types cubic
} else {
if (pt + 1 > limit || types[1] != SW_CURVE_TYPE_CUBIC)
goto invalid_outline;
pt += 2;
types += 2;
if (pt <= limit) {
_cubicTo(rw, UPSCALE(pt[-2]), UPSCALE(pt[-1]), UPSCALE(pt[0]));
continue;
}
_cubicTo(rw, UPSCALE(pt[-2]), UPSCALE(pt[-1]), start);
goto close;
}
}
_lineTo(rw, start);
close:
first = last + 1;
}
return true;
invalid_outline:
//LOG: Invalid Outline!
return false;
}
static int _genRle(RleWorker& rw)
{
if (setjmp(rw.jmpBuf) == 0) {
auto ret = _decomposeOutline(rw);
if (!rw.invalid) _recordCell(rw);
if (ret) return 0; //success
else return 1; //fail
}
return -1; //lack of cell memory
}
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
SwRleData* rleRender(const SwOutline* outline, const SwBBox& bbox, const SwSize& clip, bool antiAlias)
{
constexpr auto RENDER_POOL_SIZE = 16384L;
constexpr auto BAND_SIZE = 40;
//TODO: We can preserve several static workers in advance
RleWorker rw;
Cell buffer[RENDER_POOL_SIZE / sizeof(Cell)];
//Init Cells
rw.buffer = buffer;
rw.bufferSize = sizeof(buffer);
rw.yCells = reinterpret_cast<Cell**>(buffer);
rw.cells = nullptr;
rw.maxCells = 0;
rw.cellsCnt = 0;
rw.area = 0;
rw.cover = 0;
rw.invalid = true;
rw.cellMin = bbox.min;
rw.cellMax = bbox.max;
rw.cellXCnt = rw.cellMax.x - rw.cellMin.x;
rw.cellYCnt = rw.cellMax.y - rw.cellMin.y;
rw.ySpan = 0;
rw.outline = const_cast<SwOutline*>(outline);
rw.bandSize = rw.bufferSize / (sizeof(Cell) * 8); //bandSize: 64
rw.bandShoot = 0;
rw.clip = clip;
rw.antiAlias = antiAlias;
rw.rle = reinterpret_cast<SwRleData*>(calloc(1, sizeof(SwRleData)));
//Generate RLE
Band bands[BAND_SIZE];
Band* band;
/* set up vertical bands */
auto bandCnt = static_cast<int>((rw.cellMax.y - rw.cellMin.y) / rw.bandSize);
if (bandCnt == 0) bandCnt = 1;
else if (bandCnt >= BAND_SIZE) bandCnt = (BAND_SIZE - 1);
auto min = rw.cellMin.y;
auto yMax = rw.cellMax.y;
SwCoord max;
int ret;
for (int n = 0; n < bandCnt; ++n, min = max) {
max = min + rw.bandSize;
if (n == bandCnt -1 || max > yMax) max = yMax;
bands[0].min = min;
bands[0].max = max;
band = bands;
while (band >= bands) {
rw.yCells = static_cast<Cell**>(rw.buffer);
rw.yCnt = band->max - band->min;
int cellStart = sizeof(Cell*) * (int)rw.yCnt;
int cellMod = cellStart % sizeof(Cell);
if (cellMod > 0) cellStart += sizeof(Cell) - cellMod;
auto cellEnd = rw.bufferSize;
cellEnd -= cellEnd % sizeof(Cell);
auto cellsMax = reinterpret_cast<Cell*>((char*)rw.buffer + cellEnd);
rw.cells = reinterpret_cast<Cell*>((char*)rw.buffer + cellStart);
if (rw.cells >= cellsMax) goto reduce_bands;
rw.maxCells = cellsMax - rw.cells;
if (rw.maxCells < 2) goto reduce_bands;
for (int y = 0; y < rw.yCnt; ++y)
rw.yCells[y] = nullptr;
rw.cellsCnt = 0;
rw.invalid = true;
rw.cellMin.y = band->min;
rw.cellMax.y = band->max;
rw.cellYCnt = band->max - band->min;
ret = _genRle(rw);
if (ret == 0) {
_sweep(rw);
--band;
continue;
} else if (ret == 1) {
goto error;
}
reduce_bands:
/* render pool overflow: we will reduce the render band by half */
auto bottom = band->min;
auto top = band->max;
auto middle = bottom + ((top - bottom) >> 1);
/* This is too complex for a single scanline; there must
be some problems */
if (middle == bottom) goto error;
if (bottom - top >= rw.bandSize) ++rw.bandShoot;
band[1].min = bottom;
band[1].max = middle;
band[0].min = middle;
band[0].max = top;
++band;
}
}
if (rw.bandShoot > 8 && rw.bandSize > 16)
rw.bandSize = (rw.bandSize >> 1);
return rw.rle;
error:
free(rw.rle);
rw.rle = nullptr;
return nullptr;
}
void rleFree(SwRleData* rle)
{
if (!rle) return;
if (rle->spans) free(rle->spans);
free(rle);
}

View file

@ -0,0 +1,672 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "tvgSwCommon.h"
/************************************************************************/
/* Internal Class Implementation */
/************************************************************************/
struct Line
{
Point pt1;
Point pt2;
};
static SwPoint _transform(const Point* to, const Matrix* transform)
{
if (!transform) return {TO_SWCOORD(to->x), TO_SWCOORD(to->y)};
auto tx = round(to->x * transform->e11 + to->y * transform->e12 + transform->e13);
auto ty = round(to->x * transform->e21 + to->y * transform->e22 + transform->e23);
return {TO_SWCOORD(tx), TO_SWCOORD(ty)};
}
static float _lineLength(const Point& pt1, const Point& pt2)
{
/* approximate sqrt(x*x + y*y) using alpha max plus beta min algorithm.
With alpha = 1, beta = 3/8, giving results with the largest error less
than 7% compared to the exact value. */
Point diff = {pt2.x - pt1.x, pt2.y - pt1.y};
if (diff.x < 0) diff.x = -diff.x;
if (diff.y < 0) diff.y = -diff.y;
return (diff.x > diff.y) ? (diff.x + diff.y * 0.375f) : (diff.y + diff.x * 0.375f);
}
static void _lineSplitAt(const Line& cur, float at, Line& left, Line& right)
{
auto len = _lineLength(cur.pt1, cur.pt2);
auto dx = ((cur.pt2.x - cur.pt1.x) / len) * at;
auto dy = ((cur.pt2.y - cur.pt1.y) / len) * at;
left.pt1 = cur.pt1;
left.pt2.x = left.pt1.x + dx;
left.pt2.y = left.pt1.y + dy;
right.pt1 = left.pt2;
right.pt2 = cur.pt2;
}
static void _growOutlineContour(SwOutline& outline, uint32_t n)
{
if (outline.reservedCntrsCnt >= outline.cntrsCnt + n) return;
outline.reservedCntrsCnt = outline.cntrsCnt + n;
outline.cntrs = static_cast<uint32_t*>(realloc(outline.cntrs, outline.reservedCntrsCnt * sizeof(uint32_t)));
}
static void _growOutlinePoint(SwOutline& outline, uint32_t n)
{
if (outline.reservedPtsCnt >= outline.ptsCnt + n) return;
outline.reservedPtsCnt = outline.ptsCnt + n;
outline.pts = static_cast<SwPoint*>(realloc(outline.pts, outline.reservedPtsCnt * sizeof(SwPoint)));
outline.types = static_cast<uint8_t*>(realloc(outline.types, outline.reservedPtsCnt * sizeof(uint8_t)));
}
static void _delOutline(SwOutline* outline)
{
if (!outline) return;
if (outline->cntrs) free(outline->cntrs);
if (outline->pts) free(outline->pts);
if (outline->types) free(outline->types);
free(outline);
}
static void _outlineEnd(SwOutline& outline)
{
_growOutlineContour(outline, 1);
if (outline.ptsCnt > 0) {
outline.cntrs[outline.cntrsCnt] = outline.ptsCnt - 1;
++outline.cntrsCnt;
}
}
static void _outlineMoveTo(SwOutline& outline, const Point* to, const Matrix* transform)
{
_growOutlinePoint(outline, 1);
outline.pts[outline.ptsCnt] = _transform(to, transform);
outline.types[outline.ptsCnt] = SW_CURVE_TYPE_POINT;
if (outline.ptsCnt > 0) {
_growOutlineContour(outline, 1);
outline.cntrs[outline.cntrsCnt] = outline.ptsCnt - 1;
++outline.cntrsCnt;
}
++outline.ptsCnt;
}
static void _outlineLineTo(SwOutline& outline, const Point* to, const Matrix* transform)
{
_growOutlinePoint(outline, 1);
outline.pts[outline.ptsCnt] = _transform(to, transform);
outline.types[outline.ptsCnt] = SW_CURVE_TYPE_POINT;
++outline.ptsCnt;
}
static void _outlineCubicTo(SwOutline& outline, const Point* ctrl1, const Point* ctrl2, const Point* to, const Matrix* transform)
{
_growOutlinePoint(outline, 3);
outline.pts[outline.ptsCnt] = _transform(ctrl1, transform);
outline.types[outline.ptsCnt] = SW_CURVE_TYPE_CUBIC;
++outline.ptsCnt;
outline.pts[outline.ptsCnt] = _transform(ctrl2, transform);
outline.types[outline.ptsCnt] = SW_CURVE_TYPE_CUBIC;
++outline.ptsCnt;
outline.pts[outline.ptsCnt] = _transform(to, transform);
outline.types[outline.ptsCnt] = SW_CURVE_TYPE_POINT;
++outline.ptsCnt;
}
static void _outlineClose(SwOutline& outline)
{
uint32_t i = 0;
if (outline.cntrsCnt > 0) {
i = outline.cntrs[outline.cntrsCnt - 1] + 1;
} else {
i = 0; //First Path
}
//Make sure there is at least one point in the current path
if (outline.ptsCnt == i) {
outline.opened = true;
return;
}
//Close the path
_growOutlinePoint(outline, 1);
outline.pts[outline.ptsCnt] = outline.pts[i];
outline.types[outline.ptsCnt] = SW_CURVE_TYPE_POINT;
++outline.ptsCnt;
outline.opened = false;
}
static void _initBBox(SwBBox& bbox)
{
bbox.min.x = bbox.min.y = 0;
bbox.max.x = bbox.max.y = 0;
}
static bool _updateBBox(SwOutline* outline, SwBBox& bbox)
{
if (!outline) return false;
auto pt = outline->pts;
if (outline->ptsCnt <= 0) {
_initBBox(bbox);
return false;
}
auto xMin = pt->x;
auto xMax = pt->x;
auto yMin = pt->y;
auto yMax = pt->y;
++pt;
for(uint32_t i = 1; i < outline->ptsCnt; ++i, ++pt) {
if (xMin > pt->x) xMin = pt->x;
if (xMax < pt->x) xMax = pt->x;
if (yMin > pt->y) yMin = pt->y;
if (yMax < pt->y) yMax = pt->y;
}
bbox.min.x = xMin >> 6;
bbox.max.x = (xMax + 63) >> 6;
bbox.min.y = yMin >> 6;
bbox.max.y = (yMax + 63) >> 6;
if (xMax - xMin < 1 && yMax - yMin < 1) return false;
return true;
}
static bool _checkValid(const SwOutline* outline, const SwBBox& bbox, const SwSize& clip)
{
if (outline->ptsCnt == 0 || outline->cntrsCnt <= 0) return false;
//Check boundary
if (bbox.min.x >= clip.w || bbox.min.y >= clip.h || bbox.max.x <= 0 || bbox.max.y <= 0) return false;
return true;
}
static void _dashLineTo(SwDashStroke& dash, const Point* to, const Matrix* transform)
{
_growOutlinePoint(*dash.outline, dash.outline->ptsCnt >> 1);
_growOutlineContour(*dash.outline, dash.outline->cntrsCnt >> 1);
Line cur = {dash.ptCur, *to};
auto len = _lineLength(cur.pt1, cur.pt2);
if (len < dash.curLen) {
dash.curLen -= len;
if (!dash.curOpGap) {
_outlineMoveTo(*dash.outline, &dash.ptCur, transform);
_outlineLineTo(*dash.outline, to, transform);
}
} else {
while (len > dash.curLen) {
len -= dash.curLen;
Line left, right;
_lineSplitAt(cur, dash.curLen, left, right);;
dash.curIdx = (dash.curIdx + 1) % dash.cnt;
if (!dash.curOpGap) {
_outlineMoveTo(*dash.outline, &left.pt1, transform);
_outlineLineTo(*dash.outline, &left.pt2, transform);
}
dash.curLen = dash.pattern[dash.curIdx];
dash.curOpGap = !dash.curOpGap;
cur = right;
dash.ptCur = cur.pt1;
}
//leftovers
dash.curLen -= len;
if (!dash.curOpGap) {
_outlineMoveTo(*dash.outline, &cur.pt1, transform);
_outlineLineTo(*dash.outline, &cur.pt2, transform);
}
if (dash.curLen < 1) {
//move to next dash
dash.curIdx = (dash.curIdx + 1) % dash.cnt;
dash.curLen = dash.pattern[dash.curIdx];
dash.curOpGap = !dash.curOpGap;
}
}
dash.ptCur = *to;
}
static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ctrl2, const Point* to, const Matrix* transform)
{
_growOutlinePoint(*dash.outline, dash.outline->ptsCnt >> 1);
_growOutlineContour(*dash.outline, dash.outline->cntrsCnt >> 1);
Bezier cur = { dash.ptCur, *ctrl1, *ctrl2, *to};
auto len = bezLength(cur);
if (len < dash.curLen) {
dash.curLen -= len;
if (!dash.curOpGap) {
_outlineMoveTo(*dash.outline, &dash.ptCur, transform);
_outlineCubicTo(*dash.outline, ctrl1, ctrl2, to, transform);
}
} else {
while (len > dash.curLen) {
Bezier left, right;
len -= dash.curLen;
bezSplitAt(cur, dash.curLen, left, right);
dash.curIdx = (dash.curIdx + 1) % dash.cnt;
if (!dash.curOpGap) {
_outlineMoveTo(*dash.outline, &left.start, transform);
_outlineCubicTo(*dash.outline, &left.ctrl1, &left.ctrl2, &left.end, transform);
}
dash.curLen = dash.pattern[dash.curIdx];
dash.curOpGap = !dash.curOpGap;
cur = right;
dash.ptCur = right.start;
}
//leftovers
dash.curLen -= len;
if (!dash.curOpGap) {
_outlineMoveTo(*dash.outline, &cur.start, transform);
_outlineCubicTo(*dash.outline, &cur.ctrl1, &cur.ctrl2, &cur.end, transform);
}
if (dash.curLen < 1) {
//move to next dash
dash.curIdx = (dash.curIdx + 1) % dash.cnt;
dash.curLen = dash.pattern[dash.curIdx];
dash.curOpGap = !dash.curOpGap;
}
}
dash.ptCur = *to;
}
SwOutline* _genDashOutline(const Shape* sdata, const Matrix* transform)
{
const PathCommand* cmds = nullptr;
auto cmdCnt = sdata->pathCommands(&cmds);
const Point* pts = nullptr;
auto ptsCnt = sdata->pathCoords(&pts);
//No actual shape data
if (cmdCnt == 0 || ptsCnt == 0) return nullptr;
SwDashStroke dash;
dash.curIdx = 0;
dash.curLen = 0;
dash.ptStart = {0, 0};
dash.ptCur = {0, 0};
dash.curOpGap = false;
const float* pattern;
dash.cnt = sdata->strokeDash(&pattern);
//Is it safe to mutual exclusive?
dash.pattern = const_cast<float*>(pattern);
dash.outline = static_cast<SwOutline*>(calloc(1, sizeof(SwOutline)));
dash.outline->opened = true;
//smart reservation
auto outlinePtsCnt = 0;
auto outlineCntrsCnt = 0;
for (uint32_t i = 0; i < cmdCnt; ++i) {
switch(*(cmds + i)) {
case PathCommand::Close: {
++outlinePtsCnt;
break;
}
case PathCommand::MoveTo: {
++outlineCntrsCnt;
++outlinePtsCnt;
break;
}
case PathCommand::LineTo: {
++outlinePtsCnt;
break;
}
case PathCommand::CubicTo: {
outlinePtsCnt += 3;
break;
}
}
}
++outlinePtsCnt; //for close
++outlineCntrsCnt; //for end
//Reserve Approximitely 20x...
_growOutlinePoint(*dash.outline, outlinePtsCnt * 20);
_growOutlineContour(*dash.outline, outlineCntrsCnt * 20);
while (cmdCnt-- > 0) {
switch(*cmds) {
case PathCommand::Close: {
_dashLineTo(dash, &dash.ptStart, transform);
break;
}
case PathCommand::MoveTo: {
//reset the dash
dash.curIdx = 0;
dash.curLen = *dash.pattern;
dash.curOpGap = false;
dash.ptStart = dash.ptCur = *pts;
++pts;
break;
}
case PathCommand::LineTo: {
_dashLineTo(dash, pts, transform);
++pts;
break;
}
case PathCommand::CubicTo: {
_dashCubicTo(dash, pts, pts + 1, pts + 2, transform);
pts += 3;
break;
}
}
++cmds;
}
_outlineEnd(*dash.outline);
return dash.outline;
}
bool _fastTrack(const SwOutline* outline)
{
//Fast Track: Othogonal rectangle?
if (outline->ptsCnt != 5) return false;
auto pt1 = outline->pts + 0;
auto pt2 = outline->pts + 1;
auto pt3 = outline->pts + 2;
auto pt4 = outline->pts + 3;
auto min1 = pt1->y < pt3->y ? pt1 : pt3;
auto min2 = pt2->y < pt4->y ? pt2 : pt4;
if (min1->y != min2->y) return false;
SwCoord len1 = pow(pt1->x - pt3->x, 2) + pow(pt1->y - pt3->y, 2);
SwCoord len2 = pow(pt2->x - pt4->x, 2) + pow(pt2->y - pt4->y, 2);
if (len1 == len2) return true;
return false;
}
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
bool shapePrepare(SwShape* shape, const Shape* sdata, const SwSize& clip, const Matrix* transform)
{
if (!shapeGenOutline(shape, sdata, transform)) return false;
if (!_updateBBox(shape->outline, shape->bbox)) return false;
if (!_checkValid(shape->outline, shape->bbox, clip)) return false;
return true;
}
bool shapeGenRle(SwShape* shape, TVG_UNUSED const Shape* sdata, const SwSize& clip, bool antiAlias)
{
//FIXME: Should we draw it?
//Case: Stroke Line
//if (shape.outline->opened) return true;
//Case A: Fast Track Rectangle Drawing
if ((shape->rect = _fastTrack(shape->outline))) return true;
//Case B: Normale Shape RLE Drawing
if ((shape->rle = rleRender(shape->outline, shape->bbox, clip, antiAlias))) return true;
return false;
}
void shapeDelOutline(SwShape* shape)
{
auto outline = shape->outline;
_delOutline(outline);
shape->outline = nullptr;
}
void shapeReset(SwShape* shape)
{
shapeDelOutline(shape);
rleFree(shape->rle);
shape->rle = nullptr;
shape->rect = false;
_initBBox(shape->bbox);
}
bool shapeGenOutline(SwShape* shape, const Shape* sdata, const Matrix* transform)
{
const PathCommand* cmds = nullptr;
auto cmdCnt = sdata->pathCommands(&cmds);
const Point* pts = nullptr;
auto ptsCnt = sdata->pathCoords(&pts);
//No actual shape data
if (cmdCnt == 0 || ptsCnt == 0) return false;
//smart reservation
auto outlinePtsCnt = 0;
auto outlineCntrsCnt = 0;
for (uint32_t i = 0; i < cmdCnt; ++i) {
switch(*(cmds + i)) {
case PathCommand::Close: {
++outlinePtsCnt;
break;
}
case PathCommand::MoveTo: {
++outlineCntrsCnt;
++outlinePtsCnt;
break;
}
case PathCommand::LineTo: {
++outlinePtsCnt;
break;
}
case PathCommand::CubicTo: {
outlinePtsCnt += 3;
break;
}
}
}
++outlinePtsCnt; //for close
++outlineCntrsCnt; //for end
auto outline = shape->outline;
if (!outline) outline = static_cast<SwOutline*>(calloc(1, sizeof(SwOutline)));
outline->opened = true;
_growOutlinePoint(*outline, outlinePtsCnt);
_growOutlineContour(*outline, outlineCntrsCnt);
auto closed = false;
//Generate Outlines
while (cmdCnt-- > 0) {
switch(*cmds) {
case PathCommand::Close: {
_outlineClose(*outline);
closed = true;
break;
}
case PathCommand::MoveTo: {
_outlineMoveTo(*outline, pts, transform);
++pts;
break;
}
case PathCommand::LineTo: {
_outlineLineTo(*outline, pts, transform);
++pts;
break;
}
case PathCommand::CubicTo: {
_outlineCubicTo(*outline, pts, pts + 1, pts + 2, transform);
pts += 3;
break;
}
}
++cmds;
}
_outlineEnd(*outline);
if (closed) outline->opened = false;
//FIXME:
//outline->flags = SwOutline::FillRule::Winding;
shape->outline = outline;
return true;
}
void shapeFree(SwShape* shape)
{
shapeDelOutline(shape);
rleFree(shape->rle);
shapeDelFill(shape);
if (shape->stroke) {
rleFree(shape->strokeRle);
strokeFree(shape->stroke);
}
}
void shapeDelStroke(SwShape* shape)
{
if (!shape->stroke) return;
rleFree(shape->strokeRle);
shape->strokeRle = nullptr;
strokeFree(shape->stroke);
shape->stroke = nullptr;
}
void shapeResetStroke(SwShape* shape, const Shape* sdata, const Matrix* transform)
{
if (!shape->stroke) shape->stroke = static_cast<SwStroke*>(calloc(1, sizeof(SwStroke)));
auto stroke = shape->stroke;
if (!stroke) return;
strokeReset(stroke, sdata, transform);
rleFree(shape->strokeRle);
shape->strokeRle = nullptr;
}
bool shapeGenStrokeRle(SwShape* shape, const Shape* sdata, const Matrix* transform, const SwSize& clip)
{
SwOutline* shapeOutline = nullptr;
//Dash Style Stroke
if (sdata->strokeDash(nullptr) > 0) {
shapeOutline = _genDashOutline(sdata, transform);
if (!shapeOutline) return false;
//Normal Style stroke
} else {
if (!shape->outline) {
if (!shapeGenOutline(shape, sdata, transform)) return false;
}
shapeOutline = shape->outline;
}
if (!strokeParseOutline(shape->stroke, *shapeOutline)) return false;
auto strokeOutline = strokeExportOutline(shape->stroke);
if (!strokeOutline) return false;
SwBBox bbox;
_updateBBox(strokeOutline, bbox);
if (!_checkValid(strokeOutline, bbox, clip)) return false;
shape->strokeRle = rleRender(strokeOutline, bbox, clip, true);
_delOutline(strokeOutline);
return true;
}
bool shapeGenFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, bool ctable)
{
return fillGenColorTable(shape->fill, fill, transform, surface, ctable);
}
void shapeResetFill(SwShape* shape)
{
if (!shape->fill) {
shape->fill = static_cast<SwFill*>(calloc(1, sizeof(SwFill)));
if (!shape->fill) return;
}
fillReset(shape->fill);
}
void shapeDelFill(SwShape* shape)
{
if (!shape->fill) return;
fillFree(shape->fill);
shape->fill = nullptr;
}

View file

@ -0,0 +1,924 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "tvgSwCommon.h"
/************************************************************************/
/* Internal Class Implementation */
/************************************************************************/
static constexpr auto SW_STROKE_TAG_POINT = 1;
static constexpr auto SW_STROKE_TAG_CUBIC = 2;
static constexpr auto SW_STROKE_TAG_BEGIN = 4;
static constexpr auto SW_STROKE_TAG_END = 8;
static inline SwFixed SIDE_TO_ROTATE(const int32_t s)
{
return (SW_ANGLE_PI2 - static_cast<SwFixed>(s) * SW_ANGLE_PI);
}
static inline void SCALE(SwStroke& stroke, SwPoint& pt)
{
pt.x *= stroke.sx;
pt.y *= stroke.sy;
}
static void _growBorder(SwStrokeBorder* border, uint32_t newPts)
{
auto maxOld = border->maxPts;
auto maxNew = border->ptsCnt + newPts;
if (maxNew <= maxOld) return;
auto maxCur = maxOld;
while (maxCur < maxNew)
maxCur += (maxCur >> 1) + 16;
border->pts = static_cast<SwPoint*>(realloc(border->pts, maxCur * sizeof(SwPoint)));
border->tags = static_cast<uint8_t*>(realloc(border->tags, maxCur * sizeof(uint8_t)));
border->maxPts = maxCur;
}
static void _borderClose(SwStrokeBorder* border, bool reverse)
{
auto start = border->start;
auto count = border->ptsCnt;
//Don't record empty paths!
if (count <= start + 1U) {
border->ptsCnt = start;
} else {
/* Copy the last point to the start of this sub-path,
since it contains the adjusted starting coordinates */
border->ptsCnt = --count;
border->pts[start] = border->pts[count];
if (reverse) {
//reverse the points
auto pt1 = border->pts + start + 1;
auto pt2 = border->pts + count - 1;
while (pt1 < pt2) {
auto tmp = *pt1;
*pt1 = *pt2;
*pt2 = tmp;
++pt1;
--pt2;
}
//reverse the tags
auto tag1 = border->tags + start + 1;
auto tag2 = border->tags + count - 1;
while (tag1 < tag2) {
auto tmp = *tag1;
*tag1 = *tag2;
*tag2 = tmp;
++tag1;
--tag2;
}
}
border->tags[start] |= SW_STROKE_TAG_BEGIN;
border->tags[count - 1] |= SW_STROKE_TAG_END;
}
border->start = -1;
border->movable = false;
}
static void _borderCubicTo(SwStrokeBorder* border, SwPoint& ctrl1, SwPoint& ctrl2, SwPoint& to)
{
_growBorder(border, 3);
auto pt = border->pts + border->ptsCnt;
auto tag = border->tags + border->ptsCnt;
pt[0] = ctrl1;
pt[1] = ctrl2;
pt[2] = to;
tag[0] = SW_STROKE_TAG_CUBIC;
tag[1] = SW_STROKE_TAG_CUBIC;
tag[2] = SW_STROKE_TAG_POINT;
border->ptsCnt += 3;
border->movable = false;
}
static void _borderArcTo(SwStrokeBorder* border, SwPoint& center, SwFixed radius, SwFixed angleStart, SwFixed angleDiff, SwStroke& stroke)
{
constexpr SwFixed ARC_CUBIC_ANGLE = SW_ANGLE_PI / 2;
SwPoint a = {static_cast<SwCoord>(radius), 0};
mathRotate(a, angleStart);
SCALE(stroke, a);
a += center;
auto total = angleDiff;
auto angle = angleStart;
auto rotate = (angleDiff >= 0) ? SW_ANGLE_PI2 : -SW_ANGLE_PI2;
while (total != 0) {
auto step = total;
if (step > ARC_CUBIC_ANGLE) step = ARC_CUBIC_ANGLE;
else if (step < -ARC_CUBIC_ANGLE) step = -ARC_CUBIC_ANGLE;
auto next = angle + step;
auto theta = step;
if (theta < 0) theta = -theta;
theta >>= 1;
//compute end point
SwPoint b = {static_cast<SwCoord>(radius), 0};
mathRotate(b, next);
SCALE(stroke, b);
b += center;
//compute first and second control points
auto length = mathMulDiv(radius, mathSin(theta) * 4, (0x10000L + mathCos(theta)) * 3);
SwPoint a2 = {static_cast<SwCoord>(length), 0};
mathRotate(a2, angle + rotate);
SCALE(stroke, a2);
a2 += a;
SwPoint b2 = {static_cast<SwCoord>(length), 0};
mathRotate(b2, next - rotate);
SCALE(stroke, b2);
b2 += b;
//add cubic arc
_borderCubicTo(border, a2, b2, b);
//process the rest of the arc?
a = b;
total -= step;
angle = next;
}
}
static void _borderLineTo(SwStrokeBorder* border, SwPoint& to, bool movable)
{
if (border->movable) {
//move last point
border->pts[border->ptsCnt - 1] = to;
} else {
//don't add zero-length line_to
if (border->ptsCnt > 0 && (border->pts[border->ptsCnt - 1] - to).small()) return;
_growBorder(border, 1);
border->pts[border->ptsCnt] = to;
border->tags[border->ptsCnt] = SW_STROKE_TAG_POINT;
border->ptsCnt += 1;
}
border->movable = movable;
}
static void _borderMoveTo(SwStrokeBorder* border, SwPoint& to)
{
//close current open path if any?
if (border->start >= 0) _borderClose(border, false);
border->start = border->ptsCnt;
border->movable = false;
_borderLineTo(border, to, false);
}
static void _arcTo(SwStroke& stroke, int32_t side)
{
auto border = stroke.borders + side;
auto rotate = SIDE_TO_ROTATE(side);
auto total = mathDiff(stroke.angleIn, stroke.angleOut);
if (total == SW_ANGLE_PI) total = -rotate * 2;
_borderArcTo(border, stroke.center, stroke.width, stroke.angleIn + rotate, total, stroke);
border->movable = false;
}
static void _outside(SwStroke& stroke, int32_t side, SwFixed lineLength)
{
constexpr SwFixed MITER_LIMIT = 4 * (1 << 16);
auto border = stroke.borders + side;
if (stroke.join == StrokeJoin::Round) {
_arcTo(stroke, side);
} else {
//this is a mitered (pointed) or beveled (truncated) corner
auto rotate = SIDE_TO_ROTATE(side);
auto bevel = (stroke.join == StrokeJoin::Bevel) ? true : false;
SwFixed phi = 0;
SwFixed thcos = 0;
if (!bevel) {
auto theta = mathDiff(stroke.angleIn, stroke.angleOut);
if (theta == SW_ANGLE_PI) {
theta = rotate;
phi = stroke.angleIn;
} else {
theta /= 2;
phi = stroke.angleIn + theta + rotate;
}
thcos = mathCos(theta);
auto sigma = mathMultiply(MITER_LIMIT, thcos);
//is miter limit exceeded?
if (sigma < 0x10000L) bevel = true;
}
//this is a bevel (broken angle)
if (bevel) {
SwPoint delta = {static_cast<SwCoord>(stroke.width), 0};
mathRotate(delta, stroke.angleOut + rotate);
SCALE(stroke, delta);
delta += stroke.center;
border->movable = false;
_borderLineTo(border, delta, false);
//this is a miter (intersection)
} else {
auto length = mathDivide(stroke.width, thcos);
SwPoint delta = {static_cast<SwCoord>(length), 0};
mathRotate(delta, phi);
SCALE(stroke, delta);
delta += stroke.center;
_borderLineTo(border, delta, false);
/* Now add and end point
Only needed if not lineto (lineLength is zero for curves) */
if (lineLength == 0) {
delta = {static_cast<SwCoord>(stroke.width), 0};
mathRotate(delta, stroke.angleOut + rotate);
SCALE(stroke, delta);
delta += stroke.center;
_borderLineTo(border, delta, false);
}
}
}
}
static void _inside(SwStroke& stroke, int32_t side, SwFixed lineLength)
{
auto border = stroke.borders + side;
auto theta = mathDiff(stroke.angleIn, stroke.angleOut) / 2;
SwPoint delta;
bool intersect = false;
/* Only intersect borders if between two line_to's and both
lines are long enough (line length is zero fur curves). */
if (border->movable && lineLength > 0) {
//compute minimum required length of lines
SwFixed minLength = abs(mathMultiply(stroke.width, mathTan(theta)));
if (stroke.lineLength >= minLength && lineLength >= minLength) intersect = true;
}
auto rotate = SIDE_TO_ROTATE(side);
if (!intersect) {
delta = {static_cast<SwCoord>(stroke.width), 0};
mathRotate(delta, stroke.angleOut + rotate);
SCALE(stroke, delta);
delta += stroke.center;
border->movable = false;
} else {
//compute median angle
auto phi = stroke.angleIn + theta;
auto thcos = mathCos(theta);
delta = {static_cast<SwCoord>(mathDivide(stroke.width, thcos)), 0};
mathRotate(delta, phi + rotate);
SCALE(stroke, delta);
delta += stroke.center;
}
_borderLineTo(border, delta, false);
}
void _processCorner(SwStroke& stroke, SwFixed lineLength)
{
auto turn = mathDiff(stroke.angleIn, stroke.angleOut);
//no specific corner processing is required if the turn is 0
if (turn == 0) return;
//when we turn to the right, the inside side is 0
int32_t inside = 0;
//otherwise, the inside is 1
if (turn < 0) inside = 1;
//process the inside
_inside(stroke, inside, lineLength);
//process the outside
_outside(stroke, 1 - inside, lineLength);
}
void _firstSubPath(SwStroke& stroke, SwFixed startAngle, SwFixed lineLength)
{
SwPoint delta = {static_cast<SwCoord>(stroke.width), 0};
mathRotate(delta, startAngle + SW_ANGLE_PI2);
SCALE(stroke, delta);
auto pt = stroke.center + delta;
auto border = stroke.borders;
_borderMoveTo(border, pt);
pt = stroke.center - delta;
++border;
_borderMoveTo(border, pt);
/* Save angle, position and line length for last join
lineLength is zero for curves */
stroke.subPathAngle = startAngle;
stroke.firstPt = false;
stroke.subPathLineLength = lineLength;
}
static void _lineTo(SwStroke& stroke, const SwPoint& to)
{
auto delta = to - stroke.center;
//a zero-length lineto is a no-op; avoid creating a spurious corner
if (delta.zero()) return;
//compute length of line
auto lineLength = mathLength(delta);
auto angle = mathAtan(delta);
delta = {static_cast<SwCoord>(stroke.width), 0};
mathRotate(delta, angle + SW_ANGLE_PI2);
SCALE(stroke, delta);
//process corner if necessary
if (stroke.firstPt) {
/* This is the first segment of a subpath. We need to add a point to each border
at their respective starting point locations. */
_firstSubPath(stroke, angle, lineLength);
} else {
//process the current corner
stroke.angleOut = angle;
_processCorner(stroke, lineLength);
}
//now add a line segment to both the inside and outside paths
auto border = stroke.borders;
auto side = 1;
while (side >= 0) {
auto pt = to + delta;
//the ends of lineto borders are movable
_borderLineTo(border, pt, true);
delta.x = -delta.x;
delta.y = -delta.y;
--side;
++border;
}
stroke.angleIn = angle;
stroke.center = to;
stroke.lineLength = lineLength;
}
static void _cubicTo(SwStroke& stroke, const SwPoint& ctrl1, const SwPoint& ctrl2, const SwPoint& to)
{
/* if all control points are coincident, this is a no-op;
avoid creating a spurious corner */
if ((stroke.center - ctrl1).small() && (ctrl1 - ctrl2).small() && (ctrl2 - to).small()) {
stroke.center = to;
return;
}
SwPoint bezStack[37]; //TODO: static?
auto limit = bezStack + 32;
auto arc = bezStack;
auto firstArc = true;
arc[0] = to;
arc[1] = ctrl2;
arc[2] = ctrl1;
arc[3] = stroke.center;
while (arc >= bezStack) {
SwFixed angleIn, angleOut, angleMid;
//initialize with current direction
angleIn = angleOut = angleMid = stroke.angleIn;
if (arc < limit && !mathSmallCubic(arc, angleIn, angleMid, angleOut)) {
if (stroke.firstPt) stroke.angleIn = angleIn;
mathSplitCubic(arc);
arc += 3;
continue;
}
if (firstArc) {
firstArc = false;
//process corner if necessary
if (stroke.firstPt) {
_firstSubPath(stroke, angleIn, 0);
} else {
stroke.angleOut = angleIn;
_processCorner(stroke, 0);
}
} else if (abs(mathDiff(stroke.angleIn, angleIn)) > (SW_ANGLE_PI / 8) / 4) {
//if the deviation from one arc to the next is too great add a round corner
stroke.center = arc[3];
stroke.angleOut = angleIn;
stroke.join = StrokeJoin::Round;
_processCorner(stroke, 0);
//reinstate line join style
stroke.join = stroke.joinSaved;
}
//the arc's angle is small enough; we can add it directly to each border
auto theta1 = mathDiff(angleIn, angleMid) / 2;
auto theta2 = mathDiff(angleMid, angleOut) / 2;
auto phi1 = mathMean(angleIn, angleMid);
auto phi2 = mathMean(angleMid, angleOut);
auto length1 = mathDivide(stroke.width, mathCos(theta1));
auto length2 = mathDivide(stroke.width, mathCos(theta2));
SwFixed alpha0 = 0;
//compute direction of original arc
if (stroke.handleWideStrokes) {
alpha0 = mathAtan(arc[0] - arc[3]);
}
auto border = stroke.borders;
int32_t side = 0;
while (side <= 1)
{
auto rotate = SIDE_TO_ROTATE(side);
//compute control points
SwPoint _ctrl1 = {static_cast<SwCoord>(length1), 0};
mathRotate(_ctrl1, phi1 + rotate);
SCALE(stroke, _ctrl1);
_ctrl1 += arc[2];
SwPoint _ctrl2 = {static_cast<SwCoord>(length2), 0};
mathRotate(_ctrl2, phi2 + rotate);
SCALE(stroke, _ctrl2);
_ctrl2 += arc[1];
//compute end point
SwPoint _end = {static_cast<SwCoord>(stroke.width), 0};
mathRotate(_end, angleOut + rotate);
SCALE(stroke, _end);
_end += arc[0];
if (stroke.handleWideStrokes) {
/* determine whether the border radius is greater than the radius of
curvature of the original arc */
auto _start = border->pts[border->ptsCnt - 1];
auto alpha1 = mathAtan(_end - _start);
//is the direction of the border arc opposite to that of the original arc?
if (abs(mathDiff(alpha0, alpha1)) > SW_ANGLE_PI / 2) {
//use the sine rule to find the intersection point
auto beta = mathAtan(arc[3] - _start);
auto gamma = mathAtan(arc[0] - _end);
auto bvec = _end - _start;
auto blen = mathLength(bvec);
auto sinA = abs(mathSin(alpha1 - gamma));
auto sinB = abs(mathSin(beta - gamma));
auto alen = mathMulDiv(blen, sinA, sinB);
SwPoint delta = {static_cast<SwCoord>(alen), 0};
mathRotate(delta, beta);
delta += _start;
//circumnavigate the negative sector backwards
border->movable = false;
_borderLineTo(border, delta, false);
_borderLineTo(border, _end, false);
_borderCubicTo(border, _ctrl2, _ctrl1, _start);
//and then move to the endpoint
_borderLineTo(border, _end, false);
++side;
++border;
continue;
}
//else fall through
}
_borderCubicTo(border, _ctrl1, _ctrl2, _end);
++side;
++border;
}
arc -= 3;
stroke.angleIn = angleOut;
}
stroke.center = to;
}
static void _addCap(SwStroke& stroke, SwFixed angle, int32_t side)
{
if (stroke.cap == StrokeCap::Square) {
auto rotate = SIDE_TO_ROTATE(side);
auto border = stroke.borders + side;
SwPoint delta = {static_cast<SwCoord>(stroke.width), 0};
mathRotate(delta, angle);
SCALE(stroke, delta);
SwPoint delta2 = {static_cast<SwCoord>(stroke.width), 0};
mathRotate(delta2, angle + rotate);
SCALE(stroke, delta2);
delta += stroke.center + delta2;
_borderLineTo(border, delta, false);
delta = {static_cast<SwCoord>(stroke.width), 0};
mathRotate(delta, angle);
SCALE(stroke, delta);
delta2 = {static_cast<SwCoord>(stroke.width), 0};
mathRotate(delta2, angle - rotate);
SCALE(stroke, delta2);
delta += delta2 + stroke.center;
_borderLineTo(border, delta, false);
} else if (stroke.cap == StrokeCap::Round) {
stroke.angleIn = angle;
stroke.angleOut = angle + SW_ANGLE_PI;
_arcTo(stroke, side);
return;
} else { //Butt
auto rotate = SIDE_TO_ROTATE(side);
auto border = stroke.borders + side;
SwPoint delta = {static_cast<SwCoord>(stroke.width), 0};
mathRotate(delta, angle + rotate);
SCALE(stroke, delta);
delta += stroke.center;
_borderLineTo(border, delta, false);
delta = {static_cast<SwCoord>(stroke.width), 0};
mathRotate(delta, angle - rotate);
SCALE(stroke, delta);
delta += stroke.center;
_borderLineTo(border, delta, false);
}
}
static void _addReverseLeft(SwStroke& stroke, bool opened)
{
auto right = stroke.borders + 0;
auto left = stroke.borders + 1;
auto newPts = left->ptsCnt - left->start;
if (newPts <= 0) return;
_growBorder(right, newPts);
auto dstPt = right->pts + right->ptsCnt;
auto dstTag = right->tags + right->ptsCnt;
auto srcPt = left->pts + left->ptsCnt - 1;
auto srcTag = left->tags + left->ptsCnt - 1;
while (srcPt >= left->pts + left->start) {
*dstPt = *srcPt;
*dstTag = *srcTag;
if (opened) {
dstTag[0] &= ~(SW_STROKE_TAG_BEGIN | SW_STROKE_TAG_END);
} else {
//switch begin/end tags if necessary
auto ttag = dstTag[0] & (SW_STROKE_TAG_BEGIN | SW_STROKE_TAG_END);
if (ttag == SW_STROKE_TAG_BEGIN || ttag == SW_STROKE_TAG_END)
dstTag[0] ^= (SW_STROKE_TAG_BEGIN | SW_STROKE_TAG_END);
}
--srcPt;
--srcTag;
++dstPt;
++dstTag;
}
left->ptsCnt = left->start;
right->ptsCnt += newPts;
right->movable = false;
left->movable = false;
}
static void _beginSubPath(SwStroke& stroke, SwPoint& to, bool opened)
{
/* We cannot process the first point because there is not enough
information regarding its corner/cap. Later, it will be processed
in the _endSubPath() */
stroke.firstPt = true;
stroke.center = to;
stroke.openSubPath = opened;
/* Determine if we need to check whether the border radius is greater
than the radius of curvature of a curve, to handle this case specially.
This is only required if bevel joins or butt caps may be created because
round & miter joins and round & square caps cover the nagative sector
created with wide strokes. */
if ((stroke.join != StrokeJoin::Round) || (stroke.openSubPath && stroke.cap == StrokeCap::Butt))
stroke.handleWideStrokes = true;
else
stroke.handleWideStrokes = false;
stroke.ptStartSubPath = to;
stroke.angleIn = 0;
}
static void _endSubPath(SwStroke& stroke)
{
if (stroke.openSubPath) {
auto right = stroke.borders;
/* all right, this is an opened path, we need to add a cap between
right & left, add the reverse of left, then add a final cap
between left & right */
_addCap(stroke, stroke.angleIn, 0);
//add reversed points from 'left' to 'right'
_addReverseLeft(stroke, true);
//now add the final cap
stroke.center = stroke.ptStartSubPath;
_addCap(stroke, stroke.subPathAngle + SW_ANGLE_PI, 0);
/* now end the right subpath accordingly. The left one is rewind
and deosn't need further processing */
_borderClose(right, false);
} else {
//close the path if needed
if (stroke.center != stroke.ptStartSubPath)
_lineTo(stroke, stroke.ptStartSubPath);
//process the corner
stroke.angleOut = stroke.subPathAngle;
auto turn = mathDiff(stroke.angleIn, stroke.angleOut);
//No specific corner processing is required if the turn is 0
if (turn != 0) {
//when we turn to the right, the inside is 0
int32_t inside = 0;
//otherwise, the inside is 1
if (turn < 0) inside = 1;
_inside(stroke, inside, stroke.subPathLineLength); //inside
_outside(stroke, 1 - inside, stroke.subPathLineLength); //outside
}
_borderClose(stroke.borders + 0, false);
_borderClose(stroke.borders + 1, true);
}
}
static void _getCounts(SwStrokeBorder* border, uint32_t& ptsCnt, uint32_t& cntrsCnt)
{
auto count = border->ptsCnt;
auto tags = border->tags;
uint32_t _ptsCnt = 0;
uint32_t _cntrsCnt = 0;
bool inCntr = false;
while (count > 0) {
if (tags[0] & SW_STROKE_TAG_BEGIN) {
if (inCntr) goto fail;
inCntr = true;
} else if (!inCntr) goto fail;
if (tags[0] & SW_STROKE_TAG_END) {
inCntr = false;
++_cntrsCnt;
}
--count;
++_ptsCnt;
++tags;
}
if (inCntr) goto fail;
border->valid = true;
ptsCnt = _ptsCnt;
cntrsCnt = _cntrsCnt;
return;
fail:
ptsCnt = 0;
cntrsCnt = 0;
}
static void _exportBorderOutline(SwStroke& stroke, SwOutline* outline, uint32_t side)
{
auto border = stroke.borders + side;
if (!border->valid) return;
memcpy(outline->pts + outline->ptsCnt, border->pts, border->ptsCnt * sizeof(SwPoint));
auto cnt = border->ptsCnt;
auto src = border->tags;
auto tags = outline->types + outline->ptsCnt;
auto cntrs = outline->cntrs + outline->cntrsCnt;
uint16_t idx = outline->ptsCnt;
while (cnt > 0) {
if (*src & SW_STROKE_TAG_POINT) *tags = SW_CURVE_TYPE_POINT;
else if (*src & SW_STROKE_TAG_CUBIC) *tags = SW_CURVE_TYPE_CUBIC;
else {
//LOG: What type of stroke outline??
}
if (*src & SW_STROKE_TAG_END) {
*cntrs = idx;
++cntrs;
++outline->cntrsCnt;
}
++src;
++tags;
++idx;
--cnt;
}
outline->ptsCnt = outline->ptsCnt + border->ptsCnt;
}
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
void strokeFree(SwStroke* stroke)
{
if (!stroke) return;
//free borders
if (stroke->borders[0].pts) free(stroke->borders[0].pts);
if (stroke->borders[0].tags) free(stroke->borders[0].tags);
if (stroke->borders[1].pts) free(stroke->borders[1].pts);
if (stroke->borders[1].tags) free(stroke->borders[1].tags);
free(stroke);
}
void strokeReset(SwStroke* stroke, const Shape* sdata, const Matrix* transform)
{
if (transform) {
stroke->sx = sqrt(pow(transform->e11, 2) + pow(transform->e21, 2));
stroke->sy = sqrt(pow(transform->e12, 2) + pow(transform->e22, 2));
} else {
stroke->sx = stroke->sy = 1.0f;
}
stroke->width = TO_SWCOORD(sdata->strokeWidth() * 0.5);
stroke->cap = sdata->strokeCap();
//Save line join: it can be temporarily changed when stroking curves...
stroke->joinSaved = stroke->join = sdata->strokeJoin();
stroke->borders[0].ptsCnt = 0;
stroke->borders[0].start = -1;
stroke->borders[0].valid = false;
stroke->borders[1].ptsCnt = 0;
stroke->borders[1].start = -1;
stroke->borders[1].valid = false;
}
bool strokeParseOutline(SwStroke* stroke, const SwOutline& outline)
{
uint32_t first = 0;
for (uint32_t i = 0; i < outline.cntrsCnt; ++i) {
auto last = outline.cntrs[i]; //index of last point in contour
auto limit = outline.pts + last;
//Skip empty points
if (last <= first) {
first = last + 1;
continue;
}
auto start = outline.pts[first];
auto pt = outline.pts + first;
auto types = outline.types + first;
auto type = types[0];
//A contour cannot start with a cubic control point
if (type == SW_CURVE_TYPE_CUBIC) return false;
_beginSubPath(*stroke, start, outline.opened);
while (pt < limit) {
++pt;
++types;
//emit a signel line_to
if (types[0] == SW_CURVE_TYPE_POINT) {
_lineTo(*stroke, *pt);
//types cubic
} else {
if (pt + 1 > limit || types[1] != SW_CURVE_TYPE_CUBIC) return false;
pt += 2;
types += 2;
if (pt <= limit) {
_cubicTo(*stroke, pt[-2], pt[-1], pt[0]);
continue;
}
_cubicTo(*stroke, pt[-2], pt[-1], start);
goto close;
}
}
close:
if (!stroke->firstPt) _endSubPath(*stroke);
first = last + 1;
}
return true;
}
SwOutline* strokeExportOutline(SwStroke* stroke)
{
uint32_t count1, count2, count3, count4;
_getCounts(stroke->borders + 0, count1, count2);
_getCounts(stroke->borders + 1, count3, count4);
auto ptsCnt = count1 + count3;
auto cntrsCnt = count2 + count4;
auto outline = static_cast<SwOutline*>(calloc(1, sizeof(SwOutline)));
outline->pts = static_cast<SwPoint*>(malloc(sizeof(SwPoint) * ptsCnt));
outline->types = static_cast<uint8_t*>(malloc(sizeof(uint8_t) * ptsCnt));
outline->cntrs = static_cast<uint32_t*>(malloc(sizeof(uint32_t) * cntrsCnt));
_exportBorderOutline(*stroke, outline, 0); //left
_exportBorderOutline(*stroke, outline, 1); //right
return outline;
}

143
src/lib/tvgBezier.cpp Normal file
View file

@ -0,0 +1,143 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "tvgCommon.h"
/************************************************************************/
/* Internal Class Implementation */
/************************************************************************/
static float _lineLength(const Point& pt1, const Point& pt2)
{
/* approximate sqrt(x*x + y*y) using alpha max plus beta min algorithm.
With alpha = 1, beta = 3/8, giving results with the largest error less
than 7% compared to the exact value. */
Point diff = {pt2.x - pt1.x, pt2.y - pt1.y};
if (diff.x < 0) diff.x = -diff.x;
if (diff.y < 0) diff.y = -diff.y;
return (diff.x > diff.y) ? (diff.x + diff.y * 0.375f) : (diff.y + diff.x * 0.375f);
}
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
namespace tvg
{
void bezSplit(const Bezier&cur, Bezier& left, Bezier& right)
{
auto c = (cur.ctrl1.x + cur.ctrl2.x) * 0.5f;
left.ctrl1.x = (cur.start.x + cur.ctrl1.x) * 0.5f;
right.ctrl2.x = (cur.ctrl2.x + cur.end.x) * 0.5f;
left.start.x = cur.start.x;
right.end.x = cur.end.x;
left.ctrl2.x = (left.ctrl1.x + c) * 0.5f;
right.ctrl1.x = (right.ctrl2.x + c) * 0.5f;
left.end.x = right.start.x = (left.ctrl2.x + right.ctrl1.x) * 0.5f;
c = (cur.ctrl1.y + cur.ctrl2.y) * 0.5f;
left.ctrl1.y = (cur.start.y + cur.ctrl1.y) * 0.5f;
right.ctrl2.y = (cur.ctrl2.y + cur.end.y) * 0.5f;
left.start.y = cur.start.y;
right.end.y = cur.end.y;
left.ctrl2.y = (left.ctrl1.y + c) * 0.5f;
right.ctrl1.y = (right.ctrl2.y + c) * 0.5f;
left.end.y = right.start.y = (left.ctrl2.y + right.ctrl1.y) * 0.5f;
}
float bezLength(const Bezier& cur)
{
Bezier left, right;
auto len = _lineLength(cur.start, cur.ctrl1) + _lineLength(cur.ctrl1, cur.ctrl2) + _lineLength(cur.ctrl2, cur.end);
auto chord = _lineLength(cur.start, cur.end);
if (fabs(len - chord) > FLT_EPSILON) {
bezSplit(cur, left, right);
return bezLength(left) + bezLength(right);
}
return len;
}
void bezSplitLeft(Bezier& cur, float at, Bezier& left)
{
left.start = cur.start;
left.ctrl1.x = cur.start.x + at * (cur.ctrl1.x - cur.start.x);
left.ctrl1.y = cur.start.y + at * (cur.ctrl1.y - cur.start.y);
left.ctrl2.x = cur.ctrl1.x + at * (cur.ctrl2.x - cur.ctrl1.x); // temporary holding spot
left.ctrl2.y = cur.ctrl1.y + at * (cur.ctrl2.y - cur.ctrl1.y); // temporary holding spot
cur.ctrl2.x = cur.ctrl2.x + at * (cur.end.x - cur.ctrl2.x);
cur.ctrl2.y = cur.ctrl2.y + at * (cur.end.y - cur.ctrl2.y);
cur.ctrl1.x = left.ctrl2.x + at * (cur.ctrl2.x - left.ctrl2.x);
cur.ctrl1.y = left.ctrl2.y + at * (cur.ctrl2.y - left.ctrl2.y);
left.ctrl2.x = left.ctrl1.x + at * (left.ctrl2.x - left.ctrl1.x);
left.ctrl2.y = left.ctrl1.y + at * (left.ctrl2.y - left.ctrl1.y);
left.end.x = cur.start.x = left.ctrl2.x + at * (cur.ctrl1.x - left.ctrl2.x);
left.end.y = cur.start.y = left.ctrl2.y + at * (cur.ctrl1.y - left.ctrl2.y);
}
float bezAt(const Bezier& bz, float at)
{
auto len = bezLength(bz);
auto biggest = 1.0f;
if (at >= len) return 1.0f;
at *= 0.5f;
while (true) {
auto right = bz;
Bezier left;
bezSplitLeft(right, at, left);
auto len2 = bezLength(left);
if (fabs(len2 - len) < FLT_EPSILON) break;
if (len2 < len) {
at += (biggest - at) * 0.5f;
} else {
biggest = at;
at -= (at * 0.5f);
}
}
return at;
}
void bezSplitAt(const Bezier& cur, float at, Bezier& left, Bezier& right)
{
right = cur;
auto t = bezAt(right, at);
bezSplitLeft(right, t, left);
}
}

44
src/lib/tvgBezier.h Normal file
View file

@ -0,0 +1,44 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef _TVG_BEZIER_H_
#define _TVG_BEZIER_H_
namespace tvg
{
struct Bezier
{
Point start;
Point ctrl1;
Point ctrl2;
Point end;
};
void bezSplit(const Bezier&cur, Bezier& left, Bezier& right);
float bezLength(const Bezier& cur);
void bezSplitLeft(Bezier& cur, float at, Bezier& left);
float bezAt(const Bezier& bz, float at);
void bezSplitAt(const Bezier& cur, float at, Bezier& left, Bezier& right);
}
#endif //_TVG_BEZIER_H_

75
src/lib/tvgCanvas.cpp Normal file
View file

@ -0,0 +1,75 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "tvgCommon.h"
#include "tvgCanvasImpl.h"
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
Canvas::Canvas(RenderMethod *pRenderer):pImpl(make_unique<Impl>(pRenderer))
{
}
Canvas::~Canvas()
{
}
Result Canvas::reserve(uint32_t n) noexcept
{
IMPL->paints.reserve(n);
return Result::Success;
}
Result Canvas::push(unique_ptr<Paint> paint) noexcept
{
return IMPL->push(move(paint));
}
Result Canvas::clear() noexcept
{
return IMPL->clear();
}
Result Canvas::draw() noexcept
{
return IMPL->draw();
}
Result Canvas::update(Paint* paint) noexcept
{
return IMPL->update(paint);
}
Result Canvas::sync() noexcept
{
if (IMPL->renderer->flush()) return Result::Success;
return Result::InsufficientCondition;
}

108
src/lib/tvgCanvasImpl.h Normal file
View file

@ -0,0 +1,108 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef _TVG_CANVAS_IMPL_H_
#define _TVG_CANVAS_IMPL_H_
#include "tvgCommon.h"
/************************************************************************/
/* Internal Class Implementation */
/************************************************************************/
struct Canvas::Impl
{
vector<Paint*> paints;
RenderMethod* renderer;
Impl(RenderMethod* pRenderer):renderer(pRenderer)
{
renderer->ref();
}
~Impl()
{
clear();
renderer->unref();
}
Result push(unique_ptr<Paint> paint)
{
auto p = paint.release();
if (!p) return Result::MemoryCorruption;
paints.push_back(p);
return update(p);
}
Result clear()
{
if (!renderer) return Result::InsufficientCondition;
//Clear render target before drawing
if (!renderer->clear()) return Result::InsufficientCondition;
for (auto paint : paints) {
paint->IMPL->dispose(*renderer);
delete(paint);
}
paints.clear();
return Result::Success;
}
Result update(Paint* paint)
{
if (!renderer) return Result::InsufficientCondition;
//Update single paint node
if (paint) {
if (!paint->IMPL->update(*renderer, nullptr, RenderUpdateFlag::None)) {
return Result::InsufficientCondition;
}
//Update retained all paint nodes
} else {
for(auto paint: paints) {
if (!paint->IMPL->update(*renderer, nullptr, RenderUpdateFlag::None)) {
return Result::InsufficientCondition;
}
}
}
return Result::Success;
}
Result draw()
{
if (!renderer) return Result::InsufficientCondition;
if (!renderer->preRender()) return Result::InsufficientCondition;
for(auto paint: paints) {
if(!paint->IMPL->render(*renderer)) return Result::InsufficientCondition;
}
if (!renderer->postRender()) return Result::InsufficientCondition;
return Result::Success;
}
};
#endif /* _TVG_CANVAS_IMPL_H_ */

54
src/lib/tvgCommon.h Normal file
View file

@ -0,0 +1,54 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef _TVG_COMMON_H_
#define _TVG_COMMON_H_
#include "config.h"
#include <iostream>
#include <vector>
#include <math.h>
#include <float.h>
#include <string.h>
#include <memory>
#include <future>
#include "thorvg.h"
using namespace std;
using namespace tvg;
#define IMPL pImpl.get()
#define FILL_ID_LINEAR 0
#define FILL_ID_RADIAL 1
#define TVG_UNUSED __attribute__ ((__unused__))
#include "tvgBezier.h"
#include "tvgLoader.h"
#include "tvgLoaderMgr.h"
#include "tvgRender.h"
#include "tvgPaint.h"
#include "tvgTaskScheduler.h"
#endif //_TVG_COMMON_H_

99
src/lib/tvgFill.cpp Normal file
View file

@ -0,0 +1,99 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "tvgCommon.h"
/************************************************************************/
/* Internal Class Implementation */
/************************************************************************/
struct Fill::Impl
{
ColorStop* colorStops = nullptr;
uint32_t cnt = 0;
FillSpread spread;
~Impl()
{
if (colorStops) free(colorStops);
}
};
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
Fill::Fill():pImpl(make_unique<Impl>())
{
}
Fill::~Fill()
{
}
Result Fill::colorStops(const ColorStop* colorStops, uint32_t cnt) noexcept
{
auto impl = pImpl.get();
if (cnt == 0) {
if (impl->colorStops) {
free(impl->colorStops);
impl->colorStops = nullptr;
impl->cnt = cnt;
}
return Result::Success;
}
if (impl->cnt != cnt) {
impl->colorStops = static_cast<ColorStop*>(realloc(impl->colorStops, cnt * sizeof(ColorStop)));
}
impl->cnt = cnt;
memcpy(impl->colorStops, colorStops, cnt * sizeof(ColorStop));
return Result::Success;
}
uint32_t Fill::colorStops(const ColorStop** colorStops) const noexcept
{
if (colorStops) *colorStops = IMPL->colorStops;
return IMPL->cnt;
}
Result Fill::spread(FillSpread s) noexcept
{
IMPL->spread = s;
return Result::Success;
}
FillSpread Fill::spread() const noexcept
{
return IMPL->spread;
}

84
src/lib/tvgGlCanvas.cpp Normal file
View file

@ -0,0 +1,84 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "tvgCommon.h"
#include "tvgCanvasImpl.h"
#ifdef THORVG_GL_RASTER_SUPPORT
#include "tvgGlRenderer.h"
#else
class GlRenderer : public RenderMethod
{
//Non Supported. Dummy Class */
};
#endif
/************************************************************************/
/* Internal Class Implementation */
/************************************************************************/
struct GlCanvas::Impl
{
Impl() {}
};
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
#ifdef THORVG_GL_RASTER_SUPPORT
GlCanvas::GlCanvas() : Canvas(GlRenderer::inst()), pImpl(make_unique<Impl>())
#else
GlCanvas::GlCanvas() : Canvas(nullptr), pImpl(make_unique<Impl>())
#endif
{
}
GlCanvas::~GlCanvas()
{
}
Result GlCanvas::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h) noexcept
{
#ifdef THORVG_GL_RASTER_SUPPORT
//We know renderer type, avoid dynamic_cast for performance.
auto renderer = static_cast<GlRenderer*>(Canvas::pImpl.get()->renderer);
if (!renderer) return Result::MemoryCorruption;
if (!renderer->target(buffer, stride, w, h, 0)) return Result::Unknown;
return Result::Success;
#endif
return Result::NonSupport;
}
unique_ptr<GlCanvas> GlCanvas::gen() noexcept
{
#ifdef THORVG_GL_RASTER_SUPPORT
return unique_ptr<GlCanvas>(new GlCanvas);
#endif
return nullptr;
}

104
src/lib/tvgInitializer.cpp Normal file
View file

@ -0,0 +1,104 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "tvgCommon.h"
#include "tvgLoaderMgr.h"
#ifdef THORVG_SW_RASTER_SUPPORT
#include "tvgSwRenderer.h"
#endif
#ifdef THORVG_GL_RASTER_SUPPORT
#include "tvgGlRenderer.h"
#endif
/************************************************************************/
/* Internal Class Implementation */
/************************************************************************/
static bool initialized = false;
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
Result Initializer::init(CanvasEngine engine, uint32_t threads) noexcept
{
if (initialized) return Result::InsufficientCondition;
auto nonSupport = true;
if (static_cast<uint32_t>(engine) & static_cast<uint32_t>(CanvasEngine::Sw)) {
#ifdef THORVG_SW_RASTER_SUPPORT
if (!SwRenderer::init()) return Result::InsufficientCondition;
nonSupport = false;
#endif
} else if (static_cast<uint32_t>(engine) & static_cast<uint32_t>(CanvasEngine::Gl)) {
#ifdef THORVG_GL_RASTER_SUPPORT
if (!GlRenderer::init()) return Result::InsufficientCondition;
nonSupport = false;
#endif
} else {
return Result::InvalidArguments;
}
if (nonSupport) return Result::NonSupport;
if (!LoaderMgr::init()) return Result::Unknown;
TaskScheduler::init(threads);
initialized = true;
return Result::Success;
}
Result Initializer::term(CanvasEngine engine) noexcept
{
if (!initialized) return Result::InsufficientCondition;
auto nonSupport = true;
if (static_cast<uint32_t>(engine) & static_cast<uint32_t>(CanvasEngine::Sw)) {
#ifdef THORVG_SW_RASTER_SUPPORT
if (!SwRenderer::term()) return Result::InsufficientCondition;
nonSupport = false;
#endif
} else if (static_cast<uint32_t>(engine) & static_cast<uint32_t>(CanvasEngine::Gl)) {
#ifdef THORVG_GL_RASTER_SUPPORT
if (!GlRenderer::term()) return Result::InsufficientCondition;
nonSupport = false;
#endif
} else {
return Result::InvalidArguments;
}
if (nonSupport) return Result::NonSupport;
TaskScheduler::term();
if (!LoaderMgr::term()) return Result::Unknown;
initialized = false;
return Result::Success;
}

View file

@ -0,0 +1,77 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "tvgCommon.h"
/************************************************************************/
/* Internal Class Implementation */
/************************************************************************/
struct LinearGradient::Impl
{
float x1, y1, x2, y2;
};
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
LinearGradient::LinearGradient():pImpl(make_unique<Impl>())
{
_id = FILL_ID_LINEAR;
}
LinearGradient::~LinearGradient()
{
}
Result LinearGradient::linear(float x1, float y1, float x2, float y2) noexcept
{
if (fabsf(x2 - x1) < FLT_EPSILON && fabsf(y2 - y1) < FLT_EPSILON)
return Result::InvalidArguments;
IMPL->x1 = x1;
IMPL->y1 = y1;
IMPL->x2 = x2;
IMPL->y2 = y2;
return Result::Success;
}
Result LinearGradient::linear(float* x1, float* y1, float* x2, float* y2) const noexcept
{
if (x1) *x1 = IMPL->x1;
if (x2) *x2 = IMPL->x2;
if (y1) *y1 = IMPL->y1;
if (y2) *y2 = IMPL->y2;
return Result::Success;
}
unique_ptr<LinearGradient> LinearGradient::gen() noexcept
{
return unique_ptr<LinearGradient>(new LinearGradient);
}

48
src/lib/tvgLoader.h Normal file
View file

@ -0,0 +1,48 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef _TVG_LOADER_H_
#define _TVG_LOADER_H_
namespace tvg
{
class Loader
{
public:
//default view box, if any.
float vx = 0;
float vy = 0;
float vw = 0;
float vh = 0;
virtual ~Loader() {}
virtual bool open(const char* path) = 0;
virtual bool open(const char* data, uint32_t size) = 0;
virtual bool read() = 0;
virtual bool close() = 0;
virtual unique_ptr<Scene> data() = 0;
};
}
#endif //_TVG_LOADER_H_

56
src/lib/tvgLoaderMgr.cpp Normal file
View file

@ -0,0 +1,56 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "tvgCommon.h"
#ifdef THORVG_SVG_LOADER_SUPPORT
#include "tvgSvgLoader.h"
#endif
static int initCnt = 0;
bool LoaderMgr::init()
{
if (initCnt > 0) return true;
++initCnt;
//TODO:
return true;
}
bool LoaderMgr::term()
{
--initCnt;
if (initCnt > 0) return true;
//TODO:
return true;
}
unique_ptr<Loader> LoaderMgr::loader()
{
#ifdef THORVG_SVG_LOADER_SUPPORT
return unique_ptr<SvgLoader>(new SvgLoader);
#endif
return nullptr;
}

32
src/lib/tvgLoaderMgr.h Normal file
View file

@ -0,0 +1,32 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef _TVG_LOADER_MGR_H_
#define _TVG_LOADER_MGR_H_
struct LoaderMgr
{
static bool init();
static bool term();
static unique_ptr<Loader> loader();
};
#endif //_TVG_LOADER_MGR_H_

74
src/lib/tvgPaint.cpp Normal file
View file

@ -0,0 +1,74 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "tvgCommon.h"
/************************************************************************/
/* Internal Class Implementation */
/************************************************************************/
Paint :: Paint() : pImpl(make_unique<Impl>())
{
}
Paint :: ~Paint()
{
}
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
Result Paint::rotate(float degree) noexcept
{
if (IMPL->rotate(degree)) return Result::Success;
return Result::FailedAllocation;
}
Result Paint::scale(float factor) noexcept
{
if (IMPL->scale(factor)) return Result::Success;
return Result::FailedAllocation;
}
Result Paint::translate(float x, float y) noexcept
{
if (IMPL->translate(x, y)) return Result::Success;
return Result::FailedAllocation;
}
Result Paint::transform(const Matrix& m) noexcept
{
if (IMPL->transform(m)) return Result::Success;
return Result::FailedAllocation;
}
Result Paint::bounds(float* x, float* y, float* w, float* h) const noexcept
{
if (IMPL->bounds(x, y, w, h)) return Result::Success;
return Result::InsufficientCondition;
}

180
src/lib/tvgPaint.h Normal file
View file

@ -0,0 +1,180 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef _TVG_PAINT_H_
#define _TVG_PAINT_H_
namespace tvg
{
struct StrategyMethod
{
virtual ~StrategyMethod(){}
virtual bool dispose(RenderMethod& renderer) = 0;
virtual bool update(RenderMethod& renderer, const RenderTransform* transform, RenderUpdateFlag pFlag) = 0;
virtual bool render(RenderMethod& renderer) = 0;
virtual bool bounds(float* x, float* y, float* w, float* h) const = 0;
};
struct Paint::Impl
{
StrategyMethod* smethod = nullptr;
RenderTransform *rTransform = nullptr;
uint32_t flag = RenderUpdateFlag::None;
~Impl() {
if (smethod) delete(smethod);
if (rTransform) delete(rTransform);
}
void method(StrategyMethod* method)
{
smethod = method;
}
bool rotate(float degree)
{
if (rTransform) {
if (fabsf(degree - rTransform->degree) <= FLT_EPSILON) return true;
} else {
if (fabsf(degree) <= FLT_EPSILON) return true;
rTransform = new RenderTransform();
if (!rTransform) return false;
}
rTransform->degree = degree;
if (!rTransform->overriding) flag |= RenderUpdateFlag::Transform;
return true;
}
bool scale(float factor)
{
if (rTransform) {
if (fabsf(factor - rTransform->scale) <= FLT_EPSILON) return true;
} else {
if (fabsf(factor) <= FLT_EPSILON) return true;
rTransform = new RenderTransform();
if (!rTransform) return false;
}
rTransform->scale = factor;
if (!rTransform->overriding) flag |= RenderUpdateFlag::Transform;
return true;
}
bool translate(float x, float y)
{
if (rTransform) {
if (fabsf(x - rTransform->x) <= FLT_EPSILON && fabsf(y - rTransform->y) <= FLT_EPSILON) return true;
} else {
if (fabsf(x) <= FLT_EPSILON && fabsf(y) <= FLT_EPSILON) return true;
rTransform = new RenderTransform();
if (!rTransform) return false;
}
rTransform->x = x;
rTransform->y = y;
if (!rTransform->overriding) flag |= RenderUpdateFlag::Transform;
return true;
}
bool transform(const Matrix& m)
{
if (!rTransform) {
rTransform = new RenderTransform();
if (!rTransform) return false;
}
rTransform->override(m);
flag |= RenderUpdateFlag::Transform;
return true;
}
bool bounds(float* x, float* y, float* w, float* h) const
{
return smethod->bounds(x, y, w, h);
}
bool dispose(RenderMethod& renderer)
{
return smethod->dispose(renderer);
}
bool update(RenderMethod& renderer, const RenderTransform* pTransform, uint32_t pFlag)
{
if (flag & RenderUpdateFlag::Transform) {
if (!rTransform) return false;
if (!rTransform->update()) {
delete(rTransform);
rTransform = nullptr;
}
}
auto newFlag = static_cast<RenderUpdateFlag>(pFlag | flag);
flag = RenderUpdateFlag::None;
if (rTransform && pTransform) {
RenderTransform outTransform(pTransform, rTransform);
return smethod->update(renderer, &outTransform, newFlag);
} else {
auto outTransform = pTransform ? pTransform : rTransform;
return smethod->update(renderer, outTransform, newFlag);
}
}
bool render(RenderMethod& renderer)
{
return smethod->render(renderer);
}
};
template<class T>
struct PaintMethod : StrategyMethod
{
T* inst = nullptr;
PaintMethod(T* _inst) : inst(_inst) {}
~PaintMethod(){}
bool bounds(float* x, float* y, float* w, float* h) const override
{
return inst->bounds(x, y, w, h);
}
bool dispose(RenderMethod& renderer)
{
return inst->dispose(renderer);
}
bool update(RenderMethod& renderer, const RenderTransform* transform, RenderUpdateFlag flag)
{
return inst->update(renderer, transform, flag);
}
bool render(RenderMethod& renderer)
{
return inst->render(renderer);
}
};
}
#endif //_TVG_PAINT_H_

65
src/lib/tvgPicture.cpp Normal file
View file

@ -0,0 +1,65 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "tvgPictureImpl.h"
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
Picture::Picture() : pImpl(make_unique<Impl>())
{
Paint::IMPL->method(new PaintMethod<Picture::Impl>(IMPL));
}
Picture::~Picture()
{
}
unique_ptr<Picture> Picture::gen() noexcept
{
return unique_ptr<Picture>(new Picture);
}
Result Picture::load(const std::string& path) noexcept
{
if (path.empty()) return Result::InvalidArguments;
return IMPL->load(path);
}
Result Picture::load(const char* data, uint32_t size) noexcept
{
if (!data || size <= 0) return Result::InvalidArguments;
return IMPL->load(data, size);
}
Result Picture::viewbox(float* x, float* y, float* w, float* h) const noexcept
{
if (IMPL->viewbox(x, y, w, h)) return Result::Success;
return Result::InsufficientCondition;
}

109
src/lib/tvgPictureImpl.h Normal file
View file

@ -0,0 +1,109 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef _TVG_PICTURE_IMPL_H_
#define _TVG_PICTURE_IMPL_H_
#include "tvgCommon.h"
/************************************************************************/
/* Internal Class Implementation */
/************************************************************************/
struct Picture::Impl
{
unique_ptr<Loader> loader = nullptr;
Paint* paint = nullptr;
bool dispose(RenderMethod& renderer)
{
if (!paint) return false;
paint->IMPL->dispose(renderer);
delete(paint);
return true;
}
bool update(RenderMethod &renderer, const RenderTransform* transform, RenderUpdateFlag flag)
{
if (loader) {
auto scene = loader->data();
if (scene) {
this->paint = scene.release();
if (!this->paint) return false;
loader->close();
}
}
if (!paint) return false;
return paint->IMPL->update(renderer, transform, flag);
}
bool render(RenderMethod &renderer)
{
if (!paint) return false;
return paint->IMPL->render(renderer);
}
bool viewbox(float* x, float* y, float* w, float* h)
{
if (!loader) return false;
if (x) *x = loader->vx;
if (y) *y = loader->vy;
if (w) *w = loader->vw;
if (h) *h = loader->vh;
return true;
}
bool bounds(float* x, float* y, float* w, float* h)
{
if (!paint) return false;
return paint->IMPL->bounds(x, y, w, h);
}
Result load(const string& path)
{
if (loader) loader->close();
loader = LoaderMgr::loader();
if (!loader || !loader->open(path.c_str())) {
//LOG: Non supported format
return Result::NonSupport;
}
if (!loader->read()) return Result::Unknown;
return Result::Success;
}
Result load(const char* data, uint32_t size)
{
if (loader) loader->close();
loader = LoaderMgr::loader();
if (!loader || !loader->open(data, size)) {
//LOG: Non supported load data
return Result::NonSupport;
}
if (!loader->read()) return Result::Unknown;
return Result::Success;
}
};
#endif //_TVG_PICTURE_IMPL_H_

View file

@ -0,0 +1,74 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "tvgCommon.h"
/************************************************************************/
/* Internal Class Implementation */
/************************************************************************/
struct RadialGradient::Impl
{
float cx, cy, radius;
};
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
RadialGradient::RadialGradient():pImpl(make_unique<Impl>())
{
_id = FILL_ID_RADIAL;
}
RadialGradient::~RadialGradient()
{
}
Result RadialGradient::radial(float cx, float cy, float radius) noexcept
{
if (radius < FLT_EPSILON) return Result::InvalidArguments;
IMPL->cx = cx;
IMPL->cy = cy;
IMPL->radius = radius;
return Result::Success;
}
Result RadialGradient::radial(float* cx, float* cy, float* radius) const noexcept
{
if (cx) *cx = IMPL->cx;
if (cy) *cy = IMPL->cy;
if (radius) *radius = IMPL->radius;
return Result::Success;
}
unique_ptr<RadialGradient> RadialGradient::gen() noexcept
{
return unique_ptr<RadialGradient>(new RadialGradient);
}

118
src/lib/tvgRender.cpp Normal file
View file

@ -0,0 +1,118 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "tvgCommon.h"
/************************************************************************/
/* Internal Class Implementation */
/************************************************************************/
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
void RenderTransform::override(const Matrix& m)
{
this->m = m;
if (m.e11 == 0.0f && m.e12 == 0.0f && m.e13 == 0.0f &&
m.e21 == 0.0f && m.e22 == 0.0f && m.e23 == 0.0f &&
m.e31 == 0.0f && m.e32 == 0.0f && m.e33 == 0.0f) {
overriding = false;
} else overriding = true;
}
bool RenderTransform::update()
{
constexpr auto PI = 3.141592f;
if (overriding) return true;
//Init Status
if (fabsf(x) <= FLT_EPSILON && fabsf(y) <= FLT_EPSILON &&
fabsf(degree) <= FLT_EPSILON && fabsf(scale - 1) <= FLT_EPSILON) {
return false;
}
//identity
m.e11 = 1.0f;
m.e12 = 0.0f;
m.e13 = 0.0f;
m.e21 = 0.0f;
m.e22 = 1.0f;
m.e23 = 0.0f;
m.e31 = 0.0f;
m.e32 = 0.0f;
m.e33 = 1.0f;
//scale
m.e11 *= scale;
m.e22 *= scale;
//rotation
if (fabsf(degree) > FLT_EPSILON) {
auto radian = degree / 180.0f * PI;
auto cosVal = cosf(radian);
auto sinVal = sinf(radian);
auto t11 = m.e11 * cosVal + m.e12 * sinVal;
auto t12 = m.e11 * -sinVal + m.e12 * cosVal;
auto t21 = m.e21 * cosVal + m.e22 * sinVal;
auto t22 = m.e21 * -sinVal + m.e22 * cosVal;
auto t13 = m.e13 * cosVal + m.e23 * sinVal;
auto t23 = m.e13 * -sinVal + m.e23 * cosVal;
m.e11 = t11;
m.e12 = t12;
m.e21 = t21;
m.e22 = t22;
m.e13 = t13;
m.e23 = t23;
}
m.e13 += x;
m.e23 += y;
return true;
}
RenderTransform::RenderTransform()
{
}
RenderTransform::RenderTransform(const RenderTransform* lhs, const RenderTransform* rhs)
{
m.e11 = lhs->m.e11 * rhs->m.e11 + lhs->m.e12 * rhs->m.e21 + lhs->m.e13 * rhs->m.e31;
m.e12 = lhs->m.e11 * rhs->m.e12 + lhs->m.e12 * rhs->m.e22 + lhs->m.e13 * rhs->m.e32;
m.e13 = lhs->m.e11 * rhs->m.e13 + lhs->m.e12 * rhs->m.e23 + lhs->m.e13 * rhs->m.e33;
m.e21 = lhs->m.e21 * rhs->m.e11 + lhs->m.e22 * rhs->m.e21 + lhs->m.e23 * rhs->m.e31;
m.e22 = lhs->m.e21 * rhs->m.e12 + lhs->m.e22 * rhs->m.e22 + lhs->m.e23 * rhs->m.e32;
m.e23 = lhs->m.e21 * rhs->m.e13 + lhs->m.e22 * rhs->m.e23 + lhs->m.e23 * rhs->m.e33;
m.e31 = lhs->m.e31 * rhs->m.e11 + lhs->m.e32 * rhs->m.e21 + lhs->m.e33 * rhs->m.e31;
m.e32 = lhs->m.e31 * rhs->m.e12 + lhs->m.e32 * rhs->m.e22 + lhs->m.e33 * rhs->m.e32;
m.e33 = lhs->m.e31 * rhs->m.e13 + lhs->m.e32 * rhs->m.e23 + lhs->m.e33 * rhs->m.e33;
}

128
src/lib/tvgRender.h Normal file
View file

@ -0,0 +1,128 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef _TVG_RENDER_H_
#define _TVG_RENDER_H_
namespace tvg
{
struct Surface
{
//TODO: Union for multiple types
uint32_t* buffer;
uint32_t stride;
uint32_t w, h;
uint32_t cs;
};
enum RenderUpdateFlag {None = 0, Path = 1, Color = 2, Gradient = 4, Stroke = 8, Transform = 16, All = 32};
struct RenderTransform
{
Matrix m; //3x3 Matrix Elements
float x = 0.0f;
float y = 0.0f;
float degree = 0.0f; //rotation degree
float scale = 1.0f; //scale factor
bool overriding = false; //user transform?
bool update();
void override(const Matrix& m);
RenderTransform();
RenderTransform(const RenderTransform* lhs, const RenderTransform* rhs);
};
class RenderMethod
{
public:
virtual ~RenderMethod() {}
virtual void* prepare(TVG_UNUSED const Shape& shape, TVG_UNUSED void* data, TVG_UNUSED const RenderTransform* transform, TVG_UNUSED RenderUpdateFlag flags) { return nullptr; }
virtual bool dispose(TVG_UNUSED const Shape& shape, TVG_UNUSED void *data) { return true; }
virtual bool preRender() { return true; }
virtual bool render(TVG_UNUSED const Shape& shape, TVG_UNUSED void *data) { return true; }
virtual bool postRender() { return true; }
virtual bool clear() { return true; }
virtual bool flush() { return true; }
virtual uint32_t ref() { return 0; }
virtual uint32_t unref() { return 0; }
};
struct RenderInitializer
{
RenderMethod* pInst = nullptr;
uint32_t refCnt = 0;
bool initialized = false;
static bool init(RenderInitializer& renderInit, RenderMethod* engine)
{
if (renderInit.pInst || renderInit.refCnt > 0) return false;
renderInit.pInst = engine;
renderInit.refCnt = 0;
renderInit.initialized = true;
return true;
}
static bool term(RenderInitializer& renderInit)
{
if (!renderInit.pInst || !renderInit.initialized) return false;
renderInit.initialized = false;
//Still it's refered....
if (renderInit.refCnt > 0) return true;
delete(renderInit.pInst);
renderInit.pInst = nullptr;
return true;
}
static uint32_t unref(RenderInitializer& renderInit)
{
--renderInit.refCnt;
//engine has been requested to termination
if (!renderInit.initialized && renderInit.refCnt == 0) {
if (renderInit.pInst) {
delete(renderInit.pInst);
renderInit.pInst = nullptr;
}
}
return renderInit.refCnt;
}
static RenderMethod* inst(RenderInitializer& renderInit)
{
return renderInit.pInst;
}
static uint32_t ref(RenderInitializer& renderInit)
{
return ++renderInit.refCnt;
}
};
}
#endif //_TVG_RENDER_H_

60
src/lib/tvgScene.cpp Normal file
View file

@ -0,0 +1,60 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "tvgSceneImpl.h"
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
Scene::Scene() : pImpl(make_unique<Impl>())
{
Paint::IMPL->method(new PaintMethod<Scene::Impl>(IMPL));
}
Scene::~Scene()
{
}
unique_ptr<Scene> Scene::gen() noexcept
{
return unique_ptr<Scene>(new Scene);
}
Result Scene::push(unique_ptr<Paint> paint) noexcept
{
auto p = paint.release();
if (!p) return Result::MemoryCorruption;
IMPL->paints.push_back(p);
return Result::Success;
}
Result Scene::reserve(uint32_t size) noexcept
{
IMPL->paints.reserve(size);
return Result::Success;
}

93
src/lib/tvgSceneImpl.h Normal file
View file

@ -0,0 +1,93 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef _TVG_SCENE_IMPL_H_
#define _TVG_SCENE_IMPL_H_
#include "tvgCommon.h"
/************************************************************************/
/* Internal Class Implementation */
/************************************************************************/
struct Scene::Impl
{
vector<Paint*> paints;
bool dispose(RenderMethod& renderer)
{
for (auto paint : paints) {
paint->IMPL->dispose(renderer);
delete(paint);
}
paints.clear();
return true;
}
bool update(RenderMethod &renderer, const RenderTransform* transform, RenderUpdateFlag flag)
{
for(auto paint: paints) {
if (!paint->IMPL->update(renderer, transform, static_cast<uint32_t>(flag))) return false;
}
return true;
}
bool render(RenderMethod &renderer)
{
for(auto paint: paints) {
if(!paint->IMPL->render(renderer)) return false;
}
return true;
}
bool bounds(float* px, float* py, float* pw, float* ph)
{
auto x = FLT_MAX;
auto y = FLT_MAX;
auto w = 0.0f;
auto h = 0.0f;
for(auto paint: paints) {
auto x2 = FLT_MAX;
auto y2 = FLT_MAX;
auto w2 = 0.0f;
auto h2 = 0.0f;
if (paint->IMPL->bounds(&x2, &y2, &w2, &h2)) return false;
//Merge regions
if (x2 < x) x = x2;
if (x + w < x2 + w2) w = (x2 + w2) - x;
if (y2 < y) y = x2;
if (y + h < y2 + h2) h = (y2 + h2) - y;
}
if (px) *px = x;
if (py) *py = y;
if (pw) *pw = w;
if (ph) *ph = h;
return true;
}
};
#endif //_TVG_SCENE_IMPL_H_

405
src/lib/tvgShape.cpp Normal file
View file

@ -0,0 +1,405 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <limits>
#include "tvgShapeImpl.h"
/************************************************************************/
/* Internal Class Implementation */
/************************************************************************/
constexpr auto PATH_KAPPA = 0.552284f;
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
Shape :: Shape() : pImpl(make_unique<Impl>(this))
{
Paint::IMPL->method(new PaintMethod<Shape::Impl>(IMPL));
}
Shape :: ~Shape()
{
}
unique_ptr<Shape> Shape::gen() noexcept
{
return unique_ptr<Shape>(new Shape);
}
Result Shape::reset() noexcept
{
IMPL->path->reset();
IMPL->flag |= RenderUpdateFlag::Path;
return Result::Success;
}
uint32_t Shape::pathCommands(const PathCommand** cmds) const noexcept
{
if (!cmds) return 0;
*cmds = IMPL->path->cmds;
return IMPL->path->cmdCnt;
}
uint32_t Shape::pathCoords(const Point** pts) const noexcept
{
if (!pts) return 0;
*pts = IMPL->path->pts;
return IMPL->path->ptsCnt;
}
Result Shape::appendPath(const PathCommand *cmds, uint32_t cmdCnt, const Point* pts, uint32_t ptsCnt) noexcept
{
if (cmdCnt == 0 || ptsCnt == 0 || !pts || !ptsCnt) return Result::InvalidArguments;
IMPL->path->grow(cmdCnt, ptsCnt);
IMPL->path->append(cmds, cmdCnt, pts, ptsCnt);
IMPL->flag |= RenderUpdateFlag::Path;
return Result::Success;
}
Result Shape::moveTo(float x, float y) noexcept
{
IMPL->path->moveTo(x, y);
IMPL->flag |= RenderUpdateFlag::Path;
return Result::Success;
}
Result Shape::lineTo(float x, float y) noexcept
{
IMPL->path->lineTo(x, y);
IMPL->flag |= RenderUpdateFlag::Path;
return Result::Success;
}
Result Shape::cubicTo(float cx1, float cy1, float cx2, float cy2, float x, float y) noexcept
{
IMPL->path->cubicTo(cx1, cy1, cx2, cy2, x, y);
IMPL->flag |= RenderUpdateFlag::Path;
return Result::Success;
}
Result Shape::close() noexcept
{
IMPL->path->close();
IMPL->flag |= RenderUpdateFlag::Path;
return Result::Success;
}
Result Shape::appendCircle(float cx, float cy, float rx, float ry) noexcept
{
auto impl = pImpl.get();
auto rxKappa = rx * PATH_KAPPA;
auto ryKappa = ry * PATH_KAPPA;
impl->path->grow(6, 13);
impl->path->moveTo(cx, cy - ry);
impl->path->cubicTo(cx + rxKappa, cy - ry, cx + rx, cy - ryKappa, cx + rx, cy);
impl->path->cubicTo(cx + rx, cy + ryKappa, cx + rxKappa, cy + ry, cx, cy + ry);
impl->path->cubicTo(cx - rxKappa, cy + ry, cx - rx, cy + ryKappa, cx - rx, cy);
impl->path->cubicTo(cx - rx, cy - ryKappa, cx - rxKappa, cy - ry, cx, cy - ry);
impl->path->close();
impl->flag |= RenderUpdateFlag::Path;
return Result::Success;
}
Result Shape::appendArc(float cx, float cy, float radius, float startAngle, float sweep, bool pie) noexcept
{
const float M_PI_HALF = M_PI * 0.5f;
//just circle
if (sweep >= 360) return appendCircle(cx, cy, radius, radius);
auto impl = pImpl.get();
startAngle = (startAngle * M_PI) / 180;
sweep = sweep * M_PI / 180;
auto nCurves = ceil(abs(sweep / M_PI_HALF));
auto fract = fmod(sweep, M_PI_HALF);
fract = (fract < std::numeric_limits<float>::epsilon()) ? M_PI_HALF : fract;
//Start from here
Point start = {radius * cos(startAngle), radius * sin(startAngle)};
if (pie) {
impl->path->moveTo(cx, cy);
impl->path->lineTo(start.x + cx, start.y + cy);
}else {
impl->path->moveTo(start.x + cx, start.y + cy);
}
for (int i = 0; i < nCurves; ++i) {
auto endAngle = startAngle + ((i != nCurves - 1) ? M_PI_HALF : fract);
Point end = {radius * cos(endAngle), radius * sin(endAngle)};
//variables needed to calculate bezier control points
//get bezier control points using article:
//(http://itc.ktu.lt/index.php/ITC/article/view/11812/6479)
auto ax = start.x;
auto ay = start.y;
auto bx = end.x;
auto by = end.y;
auto q1 = ax * ax + ay * ay;
auto q2 = ax * bx + ay * by + q1;
auto k2 = static_cast<float> (4.0/3.0) * ((sqrt(2 * q1 * q2) - q2) / (ax * by - ay * bx));
start = end; //Next start point is the current end point
end.x += cx;
end.y += cy;
Point ctrl1 = {ax - k2 * ay + cx, ay + k2 * ax + cy};
Point ctrl2 = {bx + k2 * by + cx, by - k2 * bx + cy};
impl->path->cubicTo(ctrl1.x, ctrl1.y, ctrl2.x, ctrl2.y, end.x, end.y);
startAngle = endAngle;
}
if (pie) {
impl->path->moveTo(cx, cy);
impl->path->close();
}
IMPL->flag |= RenderUpdateFlag::Path;
return Result::Success;
}
Result Shape::appendRect(float x, float y, float w, float h, float rx, float ry) noexcept
{
auto impl = pImpl.get();
auto halfW = w * 0.5f;
auto halfH = h * 0.5f;
//clamping cornerRadius by minimum size
if (rx > halfW) rx = halfW;
if (ry > halfH) ry = halfH;
//rectangle
if (rx == 0 && ry == 0) {
impl->path->grow(5, 4);
impl->path->moveTo(x, y);
impl->path->lineTo(x + w, y);
impl->path->lineTo(x + w, y + h);
impl->path->lineTo(x, y + h);
impl->path->close();
//circle
} else if (fabsf(rx - halfW) < FLT_EPSILON && fabsf(ry - halfH) < FLT_EPSILON) {
return appendCircle(x + (w * 0.5f), y + (h * 0.5f), rx, ry);
} else {
auto hrx = rx * 0.5f;
auto hry = ry * 0.5f;
impl->path->grow(10, 17);
impl->path->moveTo(x + rx, y);
impl->path->lineTo(x + w - rx, y);
impl->path->cubicTo(x + w - rx + hrx, y, x + w, y + ry - hry, x + w, y + ry);
impl->path->lineTo(x + w, y + h - ry);
impl->path->cubicTo(x + w, y + h - ry + hry, x + w - rx + hrx, y + h, x + w - rx, y + h);
impl->path->lineTo(x + rx, y + h);
impl->path->cubicTo(x + rx - hrx, y + h, x, y + h - ry + hry, x, y + h - ry);
impl->path->lineTo(x, y + ry);
impl->path->cubicTo(x, y + ry - hry, x + rx - hrx, y, x + rx, y);
impl->path->close();
}
impl->flag |= RenderUpdateFlag::Path;
return Result::Success;
}
Result Shape::fill(uint8_t r, uint8_t g, uint8_t b, uint8_t a) noexcept
{
auto impl = pImpl.get();
impl->color[0] = r;
impl->color[1] = g;
impl->color[2] = b;
impl->color[3] = a;
impl->flag |= RenderUpdateFlag::Color;
if (impl->fill) {
delete(impl->fill);
impl->fill = nullptr;
impl->flag |= RenderUpdateFlag::Gradient;
}
return Result::Success;
}
Result Shape::fill(unique_ptr<Fill> f) noexcept
{
auto impl = pImpl.get();
auto p = f.release();
if (!p) return Result::MemoryCorruption;
if (impl->fill) delete(impl->fill);
impl->fill = p;
impl->flag |= RenderUpdateFlag::Gradient;
return Result::Success;
}
Result Shape::fill(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const noexcept
{
auto impl = pImpl.get();
if (r) *r = impl->color[0];
if (g) *g = impl->color[1];
if (b) *b = impl->color[2];
if (a) *a = impl->color[3];
return Result::Success;
}
const Fill* Shape::fill() const noexcept
{
return IMPL->fill;
}
Result Shape::stroke(float width) noexcept
{
if (!IMPL->strokeWidth(width)) return Result::FailedAllocation;
return Result::Success;
}
float Shape::strokeWidth() const noexcept
{
if (!IMPL->stroke) return 0;
return IMPL->stroke->width;
}
Result Shape::stroke(uint8_t r, uint8_t g, uint8_t b, uint8_t a) noexcept
{
if (!IMPL->strokeColor(r, g, b, a)) return Result::FailedAllocation;
return Result::Success;
}
Result Shape::strokeColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const noexcept
{
auto impl = pImpl.get();
if (!impl->stroke) return Result::InsufficientCondition;
if (r) *r = impl->stroke->color[0];
if (g) *g = impl->stroke->color[1];
if (b) *b = impl->stroke->color[2];
if (a) *a = impl->stroke->color[3];
return Result::Success;
}
Result Shape::stroke(const float* dashPattern, uint32_t cnt) noexcept
{
if (cnt < 2 || !dashPattern) return Result::InvalidArguments;
if (!IMPL->strokeDash(dashPattern, cnt)) return Result::FailedAllocation;
return Result::Success;
}
uint32_t Shape::strokeDash(const float** dashPattern) const noexcept
{
if (!IMPL->stroke) return 0;
if (dashPattern) *dashPattern = IMPL->stroke->dashPattern;
return IMPL->stroke->dashCnt;
}
Result Shape::stroke(StrokeCap cap) noexcept
{
if (!IMPL->strokeCap(cap)) return Result::FailedAllocation;
return Result::Success;
}
Result Shape::stroke(StrokeJoin join) noexcept
{
if (!IMPL->strokeJoin(join)) return Result::FailedAllocation;
return Result::Success;
}
StrokeCap Shape::strokeCap() const noexcept
{
if (!IMPL->stroke) return StrokeCap::Square;
return IMPL->stroke->cap;
}
StrokeJoin Shape::strokeJoin() const noexcept
{
if (!IMPL->stroke) return StrokeJoin::Bevel;
return IMPL->stroke->join;
}

167
src/lib/tvgShapeImpl.h Normal file
View file

@ -0,0 +1,167 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef _TVG_SHAPE_IMPL_H_
#define _TVG_SHAPE_IMPL_H_
#include "tvgCommon.h"
#include "tvgShapePath.h"
/************************************************************************/
/* Internal Class Implementation */
/************************************************************************/
struct ShapeStroke
{
float width = 0;
uint8_t color[4] = {0, 0, 0, 0};
float* dashPattern = nullptr;
uint32_t dashCnt = 0;
StrokeCap cap = StrokeCap::Square;
StrokeJoin join = StrokeJoin::Bevel;
~ShapeStroke()
{
if (dashPattern) free(dashPattern);
}
};
struct Shape::Impl
{
ShapePath *path = nullptr;
Fill *fill = nullptr;
ShapeStroke *stroke = nullptr;
uint8_t color[4] = {0, 0, 0, 0}; //r, g, b, a
void *edata = nullptr; //engine data
Shape *shape = nullptr;
uint32_t flag = RenderUpdateFlag::None;
Impl(Shape* s) : path(new ShapePath), shape(s)
{
}
~Impl()
{
if (path) delete(path);
if (fill) delete(fill);
if (stroke) delete(stroke);
}
bool dispose(RenderMethod& renderer)
{
return renderer.dispose(*shape, edata);
}
bool render(RenderMethod& renderer)
{
return renderer.render(*shape, edata);
}
bool update(RenderMethod& renderer, const RenderTransform* transform, RenderUpdateFlag pFlag)
{
edata = renderer.prepare(*shape, edata, transform, static_cast<RenderUpdateFlag>(pFlag | flag));
flag = RenderUpdateFlag::None;
if (edata) return true;
return false;
}
bool bounds(float* x, float* y, float* w, float* h)
{
if (!path) return false;
return path->bounds(x, y, w, h);
}
bool strokeWidth(float width)
{
//TODO: Size Exception?
if (!stroke) stroke = new ShapeStroke();
if (!stroke) return false;
stroke->width = width;
flag |= RenderUpdateFlag::Stroke;
return true;
}
bool strokeCap(StrokeCap cap)
{
if (!stroke) stroke = new ShapeStroke();
if (!stroke) return false;
stroke->cap = cap;
flag |= RenderUpdateFlag::Stroke;
return true;
}
bool strokeJoin(StrokeJoin join)
{
if (!stroke) stroke = new ShapeStroke();
if (!stroke) return false;
stroke->join = join;
flag |= RenderUpdateFlag::Stroke;
return true;
}
bool strokeColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
{
if (!stroke) stroke = new ShapeStroke();
if (!stroke) return false;
stroke->color[0] = r;
stroke->color[1] = g;
stroke->color[2] = b;
stroke->color[3] = a;
flag |= RenderUpdateFlag::Stroke;
return true;
}
bool strokeDash(const float* pattern, uint32_t cnt)
{
if (!stroke) stroke = new ShapeStroke();
if (!stroke) return false;
if (stroke->dashCnt != cnt) {
if (stroke->dashPattern) free(stroke->dashPattern);
stroke->dashPattern = nullptr;
}
if (!stroke->dashPattern) stroke->dashPattern = static_cast<float*>(malloc(sizeof(float) * cnt));
for (uint32_t i = 0; i < cnt; ++i)
stroke->dashPattern[i] = pattern[i];
stroke->dashCnt = cnt;
flag |= RenderUpdateFlag::Stroke;
return true;
}
};
#endif //_TVG_SHAPE_IMPL_H_

142
src/lib/tvgShapePath.h Normal file
View file

@ -0,0 +1,142 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef _TVG_SHAPE_PATH_H_
#define _TVG_SHAPE_PATH_H_
#include "tvgCommon.h"
/************************************************************************/
/* Internal Class Implementation */
/************************************************************************/
struct ShapePath
{
PathCommand* cmds = nullptr;
uint32_t cmdCnt = 0;
uint32_t reservedCmdCnt = 0;
Point *pts = nullptr;
uint32_t ptsCnt = 0;
uint32_t reservedPtsCnt = 0;
~ShapePath()
{
if (cmds) free(cmds);
if (pts) free(pts);
}
void reserveCmd(uint32_t cmdCnt)
{
if (cmdCnt <= reservedCmdCnt) return;
reservedCmdCnt = cmdCnt;
cmds = static_cast<PathCommand*>(realloc(cmds, sizeof(PathCommand) * reservedCmdCnt));
}
void reservePts(uint32_t ptsCnt)
{
if (ptsCnt <= reservedPtsCnt) return;
reservedPtsCnt = ptsCnt;
pts = static_cast<Point*>(realloc(pts, sizeof(Point) * reservedPtsCnt));
}
void grow(uint32_t cmdCnt, uint32_t ptsCnt)
{
reserveCmd(this->cmdCnt + cmdCnt);
reservePts(this->ptsCnt + ptsCnt);
}
void reset()
{
cmdCnt = 0;
ptsCnt = 0;
}
void append(const PathCommand* cmds, uint32_t cmdCnt, const Point* pts, uint32_t ptsCnt)
{
memcpy(this->cmds + this->cmdCnt, cmds, sizeof(PathCommand) * cmdCnt);
memcpy(this->pts + this->ptsCnt, pts, sizeof(Point) * ptsCnt);
this->cmdCnt += cmdCnt;
this->ptsCnt += ptsCnt;
}
void moveTo(float x, float y)
{
if (cmdCnt + 1 > reservedCmdCnt) reserveCmd((cmdCnt + 1) * 2);
if (ptsCnt + 2 > reservedPtsCnt) reservePts((ptsCnt + 2) * 2);
cmds[cmdCnt++] = PathCommand::MoveTo;
pts[ptsCnt++] = {x, y};
}
void lineTo(float x, float y)
{
if (cmdCnt + 1 > reservedCmdCnt) reserveCmd((cmdCnt + 1) * 2);
if (ptsCnt + 2 > reservedPtsCnt) reservePts((ptsCnt + 2) * 2);
cmds[cmdCnt++] = PathCommand::LineTo;
pts[ptsCnt++] = {x, y};
}
void cubicTo(float cx1, float cy1, float cx2, float cy2, float x, float y)
{
if (cmdCnt + 1 > reservedCmdCnt) reserveCmd((cmdCnt + 1) * 2);
if (ptsCnt + 3 > reservedPtsCnt) reservePts((ptsCnt + 3) * 2);
cmds[cmdCnt++] = PathCommand::CubicTo;
pts[ptsCnt++] = {cx1, cy1};
pts[ptsCnt++] = {cx2, cy2};
pts[ptsCnt++] = {x, y};
}
void close()
{
if (cmdCnt > 0 && cmds[cmdCnt - 1] == PathCommand::Close) return;
if (cmdCnt + 1 > reservedCmdCnt) reserveCmd((cmdCnt + 1) * 2);
cmds[cmdCnt++] = PathCommand::Close;
}
bool bounds(float* x, float* y, float* w, float* h)
{
if (ptsCnt == 0) return false;
Point min = { pts[0].x, pts[0].y };
Point max = { pts[0].x, pts[0].y };
for(uint32_t i = 1; i < ptsCnt; ++i) {
if (pts[i].x < min.x) min.x = pts[i].x;
if (pts[i].y < min.y) min.y = pts[i].y;
if (pts[i].x > max.x) max.x = pts[i].x;
if (pts[i].y > max.y) max.y = pts[i].y;
}
if (x) *x = min.x;
if (y) *y = min.y;
if (w) *w = max.x - min.x;
if (h) *h = max.y - min.y;
return true;
}
};
#endif //_TVG_SHAPE_PATH_H_

83
src/lib/tvgSwCanvas.cpp Normal file
View file

@ -0,0 +1,83 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "tvgCommon.h"
#include "tvgCanvasImpl.h"
#ifdef THORVG_SW_RASTER_SUPPORT
#include "tvgSwRenderer.h"
#else
class SwRenderer : public RenderMethod
{
//Non Supported. Dummy Class */
};
#endif
/************************************************************************/
/* Internal Class Implementation */
/************************************************************************/
struct SwCanvas::Impl
{
Impl() {}
};
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
#ifdef THORVG_SW_RASTER_SUPPORT
SwCanvas::SwCanvas() : Canvas(SwRenderer::inst()), pImpl(make_unique<Impl>())
#else
SwCanvas::SwCanvas() : Canvas(nullptr), pImpl(make_unique<Impl>())
#endif
{
}
SwCanvas::~SwCanvas()
{
}
Result SwCanvas::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h, Colorspace cs) noexcept
{
#ifdef THORVG_SW_RASTER_SUPPORT
//We know renderer type, avoid dynamic_cast for performance.
auto renderer = static_cast<SwRenderer*>(Canvas::pImpl.get()->renderer);
if (!renderer) return Result::MemoryCorruption;
if (!renderer->target(buffer, stride, w, h, cs)) return Result::InvalidArguments;
return Result::Success;
#endif
return Result::NonSupport;
}
unique_ptr<SwCanvas> SwCanvas::gen() noexcept
{
#ifdef THORVG_SW_RASTER_SUPPORT
return unique_ptr<SwCanvas>(new SwCanvas);
#endif
return nullptr;
}

View file

@ -0,0 +1,189 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <deque>
#include <thread>
#include "tvgCommon.h"
/************************************************************************/
/* Internal Class Implementation */
/************************************************************************/
namespace tvg {
struct TaskQueue {
deque<shared_ptr<Task>> taskDeque;
mutex mtx;
condition_variable ready;
bool done = false;
bool tryPop(shared_ptr<Task> &task)
{
unique_lock<mutex> lock{mtx, try_to_lock};
if (!lock || taskDeque.empty()) return false;
task = move(taskDeque.front());
taskDeque.pop_front();
return true;
}
bool tryPush(shared_ptr<Task> &&task)
{
{
unique_lock<mutex> lock{mtx, try_to_lock};
if (!lock) return false;
taskDeque.push_back(move(task));
}
ready.notify_one();
return true;
}
void complete()
{
{
unique_lock<mutex> lock{mtx};
done = true;
}
ready.notify_all();
}
bool pop(shared_ptr<Task> &task)
{
unique_lock<mutex> lock{mtx};
while (taskDeque.empty() && !done) {
ready.wait(lock);
}
if (taskDeque.empty()) return false;
task = move(taskDeque.front());
taskDeque.pop_front();
return true;
}
void push(shared_ptr<Task> &&task)
{
{
unique_lock<mutex> lock{mtx};
taskDeque.push_back(move(task));
}
ready.notify_one();
}
};
class TaskSchedulerImpl
{
public:
unsigned threadCnt;
vector<thread> threads;
vector<TaskQueue> taskQueues{threadCnt};
atomic<unsigned> idx{0};
TaskSchedulerImpl()
{
for (unsigned i = 0; i < threadCnt; ++i) {
threads.emplace_back([&, i] { run(i); });
}
}
~TaskSchedulerImpl()
{
for (auto& queue : taskQueues) queue.complete();
for (auto& thread : threads) thread.join();
}
void run(unsigned i)
{
shared_ptr<Task> task;
//Thread Loop
while (true) {
auto success = false;
for (unsigned i = 0; i < threadCnt * 2; ++i) {
if (taskQueues[(i + i) % threadCnt].tryPop(task)) {
success = true;
break;
}
}
if (!success && !taskQueues[i].pop(task)) break;
(*task)();
}
}
void request(shared_ptr<Task> task)
{
//Async
if (threadCnt > 0) {
task->prepare();
auto i = idx++;
for (unsigned n = 0; n < threadCnt; ++n) {
if (taskQueues[(i + n) % threadCnt].tryPush(move(task))) return;
}
taskQueues[i % threadCnt].push(move(task));
//Sync
} else {
task->run();
}
}
};
}
static TaskSchedulerImpl* inst = nullptr;
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
void TaskScheduler::init(unsigned threads)
{
if (inst) return;
inst = new TaskSchedulerImpl;
inst->threadCnt = threads;
}
void TaskScheduler::term()
{
if (!inst) return;
delete(inst);
inst = nullptr;
}
void TaskScheduler::request(shared_ptr<Task> task)
{
if (inst) {
inst->request(move(task));
}
}

View file

@ -0,0 +1,72 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef _TVG_TASK_SCHEDULER_H_
#define _TVG_TASK_SCHEDULER_H_
#include "tvgCommon.h"
namespace tvg
{
struct Task
{
private:
std::promise<void> sender;
std::future<void> receiver;
public:
virtual ~Task() = default;
void get()
{
if (receiver.valid()) receiver.get();
}
protected:
virtual void run() = 0;
private:
void operator()()
{
run();
sender.set_value();
}
void prepare()
{
sender = std::promise<void>();
receiver = sender.get_future();
}
friend class TaskSchedulerImpl;
};
struct TaskScheduler
{
static void init(unsigned threads);
static void term();
static void request(shared_ptr<Task> task);
};
}
#endif //_TVG_TASK_SCHEDULER_H_

11
src/loaders/meson.build Normal file
View file

@ -0,0 +1,11 @@
subloader_dep = []
if get_option('loaders').contains('svg') == true
subdir('svg')
message('Enable SVG Loader')
endif
loader_dep = declare_dependency(
dependencies: subloader_dep,
include_directories : include_directories('.'),
)

View file

@ -0,0 +1,16 @@
source_file = [
'tvgSimpleXmlParser.h',
'tvgSvgLoader.h',
'tvgSvgLoaderCommon.h',
'tvgSvgPath.h',
'tvgSvgSceneBuilder.h',
'tvgSimpleXmlParser.cpp',
'tvgSvgLoader.cpp',
'tvgSvgPath.cpp',
'tvgSvgSceneBuilder.cpp',
]
subloader_dep += [declare_dependency(
include_directories : include_directories('.'),
sources : source_file
)]

View file

@ -0,0 +1,367 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "tvgSimpleXmlParser.h"
static const char* _simpleXmlFindWhiteSpace(const char* itr, const char* itrEnd)
{
for (; itr < itrEnd; itr++) {
if (isspace((unsigned char)*itr)) break;
}
return itr;
}
static const char* _simpleXmlSkipWhiteSpace(const char* itr, const char* itrEnd)
{
for (; itr < itrEnd; itr++) {
if (!isspace((unsigned char)*itr)) break;
}
return itr;
}
static const char* _simpleXmlUnskipWhiteSpace(const char* itr, const char* itrStart)
{
for (itr--; itr > itrStart; itr--) {
if (!isspace((unsigned char)*itr)) break;
}
return itr + 1;
}
static const char* _simpleXmlFindStartTag(const char* itr, const char* itrEnd)
{
return (const char*)memchr(itr, '<', itrEnd - itr);
}
static const char* _simpleXmlFindEndTag(const char* itr, const char* itrEnd)
{
bool insideQuote = false;
for (; itr < itrEnd; itr++) {
if (*itr == '"') insideQuote = !insideQuote;
if (!insideQuote) {
if ((*itr == '>') || (*itr == '<'))
return itr;
}
}
return nullptr;
}
static const char* _simpleXmlFindEndCommentTag(const char* itr, const char* itrEnd)
{
for (; itr < itrEnd; itr++) {
if ((*itr == '-') && ((itr + 1 < itrEnd) && (*(itr + 1) == '-')) && ((itr + 2 < itrEnd) && (*(itr + 2) == '>'))) return itr + 2;
}
return nullptr;
}
static const char* _simpleXmlFindEndCdataTag(const char* itr, const char* itrEnd)
{
for (; itr < itrEnd; itr++) {
if ((*itr == ']') && ((itr + 1 < itrEnd) && (*(itr + 1) == ']')) && ((itr + 2 < itrEnd) && (*(itr + 2) == '>'))) return itr + 2;
}
return nullptr;
}
static const char* _simpleXmlFindDoctypeChildEndTag(const char* itr, const char* itrEnd)
{
for (; itr < itrEnd; itr++) {
if (*itr == '>') return itr;
}
return nullptr;
}
bool simpleXmlParseAttributes(const char* buf, unsigned bufLength, simpleXMLAttributeCb func, const void* data)
{
const char *itr = buf, *itrEnd = buf + bufLength;
char* tmpBuf = (char*)alloca(bufLength + 1);
if (!buf) return false;
if (!func) return false;
while (itr < itrEnd) {
const char* p = _simpleXmlSkipWhiteSpace(itr, itrEnd);
const char *key, *keyEnd, *value, *valueEnd;
char* tval;
if (p == itrEnd) return true;
key = p;
for (keyEnd = key; keyEnd < itrEnd; keyEnd++) {
if ((*keyEnd == '=') || (isspace((unsigned char)*keyEnd))) break;
}
if (keyEnd == itrEnd) return false;
if (keyEnd == key) continue;
if (*keyEnd == '=') value = keyEnd + 1;
else {
value = (const char*)memchr(keyEnd, '=', itrEnd - keyEnd);
if (!value) return false;
value++;
}
for (; value < itrEnd; value++) {
if (!isspace((unsigned char)*value)) break;
}
if (value == itrEnd) return false;
if ((*value == '"') || (*value == '\'')) {
valueEnd = (const char*)memchr(value + 1, *value, itrEnd - value);
if (!valueEnd) return false;
value++;
} else {
valueEnd = _simpleXmlFindWhiteSpace(value, itrEnd);
}
memcpy(tmpBuf, key, keyEnd - key);
tmpBuf[keyEnd - key] = '\0';
tval = tmpBuf + (keyEnd - key) + 1;
memcpy(tval, value, valueEnd - value);
tval[valueEnd - value] = '\0';
if (!func((void*)data, tmpBuf, tval)) return false;
itr = valueEnd + 1;
}
return true;
}
bool simpleXmlParse(const char* buf, unsigned bufLength, bool strip, simpleXMLCb func, const void* data)
{
const char *itr = buf, *itrEnd = buf + bufLength;
if (!buf) return false;
if (!func) return false;
#define CB(type, start, end) \
do { \
size_t _sz = end - start; \
bool _ret; \
_ret = func((void*)data, type, start, _sz); \
if (!_ret) \
return false; \
} while (0)
while (itr < itrEnd) {
if (itr[0] == '<') {
if (itr + 1 >= itrEnd) {
CB(SimpleXMLType::Error, itr, itrEnd);
return false;
} else {
SimpleXMLType type;
size_t toff;
const char* p;
if (itr[1] == '/') {
type = SimpleXMLType::Close;
toff = 1;
} else if (itr[1] == '?') {
type = SimpleXMLType::Processing;
toff = 1;
} else if (itr[1] == '!') {
if ((itr + sizeof("<!DOCTYPE>") - 1 < itrEnd) && (!memcmp(itr + 2, "DOCTYPE", sizeof("DOCTYPE") - 1)) && ((itr[2 + sizeof("DOCTYPE") - 1] == '>') || (isspace((unsigned char)itr[2 + sizeof("DOCTYPE") - 1])))) {
type = SimpleXMLType::Doctype;
toff = sizeof("!DOCTYPE") - 1;
} else if ((itr + sizeof("<!---->") - 1 < itrEnd) && (!memcmp(itr + 2, "--", sizeof("--") - 1))) {
type = SimpleXMLType::Comment;
toff = sizeof("!--") - 1;
} else if ((itr + sizeof("<![CDATA[]]>") - 1 < itrEnd) && (!memcmp(itr + 2, "[CDATA[", sizeof("[CDATA[") - 1))) {
type = SimpleXMLType::CData;
toff = sizeof("![CDATA[") - 1;
} else if (itr + sizeof("<!>") - 1 < itrEnd) {
type = SimpleXMLType::DoctypeChild;
toff = sizeof("!") - 1;
} else {
type = SimpleXMLType::Open;
toff = 0;
}
} else {
type = SimpleXMLType::Open;
toff = 0;
}
if (type == SimpleXMLType::CData) p = _simpleXmlFindEndCdataTag(itr + 1 + toff, itrEnd);
else if (type == SimpleXMLType::DoctypeChild) p = _simpleXmlFindDoctypeChildEndTag(itr + 1 + toff, itrEnd);
else if (type == SimpleXMLType::Comment) p = _simpleXmlFindEndCommentTag(itr + 1 + toff, itrEnd);
else p = _simpleXmlFindEndTag(itr + 1 + toff, itrEnd);
if ((p) && (*p == '<')) {
type = SimpleXMLType::Error;
toff = 0;
}
if (p) {
const char *start, *end;
start = itr + 1 + toff;
end = p;
switch (type) {
case SimpleXMLType::Open: {
if (p[-1] == '/') {
type = SimpleXMLType::OpenEmpty;
end--;
}
break;
}
case SimpleXMLType::CData: {
if (!memcmp(p - 2, "]]", 2)) end -= 2;
break;
}
case SimpleXMLType::Processing: {
if (p[-1] == '?') end--;
break;
}
case SimpleXMLType::Comment: {
if (!memcmp(p - 2, "--", 2)) end -= 2;
break;
}
case SimpleXMLType::OpenEmpty:
case SimpleXMLType::Close:
case SimpleXMLType::Data:
case SimpleXMLType::Error:
case SimpleXMLType::Doctype:
case SimpleXMLType::DoctypeChild:
case SimpleXMLType::Ignored: {
break;
}
}
if ((strip) && (type != SimpleXMLType::Error) && (type != SimpleXMLType::CData)) {
start = _simpleXmlSkipWhiteSpace(start, end);
end = _simpleXmlUnskipWhiteSpace(end, start + 1);
}
CB(type, start, end);
if (type != SimpleXMLType::Error) itr = p + 1;
else itr = p;
} else {
CB(SimpleXMLType::Error, itr, itrEnd);
return false;
}
}
} else {
const char *p, *end;
if (strip) {
p = _simpleXmlSkipWhiteSpace(itr, itrEnd);
if (p) {
CB(SimpleXMLType::Ignored, itr, p);
itr = p;
}
}
p = _simpleXmlFindStartTag(itr, itrEnd);
if (!p) p = itrEnd;
end = p;
if (strip) end = _simpleXmlUnskipWhiteSpace(end, itr);
if (itr != end) CB(SimpleXMLType::Data, itr, end);
if ((strip) && (end < p)) CB(SimpleXMLType::Ignored, end, p);
itr = p;
}
}
#undef CB
return true;
}
bool simpleXmlParseW3CAttribute(const char* buf, simpleXMLAttributeCb func, const void* data)
{
const char* end;
char* key;
char* val;
char* next;
if (!buf) return false;
end = buf + strlen(buf);
key = (char*)alloca(end - buf + 1);
val = (char*)alloca(end - buf + 1);
if (buf == end) return true;
do {
char* sep = (char*)strchr(buf, ':');
next = (char*)strchr(buf, ';');
key[0] = '\0';
val[0] = '\0';
if (next == nullptr && sep != nullptr) {
memcpy(key, buf, sep - buf);
key[sep - buf] = '\0';
memcpy(val, sep + 1, end - sep - 1);
val[end - sep - 1] = '\0';
} else if (sep < next && sep != nullptr) {
memcpy(key, buf, sep - buf);
key[sep - buf] = '\0';
memcpy(val, sep + 1, next - sep - 1);
val[next - sep - 1] = '\0';
} else if (next) {
memcpy(key, buf, next - buf);
key[next - buf] = '\0';
}
if (key[0]) {
if (!func((void*)data, key, val)) return false;
}
buf = next + 1;
} while (next != nullptr);
return true;
}
const char* simpleXmlFindAttributesTag(const char* buf, unsigned bufLength)
{
const char *itr = buf, *itrEnd = buf + bufLength;
for (; itr < itrEnd; itr++) {
if (!isspace((unsigned char)*itr)) {
//User skip tagname and already gave it the attributes.
if (*itr == '=') return buf;
} else {
itr = _simpleXmlSkipWhiteSpace(itr + 1, itrEnd);
if (itr == itrEnd) return nullptr;
return itr;
}
}
return nullptr;
}

View file

@ -0,0 +1,53 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef _TVG_SIMPLE_XML_PARSER_H_
#define _TVG_SIMPLE_XML_PARSER_H_
#include <ctype.h>
#include <cstring>
#include <alloca.h>
enum class SimpleXMLType
{
Open = 0, //!< \<tag attribute="value"\>
OpenEmpty, //!< \<tag attribute="value" /\>
Close, //!< \</tag\>
Data, //!< tag text data
CData, //!< \<![cdata[something]]\>
Error, //!< error contents
Processing, //!< \<?xml ... ?\> \<?php .. ?\>
Doctype, //!< \<!doctype html
Comment, //!< \<!-- something --\>
Ignored, //!< whatever is ignored by parser, like whitespace
DoctypeChild //!< \<!doctype_child
};
typedef bool (*simpleXMLCb)(void* data, SimpleXMLType type, const char* content, unsigned length);
typedef bool (*simpleXMLAttributeCb)(void* data, const char* key, const char* value);
bool simpleXmlParseAttributes(const char* buf, unsigned buflen, simpleXMLAttributeCb func, const void* data);
bool simpleXmlParse(const char* buf, unsigned buflen, bool strip, simpleXMLCb func, const void* data);
bool simpleXmlParseW3CAttribute(const char* buf, simpleXMLAttributeCb func, const void* data);
const char *simpleXmlFindAttributesTag(const char* buf, unsigned buflen);
#endif //_TVG_SIMPLE_XML_PARSER_H_

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,53 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef _TVG_SVG_LOADER_H_
#define _TVG_SVG_LOADER_H_
#include "tvgSvgLoaderCommon.h"
#include "tvgSvgSceneBuilder.h"
class SvgLoader : public Loader
{
private:
string filePath;
const char* content = nullptr;
uint32_t size = 0;
SvgLoaderData loaderData;
SvgSceneBuilder builder;
unique_ptr<Scene> root;
public:
SvgLoader();
~SvgLoader();
bool open(const char* path) override;
bool open(const char* data, uint32_t size) override;
bool header();
bool read() override;
bool close() override;
unique_ptr<Scene> data() override;
};
#endif //_TVG_SVG_LOADER_H_

View file

@ -0,0 +1,333 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef _TVG_SVG_LOADER_COMMON_H_
#define _TVG_SVG_LOADER_COMMON_H_
#include "tvgCommon.h"
#include "tvgSimpleXmlParser.h"
enum class SvgNodeType
{
Doc,
G,
Defs,
//Switch, //Not support
Animation,
Arc,
Circle,
Ellipse,
Image,
Line,
Path,
Polygon,
Polyline,
Rect,
Text,
TextArea,
Tspan,
Use,
Video,
//Custome_command, //Not support
Unknown
};
enum class SvgLengthType
{
Percent,
Px,
Pc,
Pt,
Mm,
Cm,
In,
};
enum class SvgFillFlags
{
Paint = 0x1,
Opacity = 0x2,
Gradient = 0x4,
FillRule = 0x8
};
enum class SvgStrokeFlags
{
Paint = 0x1,
Opacity = 0x2,
Gradient = 0x4,
Scale = 0x8,
Width = 0x10,
Cap = 0x20,
Join = 0x40,
Dash = 0x80,
};
enum class SvgGradientType
{
Linear,
Radial
};
enum class SvgStyleType
{
Quality,
Fill,
ViewportFill,
Font,
Stroke,
SolidColor,
Gradient,
Transform,
Opacity,
CompOp
};
enum class SvgFillRule
{
Winding = 0,
OddEven = 1
};
//Length type to recalculate %, pt, pc, mm, cm etc
enum class SvgParserLengthType
{
Vertical,
Horizontal,
//In case of, for example, radius of radial gradient
Other
};
typedef struct _SvgNode SvgNode;
typedef struct _SvgStyleGradient SvgStyleGradient;
struct SvgDocNode
{
float w;
float h;
float vx;
float vy;
float vw;
float vh;
SvgNode* defs;
bool preserveAspect;
};
struct SvgGNode
{
};
struct SvgDefsNode
{
vector<SvgStyleGradient *> gradients;
};
struct SvgArcNode
{
};
struct SvgEllipseNode
{
float cx;
float cy;
float rx;
float ry;
};
struct SvgCircleNode
{
float cx;
float cy;
float r;
};
struct SvgRectNode
{
float x;
float y;
float w;
float h;
float rx;
float ry;
};
struct SvgLineNode
{
float x1;
float y1;
float x2;
float y2;
};
struct SvgPathNode
{
string* path;
};
struct SvgPolygonNode
{
int pointsCount;
float* points;
};
struct SvgLinearGradient
{
float x1;
float y1;
float x2;
float y2;
};
struct SvgRadialGradient
{
float cx;
float cy;
float fx;
float fy;
float r;
};
struct SvgGradientStop
{
float offset;
uint8_t r;
uint8_t g;
uint8_t b;
uint8_t a;
};
struct SvgPaint
{
SvgStyleGradient* gradient;
string *url;
uint8_t r;
uint8_t g;
uint8_t b;
bool none;
bool curColor;
};
struct SvgDash
{
float length;
float gap;
};
struct _SvgStyleGradient
{
SvgGradientType type;
string *id;
string *ref;
FillSpread spread;
SvgRadialGradient* radial;
SvgLinearGradient* linear;
Matrix* transform;
vector<Fill::ColorStop *> stops;
bool userSpace;
bool usePercentage;
};
struct SvgStyleFill
{
SvgFillFlags flags;
SvgPaint paint;
int opacity;
SvgFillRule fillRule;
};
struct SvgStyleStroke
{
SvgStrokeFlags flags;
SvgPaint paint;
int opacity;
float scale;
float width;
float centered;
StrokeCap cap;
StrokeJoin join;
SvgDash* dash;
int dashCount;
};
struct SvgStyleProperty
{
SvgStyleFill fill;
SvgStyleStroke stroke;
int opacity;
uint8_t r;
uint8_t g;
uint8_t b;
};
struct _SvgNode
{
SvgNodeType type;
SvgNode* parent;
vector<SvgNode*> child;
string *id;
SvgStyleProperty *style;
Matrix* transform;
union {
SvgGNode g;
SvgDocNode doc;
SvgDefsNode defs;
SvgArcNode arc;
SvgCircleNode circle;
SvgEllipseNode ellipse;
SvgPolygonNode polygon;
SvgPolygonNode polyline;
SvgRectNode rect;
SvgPathNode path;
SvgLineNode line;
} node;
bool display;
};
struct SvgParser
{
SvgNode* node;
SvgStyleGradient* styleGrad;
Fill::ColorStop* gradStop;
struct
{
int x, y;
uint32_t w, h;
} global;
struct
{
bool parsedFx;
bool parsedFy;
} gradient;
};
struct SvgLoaderData
{
vector<SvgNode *> stack;
SvgNode* doc = nullptr;
SvgNode* def = nullptr;
vector<SvgStyleGradient*> gradients;
SvgStyleGradient* latestGradient = nullptr; //For stops
SvgParser* svgParse = nullptr;
int level = 0;
bool result = false;
};
#endif

View file

@ -0,0 +1,522 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "tvgSvgPath.h"
static char* _skipComma(const char* content)
{
while (*content && isspace(*content)) {
content++;
}
if (*content == ',') return (char*)content + 1;
return (char*)content;
}
static bool _parseNumber(char** content, float* number)
{
char* end = NULL;
*number = strtof(*content, &end);
//If the start of string is not number
if ((*content) == end) return false;
//Skip comma if any
*content = _skipComma(end);
return true;
}
static bool _parseLong(char** content, int* number)
{
char* end = NULL;
*number = strtol(*content, &end, 10) ? 1 : 0;
//If the start of string is not number
if ((*content) == end) return false;
*content = _skipComma(end);
return true;
}
void _pathAppendArcTo(vector<PathCommand>* cmds, vector<Point>* pts, Point* cur, Point* curCtl, float x, float y, float rx, float ry, float angle, bool largeArc, bool sweep)
{
float cxp, cyp, cx, cy;
float sx, sy;
float cosPhi, sinPhi;
float dx2, dy2;
float x1p, y1p;
float x1p2, y1p2;
float rx2, ry2;
float lambda;
float c;
float at;
float theta1, deltaTheta;
float nat;
float delta, bcp;
float cosPhiRx, cosPhiRy;
float sinPhiRx, sinPhiRy;
float cosTheta1, sinTheta1;
int segments, i;
//Some helpful stuff is available here:
//http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes
sx = cur->x;
sy = cur->y;
//If start and end points are identical, then no arc is drawn
if ((fabs(x - sx) < (1.0f / 256.0f)) && (fabs(y - sy) < (1.0f / 256.0f))) return;
//Correction of out-of-range radii, see F6.6.1 (step 2)
rx = fabs(rx);
ry = fabs(ry);
if ((rx < 0.5f) || (ry < 0.5f)) {
Point p = {x, y};
cmds->push_back(PathCommand::LineTo);
pts->push_back(p);
*cur = p;
return;
}
angle = angle * M_PI / 180.0f;
cosPhi = cosf(angle);
sinPhi = sinf(angle);
dx2 = (sx - x) / 2.0f;
dy2 = (sy - y) / 2.0f;
x1p = cosPhi * dx2 + sinPhi * dy2;
y1p = cosPhi * dy2 - sinPhi * dx2;
x1p2 = x1p * x1p;
y1p2 = y1p * y1p;
rx2 = rx * rx;
ry2 = ry * ry;
lambda = (x1p2 / rx2) + (y1p2 / ry2);
//Correction of out-of-range radii, see F6.6.2 (step 4)
if (lambda > 1.0f) {
//See F6.6.3
float lambdaRoot = sqrt(lambda);
rx *= lambdaRoot;
ry *= lambdaRoot;
//Update rx2 and ry2
rx2 = rx * rx;
ry2 = ry * ry;
}
c = (rx2 * ry2) - (rx2 * y1p2) - (ry2 * x1p2);
//Check if there is no possible solution
//(i.e. we can't do a square root of a negative value)
if (c < 0.0f) {
//Scale uniformly until we have a single solution
//(see F6.2) i.e. when c == 0.0
float scale = sqrt(1.0f - c / (rx2 * ry2));
rx *= scale;
ry *= scale;
//Update rx2 and ry2
rx2 = rx * rx;
ry2 = ry * ry;
//Step 2 (F6.5.2) - simplified since c == 0.0
cxp = 0.0f;
cyp = 0.0f;
//Step 3 (F6.5.3 first part) - simplified since cxp and cyp == 0.0
cx = 0.0f;
cy = 0.0f;
} else {
//Complete c calculation
c = sqrt(c / ((rx2 * y1p2) + (ry2 * x1p2)));
//Inverse sign if Fa == Fs
if (largeArc == sweep) c = -c;
//Step 2 (F6.5.2)
cxp = c * (rx * y1p / ry);
cyp = c * (-ry * x1p / rx);
//Step 3 (F6.5.3 first part)
cx = cosPhi * cxp - sinPhi * cyp;
cy = sinPhi * cxp + cosPhi * cyp;
}
//Step 3 (F6.5.3 second part) we now have the center point of the ellipse
cx += (sx + x) / 2.0f;
cy += (sy + y) / 2.0f;
//Sstep 4 (F6.5.4)
//We dont' use arccos (as per w3c doc), see
//http://www.euclideanspace.com/maths/algebra/vectors/angleBetween/index.htm
//Note: atan2 (0.0, 1.0) == 0.0
at = atan2(((y1p - cyp) / ry), ((x1p - cxp) / rx));
theta1 = (at < 0.0f) ? 2.0f * M_PI + at : at;
nat = atan2(((-y1p - cyp) / ry), ((-x1p - cxp) / rx));
deltaTheta = (nat < at) ? 2.0f * M_PI - at + nat : nat - at;
if (sweep) {
//Ensure delta theta < 0 or else add 360 degrees
if (deltaTheta < 0.0f) deltaTheta += 2.0f * M_PI;
} else {
//Ensure delta theta > 0 or else substract 360 degrees
if (deltaTheta > 0.0f) deltaTheta -= 2.0f * M_PI;
}
//Add several cubic bezier to approximate the arc
//(smaller than 90 degrees)
//We add one extra segment because we want something
//Smaller than 90deg (i.e. not 90 itself)
segments = (int)(fabs(deltaTheta / M_PI_2)) + 1.0f;
delta = deltaTheta / segments;
//http://www.stillhq.com/ctpfaq/2001/comp.text.pdf-faq-2001-04.txt (section 2.13)
bcp = 4.0f / 3.0f * (1.0f - cos(delta / 2.0f)) / sin(delta / 2.0f);
cosPhiRx = cosPhi * rx;
cosPhiRy = cosPhi * ry;
sinPhiRx = sinPhi * rx;
sinPhiRy = sinPhi * ry;
cosTheta1 = cos(theta1);
sinTheta1 = sin(theta1);
for (i = 0; i < segments; ++i) {
//End angle (for this segment) = current + delta
float c1x, c1y, ex, ey, c2x, c2y;
float theta2 = theta1 + delta;
float cosTheta2 = cos(theta2);
float sinTheta2 = sin(theta2);
static Point p[3];
//First control point (based on start point sx,sy)
c1x = sx - bcp * (cosPhiRx * sinTheta1 + sinPhiRy * cosTheta1);
c1y = sy + bcp * (cosPhiRy * cosTheta1 - sinPhiRx * sinTheta1);
//End point (for this segment)
ex = cx + (cosPhiRx * cosTheta2 - sinPhiRy * sinTheta2);
ey = cy + (sinPhiRx * cosTheta2 + cosPhiRy * sinTheta2);
//Second control point (based on end point ex,ey)
c2x = ex + bcp * (cosPhiRx * sinTheta2 + sinPhiRy * cosTheta2);
c2y = ey + bcp * (sinPhiRx * sinTheta2 - cosPhiRy * cosTheta2);
cmds->push_back(PathCommand::CubicTo);
p[0] = {c1x, c1y};
p[1] = {c2x, c2y};
p[2] = {ex, ey};
pts->push_back(p[0]);
pts->push_back(p[1]);
pts->push_back(p[2]);
*curCtl = p[1];
*cur = p[2];
//Next start point is the current end point (same for angle)
sx = ex;
sy = ey;
theta1 = theta2;
//Avoid recomputations
cosTheta1 = cosTheta2;
sinTheta1 = sinTheta2;
}
}
static int _numberCount(char cmd)
{
int count = 0;
switch (cmd) {
case 'M':
case 'm':
case 'L':
case 'l':
case 'T':
case 't': {
count = 2;
break;
}
case 'C':
case 'c':
case 'E':
case 'e': {
count = 6;
break;
}
case 'H':
case 'h':
case 'V':
case 'v': {
count = 1;
break;
}
case 'S':
case 's':
case 'Q':
case 'q': {
count = 4;
break;
}
case 'A':
case 'a': {
count = 7;
break;
}
default:
break;
}
return count;
}
static void _processCommand(vector<PathCommand>* cmds, vector<Point>* pts, char cmd, float* arr, int count, Point* cur, Point* curCtl, bool *isQuadratic)
{
int i;
switch (cmd) {
case 'm':
case 'l':
case 'c':
case 's':
case 'q':
case 't': {
for (i = 0; i < count - 1; i += 2) {
arr[i] = arr[i] + cur->x;
arr[i + 1] = arr[i + 1] + cur->y;
}
break;
}
case 'h': {
arr[0] = arr[0] + cur->x;
break;
}
case 'v': {
arr[0] = arr[0] + cur->y;
break;
}
case 'a': {
arr[5] = arr[5] + cur->x;
arr[6] = arr[6] + cur->y;
break;
}
default: {
break;
}
}
switch (cmd) {
case 'm':
case 'M': {
Point p = {arr[0], arr[1]};
cmds->push_back(PathCommand::MoveTo);
pts->push_back(p);
*cur = {arr[0] ,arr[1]};
break;
}
case 'l':
case 'L': {
Point p = {arr[0], arr[1]};
cmds->push_back(PathCommand::LineTo);
pts->push_back(p);
*cur = {arr[0] ,arr[1]};
break;
}
case 'c':
case 'C': {
Point p[3];
cmds->push_back(PathCommand::CubicTo);
p[0] = {arr[0], arr[1]};
p[1] = {arr[2], arr[3]};
p[2] = {arr[4], arr[5]};
pts->push_back(p[0]);
pts->push_back(p[1]);
pts->push_back(p[2]);
*curCtl = p[1];
*cur = p[2];
*isQuadratic = false;
break;
}
case 's':
case 'S': {
Point p[3], ctrl;
if ((cmds->size() > 1) && (cmds->at(cmds->size() - 1) == PathCommand::CubicTo) &&
!(*isQuadratic)) {
ctrl.x = 2 * cur->x - curCtl->x;
ctrl.y = 2 * cur->y - curCtl->y;
} else {
ctrl = *cur;
}
cmds->push_back(PathCommand::CubicTo);
p[0] = ctrl;
p[1] = {arr[0], arr[1]};
p[2] = {arr[2], arr[3]};
pts->push_back(p[0]);
pts->push_back(p[1]);
pts->push_back(p[2]);
*curCtl = p[1];
*cur = p[2];
*isQuadratic = false;
break;
}
case 'q':
case 'Q': {
Point p[3];
float ctrl_x0 = (cur->x + 2 * arr[0]) * (1.0 / 3.0);
float ctrl_y0 = (cur->y + 2 * arr[1]) * (1.0 / 3.0);
float ctrl_x1 = (arr[2] + 2 * arr[0]) * (1.0 / 3.0);
float ctrl_y1 = (arr[3] + 2 * arr[1]) * (1.0 / 3.0);
cmds->push_back(PathCommand::CubicTo);
p[0] = {ctrl_x0, ctrl_y0};
p[1] = {ctrl_x1, ctrl_y1};
p[2] = {arr[2], arr[3]};
pts->push_back(p[0]);
pts->push_back(p[1]);
pts->push_back(p[2]);
*curCtl = {arr[0], arr[1]};
*cur = p[2];
*isQuadratic = true;
break;
}
case 't':
case 'T': {
Point p[3], ctrl;
if ((cmds->size() > 1) && (cmds->at(cmds->size() - 1) == PathCommand::CubicTo) &&
*isQuadratic) {
ctrl.x = 2 * cur->x - curCtl->x;
ctrl.y = 2 * cur->y - curCtl->y;
} else {
ctrl = *cur;
}
float ctrl_x0 = (cur->x + 2 * ctrl.x) * (1.0 / 3.0);
float ctrl_y0 = (cur->y + 2 * ctrl.y) * (1.0 / 3.0);
float ctrl_x1 = (arr[0] + 2 * ctrl.x) * (1.0 / 3.0);
float ctrl_y1 = (arr[1] + 2 * ctrl.y) * (1.0 / 3.0);
cmds->push_back(PathCommand::CubicTo);
p[0] = {ctrl_x0, ctrl_y0};
p[1] = {ctrl_x1, ctrl_y1};
p[2] = {arr[0], arr[1]};
pts->push_back(p[0]);
pts->push_back(p[1]);
pts->push_back(p[2]);
*curCtl = {ctrl.x, ctrl.y};
*cur = p[2];
*isQuadratic = true;
break;
}
case 'h':
case 'H': {
Point p = {arr[0], cur->y};
cmds->push_back(PathCommand::LineTo);
pts->push_back(p);
cur->x = arr[0];
break;
}
case 'v':
case 'V': {
Point p = {cur->x, arr[0]};
cmds->push_back(PathCommand::LineTo);
pts->push_back(p);
cur->y = arr[0];
break;
}
case 'z':
case 'Z': {
cmds->push_back(PathCommand::Close);
break;
}
case 'a':
case 'A': {
_pathAppendArcTo(cmds, pts, cur, curCtl, arr[5], arr[6], arr[0], arr[1], arr[2], arr[3], arr[4]);
*cur = {arr[5] ,arr[6]};
break;
}
default: {
break;
}
}
}
static char* _nextCommand(char* path, char* cmd, float* arr, int* count)
{
int i = 0, large, sweep;
path = _skipComma(path);
if (isalpha(*path)) {
*cmd = *path;
path++;
*count = _numberCount(*cmd);
} else {
if (*cmd == 'm') *cmd = 'l';
else if (*cmd == 'M') *cmd = 'L';
}
if (*count == 7) {
//Special case for arc command
if (_parseNumber(&path, &arr[0])) {
if (_parseNumber(&path, &arr[1])) {
if (_parseNumber(&path, &arr[2])) {
if (_parseLong(&path, &large)) {
if (_parseLong(&path, &sweep)) {
if (_parseNumber(&path, &arr[5])) {
if (_parseNumber(&path, &arr[6])) {
arr[3] = large;
arr[4] = sweep;
return path;
}
}
}
}
}
}
}
*count = 0;
return NULL;
}
for (i = 0; i < *count; i++) {
if (!_parseNumber(&path, &arr[i])) {
*count = 0;
return NULL;
}
path = _skipComma(path);
}
return path;
}
tuple<vector<PathCommand>, vector<Point>> svgPathToTvgPath(const char* svgPath)
{
vector<PathCommand> cmds;
vector<Point> pts;
float numberArray[7];
int numberCount = 0;
Point cur = { 0, 0 };
Point curCtl = { 0, 0 };
char cmd = 0;
bool isQuadratic = false;
char* path = (char*)svgPath;
char* curLocale;
curLocale = setlocale(LC_NUMERIC, NULL);
if (curLocale) curLocale = strdup(curLocale);
setlocale(LC_NUMERIC, "POSIX");
while ((path[0] != '\0')) {
path = _nextCommand(path, &cmd, numberArray, &numberCount);
if (!path) break;
_processCommand(&cmds, &pts, cmd, numberArray, numberCount, &cur, &curCtl, &isQuadratic);
}
setlocale(LC_NUMERIC, curLocale);
if (curLocale) free(curLocale);
return make_tuple(cmds, pts);
}

View file

@ -0,0 +1,30 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef _TVG_SVG_PATH_H_
#define _TVG_SVG_PATH_H_
#include "tvgCommon.h"
tuple<vector<tvg::PathCommand>, vector<tvg::Point>> svgPathToTvgPath(const char* svg_path_data);
#endif //_TVG_SVG_PATH_H_

View file

@ -0,0 +1,372 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "tvgSvgSceneBuilder.h"
unique_ptr<LinearGradient> _applyLinearGradientProperty(SvgStyleGradient* g, Shape* vg, float rx, float ry, float rw, float rh)
{
Fill::ColorStop* stops;
int stopCount = 0;
float fillOpacity = 255.0f;
float gx, gy, gw, gh;
auto fillGrad = LinearGradient::gen();
if (g->usePercentage) {
g->linear->x1 = g->linear->x1 * rw + rx;
g->linear->y1 = g->linear->y1 * rh + ry;
g->linear->x2 = g->linear->x2 * rw + rx;
g->linear->y2 = g->linear->y2 * rh + ry;
}
//In case of objectBoundingBox it need proper scaling
if (!g->userSpace) {
float scaleX = 1.0, scaleReversedX = 1.0;
float scaleY = 1.0, scaleReversedY = 1.0;
//Check the smallest size, find the scale value
if (rh > rw) {
scaleY = ((float)rw) / rh;
scaleReversedY = ((float)rh) / rw;
} else {
scaleX = ((float)rh) / rw;
scaleReversedX = ((float)rw) / rh;
}
vg->bounds(&gx, &gy, &gw, &gh);
float cy = ((float)gh) * 0.5 + gy;
float cy_scaled = (((float)gh) * 0.5) * scaleReversedY;
float cx = ((float)gw) * 0.5 + gx;
float cx_scaled = (((float)gw) * 0.5) * scaleReversedX;
//= T(gx, gy) x S(scaleX, scaleY) x T(cx_scaled - cx, cy_scaled - cy) x (radial->x, radial->y)
g->linear->x1 = g->linear->x1 * scaleX + scaleX * (cx_scaled - cx) + gx;
g->linear->y1 = g->linear->y1 * scaleY + scaleY * (cy_scaled - cy) + gy;
g->linear->x2 = g->linear->x2 * scaleX + scaleX * (cx_scaled - cx) + gx;
g->linear->y2 = g->linear->y2 * scaleY + scaleY * (cy_scaled - cy) + gy;
}
if (g->transform) {
float cy = ((float) rh) * 0.5 + ry;
float cx = ((float) rw) * 0.5 + rx;
//Calc start point
//= T(x - cx, y - cy) x g->transform x T(cx, cy)
g->linear->x1 = cx * (g->transform->e11 + g->transform->e31 * (g->linear->x1 - cx)) +
cx * (g->transform->e12 + g->transform->e32 * (g->linear->x1 - cx)) +
cx * (g->transform->e13 + g->transform->e33 * (g->linear->x1 - cx));
g->linear->y1 = cy * (g->transform->e21 + g->transform->e31 * (g->linear->y1 - cy)) +
cy * (g->transform->e22 + g->transform->e32 * (g->linear->y1 - cy)) +
cy * (g->transform->e23 + g->transform->e33 * (g->linear->y1 - cy));
//Calc end point
g->linear->x2 = cx * (g->transform->e11 + g->transform->e31 * (g->linear->x2 - cx)) +
cx * (g->transform->e12 + g->transform->e32 * (g->linear->x2 - cx)) +
cx * (g->transform->e13 + g->transform->e33 * (g->linear->x2 - cx));
g->linear->y2 = cy * (g->transform->e21 + g->transform->e31 * (g->linear->y2 - cy)) +
cy * (g->transform->e22 + g->transform->e32 * (g->linear->y2 - cy)) +
cy * (g->transform->e23 + g->transform->e33 * (g->linear->y2 - cy));
}
fillGrad->linear(g->linear->x1, g->linear->y1, g->linear->x2, g->linear->y2);
fillGrad->spread(g->spread);
//Update the stops
stopCount = g->stops.size();
if (stopCount > 0) {
float opacity;
float fopacity = fillOpacity / 255.0f; //fill opacity if any exists.
int i = 0;
stops = (Fill::ColorStop*)calloc(stopCount, sizeof(Fill::ColorStop));
for (auto colorStop : g->stops) {
//Use premultiplied color
opacity = ((float)colorStop->a / 255.0f) * fopacity;
stops[i].r = colorStop->r * opacity;
stops[i].g = colorStop->g * opacity;
stops[i].b = colorStop->b * opacity;
stops[i].a = colorStop->a * fopacity;
stops[i].offset = colorStop->offset;
i++;
}
fillGrad->colorStops(stops, stopCount);
free(stops);
}
return fillGrad;
}
unique_ptr<RadialGradient> _applyRadialGradientProperty(SvgStyleGradient* g, Shape* vg, float rx, float ry, float rw, float rh)
{
Fill::ColorStop *stops;
int stopCount = 0;
float gx, gy, gw, gh;
int radius;
float fillOpacity = 255.0f;
auto fillGrad = RadialGradient::gen();
radius = sqrt(pow(rw, 2) + pow(rh, 2)) / sqrt(2.0);
if (!g->userSpace) {
//That is according to Units in here
//https://www.w3.org/TR/2015/WD-SVG2-20150915/coords.html
int min = (rh > rw) ? rw : rh;
radius = sqrt(pow(min, 2) + pow(min, 2)) / sqrt(2.0);
}
if (g->usePercentage) {
g->radial->cx = g->radial->cx * rw + rx;
g->radial->cy = g->radial->cy * rh + ry;
g->radial->r = g->radial->r * radius;
g->radial->fx = g->radial->fx * rw + rx;
g->radial->fy = g->radial->fy * rh + ry;
}
//In case of objectBoundingBox it need proper scaling
if (!g->userSpace) {
float scaleX = 1.0, scaleReversedX = 1.0;
float scaleY = 1.0, scaleReversedY = 1.0;
//Check the smallest size, find the scale value
if (rh > rw) {
scaleY = ((float)rw) / rh;
scaleReversedY = ((float)rh) / rw;
} else {
scaleX = ((float)rh) / rw;
scaleReversedX = ((float)rw) / rh;
}
vg->bounds(&gx, &gy, &gw, &gh);
float cy = ((float)gh) * 0.5 + gy;
float cy_scaled = (((float)gh) * 0.5) * scaleReversedY;
float cx = ((float)gw) * 0.5 + gx;
float cx_scaled = (((float)gw) * 0.5) * scaleReversedX;
//= T(gx, gy) x S(scaleX, scaleY) x T(cx_scaled - cx, cy_scaled - cy) x (radial->x, radial->y)
g->radial->cx = g->radial->cx * scaleX + scaleX * (cx_scaled - cx) + gx;
g->radial->cy = g->radial->cy * scaleY + scaleY * (cy_scaled - cy) + gy;
}
//TODO: Radial gradient transformation is not yet supported.
//if (g->transform) {}
//TODO: Tvg is not support to focal
//if (g->radial->fx != 0 && g->radial->fy != 0) {
// fillGrad->radial(g->radial->fx, g->radial->fy, g->radial->r);
//}
fillGrad->radial(g->radial->cx, g->radial->cy, g->radial->r);
fillGrad->spread(g->spread);
//Update the stops
stopCount = g->stops.size();
if (stopCount > 0) {
float opacity;
float fopacity = fillOpacity / 255.0f; //fill opacity if any exists.
int i = 0;
stops = (Fill::ColorStop*)calloc(stopCount, sizeof(Fill::ColorStop));
for (auto colorStop : g->stops) {
//Use premultiplied color
opacity = ((float)colorStop->a / 255.0f) * fopacity;
stops[i].r = colorStop->r * opacity;
stops[i].g = colorStop->g * opacity;
stops[i].b = colorStop->b * opacity;
stops[i].a = colorStop->a * fopacity;
stops[i].offset = colorStop->offset;
i++;
}
fillGrad->colorStops(stops, stopCount);
free(stops);
}
return fillGrad;
}
void _applyProperty(SvgNode* node, Shape* vg, float vx, float vy, float vw, float vh)
{
SvgStyleProperty* style = node->style;
if (node->transform) vg->transform(*node->transform);
if (node->type == SvgNodeType::Doc || !node->display) return;
//If fill property is nullptr then do nothing
if (style->fill.paint.none) {
//Do nothing
} else if (style->fill.paint.gradient) {
if (!style->fill.paint.gradient->userSpace) vg->bounds(&vx, &vy, &vw, &vh);
if (style->fill.paint.gradient->type == SvgGradientType::Linear) {
auto linear = _applyLinearGradientProperty(style->fill.paint.gradient, vg, vx, vy, vw, vh);
vg->fill(move(linear));
} else if (style->fill.paint.gradient->type == SvgGradientType::Radial) {
auto radial = _applyRadialGradientProperty(style->fill.paint.gradient, vg, vx, vy, vw, vh);
vg->fill(move(radial));
}
} else if (style->fill.paint.curColor) {
//Apply the current style color
float fa = ((float)style->fill.opacity / 255.0);
vg->fill(((float)style->r) * fa, ((float)style->g) * fa, ((float)style->b) * fa, style->fill.opacity);
} else {
//Apply the fill color
float fa = ((float)style->fill.opacity / 255.0);
vg->fill(((float)style->fill.paint.r) * fa, ((float)style->fill.paint.g) * fa, ((float)style->fill.paint.b) * fa, style->fill.opacity);
}
//Apply node opacity
if (style->opacity < 255) {
uint8_t r, g, b, a;
vg->fill(&r, &g, &b, &a);
float fa = ((float)style->opacity / 255.0);
vg->fill(((float)r) * fa, ((float)g) * fa, ((float)b) * fa, ((float)a) * fa);
}
if (node->type == SvgNodeType::G) return;
//Apply the stroke style property
vg->stroke(style->stroke.width);
vg->stroke(style->stroke.cap);
vg->stroke(style->stroke.join);
//If stroke property is nullptr then do nothing
if (style->stroke.paint.none) {
//Do nothing
} else if (style->stroke.paint.gradient) {
//TODO: Support gradient style
} else if (style->stroke.paint.url) {
//TODO: Apply the color pointed by url
} else if (style->stroke.paint.curColor) {
//Apply the current style color
vg->stroke(style->r, style->g, style->b, style->stroke.opacity);
} else {
//Apply the stroke color
vg->stroke(style->stroke.paint.r, style->stroke.paint.g, style->stroke.paint.b, style->stroke.opacity);
}
//Apply node opacity to stroke color
if (style->opacity < 255) {
uint8_t r, g, b, a;
vg->strokeColor(&r, &g, &b, &a);
float fa = ((float)style->opacity / 255.0);
vg->stroke(((float)r) * fa, ((float)g) * fa, ((float)b) * fa, ((float)a) * fa);
}
}
unique_ptr<Shape> _shapeBuildHelper(SvgNode* node, float vx, float vy, float vw, float vh)
{
auto shape = Shape::gen();
switch (node->type) {
case SvgNodeType::Path: {
if (node->node.path.path) {
auto pathResult = svgPathToTvgPath(node->node.path.path->c_str());
shape->appendPath(get<0>(pathResult).data(), get<0>(pathResult).size(), get<1>(pathResult).data(), get<1>(pathResult).size());
}
break;
}
case SvgNodeType::Ellipse: {
shape->appendCircle(node->node.ellipse.cx, node->node.ellipse.cy, node->node.ellipse.rx, node->node.ellipse.ry);
break;
}
case SvgNodeType::Polygon: {
if (node->node.polygon.pointsCount < 2) break;
shape->moveTo(node->node.polygon.points[0], node->node.polygon.points[1]);
for (int i = 2; i < node->node.polygon.pointsCount; i += 2) {
shape->lineTo(node->node.polygon.points[i], node->node.polygon.points[i + 1]);
}
shape->close();
break;
}
case SvgNodeType::Polyline: {
if (node->node.polygon.pointsCount < 2) break;
shape->moveTo(node->node.polygon.points[0], node->node.polygon.points[1]);
for (int i = 2; i < node->node.polygon.pointsCount; i += 2) {
shape->lineTo(node->node.polygon.points[i], node->node.polygon.points[i + 1]);
}
break;
}
case SvgNodeType::Circle: {
shape->appendCircle(node->node.circle.cx, node->node.circle.cy, node->node.circle.r, node->node.circle.r);
break;
}
case SvgNodeType::Rect: {
shape->appendRect(node->node.rect.x, node->node.rect.y, node->node.rect.w, node->node.rect.h, node->node.rect.rx, node->node.rect.ry);
break;
}
case SvgNodeType::Line: {
shape->moveTo(node->node.line.x1, node->node.line.y1);
shape->lineTo(node->node.line.x2, node->node.line.y2);
break;
}
default: {
break;
}
}
_applyProperty(node, shape.get(), vx, vy, vw, vh);
return shape;
}
unique_ptr<Scene> _sceneBuildHelper(SvgNode* node, float vx, float vy, float vw, float vh, int parentOpacity)
{
if (node->type == SvgNodeType::Doc || node->type == SvgNodeType::G) {
auto scene = Scene::gen();
if (node->transform) scene->transform(*node->transform);
node->style->opacity = (node->style->opacity * parentOpacity) / 255.0f;
if (node->display) {
for (auto child : node->child) {
if (child->type == SvgNodeType::Doc || child->type == SvgNodeType::G) scene->push(_sceneBuildHelper(child, vx, vy, vw, vh, node->style->opacity));
else {
child->style->opacity = (child->style->opacity * node->style->opacity) / 255.0f;
scene->push(_shapeBuildHelper(child, vx, vy, vw, vh));
}
}
}
return scene;
}
return nullptr;
}
SvgSceneBuilder::SvgSceneBuilder()
{
}
SvgSceneBuilder::~SvgSceneBuilder()
{
}
unique_ptr<Scene> SvgSceneBuilder::build(SvgNode* node)
{
if (!node || (node->type != SvgNodeType::Doc)) return nullptr;
viewBox.x = node->node.doc.vx;
viewBox.y = node->node.doc.vy;
viewBox.w = node->node.doc.vw;
viewBox.h = node->node.doc.vh;
preserveAspect = node->node.doc.preserveAspect;
return _sceneBuildHelper(node, viewBox.x, viewBox.y, viewBox.w, viewBox.h, 255);
}

View file

@ -0,0 +1,45 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef _TVG_SVG_SCENE_BUILDER_H_
#define _TVG_SVG_SCENE_BUILDER_H_
#include "tvgSvgLoaderCommon.h"
#include "tvgSvgPath.h"
class SvgSceneBuilder
{
private:
struct {
int x, y;
uint32_t w, h;
} viewBox = {0, 0, 0, 0};
bool preserveAspect = false;
public:
SvgSceneBuilder();
~SvgSceneBuilder();
unique_ptr<Scene> build(SvgNode* node);
};
#endif //_TVG_SVG_SCENE_BUILDER_H_

42
src/meson.build Normal file
View file

@ -0,0 +1,42 @@
compiler_flags = ['-DTVG_BUILD']
cc = meson.get_compiler('cpp')
if (cc.get_id() != 'msvc')
if get_option('vectors').contains('avx')
compiler_flags += ['-mavx']
message('Enable Advanced Vector Extension')
endif
endif
subdir('lib')
subdir('loaders')
subdir('bindings')
thread_dep = meson.get_compiler('cpp').find_library('pthread')
thorvg_lib_dep = [common_dep, loader_dep, binding_dep, thread_dep]
thorvg_lib = library(
'thorvg',
include_directories : headers,
version : meson.project_version(),
dependencies : thorvg_lib_dep,
install : true,
cpp_args : compiler_flags,
gnu_symbol_visibility : 'hidden',
)
thorvg_dep = declare_dependency(
include_directories: headers,
link_with : thorvg_lib
)
pkg_mod = import('pkgconfig')
pkg_mod.generate(
libraries : thorvg_lib,
version : meson.project_version(),
name : 'libthorvg',
filebase : 'thorvg',
description : 'A Thor library for rendering vector graphics'
)

23
test/makefile Normal file
View file

@ -0,0 +1,23 @@
all:
gcc -o testShape testShape.cpp -g -lstdc++ `pkg-config --cflags --libs elementary thorvg`
gcc -o testMultiShapes testMultiShapes.cpp -g -lstdc++ `pkg-config --cflags --libs elementary thorvg`
gcc -o testBoundary testBoundary.cpp -g -lstdc++ `pkg-config --cflags --libs elementary thorvg`
gcc -o testPath testPath.cpp -g -lstdc++ `pkg-config --cflags --libs elementary thorvg`
gcc -o testPathCopy testPathCopy.cpp -g -lstdc++ `pkg-config --cflags --libs elementary thorvg`
gcc -o testBlending testBlending.cpp -g -lstdc++ `pkg-config --cflags --libs elementary thorvg`
gcc -o testUpdate testUpdate.cpp -g -lstdc++ `pkg-config --cflags --libs elementary thorvg`
gcc -o testDirectUpdate testDirectUpdate.cpp -g -lstdc++ `pkg-config --cflags --libs elementary thorvg`
gcc -o testScene testScene.cpp -g -lstdc++ `pkg-config --cflags --libs elementary thorvg`
gcc -o testTransform testTransform.cpp -g -lstdc++ `pkg-config --cflags --libs elementary thorvg`
gcc -o testCustomTransform testCustomTransform.cpp -g -lstdc++ `pkg-config --cflags --libs elementary thorvg`
gcc -o testSceneTransform testSceneTransform.cpp -g -lstdc++ `pkg-config --cflags --libs elementary thorvg`
gcc -o testStroke testStroke.cpp -g -lstdc++ `pkg-config --cflags --libs elementary thorvg`
gcc -o testStrokeLine testStrokeLine.cpp -g -lstdc++ `pkg-config --cflags --libs elementary thorvg`
gcc -o testLinearGradient testLinearGradient.cpp -g -lstdc++ `pkg-config --cflags --libs elementary thorvg`
gcc -o testRadialGradient testRadialGradient.cpp -g -lstdc++ `pkg-config --cflags --libs elementary thorvg`
gcc -o testGradientTransform testGradientTransform.cpp -g -lstdc++ `pkg-config --cflags --libs elementary thorvg`
gcc -o testSvg testSvg.cpp -g -lstdc++ `pkg-config --cflags --libs elementary thorvg`
gcc -o testSvg2 testSvg2.cpp -g -lstdc++ `pkg-config --cflags --libs elementary thorvg`
gcc -o testAsync testAsync.cpp -g -lstdc++ `pkg-config --cflags --libs elementary thorvg`
gcc -o testArc testArc.cpp -g -lstdc++ `pkg-config --cflags --libs elementary thorvg`
gcc -o testCapi testCapi.c -g `pkg-config --cflags --libs elementary thorvg`

3
test/svgs/batman1.svg Normal file
View file

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" stroke-linejoin="round" viewBox="50 -100 500 500">
<path fill="none" stroke="black" stroke-width="10" d="M 212,220 C 197,171 156,153 123,221 109,157 120,109 159,63.6 190,114 234,115 254,89.8 260,82.3 268,69.6 270,60.3 273,66.5 275,71.6 280,75.6 286,79.5 294,79.8 300,79.8 306,79.8 314,79.5 320,75.6 325,71.6 327,66.5 330,60.3 332,69.6 340,82.3 346,89.8 366,115 410,114 441,63.6 480,109 491,157 477,221 444,153 403,171 388,220 366,188 316,200 300,248 284,200 234,188 212,220 Z"/>
</svg>

After

Width:  |  Height:  |  Size: 532 B

15
test/svgs/bojo.svg Normal file
View file

@ -0,0 +1,15 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 100 100">
<a xlink:href="http://www.tbray.org/ongoing/When/200x/2004/01/11/PostelPilgrim">
<path d="M10,15c10,10,10,0,40,0c30,0,30,10,40,0q-10,30-40,30q-30,0-40-30"
stroke="black" fill="red"/>
<ellipse stroke="black" fill="white" cx="50" cy="40" rx="22" ry="35"/>
<circle cx="50" cy="40" r="5" stroke="black" fill="red"/>
<circle cx="48" cy="38" r="1" fill="white"/>
<path d="M35,45C40,75,60,75,65,45Q50,60,35,45Z"
stroke="red" stroke-width="2" stroke-linejoin="round"/>
<circle cx="40" cy="30" r="2"/>
<circle cx="60" cy="30" r="2"/>
<path d="M35,30q5-10,10,0q-5-30-10,0" stroke="black" fill="none"/>
<path d="M55,30q5-10,10,0q-5-30-10,0" stroke="black" fill="none"/>
</a>
</svg>

After

Width:  |  Height:  |  Size: 807 B

23
test/svgs/bzrfeed.svg Normal file
View file

@ -0,0 +1,23 @@
<svg xmlns="http://www.w3.org/2000/svg" stroke-linejoin="round" viewBox="0 0 100 100">
<path d="M50,4L4,50L50,96L96,50Z" stroke="#FE4" stroke-width="3"/>
<path d="M50,5L5,50L50,95L95,50Z" stroke="#333" fill="#FE4" stroke-width="3"/>
<g transform="scale(0.8) translate(14,30)">
<path d="M37,42c-1,0,11-20,13-20c1,0,15,20,13,20h-9c0,8,9,22,12,25l-4,4l-8,-7v13h-10v-35z" stroke="#CA0" fill="#CA0"/>
<path d="M35,40c-1,0,11-20,13-20c1,0,15,20,13,20h-9c0,8,9,22,12,25l-4,4l-8,-7v13h-10v-35z" stroke="#333" fill="#555"/>
</g>
<g transform="translate(50,26) scale(0.25)" stroke-width="2">
<g fill="none">
<ellipse stroke="#469" rx="6" ry="44"/>
<ellipse stroke="#ba5" rx="6" ry="44" transform="rotate(-66)"/>
<ellipse stroke="#68c" rx="6" ry="44" transform="rotate(66)"/>
<circle stroke="#331" r="44"/>
</g>
<g fill="#689" stroke="#FE4">
<circle fill="#80a3cf" r="13"/>
<circle cy="-44" r="9"/>
<circle cx="-40" cy="18" r="9"/>
<circle cx="40" cy="18" r="9"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1 KiB

11
test/svgs/cartman.svg Normal file
View file

@ -0,0 +1,11 @@
<svg viewBox='0 0 104 97' xmlns='http://www.w3.org/2000/svg'>
<path d='M14,85l3,9h72c0,0,5-9,4-10c-2-2-79,0-79,1' fill='#7C4E32'/>
<path d='M19,47c0,0-9,7-13,14c-5,6,3,7,3,7l1,14c0,0,10,8,23,8c14,0,26,1,28,0c2-1,9-2,9-4c1-1,27,1,27-9c0-10,7-20-11-29c-17-9-67-1-67-1' fill='#E30000'/>
<path d='M17,32c-3,48,80,43,71-3 l-35-15' fill='#FFE1C4'/>
<path d="M17,32c9-36,61-32,71-3c-20-9-40-9-71,3" fill="#8ED8F8"/>
<path d='M54,35a10 8 60 1 1 0,0.1zM37,38a10 8 -60 1 1 0,0.1z' fill='#FFF'/>
<path d='M41,6c1-1,4-3,8-3c3-0,9-1,14,3l-1,2h-2h-2c0,0-3,1-5,0c-2-1-1-1-1-1l-3,1l-2-1h-1c0,0-1,2-3,2c0,0-2-1-2-3M17,34l0-2c0,0,35-20,71-3v2c0,0-35-17-71,3M5,62c3-2,5-2,8,0c3,2,13,6,8,11c-2,2-6,0-8,0c-1,1-4,2-6,1c-4-3-6-8-2-12M99,59c0,0-9-2-11,4l-3,5c0,1-2,3,3,3c5,0,5,2,7,2c3,0,7-1,7-4c0-4-1-11-3-10' fill='#FFF200'/>
<path d='M56,78v1M55,69v1M55,87v1' stroke='#000' stroke-linecap='round'/>
<path d='M60,36a1 1 0 1 1 0-0.1M49,36a1 1 0 1 1 0-0.1M57,55a2 3 0 1 1 0-0.1M12,94c0,0,20-4,42,0c0,0,27-4,39,0z'/>
<path d='M50,59c0,0,4,3,10,0M56,66l2,12l-2,12M25,50c0,0,10,12,23,12c13,0,24,0,35-15' fill='none' stroke='#000' stroke-width='0.5'/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

15
test/svgs/dst.svg Normal file
View file

@ -0,0 +1,15 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" >
<g transform="translate(50,50)">
<path d="M38,-1h12v2h-12zM-38-1h-12v2h12z"/>
<path d="M40,-1h8v2h-8zM-40-1h-8v2h8z" transform="rotate(30)"/>
<path d="M40,-1h8v2h-8zM-40-1h-8v2h8z" transform="rotate(60)"/>
<path d="M38,-1h12v2h-12zM-38-1h-12v2h12z" transform="rotate(90)"/>
<path d="M40,-1h8v2h-8zM-40-1h-8v2h8z" transform="rotate(120)"/>
<path d="M40,-1h8v2h-8zM-40-1h-8v2h8z" transform="rotate(150)"/>
<path d="M-0.5,0v-35h1v35z"/>
<path d="M0,-0.75h30v1.5h-30z"/>
<path d="M0,-0.75h30v1.5h-30z" transform="rotate(-30)" fill="#F88"/>
<path d="M25-12A28,28 0,0,1 28-2m-0.3,0l-1-2h2z" stroke-width="1" stroke-linejoin="round" stroke="#F00" fill="#F00"/>
<circle r="2"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 794 B

25
test/svgs/duke.svg Normal file
View file

@ -0,0 +1,25 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 75 136">
<path d="M1190 2982 c-38-31-31-179 17-347 14-49-50 34-89 115-59 126
-75 138-139 104-89-45-31-286 106-438 37-41 37-56 2-56-64-1-147
-63-147-111 0-68 36-95 107-78 72 17 110 12 162-18 59-36 99-53 121
-53 70 0 98-274 36-339-77-80-124-64-211 69-235 363-871 1021-794
823 6-16 15-109 21-208 8-154 7-237-7-510-1-22-5-71-9-110-3
-38-8-99-11-135-16-193-59-311-172-463-89-120-103-199-62-333
46-147 32-217-65-342-86-110-34-240 74-182 15 8 16-2 16-118-1
-208 99-262 252-137 459 378 583 383 820 37 106-155 165-182 267-123 179
103 150 755-55 1272-72 180-71 272 6 419 l38 71-1 147 c0 177 4 192 76
262 82 80 99 158 65 300-25 105-22 166 16 269 47 130 23 186-68 161-65
-17-142-154-142-253 0-42-37 30-49 96-23 120-43 186-61 202-30 27
-91 31-120 7z m-342-1094 c74-22 122-90 122-170 0-77-7-87-54-86-83
2-183-78-173-139 14-82-172-40-237 54-127 183 105 413 342 341z m315
-214 c237-426 341-786 342-1179 0-460-61-531-239-277-251 358-453
367-824 35-124-110-190-136-208-80-7 22-7 235 0 272 2 11 7 45 10 75
4 30 8 66 11 80 2 14 9 52 14 85 109 661 160 849 214 789 199-228 612 2 545
303-6 28-4 32 15 36 38 7 40 4 120-139z m-900-522 c3-4-5-61-18-127
-13-66-25-131-27-145-6-36-39 38-46 101-7 76 66 213 91 171z"
transform="translate(0,136) scale(0.045181,-0.045181)"/>
<ellipse rx="12" ry="10" transform="rotate(-24) translate(5.4,68.3)"
fill="#F00" opacity="0.7"/>
<ellipse rx="3.5" ry="2" transform="rotate(-33) translate(-9,61)" fill="#FFF"
opacity="0.7"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

4
test/svgs/eee.svg Normal file
View file

@ -0,0 +1,4 @@
<svg viewBox="0 0 121 83" xmlns="http://www.w3.org/2000/svg">
<path d="M40,33c-12,2-28,9-36,20c-7,10-3,23,9,26c6,1,12,2,17,0v-1c-4,1-9,1-14,0c-11-2-19-13-11-23c6-9,15-13,24-17c5-2,13-2,16-6c-12-3-13-14-3-21c11-8,25-9,39-8v-1c-11-4-31,2-39,7c-4,2-8,6-9,10c-1,7,4,10,8,15M81,2v1c1,0,11,3,7,5c-2,2-8-1-11-1c-10-1-20,1-30,6c-3,1-8,4-8,8c1,11,21,9,28,9c2,0,9,1,6,4c-3,4-13,4-18,4c-12,2-26,6-37,13c-4,3-9,7-9,13c0,7,7,8,12,8c12,0,22-4,32-12c-1,0-1,7-3,9c-3,4-14,7-20,9v1c7-2,15-4,22-7c5,15,27,6,35-1c4,12,24,11,29,1c-9,4-25,9-23-7c8,0,17,0,24-5c8-6,3-14-6-15c-20-1-20,22-33,29c-9,4-21,3-21-9c8,0,18,0,24-5c8-6,4-14-5-15c-16-1-30,21-46,25c-6,1-16,3-20-4c-2-6,8-12,12-15c11-7,24-10,37-12c4-1,13,1,16-4c3-5-2-5-6-6c-6,0-26,1-27-7c-1-5,6-8,9-9c7-3,15-6,23-5c4,0,11,4,14,3c9-3-4-9-7-9" fill="#666"/>
<path d="M59,63c6,0,13-1,18-6c2-2,4-5,3-8c-6-7-19,10-21,14M95,63c5,0,25-4,20-13c-1-3-5-2-7-1c-6,3-11,8-13,14" fill="#fff"/>
</svg>

After

Width:  |  Height:  |  Size: 919 B

11
test/svgs/favorite_on.svg Normal file
View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="80px" height="80px" viewBox="-14 -14 80 80" enable-background="new -14 -14 80 80" xml:space="preserve">
<path fill="#FFB400" d="M39.633,49c-0.492,0-0.987-0.145-1.417-0.432l-12.212-8.141l-12.215,8.141
c-0.892,0.598-2.059,0.57-2.926-0.061c-0.866-0.629-1.245-1.734-0.949-2.766l4.104-14.365L3.846,22.429
c-0.788-0.71-1.055-1.828-0.676-2.814c0.378-0.987,1.326-1.641,2.385-1.641h13.007l5.036-13.331c0.377-0.989,1.326-1.64,2.384-1.643
h0.003c1.056,0,2.003,0.651,2.384,1.637l5.063,13.337h13.011c1.061,0,2.007,0.652,2.387,1.641c0.38,0.988,0.109,2.104-0.677,2.814
L37.98,31.377l4.104,14.365c0.294,1.031-0.085,2.137-0.947,2.766C40.692,48.834,40.162,49,39.633,49z"/>
</svg>

After

Width:  |  Height:  |  Size: 1 KiB

4
test/svgs/google.svg Normal file
View file

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<path d="M90,18c-90-45-115,102,0,69v-21l4-3h-23l-8,4h16v19c-80,15-65-106,2-63l-4,5l4-1z" fill="#CCC" stroke="#DDD" stroke-width="2" stroke-linejoin="round"/>
<path d="M87,15c-90-45-115,102,0,69v-21l4-3h-23l-8,4h16v19c-80,15-65-106,2-63l-4,5l4-1z" fill="#00F"/>
</svg>

After

Width:  |  Height:  |  Size: 330 B

44
test/svgs/ibm.svg Normal file
View file

@ -0,0 +1,44 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 148 68">
<g transform="scale(0.1578)">
<polyline fill="#5C7DB8" points="851.165,24.442 713.147,24.442 721.839,0.012 851.165,0.012 851.165,24.442 "/>
<path fill="#5C7DB8" d="M473.454,0.012L601.34,0l8.59,24.487c0.056-0.034-136.431,0.034-136.431,0S473.421,0.012,473.454,0.012"/>
<path fill="#5C7DB8" d="M368.987,0.012c21.684,2.088,40.531,9.811,56.585,24.476c0,0-238.443,0.022-238.443,0 c0-0.034,0-24.487,0-24.487L368.987,0.012"/>
<rect x="0.024" y="0.006" fill="#5C7DB8" width="165.646" height="24.425"/>
<path fill="#5C7DB8" d="M851.165,70.281H697.104c0,0,8.478-24.301,8.433-24.319h145.628V70.281"/>
<polyline fill="#5C7DB8" points="626.222,70.304 473.454,70.304 473.454,45.962 617.563,45.962 626.222,70.304 "/>
<path fill="#5C7DB8" d="M444.138,45.952c3.142,8.027,6.867,14.895,6.867,24.318H187.174V45.952H444.138"/>
<rect x="0.024" y="45.962" fill="#5C7DB8" width="165.646" height="24.307"/>
<path fill="#5C7DB8" d="M689.382,91.801l-8.546,24.42h123.438c0.057-0.056-0.033-24.38,0-24.38L689.382,91.801"/>
<polyline fill="#5C7DB8" points="633.843,91.791 642.467,116.221 520.11,116.221 520.11,91.791 633.843,91.791 "/>
<path fill="#5C7DB8" d="M453.099,91.791c0,8.381-1.047,17.096-3.489,24.431h-72.246V91.791H453.099"/>
<rect x="46.196" y="91.908" fill="#5C7DB8" width="71.188" height="24.313"/>
<rect x="235.101" y="91.791" fill="#5C7DB8" width="71.075" height="24.431"/>
<path fill="#5C7DB8" d="M673.45,136.813h130.823v24.312h-69.668c0,0.197-0.034-18.526-0.034-18.526s-6.45,18.481-6.462,18.526 h-62.776L673.45,136.813"/>
<path fill="#5C7DB8" d="M590.002,142.577c0,0,0.012,18.503,0,18.548H520.11v-24.312h129.573l8.703,24.312c0,0-61.889,0.046-61.889,0 L590.002,142.577"/>
<path fill="#5C7DB8" d="M235.101,136.802h204.038c-4.459,8.14-12.564,18.042-20.242,24.324c0,0-183.795,0.023-183.795,0 C235.101,161.081,235.101,136.857,235.101,136.802"/>
<rect x="46.196" y="136.802" fill="#5C7DB8" width="71.188" height="24.313"/>
<rect x="734.605" y="182.641" fill="#5C7DB8" width="69.668" height="24.318"/>
<path fill="#5C7DB8" d="M590.002,182.641H520.11v24.318c-0.056-0.022,69.903,0,69.903,0 C590.014,206.937,590.092,182.641,590.002,182.641"/>
<path fill="#5C7DB8" d="M720.544,182.641c-0.326-0.023-8.162,24.296-8.5,24.318l-99.232,0.044 c-0.09-0.044-8.736-24.386-8.736-24.362H720.544"/>
<path fill="#5C7DB8" d="M235.09,182.641h183.458c8.376,6.98,15.93,15.582,21.874,24.318c0.327-0.022-205.321,0-205.321,0 C235.101,206.937,235.09,182.674,235.09,182.641"/>
<path fill="#5C7DB8" d="M117.383,206.959v-24.318H46.309c0,0,0.011,24.296,0,24.296C46.297,206.937,117.337,206.959,117.383,206.959 "/>
<rect x="734.605" y="228.475" fill="#5C7DB8" width="69.668" height="24.318"/>
<rect x="520.087" y="228.484" fill="#5C7DB8" width="69.915" height="24.309"/>
<path fill="#5C7DB8" d="M695.754,252.905c0.012-0.102,8.59-24.454,8.724-24.431h-84.359c-0.158-0.012,8.646,24.329,8.646,24.329 S695.742,252.995,695.754,252.905"/>
<path fill="#5C7DB8" d="M235.101,252.781c0,0.101-0.011-24.307-0.011-24.307h72.134c0,0,0.056,24.329,0,24.329 C307.156,252.804,235.101,252.77,235.101,252.781"/>
<path fill="#5C7DB8" d="M450.544,228.475c3.479,7.329,3.838,16.042,4.526,24.431h-76.423v-24.431H450.544"/>
<rect x="46.309" y="228.463" fill="#5C7DB8" width="71.075" height="24.318"/>
<rect x="734.605" y="273.361" fill="#5C7DB8" width="116.548" height="24.318"/>
<rect x="473.454" y="273.361" fill="#5C7DB8" width="116.548" height="24.318"/>
<polyline fill="#5C7DB8" points="680.082,297.68 644.492,297.68 635.958,273.361 688.311,273.361 680.082,297.68 "/>
<path fill="#5C7DB8" d="M0.013,273.361v24.318h165.522c0.079,0.045-0.068-24.296,0-24.296 C165.614,273.384-0.189,273.361,0.013,273.361"/>
<path fill="#5C7DB8" d="M452.052,273.361c-1.745,8.038-3.344,17.812-9.197,24.318h-2.195H187.163v-24.318H452.052"/>
<polyline fill="#5C7DB8" points="663.948,343.581 660.672,343.581 652.363,319.205 672.481,319.205 663.948,343.581 "/>
<rect x="734.605" y="319.205" fill="#5C7DB8" width="116.548" height="24.432"/>
<path fill="#5C7DB8" d="M187.129,343.581v-24.342c0,0,239.153,0.035,239.84,0.035c-16.055,15.355-38.088,24.014-61.809,24.362 l-177.997-0.046"/>
<rect x="473.454" y="319.205" fill="#5C7DB8" width="116.548" height="24.432"/>
<path fill="#5C7DB8" d="M165.536,319.318c0,0-0.068,24.263,0,24.263c0.079,0-165.478,0.1-165.522,0.044 c-0.045-0.044,0.045-24.386,0-24.386C-0.032,319.239,165.468,319.386,165.536,319.318"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.4 KiB

20
test/svgs/lineargrad1.svg Normal file
View file

@ -0,0 +1,20 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<defs
id="defs4">
<linearGradient
id="linearGradient1"
x1="0"
y1="0"
x2="0.2"
y2="0.2"
spreadMethod="reflect">
<stop
style="stop-color:#ff0000;stop-opacity:1;"
offset="0"/>
<stop
style="stop-color:#0000ff;stop-opacity:1;"
offset="1"/>
</linearGradient>
</defs>
<rect x="0" y="0" width="100" height="100" fill="url(#linearGradient1)"/>
</svg>

After

Width:  |  Height:  |  Size: 514 B

19
test/svgs/radialgrad1.svg Normal file
View file

@ -0,0 +1,19 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<defs
id="defs4">
<radialGradient
id="radialGradient1"
r="0.2"
cx="0.3"
cy="0.3"
spreadMethod="reflect">
<stop
style="stop-color:#ff0000;stop-opacity:1;"
offset="0"/>
<stop
style="stop-color:#0000ff;stop-opacity:1;"
offset="1"/>
</radialGradient>
</defs>
<rect x="0" y="0" width="100" height="100" fill="url(#radialGradient1)"/>
</svg>

After

Width:  |  Height:  |  Size: 501 B

9
test/svgs/scion.svg Normal file
View file

@ -0,0 +1,9 @@
<svg viewBox="0 0 120 83" xmlns="http://www.w3.org/2000/svg">
<path d="M60,0c-33,0-60,19-60,42c0,22,27,41,60,41c33,0,60-19,60-41c0-23-27-42-60-42M60,77c-27,0-48-16-48-35c0-20,21-36,48-36c27,0,49,16,49,36c0,19-22,35-49,35" fill="#AAA"/>
<path d="M60,4c-28,0-52,17-52,38c0,20,24,37,52,37c29,0,52-17,52-37c0-21-23-38-52-38M60,77c-27,0-48-16-48-35c0-20,21-36,48-36c27,0,49,16,49,36c0,19-22,35-49,35" fill="#717279"/>
<path d="M60,3c-29,0-52,17-52,39c0,21,23,38,52,38c29,0,53-17,53-38c0-22-24-39-53-39M60,79c-28,0-52-17-52-37c0-21,24-38,52-38c29,0,52,17,52,38c0,20-23,37-52,37M111,35h-102l-1,7l1,6h102c1-2,1-4,1-6c0-3,0-5-1-7" fill="#EEE"/>
<path d="M108,34h-95l-4,1h3h96h3l-3-1" fill="#58585E"/>
<path d="M12,48h-3l4,1h95l3-1h-3z" fill="#3A3B3E"/>
<path d="M62,5c0,0-14,13-16,30h12c-4-13,4-30,4-30M59,78c0,0,13-13,15-30h-11c4,13-4,30-4,30" fill="#BBB"/>
<path d="M58,35h9c-11-6-5-30-5-30s-8,17-4,30M63,48h-10c11,6,6,30,6,30s8-17,4-30M109,45c0,1-1,1-2,1h-1v-7l-1-1h-15v8h-3v-9h19c1,0,3,1,3,2zM12,45h17c1,0,1-1,1-1v-2h-18v-3c0-1,1-2,3-2h18v1h-17c-1,0-1,1-1,1v2h18v3c0,1-1,2-3,2h-16c-1,0-2,0-2-1M38,44l1,1h17v1h-18c-2,0-3-1-3-2v-5c0-1,1-2,3-2h18v1h-17l-1,1zM62,37v9h-3v-9zM85,39v5c0,1-1,2-2,2h-16c-2,0-3-1-3-2v-5c0-1,1-2,3-2h16c1,0,2,1,2,2M81,38h-13c-1,0-1,1-1,1v5c0,0,0,1,1,1h13c1,0,1-1,1-1v-5c0,0,0-1-1-1" fill="#060506"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

829
test/svgs/tiger.svg Normal file
View file

@ -0,0 +1,829 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14948) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1 Basic//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-basic.dtd">
<svg version="1.1" baseProfile="basic" id="svg2" xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="900px" height="900px"
viewBox="0 0 900 900" xml:space="preserve">
<path id="path482" fill="none" d="M184.013,144.428"/>
<path id="path6" fill="#FFFFFF" stroke="#000000" stroke-width="0.172" d="M108.956,403.826c0,0,0.178,3.344-1.276,3.311
c-1.455-0.033-30.507-84.917-66.752-80.957C40.928,326.18,72.326,313.197,108.956,403.826z"/>
<path id="path10" fill="#FFFFFF" stroke="#000000" stroke-width="0.172" d="M115.189,398.488c0,0-0.97,3.207-2.327,2.679
c-1.356-0.526,0.203-90.231-35.227-98.837C77.635,302.33,111.576,300.804,115.189,398.488z"/>
<path id="path14" fill="#FFFFFF" stroke="#000000" stroke-width="0.172" d="M163.727,473.225c0,0,2.888,1.695,2.059,2.892
c-0.832,1.194-87.655-21.408-104.35,11.003C61.436,487.118,67.931,453.771,163.727,473.225z"/>
<path id="path18" fill="#FFFFFF" stroke="#000000" stroke-width="0.172" d="M158.767,491.254c0,0,3.277,0.699,2.864,2.096
c-0.411,1.396-89.935,7.298-95.567,43.318C66.063,536.668,61.723,502.971,158.767,491.254z"/>
<path id="path22" fill="#FFFFFF" stroke="#000000" stroke-width="0.172" d="M151.332,481.498c0,0,3.139,1.171,2.528,2.492
c-0.611,1.319-90.037-5.899-100.864,28.915C52.996,512.905,53.617,478.938,151.332,481.498z"/>
<path id="path26" fill="#FFFFFF" stroke="#000000" stroke-width="0.172" d="M132.43,449.356c0,0,2.31,2.427,1.181,3.347
c-1.128,0.919-78.363-44.729-103.341-18.171C30.27,434.532,45.704,404.264,132.43,449.356z"/>
<path id="path30" fill="#FFFFFF" stroke="#000000" stroke-width="0.172" d="M119.108,456.757c0,0,2.571,2.148,1.554,3.192
c-1.017,1.041-82.921-35.576-104.734-6.36C15.928,453.589,27.837,421.769,119.108,456.757z"/>
<path id="path34" fill="#FFFFFF" stroke="#000000" stroke-width="0.172" d="M114.518,463.946c0,0,2.839,1.778,1.974,2.95
c-0.865,1.171-86.997-23.942-104.623,7.974C11.869,474.87,19.329,441.724,114.518,463.946z"/>
<path id="path38" fill="#FFFFFF" stroke="#000000" stroke-width="0.172" d="M133.47,465.03c0,0,1.981,2.703,0.743,3.472
c-1.237,0.768-71.985-54.405-100.161-31.267C34.052,437.235,53.236,409.195,133.47,465.03z"/>
<path id="path42" fill="#FFFFFF" stroke="#000000" stroke-width="0.172" d="M98.546,413.917c0,0,1.06,3.178-0.353,3.531
c-1.413,0.353-51.91-73.804-85.812-60.385C12.381,357.063,39.22,336.229,98.546,413.917z"/>
<path id="path46" fill="#FFFFFF" stroke="#000000" stroke-width="0.172" d="M99.773,426.239c0,0,1.722,2.876,0.417,3.523
c-1.303,0.649-66.605-60.873-96.813-40.458C3.376,389.306,25.088,363.174,99.773,426.239z"/>
<path id="path50" fill="#FFFFFF" stroke="#000000" stroke-width="0.172" d="M99.57,433.955c0,0,1.981,2.703,0.744,3.472
c-1.238,0.767-71.985-54.405-100.162-31.267C0.152,406.16,19.335,378.12,99.57,433.955z"/>
<path id="path54" fill="#FFFFFF" stroke="#000000" d="M95.668,436.985c0.888,10.678,2.632,22.275,5.703,27.783
c0,0-6.356,21.895,9.181,45.2c0,0-0.707,12.712,2.119,18.362c0,0,7.063,14.832,15.538,16.244c6.858,1.143,22.26,6.561,39.67,9.04
c0,0,30.249,24.859,24.599,47.461c0,0-0.706,28.956-7.063,31.781c0,0,20.481-19.775,3.531,9.888l-7.769,33.192
c0,0,45.201-38.138,17.657-5.648l-17.657,45.906c0,0,34.607-32.487,21.894-17.656l-5.65,15.538c0,0,76.276-48.025,21.894,4.237
c0,0,14.125-6.356,21.894-1.412c0,0,12.006-2.119,10.594,0.706c0,0-36.726,18.361-43.082,50.851c0,0,14.831-17.657,9.181,1.412
l0.706,20.48c0,0,7.063-38.138,6.356,28.25c0,0,33.9-31.78,13.419,4.944v29.662c0,0,26.838-28.956,15.538-6.354
c0,0,17.656-15.538,10.594,11.3c0,0-1.413,18.361,6.356-1.412c0,0,28.25-54.029,17.656-7.771c0,0-1.412,33.9,7.063,7.771
c0,0,0.706,18.362,16.95,31.075c0,0-2.119-89.695,20.48-26.133l7.063,28.957c0,0,4.943-16.244,4.237-25.426
c0,0,26.132-28.957,14.125,14.125c0,0,26.838-40.257,21.188-16.95c0,0-13.419,28.251-10.594,36.727c0,0,29.663-61.444,31.782-64.271
c0,0-3.531,74.865,15.537,11.3c0,0,9.888,21.188,4.943,28.957c0,0,14.125-14.125,12.712-19.774c0,0,8.122-14.479,13.066,9.534
c0,0,3.178,16.598,6.003,10.946c0,0,7.063,42.377,9.182,2.119c0,0,2.825-24.013-9.888-44.494c0,0,1.412-5.649-3.531-12.713
c0,0,24.014,38.139,11.3-12.713c0,0,19.777,14.125,21.896,14.125c0,0-24.015-40.963-8.477-32.487c0,0-9.183-18.362,22.602,2.825
c0,0-28.252-28.251,2.825-11.301c0,0,14.125,11.301,0.706-6.356c0,0-25.428-28.25,13.419,3.532c0,0,20.48,28.956,21.895,33.9
c0,0-17.655-51.559-25.426-56.501c0,0,14.832-64.271,87.576-36.727c0,0,12.007,30.369,19.774-2.118c0,0,22.602-11.301,42.375,37.432
c0,0,7.063-24.013,5.65-28.956c0,0,12.007,2.119,10.594,0c0,0,23.308,7.769,25.427,6.356c0,0,12.006,12.006,12.712,5.648
c0,0,16.244,4.944,12.713-1.412c0,0,15.538,27.544,16.244,33.9l4.236-24.719l3.531,4.942c0,0,2.825-13.419,1.413-15.537
c-1.413-2.119,35.313,12.006,43.787,48.731l3.531,14.831c0,0,10.594-26.131,7.77-33.193c0,0,9.181,1.412,9.888,9.181
c0,0,7.063-40.963-1.412-51.557c0,0,7.769-1.412,9.888,4.944V714.78c0,0,12.713,1.411,12.713-2.825c0,0,7.769-7.063,11.3,1.412
c0,0-21.894-62.15,10.594-28.25c0,0,12.714,19.068,6.356-14.125c-6.357-33.194-13.419-36.021-4.943-36.727
c0,0,1.412-6.355-2.118-9.181c-3.531-2.825,2.118,0,2.118,0s8.476,7.063-0.707-31.782c0,0,11.302,2.825-9.888-48.73
c0,0,4.944-4.237-2.118-19.069c0,0,14.125,7.77,19.069,4.944c0,0-0.707-2.825-6.356-9.889c0,0-38.139-96.759-2.118-57.913
c0,0,20.923,23.925,9.623-16.332c0,0-16.088-42.394-14.716-49.979L95.668,436.985z"/>
<path id="path58" fill="#CC7226" stroke="#000000" d="M854.095,396.693c1.108,0.32,5.004,2.304,7.211,5.217
c0,0,12.006,19.068,2.825-13.418c0,0-16.244-50.851-0.707-31.076c0,0,10.594,12.713,4.944-11.3
c-6.824-29.004-11.301-40.257-11.301-40.257s20.48,8.475-26.837-61.444l15.536,6.356c0,0-34.605-69.919-72.743-79.101
l-14.125-10.594c0,0,67.8-67.094,45.199-132.07c0,0-12.007-9.182-28.957,7.063c0,0-11.3,8.475-21.894,5.65
c0,0-54.382,2.119-57.913,2.119S630.359-21.844,514.533,9.231c0,0-9.183,3.531-16.95,1.413c0,0-32.489-28.25-118.653,12.006
c0,0-17.655,3.531-20.48,3.531s-7.77,0-21.895,11.3c-14.125,11.3-14.832,12.712-18.362,15.538c0,0-28.957,19.775-37.432,21.188
c0,0-20.481,11.3-28.25,28.957l-6.356,2.119c0,0-2.825,12.713-3.532,14.832c0,0-8.475,6.356-9.887,16.244
c0,0-15.538,10.594-14.832,18.362c0,0-2.825,9.182-4.238,17.657c0,0-12.712,8.475-11.3,13.419c0,0-13.419,24.719-11.3,36.725
c0,0-11.3-0.706-16.244,3.531c0,0-1.413,8.475-4.238,9.182c0,0-4.944,2.119-0.706,9.181c0,0-2.825,4.944-3.531,7.769
c0,0,1.412,4.944-6.356,14.831c0,0-11.3,33.194-7.769,42.375c0,0,0.707,8.475-4.237,11.3c0,0-6.356-0.707,8.475,20.481
c0,0,1.413,2.119-4.238,6.356c0,0-30.369,6.356-34.606,35.313c0,0-24.013,26.131-24.013,35.313c0,4.069,0.479,9.626,1.713,17.771
c0,0-1.007,14.718,47.725,16.131C191.772,453.469,854.095,396.693,854.095,396.693z"/>
<path id="path62" fill="#CC7226" d="M120.793,436.164c-44.141-69.566-18.716,30.018-18.716,30.018
c15.538,60.738,244.365-5.649,244.365-5.649s298.042-53.677,317.816-60.739c19.775-7.063,187.864,4.237,187.864,4.237l-9.888-29.663
c-114.414-81.926-148.314-40.963-172.327-48.025c-24.013-7.062-19.774,9.888-25.425,11.3c-5.651,1.412-74.863-42.375-86.163-40.963
c-11.301,1.413-56.045-40.523-29.663,15.538c28.25,60.032-103.115,69.213-132.778,49.438
c-29.663-19.775,12.713,32.488,12.713,32.488c32.487,35.313-28.25,5.65-28.25,5.65c-60.737-22.601-103.114,22.6-108.764,24.013
c-5.65,1.412-14.125,7.063-15.538-4.237c-1.412-11.301-14.672-40.789-70.625,5.649c-35.313,29.313-59.679-9.534-59.679-9.534
L120.793,436.164z"/>
<path id="path66" fill="#E87F3A" d="M560.632,299.761c-11.3,1.413-56.089-40.502-29.662,15.538
c29.311,62.151-103.113,69.213-132.775,49.438c-29.665-19.775,12.712,32.488,12.712,32.488c32.488,35.313-28.252,5.649-28.252,5.649
c-60.737-22.6-103.113,22.601-108.763,24.013c-5.65,1.413-14.125,7.063-15.538-4.236c-1.413-11.301-14.441-40.494-70.626,5.649
c-37.495,30.627-61.315-7.255-61.315-7.255l-5.65,17.849c-44.141-70.271-17.529,32.682-17.529,32.682
c15.54,60.739,245.521-7.962,245.521-7.962s298.043-53.676,317.817-60.738c19.774-7.062,186.325,4.109,186.325,4.109l-9.762-30.563
c-114.413-81.926-146.9-39.935-170.914-46.998c-24.013-7.063-19.774,9.888-25.425,11.3
C641.146,342.136,571.933,298.349,560.632,299.761z"/>
<path id="path70" fill="#EA8C4D" d="M562.943,302.842c-11.301,1.413-54.973-41.014-29.663,15.538
c28.604,63.918-103.113,69.215-132.776,49.44c-29.662-19.775,12.713,32.488,12.713,32.488c32.488,35.313-28.25,5.649-28.25,5.649
c-60.738-22.6-103.115,22.601-108.766,24.013c-5.65,1.413-14.125,7.063-15.538-4.236c-1.413-11.301-14.21-40.198-70.625,5.649
c-39.68,31.942-62.952-4.976-62.952-4.976l-6.356,15.216c-42.022-68.86-16.341,35.345-16.341,35.345
c15.538,60.738,246.678-10.271,246.678-10.271s298.04-53.677,317.814-60.738c19.775-7.063,184.783,3.979,184.783,3.979l-9.63-31.46
c-114.415-81.926-145.49-38.909-169.503-45.972c-24.014-7.063-19.775,9.888-25.427,11.302
C643.457,345.219,574.243,301.429,562.943,302.842z"/>
<path id="path74" fill="#EC9961" d="M565.255,305.925c-11.301,1.413-54.963-41.02-29.663,15.538
c29.663,66.311-104.057,68.586-132.775,49.438c-29.663-19.775,12.713,32.488,12.713,32.488c32.486,35.313-28.25,5.649-28.25,5.649
c-60.738-22.6-103.114,22.601-108.764,24.013c-5.65,1.413-14.125,7.063-15.538-4.236c-1.413-11.301-13.979-39.9-70.627,5.649
c-41.862,33.259-64.591-2.696-64.591-2.696l-7.063,12.584c-38.491-64.976-15.151,38.012-15.151,38.012
c15.538,60.736,247.833-12.586,247.833-12.586s298.04-53.677,317.817-60.738c19.773-7.063,183.24,3.853,183.24,3.853l-9.502-32.358
c-114.414-81.928-144.076-37.882-168.09-44.945c-24.015-7.063-19.775,9.888-25.427,11.3
C645.766,348.301,576.555,304.512,565.255,305.925z"/>
<path id="path78" fill="#EEA575" d="M567.567,309.008c-11.303,1.412-54.07-41.412-29.664,15.538
c29.664,69.213-103.114,69.213-132.776,49.438c-29.663-19.775,12.713,32.487,12.713,32.487c32.487,35.313-28.251,5.65-28.251,5.65
c-60.738-22.6-103.113,22.601-108.763,24.013c-5.65,1.412-14.125,7.063-15.538-4.237s-13.746-39.604-70.626,5.649
c-44.046,34.575-66.229-0.418-66.229-0.418l-7.769,9.953c-34.96-61.446-13.964,40.673-13.964,40.673
c15.538,60.74,248.989-14.895,248.989-14.895s298.043-53.677,317.816-60.738c19.775-7.063,181.701,3.724,181.701,3.724
l-9.374-33.259c-114.414-81.926-142.664-36.853-166.677-43.915c-24.014-7.062-19.775,9.888-25.426,11.3
C648.081,351.383,578.868,307.595,567.567,309.008z"/>
<path id="path82" fill="#F1B288" d="M569.879,312.089c-11.3,1.412-57.144-39.994-29.663,15.538
c33.9,68.507-103.115,69.213-132.778,49.438c-29.661-19.775,12.714,32.487,12.714,32.487c32.487,35.313-28.25,5.65-28.25,5.65
c-60.738-22.6-103.114,22.601-108.764,24.013c-5.65,1.412-14.125,7.063-15.538-4.237c-1.413-11.3-13.514-39.309-70.626,5.649
c-46.228,35.893-67.866,1.863-67.866,1.863l-8.475,7.317c-31.782-58.619-12.776,43.341-12.776,43.341
C123.394,553.887,358,475.94,358,475.94s298.042-53.677,317.817-60.738c19.774-7.063,180.158,3.595,180.158,3.595l-9.244-34.156
c-114.413-81.926-141.251-35.827-165.265-42.889c-24.013-7.062-19.774,9.888-25.426,11.3
C650.393,354.464,581.179,310.676,569.879,312.089z"/>
<path id="path86" fill="#F3BF9C" d="M572.19,315.169c-11.303,1.413-57.813-39.656-29.665,15.538
c36.021,70.627-103.113,69.214-132.776,49.439s12.713,32.488,12.713,32.488c32.487,35.313-28.25,5.65-28.25,5.65
c-60.738-22.601-103.114,22.6-108.764,24.013c-5.65,1.412-14.125,7.063-15.538-4.237c-1.412-11.301-13.283-39.014-70.625,5.649
c-48.412,37.208-69.503,4.141-69.503,4.141l-9.181,4.688c-28.25-53.322-11.59,46.004-11.59,46.004
c15.538,60.738,251.301-19.519,251.301-19.519s298.041-53.677,317.816-60.738c19.775-7.063,178.619,3.466,178.619,3.466
l-9.117-35.055c-114.414-81.926-139.84-34.799-163.853-41.862c-24.014-7.064-19.774,9.888-25.425,11.3
C652.702,357.546,583.49,313.757,572.19,315.169z"/>
<path id="path90" fill="#F5CCB0" d="M574.501,318.252c-11.3,1.413-59.753-38.624-29.662,15.538
c38.844,69.92-103.115,69.213-132.778,49.438c-29.662-19.775,12.714,32.488,12.714,32.488c32.486,35.313-28.251,5.65-28.251,5.65
c-60.737-22.602-103.113,22.6-108.764,24.013c-5.65,1.412-14.125,7.063-15.538-4.237c-1.413-11.301-13.05-38.716-70.626,5.649
c-50.594,38.524-71.14,6.422-71.14,6.422l-9.887,2.054c-25.427-50.145-10.401,48.668-10.401,48.668
c15.538,60.74,252.455-21.829,252.455-21.829s298.043-53.677,317.816-60.738c19.775-7.063,177.078,3.339,177.078,3.339
l-8.987-35.956c-114.414-81.926-138.428-33.771-162.439-40.834c-24.013-7.063-19.774,9.888-25.425,11.3
C655.015,360.628,585.802,316.84,574.501,318.252z"/>
<path id="path94" fill="#F8D8C4" d="M576.813,321.335c-11.3,1.413-59.753-38.625-29.662,15.538
c38.845,69.919-103.113,69.213-132.776,49.438c-29.662-19.775,12.713,32.488,12.713,32.488c32.488,35.313-28.25,5.65-28.25,5.65
c-60.74-22.602-103.115,22.6-108.766,24.013c-5.65,1.412-14.125,7.063-15.538-4.238c-1.413-11.3-12.817-38.42-70.625,5.65
c-52.777,39.84-72.776,8.701-72.776,8.701l-10.594-0.579c-24.015-46.615-9.213,51.332-9.213,51.332
c15.538,60.738,253.609-24.143,253.609-24.143s298.042-53.675,317.817-60.736c19.775-7.063,175.538,3.21,175.538,3.21l-8.859-36.854
c-114.416-81.926-137.016-32.744-161.027-39.807c-24.013-7.063-19.775,9.888-25.427,11.3
C657.326,363.711,588.112,319.923,576.813,321.335z"/>
<path id="path98" fill="#FAE5D7" d="M579.124,324.417c-11.301,1.413-59.068-38.998-29.663,15.538
c38.844,72.038-103.113,69.213-132.776,49.438c-29.662-19.775,12.714,32.488,12.714,32.488c32.486,35.313-28.251,5.65-28.251,5.65
c-60.737-22.602-103.113,22.6-108.764,24.013c-5.652,1.412-14.127,7.063-15.54-4.238c-1.412-11.3-12.585-38.123-70.625,5.65
c-54.959,41.157-74.413,10.979-74.413,10.979l-11.302-3.212c-22.954-42.375-8.025,53.999-8.025,53.999
c15.538,60.738,254.769-26.455,254.769-26.455s298.04-53.675,317.814-60.736c19.775-7.063,173.997,3.082,173.997,3.082
l-8.732-37.752c-114.413-81.928-135.602-31.718-159.613-38.781c-24.014-7.063-19.774,9.888-25.426,11.3
S590.424,323.004,579.124,324.417z"/>
<path id="path102" fill="#FCF2EB" d="M581.435,327.498c-11.3,1.412-57.161-39.981-29.661,15.538
c37.432,75.571-103.114,69.215-132.776,49.439c-29.663-19.775,12.713,32.488,12.713,32.488c32.487,35.313-28.251,5.649-28.251,5.649
c-60.738-22.601-103.113,22.601-108.763,24.013c-5.65,1.413-14.125,7.063-15.538-4.237c-1.413-11.3-12.354-37.827-70.626,5.65
c-57.145,42.473-76.053,13.258-76.053,13.258l-12.006-5.842c-22.6-40.964-6.836,56.661-6.836,56.661
c15.538,60.736,255.921-28.766,255.921-28.766s298.043-53.676,317.817-60.737c19.775-7.063,172.454,2.951,172.454,2.951
l-8.604-38.65c-114.415-81.926-134.188-30.688-158.2-37.751c-24.014-7.064-19.775,9.887-25.426,11.3
C661.948,369.875,592.735,326.085,581.435,327.498z"/>
<path id="path106" fill="#FFFFFF" d="M120.44,466.182c-22.601-38.846-5.65,59.325-5.65,59.325
c15.538,60.738,257.078-31.075,257.078-31.075s298.042-53.677,317.816-60.738c19.775-7.063,170.914,2.823,170.914,2.823
l-8.475-39.55c-114.414-81.926-132.776-29.663-156.789-36.726c-24.013-7.063-19.775,9.888-25.426,11.3
c-5.649,1.413-74.862-42.375-86.163-40.963c-11.3,1.412-55.829-40.623-29.663,15.538c39.245,84.232-107.28,66.436-132.777,49.438
c-29.663-19.775,12.712,32.488,12.712,32.488c32.488,35.313-28.25,5.65-28.25,5.65c-60.737-22.602-103.113,22.602-108.764,24.014
c-5.65,1.413-14.125,7.063-15.538-4.237c-1.413-11.302-12.121-37.532-70.625,5.65c-59.326,43.788-77.688,15.537-77.688,15.537
L120.44,466.182z"/>
<path id="path110" d="M193.891,519.15c0,0-12.713,20.48,24.013,43.788c0,0,2.472,2.473-29.31-4.943c0,0-10.947-3.531-13.771-21.896
c0,0-8.475-7.769-16.95-17.655C149.397,508.557,193.891,519.15,193.891,519.15z"/>
<path id="path114" fill="#CCCCCC" d="M441.08,435.104c0,0,31.249,47.356,30.193,55.797c-2.297,18.362-2.648,35.313,3.001,42.376
c5.651,7.063,21.188,65.682,21.188,65.682s-0.706,2.119,21.188-64.976c0,0,20.48-28.25-14.831-60.738
C501.82,473.244,439.668,422.392,441.08,435.104z"/>
<path id="path118" d="M229.204,566.47c0,0,19.775,12.713-5.65,67.802l11.3-4.237c0,0-1.413,19.774-7.063,24.013l12.712-5.65
c0,0,8.475,14.127,1.413,22.602c0,0,29.663,14.125,28.25,25.425c0,0,11.3-14.125,4.237-25.425s-19.775-4.237-18.363-36.727
l-15.538,5.65c0,0,9.888-15.538,9.888-26.838l-14.125,4.237c0,0,27.313-46.928,8.475-49.438
C234.147,566.47,229.204,566.47,229.204,566.47z"/>
<path id="path122" fill="#CCCCCC" d="M286.41,596.133c0,0,4.944-7.769,0-6.355c-4.944,1.413-60.032,27.544-70.625,44.494
C215.785,634.271,276.522,591.189,286.41,596.133z"/>
<path id="path126" fill="#CCCCCC" d="M304.773,610.258c0,0,4.944-7.769,0-6.355s-60.032,27.544-70.625,44.494
C234.147,648.396,294.885,605.314,304.773,610.258z"/>
<path id="path130" fill="#CCCCCC" d="M328.079,583.42c0,0,4.944-7.769,0-6.355c-4.943,1.412-60.032,27.545-70.625,44.494
C257.454,621.559,318.191,578.477,328.079,583.42z"/>
<path id="path134" fill="#CCCCCC" d="M287.117,660.402c0,0,0-10.595-4.944-9.183c-4.944,1.413-68.507,32.488-79.101,49.438
C203.072,700.659,277.229,655.458,287.117,660.402z"/>
<path id="path138" fill="#CCCCCC" d="M289.235,641.333c0,0,2.119-8.475-2.825-7.063c-3.531,0-50.144,20.481-60.738,37.433
C225.672,671.702,277.935,633.564,289.235,641.333z"/>
<path id="path142" fill="#CCCCCC" d="M263.81,725.378l-17.656,13.419c0,0,18.362-13.419,24.719-11.3
c0,0-12.006,19.774-13.419,28.956c0,0,18.363-22.602,28.25-21.895c0,0,13.419,0.706,13.419,19.774c0,0,9.888-18.362,15.537-17.656
c0,0,2.119,11.302,0,23.308c0,0,7.063-13.419,14.125-10.595c0,0,11.301-3.53,9.888,16.95c0,0,0,18.362-1.412,23.308
c0,0,9.889-46.613,14.125-47.319c0,0,14.125-2.119,22.602,13.419c0,0-7.063-13.419,1.412-9.888c0,0,19.068,2.824,24.719,14.831
c0,0-12.006-21.188-2.118-15.537c0,0,12.006,0,14.125,11.3c0,0,14.831,37.432,18.362,40.257c0,0-13.419-38.138-10.595-38.138
c0,0-3.53-21.188,5.65,4.942c0,0-5.65-24.719,4.237-23.307c9.888,1.413,17.655,19.069,32.487,14.832
c0,0,16.952,9.888,20.483-112.295L263.81,725.378z"/>
<path id="path146" d="M272.285,561.526c0,0,26.131-10.595,96.757,0c0,0,12.713,0.706,24.72-14.831
c12.006-15.538,59.325-28.251,70.625-24.721l16.952,11.302l1.413,2.118c0,0,21.895,18.362,22.6,31.781
c0.706,13.418-25.425,98.169-42.377,126.42c-16.949,28.25-33.899,50.145-67.801,45.906c0,0-36.726-7.063-81.926,0
c0,0-51.557-2.825-56.5-16.95s19.775-40.963,19.775-40.963s7.769-14.831,5.65-40.257C280.054,615.908,280.76,566.47,272.285,561.526
z"/>
<path id="path150" fill="#E5668C" d="M311.129,565.058c14.832,32.487-37.431,147.607-37.431,147.607
c-3.531,2.825,22.353,13.499,40.256,9.182c19.327-4.657,90.401,2.825,90.401,2.825c41.669-27.544,64.27-105.938,64.27-105.938
s18.364-42.376-12.713-48.025C424.837,565.058,311.129,565.058,311.129,565.058z"/>
<path id="path154" fill="#B23259" d="M307.543,619.608c5.873-22.582,8.67-43.419,3.586-54.552c0,0,110.177,11.301,129.951-25.426
c7.488-13.904,33.55,40.257,32.842,57.207c0,0-111.236,25.426-137.367,5.65L307.543,619.608z"/>
<path id="path158" fill="#A5264C" d="M315.367,648.396c0,0,3.531,12.713-0.707,19.774c0,0-2.824,1.413-4.943,2.119
c0,0,2.119,6.356,12.713,9.182c0,0,3.531,7.77,7.77,8.476s12.713,10.594,19.774,8.475c7.063-2.118,26.839-9.181,26.839-9.181
s9.888-5.65,25.425,0.706c0,0,4.192-1.416,4.942-8.476c0.884-8.299,6.356-14.832,9.889-18.362
c3.531-3.531,20.48-26.133,18.362-26.838C433.313,633.564,315.367,648.396,315.367,648.396z"/>
<path id="path162" fill="#FF727F" stroke="#000000" d="M307.598,562.938c0,0-4.943,39.552,0.707,54.383
c5.649,14.832,4.237,18.362,2.824,25.426c-1.412,7.063,6.356,24.719,16.244,35.313l21.188,2.825c0,0,26.839-6.355,43.082-1.412
c0,0,15.881,2.371,21.895-24.014c0,0,8.476-11.3,21.188-16.243c12.713-4.943,25.426-78.395,18.362-92.52
c-7.063-14.126-32.488-21.896-60.738,5.648S360.567,550.227,307.598,562.938z"/>
<path id="path166" fill="#FFFFCC" stroke="#000000" stroke-width="0.5" d="M310.423,695.009c0,0-1.412-3.531-9.181-4.237
c0,0-39.55-6.355-54.382-28.25c0,0-12.006-9.888-4.238,10.595c0,0,18.363,36.019,30.369,40.963
C272.991,714.078,301.948,721.141,310.423,695.009z"/>
<path id="path170" fill="#CC3F4C" d="M451.572,582.058c1.163-13.96,4.61-29.169,1.515-35.361
c-11.382-22.768-41.35-13.253-60.738,5.648c-28.25,27.544-31.78-2.118-84.751,10.595c0,0-3.081,24.653-1.598,42.332
c0,0,65.867-20.438,67.28-10.551c0,0,2.823-5.649,19.067-5.649S448.747,587.001,451.572,582.058z"/>
<path id="path174" stroke="#A51926" stroke-width="2" d="M375.398,564.352c0,0,8.476,8.476,2.118,25.426
c0,0-25.426,28.25-21.895,52.97"/>
<path id="path178" fill="#FFFFCC" stroke="#000000" stroke-width="0.5" d="M290.648,714.078c0,0-7.769-22.602,7.769-10.594
c0,0,8.475,3.53,6.356,6.354C302.654,712.665,292.767,719.729,290.648,714.078z"/>
<path id="path182" fill="#FFFFCC" stroke="#000000" stroke-width="0.5" d="M299.547,716.196c0,0-6.215-18.08,6.215-8.476
c0,0,7.806,4.322,5.084,5.085C302.795,715.066,310.847,719.587,299.547,716.196z"/>
<path id="path186" fill="#FFFFCC" stroke="#000000" stroke-width="0.5" d="M308.021,716.196c0,0-6.215-18.08,6.215-8.476
c0,0,7.725,4.078,5.086,5.085C313.39,715.066,319.322,719.587,308.021,716.196z"/>
<path id="path190" fill="#FFFFCC" stroke="#000000" stroke-width="0.5" d="M319.675,716.55c0,0-6.215-18.08,6.215-8.476
c0,0,7.739,4.118,5.087,5.085C325.749,715.066,330.977,719.939,319.675,716.55z"/>
<path id="path194" fill="#FFFFCC" stroke="#000000" stroke-width="0.5" d="M331.116,716.408c0,0-6.215-18.08,6.217-8.476
c0,0,6.78,2.825,5.085,5.085C340.723,715.278,342.418,719.799,331.116,716.408z"/>
<path id="path198" fill="#FFFFCC" stroke="#000000" stroke-width="0.5" d="M342.911,717.609c0,0-8.477-21.896,7.769-10.595
c0,0,8.476,3.531,6.356,6.355C354.917,716.196,357.036,721.847,342.911,717.609z"/>
<path id="path202" stroke="#A5264C" stroke-width="2" d="M292.767,687.24c0,0,23.307-4.944,33.9,0.706
c0,0,10.594,2.119,12.713,1.412c2.118-0.706,7.77-1.412,7.77-1.412"/>
<path id="path206" stroke="#A5264C" stroke-width="2" d="M352.799,702.777c0,0,21.188-24.014,42.375-16.243
c12.389,4.543,10.594-1.413,12.006-6.356c1.413-4.943,1.768-12.358,10.596-17.656"/>
<path id="path210" fill="#FFFFCC" stroke="#000000" stroke-width="0.5" d="M383.168,674.527c0,0-7.063-19.069-12.007,3.53
c-4.944,22.602-10.594,28.957-13.419,33.9c0,0,0,9.182,14.831,8.476c0,0,19.068-0.707,19.774-5.649
C393.055,709.84,390.23,689.358,383.168,674.527z"/>
<path id="path214" stroke="#A5264C" stroke-width="2" d="M407.887,687.24c0,0,6.356-4.237,10.594-2.119"/>
<path id="path218" stroke="#A5264C" stroke-width="2" d="M419.363,658.283c0,0,5.12-8.651,13.596-10.063"/>
<path id="path222" fill="#B2B2B2" d="M279.348,723.259c0,0,31.781,5.65,39.551,2.825c0,0,15.536,0,0.706,3.531
c0,0-22.602,0-36.727-2.118C282.879,727.497,262.397,717.609,279.348,723.259z"/>
<path id="path226" fill="#FFFFCC" stroke="#000000" stroke-width="0.5" d="M304.066,558.701c0,0,31.075,0,34.606,1.412
c0,0,12.713,54.382,6.356,67.801c0,0-2.118,4.944-7.063-4.943c0,0-32.488-57.913-38.138-61.443
C294.177,557.996,301.948,558.701,304.066,558.701z"/>
<path id="path230" fill="#FFFFCC" stroke="#000000" stroke-width="0.5" d="M167.936,553.934c0,0,15.714,3.002,37.961,7.594
c0,0,8.475,39.551,14.125,48.024c5.65,8.475-0.706,8.476-7.063,3.531s-32.488-29.663-36.019-37.432
C173.409,567.882,167.936,553.934,167.936,553.934z"/>
<path id="path234" fill="#FFFFCC" stroke="#000000" stroke-width="0.5" d="M206.534,561.909c0,0,10.241,2.732,12.022,6.645
c1.78,3.909-2.123,9.73-2.123,9.73s-1.766,5.835-3.888,2.018C210.424,576.483,205.353,562.958,206.534,561.909z"/>
<path id="path238" d="M206.603,561.526c0,0,6.356,9.182,12.713,9.182c6.356,0,7.031-0.729,12.006,0.353
c8.122,1.767,7.416-1.766,19.069,0.354c4.661,0.848,9.181-0.706,14.125,1.412c4.944,2.119,10.594,0.706,12.713-2.825
s10.594-10.946,10.594-10.946s-22.6,3.179-27.544,4.591C260.279,563.645,220.729,565.764,206.603,561.526z"/>
<path id="path242" fill="#FFFFCC" stroke="#000000" stroke-width="0.5" d="M285.351,561.879c0,0-11.389,6.182-12.095,10.418
c-0.707,4.237,9.27,10.771,9.27,10.771s4.855,8.122,5.915,3.884C289.5,582.714,286.763,562.586,285.351,561.879z"/>
<path id="path246" fill="#FFFFCC" stroke="#000000" stroke-width="0.5" d="M219.166,571.527c0,0,12.372,19.754,12.755-0.041
c0,0,0.983-2.223-2.124-2.261C219.07,569.092,221.756,561.85,219.166,571.527z"/>
<path id="path250" fill="#FFFFCC" stroke="#000000" stroke-width="0.5" d="M231.839,571.967c0,0,13.986,19.752,12.863-0.164
c0,0,0.012-0.587-3.083-0.855C233.238,570.215,233.847,562.238,231.839,571.967z"/>
<path id="path254" fill="#FFFFCC" stroke="#000000" stroke-width="0.5" d="M244.575,571.98c0,0,14.054,18.766,12.873,1.697
c0,0,0.21-2.177-2.71-2.708C247.866,569.725,247.494,563.987,244.575,571.98z"/>
<path id="path258" fill="#FFFFCC" stroke="#000000" stroke-width="0.5" d="M256.716,572.122c0,0,13.948,20.412,14.563,3.143
c0,0,2.903-2.433-0.18-2.824C260.826,571.133,262.235,563.269,256.716,572.122z"/>
<path id="path262" fill="#E5E5B2" d="M192.845,578.354l-13.521-2.702c-4.591-8.828-8.299-19.688-8.299-19.688
s11.212,1.767,33.282,6.709c0,0,1.547,5.858,4.146,16.091L192.845,578.354z"/>
<path id="path266" fill="#E5E5B2" d="M307.732,570.123c-2.942-4.425-5.268-7.528-6.416-8.245c-5.32-3.325,1.995-2.659,3.989-2.659
c0,0,29.258,0,32.583,1.329c0,0,0.926,3.959,2.134,9.946C340.022,570.494,322.21,566.945,307.732,570.123z"/>
<path id="path270" fill="#CC7226" d="M402.378,326.201c48.945,6.992,94.004-55.936,97.112-73.028
c3.106-17.092-14.762-38.067-14.762-38.067c2.33-5.438-6.216-30.298-15.537-46.613c-9.322-16.314-37.398-14.595-68.367-16.314
c-27.968-1.554-60.599,39.621-62.928,42.729c-2.331,3.108,8.546,70.698,10.876,80.798s-2.33,56.712-2.33,56.712
C406.897,316.349,353.434,319.209,402.378,326.201z"/>
<path id="path274" fill="#EA8E51" d="M339.182,196.051c-2.288,3.051,8.392,69.413,10.68,79.328
c2.288,9.916-2.288,55.682-2.288,55.682c57.687-15.679,6.864-12.967,54.918-6.102c48.056,6.865,92.296-54.918,95.347-71.701
c3.051-16.781-14.492-37.375-14.492-37.375c2.288-5.339-6.103-29.748-15.255-45.766c-9.153-16.018-36.717-14.328-67.125-16.018
C373.506,152.573,341.47,193,339.182,196.051z"/>
<path id="path278" fill="#EFAA7C" d="M340.467,197.195c-2.245,2.995,8.235,68.127,10.481,77.859s-2.246,54.65-2.246,54.65
c55.448-16.173,6.737-12.727,53.9-5.989c47.166,6.738,90.587-53.901,93.581-70.373c2.994-16.47-14.224-36.683-14.224-36.683
c2.245-5.24-5.989-29.197-14.973-44.918c-8.984-15.721-36.037-14.063-65.882-15.721C374.155,154.522,342.713,194.2,340.467,197.195z
"/>
<path id="path282" fill="#F4C6A8" d="M341.753,198.339c-2.204,2.938,8.079,66.842,10.282,76.391
c2.204,9.548-2.203,53.619-2.203,53.619c51.974-15.961,6.61-12.487,52.885-5.876c46.275,6.611,88.877-52.884,91.815-69.043
c2.938-16.161-13.956-35.993-13.956-35.993c2.203-5.142-5.876-28.646-14.69-44.07c-8.813-15.425-35.355-13.799-64.638-15.425
C374.806,156.472,343.956,195.401,341.753,198.339z"/>
<path id="path286" fill="#F9E2D3" d="M343.038,199.483c-2.161,2.881,7.924,65.557,10.085,74.921
c2.161,9.365-2.161,52.588-2.161,52.588c49.205-15.75,6.483-12.246,51.868-5.763c45.386,6.483,87.168-51.868,90.049-67.718
c2.882-15.849-13.687-35.299-13.687-35.299c2.161-5.042-5.765-28.095-14.408-43.223c-8.646-15.128-34.677-13.534-63.396-15.128
C375.455,158.421,345.199,196.602,343.038,199.483z"/>
<path id="path290" fill="#FFFFFF" d="M402.942,319.984c44.493,6.356,85.459-50.85,88.283-66.388
c2.825-15.538-13.419-34.606-13.419-34.606c2.119-4.944-5.65-27.544-14.127-42.375c-8.475-14.831-33.995-13.267-62.149-14.831
c-25.427-1.413-55.088,36.019-57.207,38.844c-2.119,2.825,7.769,64.27,9.888,73.451c2.119,9.182-2.119,51.557-2.119,51.557
C397.116,310.45,358.448,313.628,402.942,319.984z"/>
<path id="path294" fill="#CCCCCC" d="M484.87,259.953c0,0-49.087,13.419-69.568,10.594c0,0-27.896-11.653-43.435,26.838
c0,0-6.356,12.713-9.889,16.244C358.447,317.16,484.87,259.953,484.87,259.953z"/>
<path id="path298" d="M491.58,256.068c0,0-51.206,21.541-68.862,20.834c0,0-28.956-8.122-43.788,17.656
c0,0-14.831,16.244-20.48,19.069c0,0-0.706,2.825,10.594-4.238l18.363,9.182c0,0,26.131,16.95,43.081-11.3
c0,0,7.063-19.775,7.063-23.307c0-3.532,37.433-13.419,40.259-14.125C480.633,269.134,492.286,261.718,491.58,256.068z"/>
<path id="path302" fill="#99CC32" d="M407.887,319.479c-12.134,0-26.918-6.824-26.918-17.857c0-11.032,14.784-22.094,26.918-22.094
c12.138,0,21.976,8.943,21.976,19.975C429.861,310.537,420.023,319.479,407.887,319.479z"/>
<path id="path306" fill="#659900" d="M401.489,290.021c-8.557,1.275-17.541,3.929-17.414,3.547
c2.719-8.156,13.95-14.041,23.812-14.041c7.585,0,14.273,3.493,18.222,8.807C426.107,288.335,416.722,287.753,401.489,290.021z"/>
<path id="path310" fill="#FFFFFF" d="M422.718,289.616c0,0-7.769-5.65-7.769-1.766C414.949,287.85,421.306,295.619,422.718,289.616z
"/>
<path id="path314" d="M405.063,303.963c-4.412,0-7.989-3.577-7.989-7.991c0-4.412,3.577-7.989,7.989-7.989
c4.413,0,7.99,3.577,7.99,7.989C413.053,300.386,409.476,303.963,405.063,303.963z"/>
<path id="path318" fill="#CC7226" d="M221.435,280.434c0,0-5.65-37.432-1.413-45.2c0,0,19.069-17.657,18.363-24.013
c0,0-0.706-31.782-2.825-33.194c-2.119-1.413-15.538-12.006-26.131-0.706c0,0-18.363,31.781-16.95,43.082v3.531
c0,0-13.419-0.706-16.244,2.825c0,0-2.119,9.181-4.238,9.888c0,0-4.944,4.237-1.413,9.181c0,0-3.531,4.237-2.825,11.3l13.419,7.063
c0,0,3.531,25.425,22.601,34.606C212.317,302.909,217.903,291.028,221.435,280.434z"/>
<path id="path322" fill="#FFFFFF" d="M219.669,277.186c0,0-5.085-33.688-1.271-40.681c0,0,17.162-15.891,16.527-21.611
c0,0-0.636-28.604-2.543-29.875c-1.907-1.271-13.984-10.806-23.518-0.636c0,0-16.526,28.604-15.255,38.773v3.178
c0,0-12.077-0.636-14.62,2.542c0,0-1.907,8.263-3.813,8.899c0,0-4.45,3.813-1.271,8.263c0,0-3.178,3.813-2.542,10.17l12.077,6.356
c0,0,3.178,22.883,20.34,31.146C211.462,297.411,216.491,286.72,219.669,277.186z"/>
<path id="path326" fill="#EB955C" d="M234.765,179.775c-1.924-1.519-15.149-11.706-25.478-0.688c0,0-17.904,30.987-16.526,42.004
v3.443c0,0-13.083-0.688-15.838,2.754c0,0-2.066,8.952-4.132,9.641c0,0-4.82,4.132-1.377,8.952c0,0-3.443,4.132-2.754,11.018
l13.083,6.886c0,0,3.443,24.79,22.035,33.741c8.323,4.008,13.772-7.574,17.215-17.903c0,0-5.509-36.496-1.377-44.07
c0,0,18.592-17.215,17.903-23.413C237.52,212.139,236.831,181.152,234.765,179.775z"/>
<path id="path330" fill="#F2B892" d="M233.971,181.523c-1.73-1.625-14.761-11.406-24.825-0.671c0,0-17.444,30.192-16.103,40.927
v3.355c0,0-12.748-0.671-15.432,2.684c0,0-2.013,8.722-4.026,9.394c0,0-4.696,4.025-1.342,8.722c0,0-3.354,4.025-2.684,10.735
l12.748,6.709c0,0,3.354,24.154,21.47,32.876c8.111,3.906,13.419-7.38,16.773-17.445c0,0-5.368-35.56-1.342-42.94
c0,0,18.115-16.773,17.444-22.812C236.654,213.057,235.983,182.865,233.971,181.523z"/>
<path id="path334" fill="#F8DCC8" d="M233.176,183.271c-1.536-1.73-14.373-11.106-24.172-0.653c0,0-16.985,29.398-15.679,39.851
v3.266c0,0-12.413-0.653-15.026,2.613c0,0-1.96,8.493-3.919,9.146c0,0-4.573,3.92-1.307,8.493c0,0-3.267,3.92-2.613,10.453
l12.413,6.533c0,0,3.266,23.518,20.905,32.011c7.897,3.803,13.065-7.186,16.332-16.985c0,0-5.227-34.624-1.307-41.811
c0,0,17.639-16.332,16.985-22.211C235.789,213.976,235.136,184.578,233.176,183.271z"/>
<path id="path338" fill="#FFFFFF" d="M219.669,277.009c0,0-5.085-33.512-1.271-40.504c0,0,17.162-15.891,16.527-21.611
c0,0-0.636-28.604-2.543-29.875c-1.342-1.836-13.984-10.806-23.518-0.636c0,0-16.526,28.604-15.255,38.773v3.178
c0,0-12.077-0.636-14.62,2.542c0,0-1.907,8.263-3.813,8.899c0,0-4.45,3.813-1.271,8.263c0,0-3.178,3.813-2.542,10.17l12.077,6.356
c0,0,3.178,22.883,20.34,31.146C211.462,297.411,216.491,286.543,219.669,277.009z"/>
<path id="path342" fill="#CCCCCC" d="M214.195,265.956c0,0-38.138-18.01-39.727-19.422c0,0,16.067,14.479,17.48,14.479
C193.361,261.013,214.195,265.956,214.195,265.956z"/>
<path id="path346" d="M184.003,255.009c0,0,32.488,6.356,32.488,14.125c0,5.141-0.429,28.834-9.888,26.131
C191.772,291.028,198.128,265.603,184.003,255.009z"/>
<path id="path350" fill="#99CC32" d="M198.834,261.718c0,0,15.852,2.603,17.656,7.416c1.06,2.825,2.23,17.494-7.416,19.422
C201.038,290.165,197.101,272.118,198.834,261.718z"/>
<path id="path354" d="M350.671,336.845c-0.878-3.076,1.438-2.845,4.601-3.794c3.53-1.06,25.071-7.769,26.483-12.359
c1.413-4.591,24.719,3.178,24.719,3.178c3.18,1.412,10.947,6.003,10.947,6.003c8.476,2.119,20.128,2.825,20.128,2.825
c4.238,1.766,10.241,6.709,10.241,6.709c25.778,18.009,47.674,5.297,47.674,5.297c35.313-11.653,24.72-42.022,24.72-42.022
c-5.298-15.891,0.354-21.894,0.354-21.894c0.354-6.709,13.064,4.591,13.064,4.591c4.592,7.416,6.005,16.244,6.005,16.244
c14.125,19.775,8.122-11.653,8.122-11.653c0.353-1.766-4.592-8.122-4.592-10.241s-3.179-8.122-3.179-8.122
c-5.297-6.003-1.06-18.363-1.06-18.363c3.179-24.366-0.706-21.188-0.706-21.188c-2.118-3.178-18.362,14.478-18.362,14.478
c-3.885,6.003-14.479,8.828-14.479,8.828c-4.942,3.178-10.946,0.707-10.946,0.707c-4.59-0.707-14.479,11.653-14.479,11.653
c4.943-0.354,9.182,7.416,13.419,7.769c4.237,0.354,7.415-4.237,10.24-5.297c2.825-1.059,7.769,9.182,7.769,9.182
c0.707,4.59-9.181,13.065-9.181,13.065c-0.707,8.122-3.531,5.297-3.531,5.297c-5.297-1.059-7.415,5.65-9.182,13.772
c-1.766,8.122-9.182,8.829-9.182,8.829c-2.825,13.065-4.945,7.769-4.945,7.769c-0.354-9.888-10.947,0.353-10.947,0.353
c-2.118,3.531-10.239-0.353-10.239-0.353c-12.008-3.531-7.77-7.063-7.77-7.063c3.178-3.884,22.953,0,22.953,0
c3.884-2.825-10.241-9.888-10.241-9.888c-1.06-3.178,0.706-10.947,0.706-10.947c2.119-5.65,14.126-15.538,14.126-15.538
c16.599-2.119,11.654-4.944,11.654-4.944c-10.946-9.182-21.189,4.237-21.189,4.237c-3.884,10.947-34.605,37.432-34.605,37.432
c-8.476,6.003-3.884-6.003-10.947,0c-7.063,6.003-43.435-9.888-43.435-9.888c-20.414-2.106-25.238,25.688-31.47,20.18
C343.93,331.689,353.496,346.732,350.671,336.845z"/>
<path id="path358" d="M694.629,43.132c0,0-45.201,14.125-50.145,47.319c0,0-4.237,40.256,31.78,71.332c0,0,0.707,11.3,4.238,16.95
c0,0-2.825,8.475,30.368-4.944l48.025-14.832c0,0,11.301-4.238,20.481-19.775c9.181-15.538,36.019-48.731,29.662-93.226
c0,0,2.119-19.775-8.475-20.481c0,0-14.832-2.825-27.544,10.594c0,0-12.008,5.65-16.244,4.944L694.629,43.132z"/>
<path id="path362" d="M791.069,41.384c0,0,3.708-15.767-4.837-7.222c0,0-12.432,10.1-25.641,10.1c0,0-25.637,3.884-33.404,27.191
c0,0-6.992,47.39,6.99,57.489c0,0,8.546,13.207,20.978,1.554C767.587,118.843,794.954,65.467,791.069,41.384z"/>
<path id="path366" fill="#323232" d="M790.409,42.016c0,0,3.689-15.438-4.7-7.048c0,0-12.204,9.916-25.173,9.916
c0,0-25.171,3.814-32.798,26.697c0,0-6.865,46.528,6.863,56.444c0,0,8.392,12.967,20.596,1.525
C767.403,118.108,794.224,65.661,790.409,42.016z"/>
<path id="path370" fill="#666666" d="M789.749,42.648c0,0,3.673-15.11-4.563-6.875c0,0-11.978,9.732-24.705,9.732
c0,0-24.705,3.743-32.191,26.202c0,0-6.738,45.667,6.737,55.399c0,0,8.234,12.727,20.213,1.497
C767.22,117.374,793.492,65.855,789.749,42.648z"/>
<path id="path374" fill="#999999" d="M789.089,43.28c0,0,3.654-14.782-4.425-6.703c0,0-11.752,9.549-24.24,9.549
c0,0-24.239,3.672-31.584,25.708c0,0-6.609,44.805,6.61,54.354c0,0,8.08,12.487,19.832,1.469
C767.036,116.639,792.762,66.05,789.089,43.28z"/>
<path id="path378" fill="#CCCCCC" d="M788.429,43.912c0,0,3.638-14.454-4.287-6.529c0,0-11.527,9.365-23.773,9.365
c0,0-23.772,3.602-30.978,25.213c0,0-6.482,43.943,6.483,53.309c0,0,7.924,12.247,19.45,1.441
C766.851,115.904,792.03,66.244,788.429,43.912z"/>
<path id="path382" fill="#FFFFFF" d="M787.767,44.544c0,0,3.619-14.125-4.148-6.356c0,0-11.301,9.181-23.308,9.181
c0,0-23.307,3.531-30.368,24.719c0,0-6.356,43.082,6.355,52.263c0,0,7.77,12.006,19.069,1.412
C766.667,115.17,791.298,66.438,787.767,44.544z"/>
<path id="path386" fill="#992600" d="M414.243,403.323c0,0-36.021-33.901-50.146-35.313c0,0-60.738-7.063-86.87,24.719
c0,0,31.076-36.019,79.807-26.131c0,0-38.138-7.769-60.032-2.119c0,0-29.663,0-46.613,24.719l-4.944,8.475
c0,0,7.063-26.131,39.55-36.726c0,0,40.256-8.475,59.326,0c0,0-38.138-12.006-55.794-8.475c0,0-53.675-4.237-76.275,42.375
c0,0,7.063-25.425,33.194-38.138c0,0,24.013-15.538,60.032-10.594c0,0,25.425,5.65,34.607,9.888
c9.182,4.237,7.063-0.707-7.769-9.182c0,0-9.889-17.656-34.607-16.95c0,0-75.57,6.356-93.932,27.544
c0,0,24.013-19.775,42.375-24.719c0,0,39.55-14.125,54.381-12.713c0,0,43.788,1.766,57.207-5.297c0,0-19.775,8.828-14.125,14.479
c5.649,5.65,17.656,19.069,17.656,21.188s42.729,41.14,49.085,48.908L414.243,403.323z"/>
<path id="path390" fill="#CCCCCC" d="M658.607,745.857c0,0-27.367-64.445-49.438-81.221c0,0,45.906,28.251,52.086,60.032
C661.256,724.67,661.256,742.326,658.607,745.857z"/>
<path id="path394" fill="#CCCCCC" d="M741.593,759.1c0,0-46.789-97.109-79.454-139.484c0,0,76.807,66.212,85.635,113.001
l0.883,9.711l-5.297-4.414C743.358,737.912,742.476,753.803,741.593,759.1z"/>
<path id="path398" fill="#CCCCCC" d="M841.352,673.466c0,0-110.353-105.056-113.001-109.47c0,0,106.821,116.533,112.118,129.775
C840.469,693.771,836.938,677.88,841.352,673.466z"/>
<path id="path402" fill="#CCCCCC" d="M508.528,750.271c0,0,34.43-91.813,67.977-52.087c0,0,26.485,17.656,25.604,22.953
c0,0-7.063-11.477-38.846-10.594C563.263,710.545,529.716,705.248,508.528,750.271z"/>
<path id="path406" fill="#CCCCCC" d="M844.883,525.152c0,0-79.454-50.321-92.695-52.971c-20.848-4.168,87.398,51.204,96.228,69.743
C848.414,541.926,851.945,537.512,844.883,525.152z"/>
<path id="path410" d="M578.803,713.371c0,0,36.02-3.531,48.025-15.537l7.769,6.356l31.075-67.802l6.356,9.183
c0,0,25.426-26.132,24.013-40.257c-1.412-14.125,22.601,10.594,22.601,10.594s-1.413-20.481,11.301-8.477
c0,0-4.237-27.544,10.594-13.419c0,0-18.604-53.246,21.188-7.769c9.889,11.3,2.119-0.706,2.119-0.706s-45.905-84.751-7.769-59.325
c0,0,3.531-40.257,1.412-48.026c-2.118-7.769-5.649-47.319-14.125-56.502c-8.477-9.182,0.706-12.006,10.594-2.824
c0,0-19.775-42.375,3.531-21.188c0,0-6.356-26.838-14.125-31.782c0,0-9.889-30.369,16.949-11.3c0,0-7.769-21.894-13.419-27.544
c0,0-20.48-48.732-7.769-40.257l7.769,6.356c0,0-12.007-24.719-0.706-16.95s11.301,7.063,11.301,7.063s-37.433-58.62-1.412-27.544
c0,0-14.406-24.574-20.481-36.726c0,0-33.193-36.019-7.77-24.719l8.476,2.825c0,0-15.538-17.656-29.663-20.481
c-14.125-2.825,4.237-14.125,15.538-10.594c11.3,3.531,38.844,16.95,38.844,16.95s22.602,33.194,29.663,33.9
c0,0-35.313-13.419-24.719,0.706c0,0,25.425,24.719,12.712,24.013c0,0-10.594,12.713-2.118,28.25c0,0-32.592-32.472-6.355,12.712
l12.006,28.957c0,0-43.081-43.788-23.306-4.944c0,0,30.369,41.669,33.899,42.375c3.531,0.707,11.3,16.244,11.3,16.244l-7.769-3.531
l9.181,15.538c0,0-19.774-21.188-9.181,2.119l9.887,25.425c0,0-36.019-38.844-12.006,13.42c0,0-28.957-9.183-13.419,21.188
c0,0-2.825,28.252-2.119,37.434c0.707,9.183,2.825,59.325-4.942,73.451c-7.77,14.125,10.594,48.025,14.125,55.088
c3.53,7.063,9.888,26.131-5.65,9.889c-15.537-16.244-7.769-6.356-4.237,9.181c3.531,15.538,14.125,43.082,12.713,52.97
c0,0-2.118,2.119-7.769-4.236c0,0-26.132-40.258-23.307-14.832c0,0-2.119,14.125-7.77,29.663c0,0-5.649,19.068-5.649,3.53
c0,0-5.65-29.663-10.595-16.244c-4.943,13.42-11.301,24.014-16.244,28.251c-4.942,4.237-14.125-36.02-16.243-17.656
c0,0-21.188-21.895-29.662,7.063l-20.482,28.957c0,0-0.706-21.894-2.824-11.3C650.135,710.547,597.165,721.141,578.803,713.371z"/>
<path id="path414" d="M518.064,83.389c0,0-20.481-14.125-27.545-13.419c-7.063,0.706,48.731-15.538,121.477,33.194
c0,0,8.476,4.944,14.832,4.238c0,0,5.648,4.237,0.706,10.594c0,0-15.538,16.95,4.237,36.725c0,0,32.487,12.006,22.601-3.531
c0,0,19.069,7.063,23.307,14.125c4.238,7.062,2.118,0,2.118,0s-11.3-12.713-21.894-21.894c0,0-9.183-3.531-14.125-18.363
c-4.944-14.832-9.183-32.488-1.413-38.138c0,0-7.063,7.769-5.649,0.706c1.412-7.063,7.77-13.419,10.594-14.125
c2.825-0.707,31.781-28.604,43.788-29.31c0,0-16.244,2.472-21.541,0.706S617.293,23.003,606.7,20.884c0,0-29.662-11.653-8.476-8.122
c0,0,63.211,6.709,95.346,30.016c0,0-12.713-14.832-45.2-27.191c0,0-39.197-22.247-101.349-13.419c0,0-31.429,5.65-45.2,8.828
c0,0-4.591-1.06-5.65-1.766c-1.059-0.706-21.896-16.597-70.627-4.237c0,0-30.016,8.122-45.2,16.597c0,0-26.838,2.119-33.193,7.769
c0,0-32.842,25.778-36.372,27.191c-3.531,1.413-23.66,14.831-25.072,15.538c0,0,43.435-11.653,47.672-15.891
c4.238-4.237,34.96-8.828,39.197-6.356c4.238,2.472,19.069,1.413,2.119,2.472c0,0,133.483,26.132,134.896,29.663
C511.002,85.507,518.064,83.389,518.064,83.389z"/>
<path id="path418" fill="#CC7226" d="M644.131,67.145c0,0-18.009-13.066-21.54-13.066c-3.532,0-25.426-18.009-32.842-17.303
c-7.415,0.707-28.956-16.95-77.335-2.472c0,0-1.061-3.531,5.297-4.944c0,0,11.301-3.884,12.007-4.944c0,0,35.666-7.416,48.378-1.06
c0,0,16.244,4.591,27.191,15.538c0,0,19.775,5.65,25.425,3.885c0,0,15.538,3.884,16.244,7.063c0,0,10.241,5.297,7.063,9.888
C654.019,59.729,654.725,62.554,644.131,67.145z"/>
<path id="path422" fill="#CC7226" d="M622.112,63.421c1.425,1.116,3.224,1.289,4.292,2.717c0.413,0.554-0.099,1.13-0.653,1.301
c-1.842,0.56-3.706-0.447-5.723,0.591c-0.71,0.366-1.844,0.044-2.819-0.219c-2.882-0.779-6.111-0.823-9.097,0.392
c-3.505-1.994-7.672-0.962-11.348-2.73c-0.102-0.047-0.493,0.563-0.625,0.516c-5.378-2.021-11.985-1.522-16.278-5.555
c-4.286-0.728-8.448-1.543-12.735-2.744c-3.21-0.899-5.697-2.645-8.558-4.114c-2.433-1.25-5.004-2.171-7.713-2.828
c-3.289-0.798-6.521-0.601-9.864-1.519c-0.164-0.044-0.503,0.563-0.648,0.516c-0.57-0.191-1.084-1.22-1.386-1.127
c-2.968,0.922-5.595-0.794-8.533-0.19c-2.08-2.161-5.131-1.729-7.859-2.509c-5.233-1.498-10.804,0.745-16.152-1.022
c7.262-3.252,15.538-1.077,22.71-4.73c4.11-2.094,8.811-0.148,13.348-1.49c0.86-0.254,2.08-0.611,2.786,0.57
c0.237-0.239,0.56-0.661,0.661-0.611c4.325,2.042,8.413,4.292,12.795,6.174c0.604,0.258,1.542-0.152,1.986,0.205
c2.684,2.147,6.114,1.965,8.569,4.119c2.998-0.886,6.164-0.215,9.218-1.317c0.137-0.048,0.55,0.554,0.606,0.516
c1.995-1.321,4.035-0.842,5.609-0.306c0.597,0.203,1.768,0.639,2.307,0.77c1.987,0.487,3.499,1.335,5.581,1.658
c0.201,0.032,0.527-0.568,0.655-0.519c1.982,0.773,3.822,0.674,4.979,2.729c0.238-0.238,0.529-0.658,0.676-0.611
c1.813,0.597,2.959,1.93,4.901,2.355c0.856,0.187,1.938,1.292,2.954,1.603c4.224,1.291,7.479,3.991,11.353,5.571
C619.447,62.132,620.994,62.545,622.112,63.421z"/>
<path id="path426" fill="#CC7226" d="M486.804,38.296c-4.445-3.046-8.627-4.999-12.938-8.152c-0.32-0.235-0.955,0.065-1.313-0.15
c-1.776-1.075-3.346-2.101-5.079-3.33c-0.952-0.674-2.4-0.655-3.299-1.11c-4.491-2.281-9.134-3.267-13.56-5.375
c1.204-1.126,3.185-0.695,4.236-2.119c0.346,0.495,0.766,0.996,1.389,0.659c2.963-1.596,6.229-1.866,9.188-1.708
c3.01,0.163,6.046,0.701,9.181,1.181c0.542,0.083,0.894,1.006,1.464,1.178c3.934,1.171,8.15,0.244,11.894,1.723
c2.81,1.111,5.581,2.564,7.77,4.815c0.444,0.459-0.13,0.991-0.623,1.333c0.685-0.193,1.167,0.171,1.361,0.724
c0.148,0.422,0.148,0.955,0,1.377c-0.196,0.551-0.689,0.729-1.351,0.819c-2.484,0.336,0.645-2.101-0.591-1.31
c-2.248,1.438-0.932,3.92-2.246,6.159c-0.494-0.342-0.9-0.728-0.706-1.413c0.413,0.922-0.65,1.434-0.947,1.992
C489.953,36.869,488.366,39.367,486.804,38.296z"/>
<path id="path430" fill="#CC7226" d="M429.424,51.27c-5.568-1.402-10.954-1.199-16.279-3.452c-0.117-0.049-0.512,0.563-0.625,0.516
c-2.411-1.049-4.032-2.754-5.933-4.602c-1.612-1.568-4.539-0.884-6.789-1.744c-0.572-0.219-0.931-1.123-1.462-1.192
c-2.152-0.277-3.789-1.953-5.634-2.961c4.124-1.404,8.381-1.349,12.729-2.027c0.199-0.031,0.455,0.535,0.69,0.535
c0.24,0,0.47-0.39,0.706-0.627c0.345,0.495,0.878,1.07,1.331,0.622c0.968-0.953,1.949-0.618,2.902-0.547
c0.255,0.018,0.476,0.553,0.709,0.553c0.24,0,0.473-0.549,0.707-0.549c0.239,0.001,0.472,0.549,0.706,0.549
c0.24,0,0.471-0.39,0.706-0.627c1.223,1.381,2.784,0.403,4.235,0.719c1.833,0.401,2.305,2.428,4.201,2.954
c8.324,2.302,15.629,6.09,23.333,9.774c0.542,0.26,0.912,0.698,0.719,1.384c0.471,0,1.023-0.155,1.359,0.078
c1.867,1.292,3.706,2.26,4.937,4.199c0.381,0.599-0.199,1.317-0.61,1.226C444.243,54.292,437.17,53.219,429.424,51.27z"/>
<path id="path434" fill="#CC7226" d="M404.952,129.332c-2.813-2.152-3.842-5.738-5.834-8.902c-0.378-0.6,0.105-1.154,0.666-1.312
c0.987-0.281,1.946,0.563,2.669,0.92c3.081,1.522,5.792,3.715,9.316,3.96c3.515,3.945,11.036,4.625,11.049,10.594
c0.002,1.517-2.521-0.104-3.278,1.412c-4.328-1.771-8.546-1.589-12.748-4.179C405.702,131.152,406.285,130.353,404.952,129.332z"/>
<path id="path438" fill="#CC7226" d="M356.33,36.5c0.238,0.002,12.652,0.413,12.622,0.614c-0.079,0.546-13.729,2.398-14.37,2.098
c-0.29-0.134-13.554,4.156-13.79,3.92C341.266,42.894,355.86,36.5,356.33,36.5z"/>
<path id="path442" d="M383.521,53.726c0,0-26.133,3.178-33.9,5.297c-7.77,2.119-40.609,15.538-45.907,19.069
c0,0-23.66,9.535-53.675,44.848c0,0,13.419-6.003,17.303-10.947c0,0,24.013-22.247,23.66-17.656c0,0,21.541-15.185,20.481-11.3
c0,0,43.082-19.775,39.551-14.125c0,0,38.138-8.122,36.372-4.591c0,0,33.192,7.769,28.25,8.122c0,0-10.241,2.119,1.06,8.475
c0,0-6.003,7.769-15.538,0.707c-9.534-7.063-4.236-3.178-13.064-1.413c0,0-4.592,1.413-12.713-5.65c0,0-9.889-8.122-25.426-1.766
c0,0-54.029,22.247-57.56,23.307c0,0-6.356,4.944-10.594,11.3c0,0-10.241,7.769-15.538,10.241c0,0-22.6,20.481-24.719,22.953
c0,0-6.003,9.181-7.416,9.888c0,0,11.3-6.709,14.831-10.241c0,0,24.719-17.656,34.253-19.069c0,0,7.769-5.297,9.182-7.769
c0,0,25.425-16.244,32.84-16.244c0,0,16.244,9.181,20.482-3.178c0,0,10.239-3.178,20.128-1.06c0,0,5.649-4.591,4.236-8.475
c0,0,2.825-3.178,4.592,3.531c0,0,6.003,6.356,14.479,2.825c0,0,7.063-0.353,3.531,3.884c0,0-7.77,6.709-28.604,7.063
c0,0-21.895,1.06-50.851,14.479c0,0-52.616,18.363-68.86,36.725c0,0-11.3,15.538-20.834,17.657c0,0-10.241,1.412-20.834,14.478
c0,0,17.303-10.241,33.194-10.241c0,0,7.063-4.237,0.353,2.119c0,0-6.356,13.418-3.531,22.953c0,0-1.06,9.181-2.472,12.006
c0,0-13.772,22.6-13.772,26.838c0,4.237,2.119,21.541,2.825,22.6c0.706,1.06-1.766-2.825,4.944,1.413
c6.709,4.237,11.653,7.063,13.065,12.006c1.413,4.944-3.531-9.535-3.884-12.713c-0.353-3.178-7.769-15.891-6.356-20.128
c0,0,1.766,1.766,3.178,4.237c0,0-1.059-1.06,0-7.416c0,0,1.413-9.182,3.885-14.832s6.003-12.359,6.709-13.772
c0.707-1.413,0.707-11.653,3.178-7.063l6.003,4.59c0,0-4.944-4.59-1.06-8.475c0,0-1.766-9.888,1.413-14.479
c0,0,12.359-14.832,15.185-16.597c2.826-1.765,0.353-1.059,0.353-1.059s10.594-7.416,0.353-4.591c0,0-7.063,2.825-12.359,2.825
c0,0-13.419,3.531-6.356-3.885s24.719-16.95,31.429-16.597l1.413,2.825l19.775-4.237l-2.119,1.413c0,0-0.353-0.354,7.063-1.06
s17.656,1.766,20.128-1.413c2.473-3.178,8.477-4.944,7.771-2.472c-0.706,2.472-1.061,6.003-1.061,6.003s8.828-10.241,7.77-6.356
c-1.061,3.884-15.537,13.065-18.011,24.013l18.363-14.479l6.356-5.297c0,0,6.355,3.884,6.709,1.06
c0.354-2.825,8.476-13.066,10.594-12.713c2.119,0.353,5.649-4.591,5.297,0c-0.353,4.591,13.066,14.125,13.066,14.125
s5.648-3.178,8.122-0.706c2.472,2.472,9.887-34.96,9.887-34.96l44.142-18.716l76.983-6.003l-30.017-12.006L383.521,53.726z"/>
<path id="path446" stroke="#4C0000" stroke-width="2" d="M415.655,405.089c0,0-26.484-29.663-41.316-34.254
c0,0-23.659-12.006-67.094,1.766"/>
<path id="path450" stroke="#4C0000" stroke-width="2" d="M368.689,368.363c0,0-44.494-14.125-71.687-6.709
c0,0-32.488,3.531-47.319,27.897"/>
<path id="path454" stroke="#4C0000" stroke-width="2" d="M362.333,366.245c0,0-30.016-12.713-56.147-16.597
c0,0-29.31-4.591-58.62,8.122c0,0-21.541,10.594-31.075,28.603"/>
<path id="path458" stroke="#4C0000" stroke-width="2" d="M364.099,366.951c0,0-27.19-19.422-28.957-21.894
c0,0-12.358-19.422-35.313-20.128c0,0-37.785,1.413-68.154,15.538"/>
<path id="path462" d="M361.794,351.072c2.723,2.583,50.33,53.664,50.33,53.664c62.15,64.624,12.713,4.236,12.713,4.236
c-13.419-8.475-29.663-41.669-29.663-41.669c-2.119-4.944,24.719,12.713,24.719,12.713c7.063,1.412,31.075,35.313,31.075,35.313
c-12.006-4.237-3.53,8.476-3.53,8.476c4.943,3.531,40.965,31.077,40.965,31.077c6.355,7.063,13.419,9.888,13.419,9.888
c24.719-9.182,13.419,14.125,13.419,14.125c4.236,12.007,14.125-8.476,14.125-8.476c19.774-29.664-9.182-25.425-9.182-25.425
c-52.972,4.942-64.978-23.31-64.978-23.31c-4.238-4.236,11.3,0,11.3,0c14.833,3.531-12.713-21.894-12.713-21.894
c4.237,0,20.481,12.006,20.481,12.006c18.363,16.244,21.896,12.713,21.896,12.713c31.782-15.538,50.146-2.119,50.146-2.119
c3.53,2.825-6.356,14.832-3.531,24.016c2.825,9.182,11.3,31.075,11.3,31.075c-4.237,2.824-3.531,21.895-3.531,21.895
c29.663,40.963,12.713,37.432,12.713,37.432c-27.544-0.707-1.411,12.712-1.411,12.712c5.648,3.531,21.188,16.244,21.188,16.244
c-4.944-2.119-7.769,7.063-7.769,7.063c8.475,7.063,3.53,15.538,3.53,15.538c-10.594,2.118-12.713,9.181-12.713,9.181
c12.006,14.126-5.649,14.832-5.649,14.832c6.355,7.769-2.118,28.956-2.118,28.956c-8.477,0-19.775,9.888-19.775,9.888
c4.237,8.477-14.125,18.363-14.125,18.363c-14.832,2.824-9.888,14.831-9.888,14.831c-14.125,10.594-18.363,38.844-18.363,38.844
c-1.412,18.363-5.648,24.014,3.531,20.481c9.182-3.531,7.77-25.425,7.77-25.425c-8.476-27.545,67.095-55.795,67.095-55.795
c7.063-2.824,8.476-12.007,8.476-12.007c3.531,0.706,19.069,14.125,19.069,14.125c13.418,19.775,14.125,3.531,14.125,3.531
c2.118-6.356-0.707-16.95-0.707-16.95c10.595-38.138-14.125-49.438-14.125-49.438c-17.656-59.326,7.063-44.494,7.063-44.494
c4.944,9.888,24.014,19.068,24.014,19.068l6.355-4.237c-2.824-8.477,12.007-19.069,12.007-19.069
c4.943,11.301,15.537-2.824,15.537-2.824c6.356-43.082,28.251-17.656,28.251-17.656c7.063,2.119,9.182-9.889,9.182-9.889
c6.355-18.361,0-42.375,0-42.375c6.355-0.706,23.307,9.889,23.307,9.889c4.944-6.356-11.3-36.021-4.237-31.781
c7.063,4.237,14.831,7.063,14.831,7.063c1.413-3.53-16.243-25.426-16.243-25.426c-7.77-4.945-16.949-40.965-16.949-40.965
c12.712,6.356-4.944-20.481-4.944-20.481c0-5.65,10.594-25.425,10.594-25.425c-1.412-12.006,0-11.3,0-11.3
c4.944,2.119,19.069,4.944,7.063-6.356c-12.006-11.3,1.413-19.775,1.413-19.775c7.769-4.944-16.244-4.238-16.244-4.238
c-9.183-7.769-8.477-14.831-8.477-14.831c14.126,3.531-11.3-21.894-15.536-28.25c-4.237-6.356,12.713-15.538,12.713-15.538
c23.307-6.356,2.823-12.006,2.823-12.006c-34.605,0.706-15.536-18.363-15.536-18.363c10.594,0.707,7.769-3.531,7.769-3.531
c-9.181-2.119-26.132-13.419-26.132-13.419c-7.063-6.356-0.706-4.944-0.706-4.944c29.663,2.119-21.188-17.656-21.188-17.656
c14.125,0-17.655-18.363-17.655-18.363c-3.531-2.825-9.183-16.244-9.183-16.244c-10.594-9.182-19.067-21.188-19.067-21.188
c-0.707-7.769-9.183-16.244-9.183-16.244c-20.48-24.013-30.369-23.307-30.369-23.307c-26.132-6.356-35.313-4.944-35.313-4.944
l-93.229,7.769c-46.612,22.6-32.842,59.679-32.842,59.679c11.301,14.831,27.544,8.122,27.544,8.122
c8.122-10.947,28.604-7.063,28.604-7.063c36.021,5.65,31.431-0.706,31.431-0.706c-4.237-8.122-32.843-19.069-33.196-20.128
c-0.353-1.06-15.891-7.063-15.891-7.063c-5.297-2.119-13.065-18.363-13.065-18.363c-5.649-6.003,22.247,4.238,22.247,4.238
c-2.119,1.766,10.947,8.828,10.947,8.828c30.724-1.766,49.439,17.303,49.439,17.303c19.068,29.31,19.422,14.832,19.422,14.832
c4.943-16.597-15.892-54.029-15.892-54.029c0.706-3.531,15.186,8.122,15.186,8.122c2.472-3.531,3.885,6.709,3.885,6.709
c0.353,4.237,7.063,18.362,7.063,18.362c4.942,22.954,11.3,9.888,11.3,9.888l8.122,16.597c2.472,4.591-8.122,18.01-8.122,18.01
c-0.354,4.944,1.06,4.59-8.828,18.009s-3.885,21.188-3.885,21.188c-2.473,11.653,13.064,10.947,13.064,10.947
c4.591,3.884,10.595,3.884,10.595,3.884c3.179,3.531,7.415,2.472,7.415,2.472c2.825-6.709,13.772-3.178,13.772-3.178
c2.472-4.238,16.95-4.944,16.95-4.944c1.766-4.591,2.472-7.416,8.475-8.475c6.004-1.06-37.432-76.982-37.432-76.982
c11.301-1.413-3.179-23.307-3.179-23.307c-3.885-11.653,16.244,14.125,20.128,16.597c3.886,2.472,5.65,6.356,2.825,6.003
s-6.003,3.532-3.531,3.885c2.473,0.354,25.427,26.837,31.43,44.847c6.003,18.01,16.597,25.072,27.544,35.666
c10.947,10.594,9.534,53.322,9.534,53.322c-0.706,15.538,9.888,34.253,9.888,34.253c3.531,6.709-3.885,38.844-3.885,38.844
c-3.531,3.884-1.06,5.297-1.06,5.297c1.767,2.119,13.771,25.425,13.771,25.425c-3.178-0.353,3.179,6.003,3.179,6.003
c9.181,10.594-2.119,5.297-2.119,5.297c-10.594-2.825,1.767,14.479,1.767,14.479c2.119,3.178-13.772-4.944-13.772-4.944
c-16.243-1.06,4.238,11.653,4.238,11.653c15.185,12.713-4.944,4.943-4.944,4.943c-8.122-3.179-2.472,8.828-2.472,8.828
c5.649,2.824,36.02,15.186,36.02,15.186c0.706,6.711-4.591,15.539-4.591,15.539c0.706,7.063-3.179,13.064-3.179,13.064
c-2.118,14.479-3.178,15.891-3.178,15.891c-7.416,0.354-20.481,24.721-20.481,24.721c-3.179,4.591-21.188,25.777-21.188,25.777
c-3.531,12.359-35.313-0.354-35.313-0.354c-11.653,6.003-8.122,0-8.122,0c-0.706-3.884,7.771-14.479,7.771-14.479
c12.358-4.59,7.769-23.658,7.769-23.658c7.063-2.473-12.713-7.416-12.359-9.534c0.354-2.119,10.595-4.591,10.595-4.591
c14.125-3.531,6.355-7.77,6.355-7.77c-1.06-7.063,4.237-16.95,4.237-16.95c20.48-1.413,0-30.019,0-30.019
c-19.068-13.418-20.835-23.659-20.835-23.659c22.247-14.478,7.77-36.372,8.122-42.729c0.354-6.356,2.473-44.494,2.473-44.494
c-3.531-10.947-8.828-34.96-8.828-34.96c3.885-9.181,16.949-31.428,16.949-31.428c4.944-7.416,20.481-15.891,16.598-21.188
c-3.885-5.297-17.655-2.119-17.655-2.119c-13.772-2.472-12.713,6.709-12.713,6.709c-2.825,1.766-4.237,10.594-4.237,10.594
c-1.273,14.007-16.95,25.072-16.95,25.072c-19.775,10.947-3.531,18.01-3.531,18.01c10.595,11.653-6.71,12.006-6.71,12.006
c-19.422-3.178-4.942,14.831-4.942,14.831c19.067,22.601,13.771,27.544,13.771,27.544c-18.009,1.766,4.237,18.009,4.237,18.009
s-1.412-3.531-1.06-0.353c0.354,3.178,5.649,10.594,7.063,14.125c1.412,3.531-5.65,3.885-5.65,3.885
c1.061,16.95-26.132,9.534-26.132,9.534s0,0-2.824,0.353c-2.824,0.354-22.601-1.059-32.841-4.944
c-10.241-3.884-22.248-3.884-22.248-3.884s-7.063,3.178-20.481,2.825s-27.544,4.59-27.544,4.59
c-7.771-0.706,7.415-8.475,7.769-8.122s10.24-9.535-3.885-8.475c-38.485,2.887-57.561-15.185-57.561-15.185
c-3.53-2.472-8.122-7.416-8.122-7.416c-17.655-3.531,2.473,21.894,2.473,21.894c2.119,2.472-0.354,4.238-0.354,4.238
c-1.413-2.825-15.185-12.359-15.185-12.359C368.316,357.817,365.91,355.461,361.794,351.072z"/>
<path id="path466" fill="#4C0000" d="M319.604,330.579c0,0,20.481,9.887,25.072,14.831c4.591,4.944,29.311,25.072,29.311,25.072
s-9.535-3.531-14.125-6.709c-4.592-3.178-23.66-17.656-23.66-17.656S329.492,335.522,319.604,330.579z"/>
<path id="path470" fill="#99CC32" d="M217.181,275.496c0.595-0.261-0.33-5.05-0.69-6.008c-1.804-4.813-17.656-7.416-17.656-7.416
c-0.401,2.41-0.498,5.229-0.311,8.121C198.523,270.192,207.119,279.936,217.181,275.496z"/>
<path id="path474" fill="#659900" d="M217.181,275.143c-0.793,0.279-0.026-4.827-0.337-5.655
c-1.804-4.813-18.009-7.592-18.009-7.592c-0.401,2.41-0.498,5.228-0.311,8.12C198.523,270.015,206.06,279.053,217.181,275.143z"/>
<path id="path478" d="M209.428,275.395c-1.104,0-1.997-2.013-1.997-4.495c0-2.481,0.894-4.494,1.997-4.494
c1.104,0,1.999,2.013,1.999,4.494C211.427,273.382,210.532,275.395,209.428,275.395z"/>
<path id="path486" d="M128.915,448.525c0,0-9.888,17.655,33.9,7.063c0,0,24.719-2.119,28.957-6.355
c2.119,1.411,16.89,6.591,21.894,7.769c12.006,2.825,26.838-14.833,26.838-14.833s8.122-18.539,13.066-18.539
c4.944,0-0.707,2.825-0.707,2.825s-11.653,17.834-10.947,20.659c0,0-9.181,35.313-37.432,36.726c0,0-28.515,1.678-26.131,12.007
c0,0,15.538-4.237,19.775,0c0,0,19.069-0.707,4.944,10.595l-12.006,20.48c0,0,0.247,6.918-17.656,0.706
c-17.303-6.003-35.489-28.78-35.489-28.78S109.758,473.156,128.915,448.525z"/>
<path id="path490" fill="#E59999" d="M126.796,455.588c0,0-3.531,16.95,61.444-1.413c0,0,7.769,0,12.007,1.413
c4.237,1.412,25.425,6.356,28.957,4.237c0,0-12.713,24.013-33.194,21.188c0,0-23.307,2.825-22.6,11.302
c0,0,7.063,12.712,15.538,16.949c0,0,4.944,4.237,4.237,9.888c-0.706,5.649-5.65,8.476-9.181,9.888
c-3.531,1.413-9.181-4.237-12.006-4.237s-17.656-11.3-25.425-19.774c-7.769-8.476-22.6-29.662-21.894-34.606
C125.384,465.476,126.796,455.588,126.796,455.588z"/>
<path id="path494" fill="#B26565" d="M132.446,486.398c4.591,6.974,10.241,14.39,14.125,18.627
c7.769,8.476,22.6,19.774,25.425,19.774c2.825,0,8.475,5.65,12.006,4.237c3.531-1.412,8.475-4.237,9.181-9.888
c0.707-5.649-4.237-9.888-4.237-9.888c-5.414-2.707-10.251-8.873-13.04-12.975c0,0,0.327,4.499-8.854,3.087
c-9.181-1.413-18.363-6.356-21.188-12.007c-2.825-5.65-7.063-9.888-4.238-3.531s7.063,12.713,9.888,13.419
c2.825,0.706,2.119,2.825-2.119,2.119c-4.238-0.707-9.182-1.413-16.95-10.594L132.446,486.398L132.446,486.398z"/>
<path id="path498" fill="#992600" d="M127.855,449.231c0,0,3.178-24.016,5.297-31.077c0,0-1.413-12.007,2.825-19.422
c4.237-7.417,7.769-18.363,13.066-27.897s5.65-16.597,12.712-19.422c7.062-2.825,17.656-18.01,22.6-19.775
c4.944-1.765,4.591-0.353,4.591-0.353s12.006-26.131,36.019-19.069c0,0-28.604-4.944-0.706-21.541c0,0-8.475,1.942-2.648-10.417
c3.886-8.242,3.001,3.708-16.421,24.542c0,0-8.828,15.185-18.009,20.481c-9.181,5.297-30.369,17.657-32.488,24.366
c-2.119,6.709-7.769,16.95-11.3,19.775c-3.531,2.825-8.475,10.241-9.181,16.244c0,0-2.119,7.063-4.591,9.181
c-2.472,2.119-2.825,7.769-2.825,11.299c0,3.532-3.531,8.477-3.178,12.714c0,0,1.412,33.549,0.706,37.079L127.855,449.231z"/>
<path id="path502" fill="#FFFFFF" d="M112.671,457.354c0,0-3.531-2.472-11.3,8.122c0,0,12.889,58.267,12.889,60.738
c0,0,1.942-3.708-0.354-16.421c-2.295-12.713-3.884-35.137-3.884-35.137L112.671,457.354z"/>
<path id="path506" fill="#992600" d="M150.809,350.354c0,0-31.076,5.65-30.369,57.207l-1.413,43.79c0,0-2.119-45.202-4.238-48.026
c-2.119-2.825,4.944-22.601-0.706-12.007c0,0-24.719,24.719-10.594,62.152c0,0,2.648,5.827-2.648-2.295
c0,0-8.122-22.249-6.18-33.549c0,0,0.353-3.885,3.708-8.828c0,0,15.185-20.659,19.952-24.72c0,0,3.178-25.425,30.369-34.606
C148.69,349.471,158.754,345.41,150.809,350.354z"/>
<path id="path510" d="M396.939,233.468c1.164-0.625,1.148-2.338,2.174-2.644c2.027-0.607,2.317-2.491,3.231-3.875
c1.542-2.329,1.883-5.036,2.91-7.668c0.48-1.236,0.527-2.922-0.024-4.087c-2.072-4.381-3.313-8.705-5.858-12.988
c-0.473-0.794-0.937-2.196-1.29-3.252c-0.817-2.443-3.037-4.193-4.556-6.524c-0.51-0.779,0.419-2.412-0.847-2.56
c-1.584-0.186-4.143-1.209-4.554,0.602c-1.038,4.568,0.747,9.022,2.456,13.334c-1.381,1.222-0.791,2.848-0.522,4.202
c1.255,6.367-0.86,12.286-2.204,18.419c-0.041,0.184,0.563,0.533,0.514,0.643c-2.158,4.743-4.722,9.06-7.935,13.264
c-1.338,1.751-2.878,3.369-3.755,5.246c-0.649,1.39-1.37,3.095-0.929,4.84c-6.065,4.908-10.038,11.697-14.647,18.488
c-0.815,1.201-0.303,3.335,0.672,3.811c1.435,0.703,3.123-1.105,3.953-2.599c0.687-1.232,1.31-2.38,2.177-3.516
c0.233-0.309-0.081-1.049,0.157-1.262c4.647-4.144,7.596-9.328,11.927-13.509c3.442-0.581,6.157-2.343,9.243-4.131
c0.544-0.316,1.469,0.124,1.98-0.221c3.095-2.078,3.091-5.673,3.278-9.045C394.58,236.872,394.927,234.547,396.939,233.468z"/>
<path id="path514" d="M381.329,225.583c0.22-0.136-0.055-0.883,0.138-1.264c0.286-0.572,0.998-0.904,1.284-1.476
c0.192-0.381-0.096-1.052,0.146-1.303c4.118-4.321,4.572-9.66,2.743-14.909c1.809-1.095,1.915-3.323,1.165-4.818
c-1.506-3.002-1.847-6.402-3.567-9.127c-1.416-2.24-4.202-4.437-6.623-2.136c-0.743,0.706-1.311,2.096-0.819,3.353
c0.113,0.288,0.616,0.545,0.568,0.69c-0.188,0.572-1.152,0.967-1.163,1.448c-0.053,2.641-1.737,5.309-0.625,7.656
c1.363,2.876,2.809,6.155,4.003,9.291c-2.179,3.736-0.355,8.06-3.45,11.374c-0.24,0.258-0.225,0.939-0.009,1.296
c0.516,0.858,1.231,1.575,2.09,2.091c0.357,0.213,0.972,0.217,1.324-0.002C379.553,227.106,380.256,226.247,381.329,225.583z"/>
<path id="path518" d="M492.233,207.377c2.451,3.164,2.964,8.099-0.653,10.554c0.971,5.842,6.888,2.348,10.594,1.412
c-0.191-0.685,0.208-1.292,0.708-1.301c1.866-0.026,3.066-1.849,4.941-1.523c0.767-2.75,3.659-3.989,4.796-6.425
c3.048-6.524,2.004-14.069-2.559-19.8c-0.356-0.449,0.025-1.361-0.192-2c-1.335-3.904-4.986-4.46-8.401-5.675
c-2.078-6.842-3.245-13.959-6.354-20.481c-2.851-0.441-4.082-3.512-6.443-4.783c-2.354-1.27-3.355,1.519-3.284,3.365
c0.014,0.362,0.812,0.757,0.512,1.402c-0.136,0.29-0.595,0.486-0.595,0.722c0.002,0.238,0.394,0.47,0.629,0.707
c-1.62,1.448-4.134,2.29-4.653,4.312c-1.686,6.55,2.857,12.068,5.804,17.72c1.044,2.004-0.256,4.249-1.598,6.381
c-0.773,1.227-0.583,3.217-0.097,4.729C486.714,200.806,489.521,203.876,492.233,207.377z"/>
<path id="path522" d="M426.622,239.84c-2.626,3.268-8.65,7.804-3.5,11.208c0.343,0.228,0.996,0.234,1.302-0.002
c3.568-2.763,7.104-4.357,11.405-5.385c0.22-0.051,0.703,0.773,1.354,0.489c2.849-1.242,6.397-1.139,8.487-3.501
c6.651,0.396,12.946-1.575,18.934-3.884c2.051-0.791,4.293-1.778,6.412-2.665c2.431-1.017,4.557-2.655,6.521-4.67
c0.233-0.24,0.858-0.082,1.331-0.082c-0.07-1.523,1.628-1.748,2.063-2.846c0.163-0.41-0.102-1.109,0.133-1.289
c3.775-2.878,5.399-6.441,3.336-10.638c-0.504-1.021-0.942-2.112-1.941-2.952c-1.916-1.608-3.862-0.101-5.711-0.637
c-0.28,1.108-1.567,0.805-2.249,1.155c-1.517,0.775-3.87-0.258-5.387,0.515c-2.405,1.227-4.598,1.526-7.106,2.191
c-0.552,0.145-1.925-0.025-2.208,1.083c-0.236-0.237-0.497-0.65-0.685-0.611c-3.369,0.699-5.595,1.077-7.892,4.064
c-0.182,0.235-0.962-0.081-1.243,0.157c-1.688,1.427-2.403,3.605-4.349,4.792c-0.354,0.217-0.977-0.079-1.319,0.148
c-1.141,0.761-1.787,1.893-2.922,2.682c-0.581,0.404-1.287-0.169-1.229-0.622c0.433-3.438,1.585-6.593,0.569-9.905
c3.667-4.449,8.111-7.891,11.301-12.713c0.025-3.824,1.248-7.613,1.049-11.28c-0.019-0.341-0.526-1.635-0.748-2.248
c-0.552-1.508,1.049-3.39-0.441-4.668c-2.479-2.124-4.761-0.578-6.216,1.953c-3.245,0.688-6.893,1.912-9.679-0.267
c-1.778-1.39-2.799-2.989-4.21-4.854c-1.738-2.299-1.147-4.834-1.023-7.596c0.011-0.226-0.546-0.466-0.546-0.703
c0.002-0.238,0.391-0.47,0.627-0.706c-1.246-1.105-1.731-2.974-3.531-3.532c0.538-1.928-0.654-3.489-2.192-4.022
c-3.522-1.22-6.483,2.156-9.823,2.285c-0.908,0.034-1.732-1.799-2.878-2.373c-0.764-0.381-2.006-0.439-2.646,0.03
c-1.215,0.89-2.255,1.091-3.593,1.453c-2.854,0.77-5.11,2.701-7.725,4.211c-2.622,1.513-4.31,4.05-6.216,6.381
c-1.661,2.034-1.901,6.296,0.605,7.179c3.254,1.148,5.557-3.625,9.027-3.049c0.551,0.09,0.915,0.639,0.721,1.324
c0.688,0.193,1.071-0.212,1.412-0.706c1.515,1.799,3.57,2.394,5.227,3.936c1.714,1.596,4.796,0.858,6.589,2.619
c2.698,2.652,1.712,7.386,5.136,9.69c-1.034,2.318-2.106,4.573-2.698,7.092c-0.497,2.129,1.258,4.243,3.396,4.082
c2.222-0.166,2.684-1.506,3.54-3.406c0.472,0.472,1.3,0.996,1.228,1.377c-0.807,4.214-2.62,7.733-3.429,12.025
c-0.104,0.56-0.644,0.917-1.33,0.722c-0.826,7.326-7.98,11.553-12.475,17.141c-0.712,0.886-0.719,3.092,0.004,3.803
c2.478,2.449,5.938-0.281,8.938-1.169c0.376-2.129,1.893-3.792,4.245-3.694c0.452,0.018,0.866-0.939,1.438-1.169
c0.614-0.244,1.501,0.152,2.007-0.198c3.053-2.11,5.539-4.063,8.606-6.162c0.339-0.231,0.946,0.05,1.328-0.141
c0.574-0.286,0.904-0.969,1.475-1.296c0.614-0.353,1.041,0.159,1.383,0.653c-1.142,0.616-1.147,2.306-2.176,2.663
c-1.367,0.473-2.358,1.379-3.549,2.168c-0.516,0.341-1.68-0.097-1.862,0.219C429.966,237.508,427.875,238.281,426.622,239.84z"/>
<path id="path526" d="M328.785,152.602c0,0-16.312-5.071-36.019,40.257c0,0-4.238,9.181-8.475,12.712
c-4.238,3.531-24.013,9.888-27.544,16.95l-18.363,28.25c0,0,26.131-28.25,31.782-32.488c0,0,14.125-14.832,8.475-2.825
c0,0-24.719,19.069-22.601,35.313c0,0-9.887,25.425-11.3,28.957c0,0,28.25-56.5,32.488-58.62c4.237-2.119,6.356-2.119,4.237,4.238
c-2.119,6.357-2.825,35.313-7.769,38.844c0,0,14.125-36.02,12.712-41.669c0,0,5.65-6.356,9.888,2.825l-2.119,28.25l7.769,21.188
c0,0-4.237-19.775-1.413-47.319c0,0-3.531-18.363,3.531-8.475c7.062,9.888,24.013,20.481,24.013,28.957
c0,0-9.181-31.075-25.425-39.55l-7.063,10.594l-2.119-3.531c0,0-6.356-1.413,1.413-13.419c7.769-12.006,7.063-13.419,7.063-13.419
s11.3,12.713,14.125,12.713c0,0,23.307-13.419,25.425,29.663c0,0,12.007-25.425-4.237-37.432c0,0-26.132-3.531-24.013-12.712
l12.713-21.894c6.356-9.182,3.531-4.238,3.531-4.238L328.785,152.602z"/>
<path id="path530" d="M293.473,181.558c0,0-22.6,0-28.25,9.181l-12.713,16.95c0,0,30.369-17.656,37.432-19.775
S293.473,181.558,293.473,181.558z"/>
<path id="path534" d="M222.847,192.858c0,0-3.531,2.119-4.238,7.063c-0.706,4.944-4.944,5.65-3.531,10.594
c1.413,4.944,4.944,9.182,4.944,2.119c0-7.063,2.825-10.594,4.238-12.712C225.672,197.802,228.497,190.033,222.847,192.858z"/>
<path id="path538" d="M207.31,300.916c0,0-14.832-7.063-20.481-13.419c-5.65-6.356-4.852,2.765-13.419,2.119
c-10.324-0.779-8.475-28.957-8.475-28.957l-7.063,13.418c0,0-2.119,25.425,12.006,21.188c6.898-2.069,9.181,0.706,6.356,2.119
c-2.825,1.413,9.887,2.119,4.943,4.944c-4.943,2.825,20.481-6.356,16.244,12.006L207.31,300.916z"/>
<path id="path542" d="M185.063,326.341c0,0-27.191,7.769-33.547-9.181c0,0-8.475,4.237-4.591,9.534
c3.885,5.297,6.003,6.003,6.003,6.003s9.534,2.119,8.475,3.531c-1.06,1.413-5.297,7.416-5.297,7.416S174.115,333.05,185.063,326.341
z"/>
<path id="path546" fill="#FFFFFF" d="M588.337,464.416c-0.754,3.768-3.704,5.182-7.063,6.355c-3.386-1.69-7.973-7.176-11.301-3.53
c-0.837-0.849-2.213-0.954-2.819-2.123c-0.82-1.585-0.342-3.433-0.944-4.841c-0.962-2.246-2.214-4.658-1.886-7.161
c3.188-1.258,4.239-4.623,3.401-7.735c-0.122-0.454-0.879-0.802-0.525-1.418c0.329-0.57,0.89-0.972,1.36-1.441
c-0.237,0.237-0.493,0.648-0.689,0.613c-1.077-0.188-0.857-1.313-0.628-1.995c1.032-3.083,4.589-3.549,6.969-1.443
c0.452-0.998,1.352-0.655,2.118-0.706c-0.088-1.022,0.633-1.953,0.982-2.694c0.913-1.938,3.791,0.014,5.197-1.065
c1.899-1.457,3.776-2.691,5.681-1.628c3.193,1.789,6.212,3.93,8.327,7.004c1.017,1.473,1.439,3.733,1.338,5.426
c-0.067,1.143-2.507,0.521-3.111,2.161c-1.139,3.086,2.095,4.003,3.43,6.364c0.35,0.616-0.117,1.153-0.673,1.326
c-0.726,0.227-2.11-0.107-1.866,0.691C597.351,462.212,592.484,463.409,588.337,464.416z"/>
<path id="path550" fill="#FFFFFF" d="M571.385,499.022c-0.012-3.068-2.839-6.17-0.704-9.183c0.238,0.237,0.471,0.627,0.706,0.627
c0.238,0,0.471-0.39,0.706-0.627c2.641,3.913,9.088,5.552,8.837,10.576c-0.038,0.79-1.958,2.41-0.36,3.55
c-3.201,2.38-3.3,6.564-4.944,9.887c-2.186-0.505-4.325-1.146-6.356-2.117c0.622-2.624,0.415-5.599,1.863-7.929
C571.896,502.575,571.391,500.67,571.385,499.022z"/>
<path id="path554" fill="#CCCCCC" d="M277.935,483.132c0,0-29.765,17.896-4.944-9.182c15.538-16.95,33.194-26.838,33.194-26.838
s18.362-7.771,24.719-9.89c6.355-2.119,33.193-11.301,38.845-12.007c5.649-0.706,22.6-7.769,34.606-0.706
c12.006,7.063,26.131,14.831,26.131,14.831s-28.956-14.831-35.313-10.594c-6.356,4.237-19.069,3.531-29.663,9.182
c0,0-26.131,7.771-31.781,11.303c-5.649,3.53-24.013,24.013-26.837,22.601c-2.825-1.413,0.706-2.119,2.825-7.063
c2.119-4.943-1.412-7.77-15.538,3.531C280.054,479.601,277.935,483.132,277.935,483.132z"/>
<path id="path558" d="M291.01,472.596c0,0,2.49-23.022,17.459-20.084c0,0,14.523-7.361,19.33-10.837c0,0,14.37-3.006,16.685-4.095
c32.627-15.361,58.614-7.383,59.581-9.359c0.965-1.977,35.614,10.59,41.986,17.806c0.69,0.781-18.063-9.884-35.188-13.223
c-14.607-2.85-52.748,0.438-72.005,10.211c-5.249,2.664-21.043,12.877-25.513,12.682C308.878,455.498,291.01,472.596,291.01,472.596
z"/>
<path id="path562" fill="#CCCCCC" d="M284.292,517.738c0,0-26.838-4.237,2.825-7.063c0,0,31.782-3.531,38.844-12.713
c0,0,24.013-16.244,28.956-16.95c4.944-0.706,57.913-13.419,58.619-17.656c0.707-4.236,10.595-4.236,13.419-2.824
c2.825,1.413,1.413,3.53-3.531,4.943c-4.943,1.412-60.031,30.369-71.332,32.487c-11.3,2.119-31.781,15.538-40.256,17.656
C303.36,517.738,284.292,517.738,284.292,517.738z"/>
<path id="path566" d="M318.757,504.676c0,0-15.153-1.464,0.033-2.909c0,0,15.566-6.046,19.183-10.748c0,0,12.296-8.316,14.826-8.678
c2.531-0.362,27.18-6.872,27.542-9.04c0.362-2.17,60.51-24.384,68.314-18.751c5.14,3.709-12.343,0.748-29.354,8.535
c-2.393,1.095-62.164,26.85-67.95,27.934c-5.785,1.087-16.271,7.956-20.611,9.04C326.402,501.145,318.757,504.676,318.757,504.676z"
/>
<path id="path570" d="M304.773,508.557c0,0,9.181-0.706,7.063,2.119c-2.119,2.825-6.357,1.412-6.357,1.412L304.773,508.557z"/>
<path id="path574" d="M292.061,511.382c0,0,9.181-0.706,7.063,2.119c-2.119,2.825-6.356,1.412-6.356,1.412L292.061,511.382z"/>
<path id="path578" d="M273.698,514.207c0,0,9.181-0.706,7.063,2.119c-2.119,2.824-6.356,1.412-6.356,1.412L273.698,514.207z"/>
<path id="path582" d="M260.279,515.619c0,0,9.181-0.706,7.063,2.119c-2.118,2.825-6.356,1.412-6.356,1.412L260.279,515.619z"/>
<path id="path586" d="M328.079,445.7c0,0,7.77,0,5.649,2.825c-2.119,2.824-7.769,2.117-7.769,2.117L328.079,445.7z"/>
<path id="path590" d="M310.423,455.588c0,0,11.487-3.78,7.063,2.118c-2.118,2.825-6.356,1.413-6.356,1.413L310.423,455.588z"/>
<path id="path594" d="M290.648,464.063c0,0,9.181-0.705,7.063,2.119c-2.118,2.825-6.356,1.412-6.356,1.412L290.648,464.063z"/>
<path id="path598" d="M277.229,474.656c0,0,9.181-0.706,7.063,2.119c-2.118,2.824-6.356,1.411-6.356,1.411L277.229,474.656z"/>
<path id="path602" d="M265.223,483.132c0,0,9.181-0.706,7.063,2.118c-2.119,2.825-6.356,1.413-6.356,1.413L265.223,483.132z"/>
<path id="path606" d="M334.228,494.427c0,0,12.221-0.938,9.4,2.819c-2.82,3.761-8.461,1.881-8.461,1.881L334.228,494.427z"/>
<path id="path610" d="M352.59,485.951c0,0,12.221-0.939,9.4,2.82c-2.819,3.761-8.461,1.88-8.461,1.88L352.59,485.951z"/>
<path id="path614" d="M371.659,478.183c0,0,12.22-0.938,9.399,2.819c-2.819,3.761-8.461,1.881-8.461,1.881L371.659,478.183z"/>
<path id="path618" d="M390.021,469.708c0,0,12.221-0.939,9.399,2.819c-2.819,3.761-8.461,1.88-8.461,1.88L390.021,469.708z"/>
<path id="path622" d="M341.29,437.926c0,0,12.22-0.938,9.4,2.82c-2.82,3.761-9.874,3.293-9.874,3.293L341.29,437.926z"/>
<path id="path626" d="M358.946,432.276c0,0,12.22-0.939,9.399,2.818c-2.818,3.762-10.58,3.293-10.58,3.293L358.946,432.276z"/>
<path id="path630" d="M318.898,502.907c0,0,9.181-0.706,7.063,2.118c-2.119,2.824-6.355,1.413-6.355,1.413L318.898,502.907z"/>
<path id="path634" fill="#992600" d="M189.653,327.753c0,0-7.769,15.538-8.475,21.188c0,0,1.413-15.538,3.531-19.069
C186.828,326.341,189.653,327.753,189.653,327.753z"/>
<path id="path638" fill="#992600" d="M157.165,352.472c0,0-5.65,25.425-4.944,30.369c0,0-2.119-20.481-1.412-22.6
C151.515,358.123,157.165,352.472,157.165,352.472z"/>
<path id="path642" fill="#CCCCCC" d="M193.891,220.755l-0.353,5.65l-3.885,0.354c0,0,25.072,22.247,26.132,35.666
C215.785,262.425,217.197,247.946,193.891,220.755z"/>
<path id="path646" d="M200.925,222.989c-0.761-0.734-0.374-2.05-1.095-2.509c-1.428-0.911,2.292-1.012,1.889-2.276
c-0.676-2.129-0.346-2.167-0.562-4.419c-0.101-1.056,0.938-3.775,1.618-4.552c2.553-2.917,0.215-8.094,3.111-10.833
c0.537-0.51,1.201-1.485,1.704-2.223c1.164-1.7,3.254-2.562,4.931-4.024c0.562-0.487,0.207-1.948,1.211-1.785
c1.261,0.203,3.452-0.026,3.373,1.458c-0.2,3.743-2.546,6.78-4.806,9.725c0.796,1.243-0.013,2.364-0.514,3.348
c-2.357,4.626-2.023,9.642-2.331,14.657c-0.009,0.15-0.551,0.288-0.537,0.381c0.623,4.123,1.654,8.005,3.207,11.941
c0.646,1.642,1.478,3.222,1.743,4.859c0.196,1.211,0.378,2.682-0.343,3.927c3.593,5.103,1.282,9.783,3.346,16.018
c0.365,1.104,3.353,4.483,2.535,4.199c-4.437-1.538-4.635-2.241-4.947-3.57c-0.258-1.1-0.84-3.531-1.259-4.594
c-0.113-0.29-0.415-3.616-0.553-3.832c-2.671-4.206-0.274-3.895-2.692-8.059c-2.521-1.201-4.227-3.15-6.21-5.202
c-0.35-0.36,1.668-1.638,1.349-2.014c-1.928-2.276-3.964-3.63-3.371-6.267C201.997,226.126,202.238,224.26,200.925,222.989z"/>
<path id="path650" d="M194.597,226.052c0,0,0.707,12.006,4.944,14.832c4.238,2.825,2.119,1.413-3.531-0.706
c-5.65-2.119-3.531-3.531-3.531-3.531s-4.944,0.706-0.706,4.237c4.237,3.531,10.594,7.769,7.769,7.769s-16.244-7.063-16.244-12.006
c0-4.944-1.766-12.183-1.766-12.183s1.942-1.413,10.417-1.236C191.948,223.228,194.42,224.463,194.597,226.052z"/>
<path id="path654" fill="#FFFFFF" stroke="#000000" stroke-width="0.1" d="M193.184,258.894c0,0-15.043-4.928-47.672,1.059
c0,0,15.946-3.669,49.085,0.353C212.783,262.513,193.184,258.894,193.184,258.894z"/>
<path id="path658" fill="#FFFFFF" stroke="#000000" stroke-width="0.1" d="M196.889,258.768c0,0-14.56-6.211-47.586-3.067
c0,0,16.205-2.276,48.871,4.596C216.103,264.068,196.889,258.768,196.889,258.768z"/>
<path id="path662" fill="#FFFFFF" stroke="#000000" stroke-width="0.1" d="M200.045,258.932c0,0-14.058-7.276-47.226-6.596
c0,0,16.329-1.066,48.395,8.217C218.811,265.647,200.045,258.932,200.045,258.932z"/>
<path id="path666" fill="#FFFFFF" stroke="#000000" stroke-width="0.1" d="M202.288,259.326c0,0-12.049-7.604-41.842-9.543
c0,0,14.724,0.3,42.764,11.086C218.599,266.789,202.288,259.326,202.288,259.326z"/>
<path id="path670" fill="#FFFFFF" stroke="#000000" stroke-width="0.1" d="M405.838,277.894c0,0-1.642,1.137-1.264-0.948
c0.38-2.085,50.185-25.339,56.564-24.897C461.14,252.048,407.732,275.365,405.838,277.894z"/>
<path id="path674" fill="#FFFFFF" stroke="#000000" stroke-width="0.1" d="M399.846,279.021c0,0-1.547,1.263-1.333-0.846
c0.214-2.108,48.04-29.202,54.436-29.262C452.947,248.914,401.537,276.354,399.846,279.021z"/>
<path id="path678" fill="#FFFFFF" stroke="#000000" stroke-width="0.1" d="M394.044,281.449c0,0-1.462,1.363-1.388-0.755
c0.074-2.117,35.063-29.479,52.389-32.788C445.045,247.906,413.21,262.255,394.044,281.449z"/>
<path id="path682" fill="#FFFFFF" stroke="#000000" stroke-width="0.1" d="M388.966,284.739c0,0-1.314,1.226-1.248-0.68
c0.066-1.907,31.557-26.532,47.147-29.509C434.865,254.55,406.216,267.464,388.966,284.739z"/>
<path id="path686" fill="#CCCCCC" d="M333.023,545.988c0,0-26.838-4.237,2.824-7.063c0,0,31.781-3.531,38.845-12.712
c0,0,24.013-16.244,28.956-16.95c4.943-0.707,33.899-7.063,34.606-11.301c0.706-4.237,11.3-8.475,14.125-7.063
c2.825,1.413,2.825,17.655-2.119,19.068c-4.942,1.412-38.138,14.125-49.438,16.244c-11.301,2.118-31.782,15.537-40.257,17.656
C352.092,545.988,333.023,545.988,333.023,545.988z"/>
<path id="path690" d="M461.915,479.953c0,0-5.297,2.825-7.416,7.416c0,0-11.3,18.716-36.372,24.366c0,0-40.609,15.891-54.382,19.422
c0,0-23.659,8.828-36.727,7.416c0,0-12.358,0.353-1.411,3.178c0,0,35.666-3.531,41.669-6.709c0,0,27.544-9.182,32.841-13.772
c5.297-4.59,37.432-13.419,41.315-16.949C445.317,500.789,462.621,485.957,461.915,479.953z"/>
<path id="path694" d="M358.24,535.589c0,0,9.231-0.398,7.195,2.336c-2.034,2.737-6.356,1.193-6.356,1.193L358.24,535.589z"/>
<path id="path698" d="M345.523,537.977c0,0,9.23-0.398,7.196,2.336c-2.036,2.736-6.357,1.195-6.357,1.195L345.523,537.977z"/>
<path id="path702" d="M327.11,540.18c0,0,9.231-0.399,7.195,2.336c-2.034,2.735-6.356,1.193-6.356,1.193L327.11,540.18z"/>
<path id="path706" d="M313.631,541.141c0,0,9.232-0.398,7.197,2.336c-2.036,2.736-6.358,1.193-6.358,1.193L313.631,541.141z"/>
<path id="path710" d="M387.432,522.526c0,0,12.289-0.531,9.578,3.108c-2.708,3.642-8.463,1.59-8.463,1.59L387.432,522.526z"/>
<path id="path714" d="M405.645,514.714c0,0,10.521-5.828,9.578,3.109c-0.477,4.513-8.463,1.589-8.463,1.589L405.645,514.714z"/>
<path id="path718" d="M421.768,509.745c0,0,12.642-6.534,9.579,3.108c-1.374,4.326-8.465,1.59-8.465,1.59L421.768,509.745z"/>
<path id="path722" d="M438.566,501.226c0,0,7.695-8.652,9.578,3.109c0.717,4.481-8.464,1.59-8.464,1.59L438.566,501.226z"/>
<path id="path726" d="M372.28,530.444c0,0,9.23-0.401,7.196,2.336c-2.035,2.733-6.359,1.192-6.359,1.192L372.28,530.444z"/>
<path id="path730" fill="#FFFFFF" stroke="#000000" stroke-width="0.1" d="M435.138,316.105c0,0-1.282,1.174-1.284-0.717
c0-1.889,30.871-25.309,46.484-27.752C480.338,287.636,451.913,299.517,435.138,316.105z"/>
<path id="path734" d="M440.374,428.748c0,0,38.847,39.553,55.09,45.908c0,0,16.244,19.774,9.183,65.683
c0,0-5.65,13.419-11.301-23.307c0,0,5.649-44.494-14.125-16.244c0,0-14.834-17.479-3.533-16.95c0,0,5.651,3.531,6.357,0.706
c0.707-2.825-13.42-26.838-43.789-52.265C407.887,406.854,440.374,428.748,440.374,428.748z"/>
<path id="path738" fill="#FFFFFF" stroke="#000000" stroke-width="0.1" d="M337.261,497.257c0,0-0.354-3.178,2.825-1.766
c3.178,1.412,169.503,12.358,225.298,54.734C565.384,550.227,485.576,509.264,337.261,497.257z"/>
<path id="path742" fill="#FFFFFF" stroke="#000000" stroke-width="0.1" d="M355.623,489.488c0,0-0.354-3.18,2.825-1.767
c3.179,1.412,244.367-0.354,286.036,56.854C644.484,544.576,605.641,500.082,355.623,489.488z"/>
<path id="path746" fill="#FFFFFF" stroke="#000000" stroke-width="0.1" d="M376.104,482.426c0,0-0.353-3.179,2.825-1.766
c3.18,1.412,309.343-21.541,351.013,35.666C729.941,516.326,712.991,471.125,376.104,482.426z"/>
<path id="path750" fill="#FFFFFF" stroke="#000000" stroke-width="0.1" d="M393.762,473.95c0,0-0.354-3.178,2.824-1.767
c3.179,1.413,218.941-66.742,260.611-9.533C657.197,462.65,633.537,419.214,393.762,473.95z"/>
<path id="path754" fill="#FFFFFF" stroke="#000000" stroke-width="0.1" d="M291.354,514.207c0,0-0.353-3.178,2.825-1.766
c3.178,1.412,34.606,5.297,38.138,73.804C332.317,586.245,319.604,512.088,291.354,514.207z"/>
<path id="path758" fill="#FFFFFF" stroke="#000000" stroke-width="0.1" d="M275.816,517.032c0,0-0.353-3.18,2.825-1.767
c3.178,1.412,28.25-6.71,23.306,61.797C301.948,577.063,304.066,514.913,275.816,517.032z"/>
<path id="path762" fill="#FFFFFF" stroke="#000000" stroke-width="0.1" d="M261.691,517.738c0,0-0.354-3.179,2.825-1.767
c3.179,1.412,30.369,2.473,8.475,42.022C272.991,557.995,289.941,515.619,261.691,517.738z"/>
<path id="path766" fill="#FFFFFF" stroke="#000000" stroke-width="0.1" d="M345.252,439.457c0,0-0.784,3.529,1.951,1.381
c28.37-22.292,85.65-126.292,183.971-136.239C531.174,304.599,463.536,283.217,345.252,439.457z"/>
<path id="path770" fill="#FFFFFF" stroke="#000000" stroke-width="0.1" d="M365.027,436.278c0,0-2.406-2.106,0.892-3.21
c3.298-1.104,201.831-129.115,271.194-115.05C637.113,318.018,589.252,304.758,365.027,436.278z"/>
<path id="path774" fill="#FFFFFF" stroke="#000000" stroke-width="0.1" d="M328.229,447.053c0,0-0.897,2.823,2.122,1.101
c15.848-9.04,22.229-110.054,99.171-112.271C429.521,335.882,372.297,309.903,328.229,447.053z"/>
<path id="path778" fill="#FFFFFF" stroke="#000000" stroke-width="0.1" d="M293.053,466.521c0,0-1.902,2.271,1.546,1.821
c18.091-2.352,55.884-75.222,134.348-66.254C428.947,402.089,372.507,376.759,293.053,466.521z"/>
<path id="path782" fill="#FFFFFF" stroke="#000000" stroke-width="0.1" d="M312.895,455.704c0,0-1.432,2.594,1.868,1.49
c17.303-5.78,40.403-84.549,119.13-90.813C433.893,366.382,373.639,352.357,312.895,455.704z"/>
<path id="path786" fill="#FFFFFF" stroke="#000000" stroke-width="0.1" d="M280.623,475.559c0,0-1.542,1.841,1.252,1.478
c14.653-1.905,45.265-60.929,108.822-53.665C390.696,423.37,344.979,402.854,280.623,475.559z"/>
<path id="path790" fill="#FFFFFF" stroke="#000000" stroke-width="0.1" d="M267.206,485.992c0,0-1.775,1.845,1.035,1.637
c7.359-0.546,61.455-58.951,94.063-31.58C362.303,456.049,341.089,422.99,267.206,485.992z"/>
<path id="path794" fill="#FFFFFF" stroke="#000000" stroke-width="0.1" d="M389.974,429.627c0,0-2.12-2.392,1.291-3.071
c3.411-0.679,216.529-102.579,283.56-79.862C674.823,346.693,629.021,327.494,389.974,429.627z"/>
<path id="path798" fill="#FFFFFF" stroke="#000000" stroke-width="0.1" d="M330.904,543.164c0,0-0.354-3.179,2.824-1.768
c3.179,1.413,30.369,2.474,8.476,42.022C342.204,583.42,359.154,541.045,330.904,543.164z"/>
<path id="path802" fill="#FFFFFF" stroke="#000000" stroke-width="0.1" d="M349.268,540.339c0,0-0.354-3.179,2.824-1.766
c3.18,1.412,34.607,5.297,38.14,73.804C390.23,612.377,377.518,538.22,349.268,540.339z"/>
<path id="path806" fill="#FFFFFF" stroke="#000000" stroke-width="0.1" d="M361.273,537.514c0,0-0.354-3.179,2.824-1.766
c3.179,1.412,46.613,7.416,88.282,64.622C452.381,600.37,389.523,535.395,361.273,537.514z"/>
<path id="path810" fill="#FFFFFF" stroke="#000000" stroke-width="0.1" d="M374.736,533.931c0,0-0.771-3.104,2.564-2.125
c3.337,0.979,39.416-2.375,106.684,57.969C483.984,589.773,402.455,528.076,374.736,533.931z"/>
<path id="path814" fill="#FFFFFF" stroke="#000000" stroke-width="0.1" d="M393.1,526.162c0,0-0.771-3.104,2.565-2.126
c3.337,0.979,64.841,8.926,156.119,70.681C551.784,594.717,420.818,520.308,393.1,526.162z"/>
<path id="path818" fill="#FFFFFF" stroke="#000000" stroke-width="0.1" d="M321.723,505.732c0,0-0.353-3.18,2.825-1.767
c3.179,1.412,97.464,6.003,151.14,53.322C475.688,557.289,414.064,513.545,321.723,505.732z"/>
<path id="path822" fill="#FFFFFF" stroke="#000000" stroke-width="0.1" d="M304.066,512.795c0,0-0.353-3.179,2.825-1.766
c3.179,1.412,46.613,7.415,88.282,64.622C395.174,575.651,332.317,510.676,304.066,512.795z"/>
<path id="path826" fill="#FFFFFF" stroke="#000000" stroke-width="0.1" d="M412.306,518.021c0,0-0.997-3.037,2.403-2.308
s65.321,4.147,160.88,59.049C575.589,574.764,438.462,514.036,412.306,518.021z"/>
<path id="path830" fill="#FFFFFF" stroke="#000000" stroke-width="0.1" d="M427.138,513.785c0,0-0.998-3.039,2.402-2.309
c3.401,0.729,65.322,4.147,160.88,59.049C590.42,570.525,454.354,509.092,427.138,513.785z"/>
<path id="path834" fill="#FFFFFF" stroke="#000000" stroke-width="0.1" d="M444.088,504.957c0,0-0.998-3.039,2.402-2.308
c3.399,0.729,79.447,8.385,237.863,68.936C684.354,571.585,471.303,500.264,444.088,504.957z"/>
<path id="path838" d="M247.566,517.032c0,0,9.182-0.706,7.063,2.118s-6.356,1.412-6.356,1.412L247.566,517.032z"/>
<path id="path842" d="M301.948,541.751c0,0,9.181-0.706,7.063,2.119c-2.119,2.825-6.356,1.412-6.356,1.412L301.948,541.751z"/>
<path id="path846" d="M286.41,541.045c0,0,9.182-0.706,7.063,2.119c-2.119,2.824-6.356,1.412-6.356,1.412L286.41,541.045z"/>
<path id="path850" d="M118.022,520.177c0,0,8.908,2.336,5.98,4.313c-2.926,1.978-6.469-0.745-6.469-0.745L118.022,520.177z"/>
<path id="path854" d="M121.554,503.227c0,0,8.908,2.336,5.98,4.313c-2.926,1.978-6.469-0.745-6.469-0.745L121.554,503.227z"/>
<path id="path858" d="M108.841,495.458c0,0,8.908,2.336,5.98,4.312c-2.925,1.979-6.469-0.744-6.469-0.744L108.841,495.458z"/>
<path id="path862" fill="#CCCCCC" d="M249.685,627.914c0,0-2.825,0-9.888,3.531c-3.531,0-23.307,6.355-33.194,24.013
C206.603,655.458,228.497,638.508,249.685,627.914z"/>
<path id="path866" fill="#CCCCCC" d="M404.56,791.494c0.249,0.456,0.348,1.197,0.862,1.228c1.161,0.07,3.339,0.603,3.118-0.521
c-1.497-7.604-3.041-16.319-10.338-19.51c-1.129-0.493-3.675,0.235-3.806,1.797c-0.225,2.69-0.432,5.072,0.114,7.661
c0.529,2.509,4.34,2.525,5.959,0.083C402.123,785.184,402.886,788.438,404.56,791.494z"/>
<path id="path870" fill="#CCCCCC" d="M385,799.854c1.321,2.494,1.097,5.776,3.595,6.771c1.308,0.519,4.573-1.202,3.835-3.099
c-1.416-3.64-2.101-7.594-4.554-10.79c-0.353-0.463,0.071-1.403-0.212-1.982c-1.048-2.154-3.07-3.452-5.556-2.871
c-1.97,3.891,0.058,7.648,2.744,10.666C385.094,798.816,384.801,799.48,385,799.854z"/>
<path id="path874" fill="#CCCCCC" d="M315.077,790.689c-0.19-0.666-0.258-1.483,0.033-2.052c0.938-1.822,2.338-3.805,1.742-5.608
c-0.613-1.864-2.585-1.543-3.731-0.538c-2.004,1.755-2.091,4.979-3.312,7.379c-0.347,0.682-0.256,1.692-1.034,2.383
c-0.838,0.744-1.613,3.435-1.444,4.442c0.094,0.553-0.229,18.047,0.163,17.583c1.093-1.295,6.478-18.481,6.6-20.058
C314.194,792.932,315.487,792.11,315.077,790.689z"/>
<path id="path878" fill="#CCCCCC" d="M269.81,778.697c4.651-4.413,9.577-9.642,8.796-16.195c-0.205-1.723-3.339-0.792-3.669,0.701
c-1.416,6.4-5.016,11.099-9.55,15.322c-3.877,3.613-7.165,14.814-7.58,15.713C264.334,784.958,268.319,780.109,269.81,778.697z"/>
<path id="path882" fill="#CCCCCC" d="M245.843,768.167c0.923-0.653,0.39-1.521,0.773-2.106c1.683-2.574,3.979-4.773,4.012-7.844
c0.005-0.489-0.662-1.034-1.254-0.639c-0.489,0.324-1.093,0.555-1.284,0.784c-3.584,4.322-6.056,9.04-8.604,14.005
c-0.323,0.63-2.343,8.56-1.79,8.756c0.422,0.148,3.459-7.232,3.83-7.434C243.756,772.479,243.777,769.627,245.843,768.167z"/>
<path id="path886" fill="#CCCCCC" d="M275.387,802.674c0.784-1.534,3.567-3.656,3.367-5.226c-0.208-1.64,0.618-4.188-0.992-2.973
c-2.22,1.675-8.309,4.057-8.786,14.312C268.93,809.795,274.182,805.04,275.387,802.674z"/>
<path id="path890" fill="#CCCCCC" d="M300.889,772.344c0.706-1.179,1.956-0.344,2.767-0.809c1.144-0.656,2.223-1.643,2.738-2.788
c1.713-3.794,4.836-7.008,5.089-11.234c-2.634-2.479-3.831,1.121-4.944,2.825c-2.336-2.908-4.1,0.4-6.395,1.316
c-0.124,0.05-0.5-0.563-0.632-0.516c-2.078,0.776-3.279,2.687-5.041,4.064c-0.302,0.236-1.017-0.082-1.276,0.158
c-1.151,1.064-2.869,1.639-3.364,2.843c-1.959,4.78-7.504,8.479-10.835,21.795c0.672,1.604,7.966-11.728,8.826-12.959
c1.476-2.112,1.685,2.933,3.938,1.757c0.09-0.048,0.418,0.372,0.655,0.608c0.342-0.494,0.727-0.898,1.413-0.706
c0-0.706-0.237-1.688,0.118-1.969c2.184-1.726,2.036-3.61,3.413-5.801C298.166,772.324,300.039,771.055,300.889,772.344z"/>
<path id="path894" fill="#CCCCCC" d="M406.474,868.395c0,0,13.066-36.019,5.298-55.794c0,0,20.129,38.139,12.007,57.913
c0,0-0.706-18.361-7.77-27.189C416.009,843.323,408.946,865.923,406.474,868.395z"/>
<path id="path898" fill="#CCCCCC" d="M380.343,863.805c0,0,9.534-15.538-4.591-48.024c0,0-1.413,36.019-13.419,55.439
C362.333,871.22,387.405,835.554,380.343,863.805z"/>
<path id="path902" fill="#CCCCCC" d="M362.686,860.273c0,0-0.353-35.313,0.354-40.61c0,0-6.709,29.31-24.719,46.26
C338.32,865.923,363.745,844.735,362.686,860.273z"/>
<path id="path906" fill="#CCCCCC" d="M345.736,803.771c0,0,10.594,24.014-7.063,56.502c0,0,11.301-21.541,2.825-33.9
C341.498,826.373,346.089,820.369,345.736,803.771z"/>
<path id="path910" fill="#CCCCCC" d="M311.836,859.566c0,0-1.766-27.545,1.412-31.429c0,0,0.354-11.301-0.354-13.065
c0,0,7.063-10.946,7.416,2.119c0,0,2.473,13.771,7.416,21.894c0,0,6.356,9.535,6.003,20.835
C333.729,859.92,316.073,806.598,311.836,859.566z"/>
<path id="path914" fill="#CCCCCC" d="M305.479,810.835c0,0-11.653,19.069-14.831,52.616c0,0-2.472-10.947,4.237-36.372
C294.885,827.079,302.301,799.888,305.479,810.835z"/>
<path id="path918" fill="#CCCCCC" d="M266.988,845.795c0,0,8.828-9.535,11.3-18.363c0,0,6.356-27.896-4.943-12.712
c0,0,0.353,14.125-14.125,27.19C259.219,841.91,267.694,837.673,266.988,845.795z"/>
<path id="path922" fill="#CCCCCC" d="M256.748,836.967c0,0,6.003-30.723,7.416-32.135c0,0,3.178-6.003-1.766-0.354
c0,0-15.538,33.9-22.6,45.555C239.797,850.032,253.922,833.788,256.748,836.967z"/>
<path id="path926" fill="#CCCCCC" d="M246.507,807.657c0,0,20.481-39.552-18.01,6.003
C228.497,813.66,247.919,796.356,246.507,807.657z"/>
<path id="path930" fill="#CCCCCC" d="M219.316,781.879c0,0,8.475-33.193,13.065-32.842c0,0,14.479-15.891,2.825,2.825
c0,0-10.594,16.95-9.535,34.254C225.672,786.116,224.613,769.166,219.316,781.879z"/>
<path id="path934" fill="#CCCCCC" d="M802.508,761.748c0,0-21.188-17.656-25.602-23.836c0,0,23.836,32.664,23.836,45.023
C800.742,782.938,805.156,769.693,802.508,761.748z"/>
<path id="path938" fill="#CCCCCC" d="M812.219,722.904c0,0-37.078-26.484-43.259-39.728c0,0,46.79,52.086,46.79,60.031
C815.75,743.209,816.633,727.318,812.219,722.904z"/>
<path id="path942" fill="#CCCCCC" d="M842.234,450.995c0,0-21.188-14.125-23.836-10.594c0,0,18.539,11.477,22.952,26.483
C841.352,466.886,838.703,450.995,842.234,450.995z"/>
<path id="path946" fill="#CCCCCC" d="M857.242,593.13l-30.898-21.188c0,0,33.547,30.017,34.431,37.079L857.242,593.13z"/>
<path id="path950" stroke="#000000" d="M167.317,553.402l38.844,8.387"/>
<path id="path954" stroke="#000000" d="M256.041,839.438c0,0-0.883-6.181-16.773,12.358"/>
<path id="path958" stroke="#000000" d="M265.752,848.265c0,0,3.531-11.477-7.946-3.53"/>
<path id="path962" stroke="#000000" d="M361.097,863.271c0,0,2.648-19.422-17.655,3.531"/>
</svg>

After

Width:  |  Height:  |  Size: 84 KiB

16
test/svgs/yadis.svg Normal file
View file

@ -0,0 +1,16 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 83 121">
<g fill="none" stroke-linecap="round">
<g stroke-width="8">
<path d="M23,40l2,13l-13-2" stroke="#FD4"/>
<path d="M36,40l-1,13l13-3" stroke="#D25"/>
<path d="M23,80l7-11l7,11" stroke="#8CE"/>
</g>
<g stroke-width="6">
<path d="M8,4c-8,9-5,30,17,49" stroke="#FD4"/>
<path d="M79,25c-22-6-32,14-42,26" stroke="#D25"/>
<path d="M10,118c23-8,20-30,20-44" stroke="#8CE"/>
<path d="M20,48l10,12v13m0-13l10-12" stroke="#FFF"/>
</g>
<path d="M20,48l10,12v13m0-13l10-12" stroke-width="5" stroke="#000"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 633 B

6
test/svgs/yinyang.svg Normal file
View file

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<circle cx="50" cy="50" r="48" fill="none" stroke="#000"/>
<path d="M50,2a48,48 0 1 1 0,96a24 24 0 1 1 0-48a24 24 0 1 0 0-48"/>
<circle cx="50" cy="26" r="6"/>
<circle cx="50" cy="74" r="6" fill="#FFF"/>
</svg>

After

Width:  |  Height:  |  Size: 282 B

175
test/testArc.cpp Normal file
View file

@ -0,0 +1,175 @@
#include "testCommon.h"
/************************************************************************/
/* Drawing Commands */
/************************************************************************/
void tvgDrawCmds(tvg::Canvas* canvas)
{
if (!canvas) return;
//Arc Line
auto shape1 = tvg::Shape::gen();
shape1->appendArc(150, 150, 80, 10, 180, false);
shape1->stroke(255, 255, 255, 255);
shape1->stroke(2);
if (canvas->push(move(shape1)) != tvg::Result::Success) return;
auto shape2 = tvg::Shape::gen();
shape2->appendArc(400, 150, 80, 0, 300, false);
shape2->stroke(255, 255, 255, 255);
shape2->stroke(2);
if (canvas->push(move(shape2)) != tvg::Result::Success) return;
auto shape3 = tvg::Shape::gen();
shape3->appendArc(600, 150, 80, 300, 60, false);
shape3->stroke(255, 255, 255, 255);
shape3->stroke(2);
if (canvas->push(move(shape3)) != tvg::Result::Success) return;
//Pie Line
auto shape4 = tvg::Shape::gen();
shape4->appendArc(150, 400, 80, 10, 180, true);
shape4->stroke(255, 255, 255, 255);
shape4->stroke(2);
if (canvas->push(move(shape4)) != tvg::Result::Success) return;
auto shape5 = tvg::Shape::gen();
shape5->appendArc(400, 400, 80, 0, 300, true);
shape5->stroke(255, 255, 255, 255);
shape5->stroke(2);
if (canvas->push(move(shape5)) != tvg::Result::Success) return;
auto shape6 = tvg::Shape::gen();
shape6->appendArc(600, 400, 80, 300, 60, true);
shape6->stroke(255, 255, 255, 255);
shape6->stroke(2);
if (canvas->push(move(shape6)) != tvg::Result::Success) return;
//Pie Fill
auto shape7 = tvg::Shape::gen();
shape7->appendArc(150, 650, 80, 10, 180, true);
shape7->fill(255, 255, 255, 255);
shape7->stroke(255, 0, 0, 255);
shape7->stroke(2);
if (canvas->push(move(shape7)) != tvg::Result::Success) return;
auto shape8 = tvg::Shape::gen();
shape8->appendArc(400, 650, 80, 0, 300, true);
shape8->fill(255, 255, 255, 255);
shape8->stroke(255, 0, 0, 255);
shape8->stroke(2);
if (canvas->push(move(shape8)) != tvg::Result::Success) return;
auto shape9 = tvg::Shape::gen();
shape9->appendArc(600, 650, 80, 300, 60, true);
shape9->fill(255, 255, 255, 255);
shape9->stroke(255, 0, 0, 255);
shape9->stroke(2);
if (canvas->push(move(shape9)) != tvg::Result::Success) return;
}
/************************************************************************/
/* Sw Engine Test Code */
/************************************************************************/
static unique_ptr<tvg::SwCanvas> swCanvas;
void tvgSwTest(uint32_t* buffer)
{
//Create a Canvas
swCanvas = tvg::SwCanvas::gen();
swCanvas->target(buffer, WIDTH, WIDTH, HEIGHT, tvg::SwCanvas::ARGB8888);
/* Push the shape into the Canvas drawing list
When this shape is into the canvas list, the shape could update & prepare
internal data asynchronously for coming rendering.
Canvas keeps this shape node unless user call canvas->clear() */
tvgDrawCmds(swCanvas.get());
}
void drawSwView(void* data, Eo* obj)
{
if (swCanvas->draw() == tvg::Result::Success) {
swCanvas->sync();
}
}
/************************************************************************/
/* GL Engine Test Code */
/************************************************************************/
static unique_ptr<tvg::GlCanvas> glCanvas;
void initGLview(Evas_Object *obj)
{
static constexpr auto BPP = 4;
//Create a Canvas
glCanvas = tvg::GlCanvas::gen();
glCanvas->target(nullptr, WIDTH * BPP, WIDTH, HEIGHT);
/* Push the shape into the Canvas drawing list
When this shape is into the canvas list, the shape could update & prepare
internal data asynchronously for coming rendering.
Canvas keeps this shape node unless user call canvas->clear() */
tvgDrawCmds(glCanvas.get());
}
void drawGLview(Evas_Object *obj)
{
auto gl = elm_glview_gl_api_get(obj);
gl->glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
gl->glClear(GL_COLOR_BUFFER_BIT);
if (glCanvas->draw() == tvg::Result::Success) {
glCanvas->sync();
}
}
/************************************************************************/
/* Main Code */
/************************************************************************/
int main(int argc, char **argv)
{
tvg::CanvasEngine tvgEngine = tvg::CanvasEngine::Sw;
if (argc > 1) {
if (!strcmp(argv[1], "gl")) tvgEngine = tvg::CanvasEngine::Gl;
}
//Initialize ThorVG Engine
if (tvgEngine == tvg::CanvasEngine::Sw) {
cout << "tvg engine: software" << endl;
} else {
cout << "tvg engine: opengl" << endl;
}
//Threads Count
auto threads = std::thread::hardware_concurrency();
//Initialize ThorVG Engine
if (tvg::Initializer::init(tvgEngine, threads) == tvg::Result::Success) {
elm_init(argc, argv);
if (tvgEngine == tvg::CanvasEngine::Sw) {
createSwView();
} else {
createGlView();
}
elm_run();
elm_shutdown();
//Terminate ThorVG Engine
tvg::Initializer::term(tvgEngine);
} else {
cout << "engine is not supported" << endl;
}
return 0;
}

181
test/testAsync.cpp Normal file
View file

@ -0,0 +1,181 @@
#include "testCommon.h"
/************************************************************************/
/* Drawing Commands */
/************************************************************************/
#define COUNT 50
static double t1, t2, t3, t4;
static unsigned cnt = 0;
bool tvgUpdateCmds(tvg::Canvas* canvas)
{
if (!canvas) return false;
auto t = ecore_time_get();
//Explicitly clear all retained paint nodes.
if (canvas->clear() != tvg::Result::Success) {
//Logically wrong! Probably, you missed to call sync() before.
return false;
}
t1 = t;
t2 = ecore_time_get();
for (int i = 0; i < COUNT; i++) {
auto shape = tvg::Shape::gen();
float x = rand() % (WIDTH/2);
float y = rand() % (HEIGHT/2);
float w = 1 + rand() % (int)(WIDTH * 1.3 / 2);
float h = 1 + rand() % (int)(HEIGHT * 1.3 / 2);
shape->appendRect(x, y, w, h, 0, 0);
//LinearGradient
auto fill = tvg::LinearGradient::gen();
fill->linear(x, y, x + w, y + h);
//Gradient Color Stops
tvg::Fill::ColorStop colorStops[3];
colorStops[0] = {0, uint8_t(rand() % 255), uint8_t(rand() % 255), uint8_t(rand() % 255), 255};
colorStops[1] = {1, uint8_t(rand() % 255), uint8_t(rand() % 255), uint8_t(rand() % 255), 255};
colorStops[2] = {2, uint8_t(rand() % 255), uint8_t(rand() % 255), uint8_t(rand() % 255), 255};
fill->colorStops(colorStops, 3);
shape->fill(move(fill));
if (canvas->push(move(shape)) != tvg::Result::Success) {
//Did you call clear()? Make it sure if canvas is on rendering
break;
}
}
t3 = ecore_time_get();
return true;
}
/************************************************************************/
/* Sw Engine Test Code */
/************************************************************************/
static unique_ptr<tvg::SwCanvas> swCanvas;
void tvgSwTest(uint32_t* buffer)
{
//Create a Canvas
swCanvas = tvg::SwCanvas::gen();
swCanvas->target(buffer, WIDTH, WIDTH, HEIGHT, tvg::SwCanvas::ARGB8888);
}
Eina_Bool animSwCb(void* data)
{
if (!tvgUpdateCmds(swCanvas.get())) return ECORE_CALLBACK_RENEW;
//Drawing task can be performed asynchronously.
if (swCanvas->draw() != tvg::Result::Success) return false;
//Update Efl Canvas
Eo* img = (Eo*) data;
evas_object_image_pixels_dirty_set(img, EINA_TRUE);
evas_object_image_data_update_add(img, 0, 0, WIDTH, HEIGHT);
return ECORE_CALLBACK_RENEW;
}
void drawSwView(void* data, Eo* obj)
{
//Make it guarantee finishing drawing task.
swCanvas->sync();
t4 = ecore_time_get();
printf("[%5d]: total[%fms] = clear[%fms], update[%fms], render[%fms]\n", ++cnt, t4 - t1, t2 - t1, t3 - t2, t4 - t3);
}
/************************************************************************/
/* GL Engine Test Code */
/************************************************************************/
static unique_ptr<tvg::GlCanvas> glCanvas;
void initGLview(Evas_Object *obj)
{
static constexpr auto BPP = 4;
//Create a Canvas
glCanvas = tvg::GlCanvas::gen();
glCanvas->target(nullptr, WIDTH * BPP, WIDTH, HEIGHT);
}
void drawGLview(Evas_Object *obj)
{
auto gl = elm_glview_gl_api_get(obj);
gl->glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
gl->glClear(GL_COLOR_BUFFER_BIT);
glCanvas->sync();
}
Eina_Bool animGlCb(void* data)
{
if (!tvgUpdateCmds(glCanvas.get())) return ECORE_CALLBACK_RENEW;
//Drawing task can be performed asynchronously.
glCanvas->draw();
return ECORE_CALLBACK_RENEW;
}
/************************************************************************/
/* Main Code */
/************************************************************************/
int main(int argc, char **argv)
{
tvg::CanvasEngine tvgEngine = tvg::CanvasEngine::Sw;
if (argc > 1) {
if (!strcmp(argv[1], "gl")) tvgEngine = tvg::CanvasEngine::Gl;
}
//Initialize ThorVG Engine
if (tvgEngine == tvg::CanvasEngine::Sw) {
cout << "tvg engine: software" << endl;
} else {
cout << "tvg engine: opengl" << endl;
}
//Threads Count
auto threads = std::thread::hardware_concurrency();
//Initialize ThorVG Engine
if (tvg::Initializer::init(tvgEngine, threads) == tvg::Result::Success) {
elm_init(argc, argv);
if (tvgEngine == tvg::CanvasEngine::Sw) {
auto view = createSwView();
evas_object_image_pixels_get_callback_set(view, drawSwView, nullptr);
ecore_animator_add(animSwCb, view);
} else {
auto view = createGlView();
ecore_animator_add(animGlCb, view);
}
elm_run();
elm_shutdown();
//Terminate ThorVG Engine
tvg::Initializer::term(tvgEngine);
} else {
cout << "engine is not supported" << endl;
}
return 0;
}

Some files were not shown because too many files have changed in this diff Show more